Merge changes I9a357a3e,I7cfc6393,I69148243

* changes:
  logd: start cleaning up LogReaderThread
  logd: use std::function and lambdas where appropriate
  logd: rename LogTimes -> LogReaderThread
This commit is contained in:
Tom Cherry 2020-05-06 20:34:42 +00:00 committed by Gerrit Code Review
commit 72fdec1777
13 changed files with 452 additions and 471 deletions

View file

@ -36,9 +36,9 @@ cc_library_static {
"CommandListener.cpp",
"LogListener.cpp",
"LogReader.cpp",
"LogReaderThread.cpp",
"LogBuffer.cpp",
"LogBufferElement.cpp",
"LogTimes.cpp",
"LogStatistics.cpp",
"LogWhiteBlackList.cpp",
"libaudit.c",

View file

@ -277,7 +277,7 @@ int LogAudit::logPrint(const char* fmt, ...) {
: LOGGER_ENTRY_MAX_PAYLOAD;
size_t message_len = str_len + sizeof(android_log_event_string_t);
log_mask_t notify = 0;
unsigned int notify = 0;
if (events) { // begin scope for event buffer
uint32_t buffer[(message_len + sizeof(uint32_t) - 1) / sizeof(uint32_t)];

View file

@ -93,16 +93,16 @@ void LogBuffer::init() {
}
// Release any sleeping reader threads to dump their current content.
LogTimeEntry::wrlock();
LogReaderThread::wrlock();
LastLogTimes::iterator times = mTimes.begin();
while (times != mTimes.end()) {
LogTimeEntry* entry = times->get();
LogReaderThread* entry = times->get();
entry->triggerReader_Locked();
times++;
}
LogTimeEntry::unlock();
LogReaderThread::unlock();
}
LogBuffer::LogBuffer(LastLogTimes* times, LogTags* tags, PruneList* prune)
@ -579,14 +579,14 @@ class LogBufferElementLast {
// If the selected reader is blocking our pruning progress, decide on
// what kind of mitigation is necessary to unblock the situation.
void LogBuffer::kickMe(LogTimeEntry* me, log_id_t id, unsigned long pruneRows) {
void LogBuffer::kickMe(LogReaderThread* me, log_id_t id, unsigned long pruneRows) {
if (stats.sizes(id) > (2 * log_buffer_size(id))) { // +100%
// A misbehaving or slow reader has its connection
// dropped if we hit too much memory pressure.
android::prdebug("Kicking blocked reader, pid %d, from LogBuffer::kickMe()\n",
me->mClient->getPid());
me->client()->getPid());
me->release_Locked();
} else if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
} else if (me->timeout().tv_sec || me->timeout().tv_nsec) {
// Allow a blocked WRAP timeout reader to
// trigger and start reporting the log data.
me->triggerReader_Locked();
@ -594,7 +594,7 @@ void LogBuffer::kickMe(LogTimeEntry* me, log_id_t id, unsigned long pruneRows) {
// tell slow reader to skip entries to catch up
android::prdebug(
"Skipping %lu entries from slow reader, pid %d, from LogBuffer::kickMe()\n",
pruneRows, me->mClient->getPid());
pruneRows, me->client()->getPid());
me->triggerSkip_Locked(id, pruneRows);
}
}
@ -647,20 +647,19 @@ void LogBuffer::kickMe(LogTimeEntry* me, log_id_t id, unsigned long pruneRows) {
// LogBuffer::wrlock() must be held when this function is called.
//
bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
LogTimeEntry* oldest = nullptr;
LogReaderThread* oldest = nullptr;
bool busy = false;
bool clearAll = pruneRows == ULONG_MAX;
LogTimeEntry::rdlock();
LogReaderThread::rdlock();
// Region locked?
LastLogTimes::iterator times = mTimes.begin();
while (times != mTimes.end()) {
LogTimeEntry* entry = times->get();
if (entry->isWatching(id) &&
(!oldest || (oldest->mStart > entry->mStart) ||
((oldest->mStart == entry->mStart) &&
(entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec)))) {
LogReaderThread* entry = times->get();
if (entry->IsWatching(id) && (!oldest || oldest->start() > entry->start() ||
(oldest->start() == entry->start() &&
(entry->timeout().tv_sec || entry->timeout().tv_nsec)))) {
oldest = entry;
}
times++;
@ -681,7 +680,7 @@ bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
continue;
}
if (oldest && oldest->mStart <= element->getSequence()) {
if (oldest && oldest->start() <= element->getSequence()) {
busy = true;
kickMe(oldest, id, pruneRows);
break;
@ -692,7 +691,7 @@ bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
break;
}
}
LogTimeEntry::unlock();
LogReaderThread::unlock();
return busy;
}
@ -772,7 +771,7 @@ bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
while (it != mLogElements.end()) {
LogBufferElement* element = *it;
if (oldest && oldest->mStart <= element->getSequence()) {
if (oldest && oldest->start() <= element->getSequence()) {
busy = true;
// Do not let chatty eliding trigger any reader mitigation
break;
@ -914,7 +913,7 @@ bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
continue;
}
if (oldest && oldest->mStart <= element->getSequence()) {
if (oldest && oldest->start() <= element->getSequence()) {
busy = true;
if (!whitelist) kickMe(oldest, id, pruneRows);
break;
@ -942,7 +941,7 @@ bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
continue;
}
if (oldest && oldest->mStart <= element->getSequence()) {
if (oldest && oldest->start() <= element->getSequence()) {
busy = true;
kickMe(oldest, id, pruneRows);
break;
@ -953,7 +952,7 @@ bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
}
}
LogTimeEntry::unlock();
LogReaderThread::unlock();
return (pruneRows > 0) && busy;
}
@ -976,20 +975,20 @@ bool LogBuffer::clear(log_id_t id, uid_t uid) {
// readers and let the clear run (below) deal with determining
// if we are still blocked and return an error code to caller.
if (busy) {
LogTimeEntry::wrlock();
LogReaderThread::wrlock();
LastLogTimes::iterator times = mTimes.begin();
while (times != mTimes.end()) {
LogTimeEntry* entry = times->get();
LogReaderThread* entry = times->get();
// Killer punch
if (entry->isWatching(id)) {
if (entry->IsWatching(id)) {
android::prdebug(
"Kicking blocked reader, pid %d, from LogBuffer::clear()\n",
entry->mClient->getPid());
entry->client()->getPid());
entry->release_Locked();
}
times++;
}
LogTimeEntry::unlock();
LogReaderThread::unlock();
}
}
wrlock();
@ -1033,7 +1032,7 @@ unsigned long LogBuffer::getSize(log_id_t id) {
uint64_t LogBuffer::flushTo(SocketClient* reader, uint64_t start, pid_t* lastTid, bool privileged,
bool security,
int (*filter)(const LogBufferElement* element, void* arg), void* arg) {
const std::function<int(const LogBufferElement* element)>& filter) {
LogBufferElementCollection::iterator it;
uid_t uid = reader->getUid();
@ -1071,7 +1070,7 @@ uint64_t LogBuffer::flushTo(SocketClient* reader, uint64_t start, pid_t* lastTid
// NB: calling out to another object with wrlock() held (safe)
if (filter) {
int ret = (*filter)(element, arg);
int ret = filter(element);
if (ret == false) {
continue;
}

View file

@ -27,9 +27,9 @@
#include <sysutils/SocketClient.h>
#include "LogBufferElement.h"
#include "LogReaderThread.h"
#include "LogStatistics.h"
#include "LogTags.h"
#include "LogTimes.h"
#include "LogWhiteBlackList.h"
//
@ -115,8 +115,7 @@ class LogBuffer {
uint64_t flushTo(SocketClient* writer, uint64_t start,
pid_t* lastTid, // &lastTid[LOG_ID_MAX] or nullptr
bool privileged, bool security,
int (*filter)(const LogBufferElement* element, void* arg) = nullptr,
void* arg = nullptr);
const std::function<int(const LogBufferElement* element)>& filter);
bool clear(log_id_t id, uid_t uid = AID_ROOT);
unsigned long getSize(log_id_t id);
@ -152,7 +151,7 @@ class LogBuffer {
static constexpr size_t maxPrune = 256;
void maybePrune(log_id_t id);
void kickMe(LogTimeEntry* me, log_id_t id, unsigned long pruneRows);
void kickMe(LogReaderThread* me, log_id_t id, unsigned long pruneRows);
bool prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
LogBufferElementCollection::iterator erase(

View file

@ -819,7 +819,7 @@ int LogKlog::log(const char* buf, ssize_t len) {
// notify readers
if (rc > 0) {
reader->notifyNewLog(static_cast<log_mask_t>(1 << LOG_ID_KERNEL));
reader->notifyNewLog(static_cast<unsigned int>(1 << LOG_ID_KERNEL));
}
return rc;

View file

@ -108,7 +108,7 @@ bool LogListener::onDataAvailable(SocketClient* cli) {
int res = logbuf->log(logId, header->realtime, cred->uid, cred->pid, header->tid, msg,
((size_t)n <= UINT16_MAX) ? (uint16_t)n : UINT16_MAX);
if (res > 0) {
reader->notifyNewLog(static_cast<log_mask_t>(1 << logId));
reader->notifyNewLog(static_cast<unsigned int>(1 << logId));
}
return true;

View file

@ -39,20 +39,20 @@ LogReader::LogReader(LogBuffer* logbuf)
// When we are notified a new log entry is available, inform
// listening sockets who are watching this entry's log id.
void LogReader::notifyNewLog(log_mask_t log_mask) {
void LogReader::notifyNewLog(unsigned int log_mask) {
LastLogTimes& times = mLogbuf.mTimes;
LogTimeEntry::wrlock();
LogReaderThread::wrlock();
for (const auto& entry : times) {
if (!entry->isWatchingMultiple(log_mask)) {
if (!entry->IsWatchingMultiple(log_mask)) {
continue;
}
if (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec) {
if (entry->timeout().tv_sec || entry->timeout().tv_nsec) {
continue;
}
entry->triggerReader_Locked();
}
LogTimeEntry::unlock();
LogReaderThread::unlock();
}
// Note returning false will release the SocketClient instance.
@ -74,15 +74,15 @@ bool LogReader::onDataAvailable(SocketClient* cli) {
// Clients are only allowed to send one command, disconnect them if they
// send another.
LogTimeEntry::wrlock();
LogReaderThread::wrlock();
for (const auto& entry : mLogbuf.mTimes) {
if (entry->mClient == cli) {
if (entry->client() == cli) {
entry->release_Locked();
LogTimeEntry::unlock();
LogReaderThread::unlock();
return false;
}
}
LogTimeEntry::unlock();
LogReaderThread::unlock();
unsigned long tail = 0;
static const char _tail[] = " tail=";
@ -137,8 +137,8 @@ bool LogReader::onDataAvailable(SocketClient* cli) {
if (!fastcmp<strncmp>(buffer, "dumpAndClose", 12)) {
// Allow writer to get some cycles, and wait for pending notifications
sched_yield();
LogTimeEntry::wrlock();
LogTimeEntry::unlock();
LogReaderThread::wrlock();
LogReaderThread::unlock();
sched_yield();
nonBlock = true;
}
@ -149,56 +149,37 @@ bool LogReader::onDataAvailable(SocketClient* cli) {
uint64_t sequence = 1;
// Convert realtime to sequence number
if (start != log_time::EPOCH) {
class LogFindStart {
const pid_t mPid;
const unsigned mLogMask;
bool startTimeSet;
const log_time start;
uint64_t& sequence;
uint64_t last;
bool isMonotonic;
public:
LogFindStart(unsigned logMask, pid_t pid, log_time start, uint64_t& sequence,
bool isMonotonic)
: mPid(pid),
mLogMask(logMask),
startTimeSet(false),
start(start),
sequence(sequence),
last(sequence),
isMonotonic(isMonotonic) {}
static int callback(const LogBufferElement* element, void* obj) {
LogFindStart* me = reinterpret_cast<LogFindStart*>(obj);
if ((!me->mPid || (me->mPid == element->getPid())) &&
(me->mLogMask & (1 << element->getLogId()))) {
if (me->start == element->getRealTime()) {
me->sequence = element->getSequence();
me->startTimeSet = true;
return -1;
} else if (!me->isMonotonic || android::isMonotonic(element->getRealTime())) {
if (me->start < element->getRealTime()) {
me->sequence = me->last;
me->startTimeSet = true;
return -1;
}
me->last = element->getSequence();
} else {
me->last = element->getSequence();
}
}
return false;
bool start_time_set = false;
bool is_monotonic = logbuf().isMonotonic() && android::isMonotonic(start);
uint64_t last = sequence;
auto log_find_start = [pid, logMask, start, is_monotonic, &sequence, &start_time_set,
&last](const LogBufferElement* element) -> int {
if (pid && pid != element->getPid()) {
return 0;
}
if ((logMask & (1 << element->getLogId())) == 0) {
return 0;
}
if (start == element->getRealTime()) {
sequence = element->getSequence();
start_time_set = true;
return -1;
} else if (!is_monotonic || android::isMonotonic(element->getRealTime())) {
if (start < element->getRealTime()) {
sequence = last;
start_time_set = true;
return -1;
}
last = element->getSequence();
} else {
last = element->getSequence();
}
return 0;
};
bool found() { return startTimeSet; }
} logFindStart(logMask, pid, start, sequence,
logbuf().isMonotonic() && android::isMonotonic(start));
logbuf().flushTo(cli, sequence, nullptr, privileged, can_read_security, log_find_start);
logbuf().flushTo(cli, sequence, nullptr, privileged, can_read_security,
logFindStart.callback, &logFindStart);
if (!logFindStart.found()) {
if (!start_time_set) {
if (nonBlock) {
doSocketDelete(cli);
return false;
@ -217,11 +198,12 @@ bool LogReader::onDataAvailable(SocketClient* cli) {
timeout = 0;
}
LogTimeEntry::wrlock();
auto entry = std::make_unique<LogTimeEntry>(*this, cli, nonBlock, tail, logMask, pid, start,
sequence, timeout, privileged, can_read_security);
LogReaderThread::wrlock();
auto entry =
std::make_unique<LogReaderThread>(*this, cli, nonBlock, tail, logMask, pid, start,
sequence, timeout, privileged, can_read_security);
if (!entry->startReader_Locked()) {
LogTimeEntry::unlock();
LogReaderThread::unlock();
return false;
}
@ -234,24 +216,24 @@ bool LogReader::onDataAvailable(SocketClient* cli) {
setsockopt(cli->getSocket(), SOL_SOCKET, SO_SNDTIMEO, (const char*)&t,
sizeof(t));
LogTimeEntry::unlock();
LogReaderThread::unlock();
return true;
}
void LogReader::doSocketDelete(SocketClient* cli) {
LastLogTimes& times = mLogbuf.mTimes;
LogTimeEntry::wrlock();
LogReaderThread::wrlock();
LastLogTimes::iterator it = times.begin();
while (it != times.end()) {
LogTimeEntry* entry = it->get();
if (entry->mClient == cli) {
LogReaderThread* entry = it->get();
if (entry->client() == cli) {
entry->release_Locked();
break;
}
it++;
}
LogTimeEntry::unlock();
LogReaderThread::unlock();
}
int LogReader::getLogSocket() {

View file

@ -14,12 +14,11 @@
* limitations under the License.
*/
#ifndef _LOGD_LOG_WRITER_H__
#define _LOGD_LOG_WRITER_H__
#pragma once
#include <sysutils/SocketListener.h>
#include "LogTimes.h"
#include "LogReaderThread.h"
#define LOGD_SNDTIMEO 32
@ -30,7 +29,7 @@ class LogReader : public SocketListener {
public:
explicit LogReader(LogBuffer* logbuf);
void notifyNewLog(log_mask_t logMask);
void notifyNewLog(unsigned int logMask);
LogBuffer& logbuf(void) const {
return mLogbuf;
@ -44,5 +43,3 @@ class LogReader : public SocketListener {
void doSocketDelete(SocketClient* cli);
};
#endif

240
logd/LogReaderThread.cpp Normal file
View file

@ -0,0 +1,240 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "LogReaderThread.h"
#include <errno.h>
#include <string.h>
#include <sys/prctl.h>
#include <thread>
#include "LogBuffer.h"
#include "LogReader.h"
using namespace std::placeholders;
pthread_mutex_t LogReaderThread::timesLock = PTHREAD_MUTEX_INITIALIZER;
LogReaderThread::LogReaderThread(LogReader& reader, SocketClient* client, bool non_block,
unsigned long tail, unsigned int log_mask, pid_t pid,
log_time start_time, uint64_t start, uint64_t timeout,
bool privileged, bool can_read_security_logs)
: leading_dropped_(false),
reader_(reader),
log_mask_(log_mask),
pid_(pid),
tail_(tail),
count_(0),
index_(0),
client_(client),
start_time_(start_time),
start_(start),
non_block_(non_block),
privileged_(privileged),
can_read_security_logs_(can_read_security_logs) {
timeout_.tv_sec = timeout / NS_PER_SEC;
timeout_.tv_nsec = timeout % NS_PER_SEC;
memset(last_tid_, 0, sizeof(last_tid_));
pthread_cond_init(&thread_triggered_condition_, nullptr);
cleanSkip_Locked();
}
bool LogReaderThread::startReader_Locked() {
auto thread = std::thread{&LogReaderThread::ThreadFunction, this};
thread.detach();
return true;
}
void LogReaderThread::ThreadFunction() {
prctl(PR_SET_NAME, "logd.reader.per");
SocketClient* client = client_;
LogBuffer& logbuf = reader_.logbuf();
leading_dropped_ = true;
wrlock();
uint64_t start = start_;
while (!release_) {
if (timeout_.tv_sec || timeout_.tv_nsec) {
if (pthread_cond_clockwait(&thread_triggered_condition_, &timesLock, CLOCK_MONOTONIC,
&timeout_) == ETIMEDOUT) {
timeout_.tv_sec = 0;
timeout_.tv_nsec = 0;
}
if (release_) {
break;
}
}
unlock();
if (tail_) {
logbuf.flushTo(client, start, nullptr, privileged_, can_read_security_logs_,
std::bind(&LogReaderThread::FilterFirstPass, this, _1));
leading_dropped_ =
true; // TODO: Likely a bug, if leading_dropped_ was not true before calling
// flushTo(), then it should not be reset to true after.
}
start = logbuf.flushTo(client, start, last_tid_, privileged_, can_read_security_logs_,
std::bind(&LogReaderThread::FilterSecondPass, this, _1));
// We only ignore entries before the original start time for the first flushTo(), if we
// get entries after this first flush before the original start time, then the client
// wouldn't have seen them.
// Note: this is still racy and may skip out of order events that came in since the last
// time the client disconnected and then reconnected with the new start time. The long term
// solution here is that clients must request events since a specific sequence number.
start_time_.tv_sec = 0;
start_time_.tv_nsec = 0;
wrlock();
if (start == LogBufferElement::FLUSH_ERROR) {
break;
}
start_ = start + 1;
if (non_block_ || release_) {
break;
}
cleanSkip_Locked();
if (!timeout_.tv_sec && !timeout_.tv_nsec) {
pthread_cond_wait(&thread_triggered_condition_, &timesLock);
}
}
LogReader& reader = reader_;
reader.release(client);
client->decRef();
LastLogTimes& times = reader.logbuf().mTimes;
auto it = std::find_if(times.begin(), times.end(),
[this](const auto& other) { return other.get() == this; });
if (it != times.end()) {
times.erase(it);
}
unlock();
}
// A first pass to count the number of elements
int LogReaderThread::FilterFirstPass(const LogBufferElement* element) {
LogReaderThread::wrlock();
if (leading_dropped_) {
if (element->getDropped()) {
LogReaderThread::unlock();
return false;
}
leading_dropped_ = false;
}
if (count_ == 0) {
start_ = element->getSequence();
}
if ((!pid_ || pid_ == element->getPid()) && IsWatching(element->getLogId()) &&
(start_time_ == log_time::EPOCH || start_time_ <= element->getRealTime())) {
++count_;
}
LogReaderThread::unlock();
return false;
}
// A second pass to send the selected elements
int LogReaderThread::FilterSecondPass(const LogBufferElement* element) {
LogReaderThread::wrlock();
start_ = element->getSequence();
if (skip_ahead_[element->getLogId()]) {
skip_ahead_[element->getLogId()]--;
goto skip;
}
if (leading_dropped_) {
if (element->getDropped()) {
goto skip;
}
leading_dropped_ = false;
}
// Truncate to close race between first and second pass
if (non_block_ && tail_ && index_ >= count_) {
goto stop;
}
if (!IsWatching(element->getLogId())) {
goto skip;
}
if (pid_ && pid_ != element->getPid()) {
goto skip;
}
if (start_time_ != log_time::EPOCH && element->getRealTime() <= start_time_) {
goto skip;
}
if (release_) {
goto stop;
}
if (!tail_) {
goto ok;
}
++index_;
if (count_ > tail_ && index_ <= (count_ - tail_)) {
goto skip;
}
if (!non_block_) {
tail_ = 0;
}
ok:
if (!skip_ahead_[element->getLogId()]) {
LogReaderThread::unlock();
return true;
}
// FALLTHRU
skip:
LogReaderThread::unlock();
return false;
stop:
LogReaderThread::unlock();
return -1;
}
void LogReaderThread::cleanSkip_Locked(void) {
memset(skip_ahead_, 0, sizeof(skip_ahead_));
}

126
logd/LogReaderThread.h Normal file
View file

@ -0,0 +1,126 @@
/*
* Copyright (C) 2012-2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <pthread.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <list>
#include <memory>
#include <log/log.h>
#include <sysutils/SocketClient.h>
class LogReader;
class LogBufferElement;
class LogReaderThread {
static pthread_mutex_t timesLock;
public:
LogReaderThread(LogReader& reader, SocketClient* client, bool non_block, unsigned long tail,
unsigned int log_mask, pid_t pid, log_time start_time, uint64_t sequence,
uint64_t timeout, bool privileged, bool can_read_security_logs);
// Protect List manipulations
static void wrlock() { pthread_mutex_lock(&timesLock); }
static void rdlock() { pthread_mutex_lock(&timesLock); }
static void unlock() { pthread_mutex_unlock(&timesLock); }
bool startReader_Locked();
void triggerReader_Locked() { pthread_cond_signal(&thread_triggered_condition_); }
void triggerSkip_Locked(log_id_t id, unsigned int skip) { skip_ahead_[id] = skip; }
void cleanSkip_Locked();
void release_Locked() {
// gracefully shut down the socket.
shutdown(client_->getSocket(), SHUT_RDWR);
release_ = true;
pthread_cond_signal(&thread_triggered_condition_);
}
bool IsWatching(log_id_t id) const { return log_mask_ & (1 << id); }
bool IsWatchingMultiple(unsigned int log_mask) const { return log_mask_ & log_mask; }
const SocketClient* client() const { return client_; }
uint64_t start() const { return start_; }
const timespec& timeout() const { return timeout_; }
private:
void ThreadFunction();
// flushTo filter callbacks
int FilterFirstPass(const LogBufferElement* element);
int FilterSecondPass(const LogBufferElement* element);
// Set to true to cause the thread to end and the LogReaderThread to delete itself.
bool release_ = false;
// Indicates whether or not 'leading' (first logs seen starting from start_) 'dropped' (chatty)
// messages should be ignored.
bool leading_dropped_;
// Condition variable for waking the reader thread if there are messages pending for its client.
pthread_cond_t thread_triggered_condition_;
// Reference to the parent thread that manages log reader sockets.
LogReader& reader_;
// A mask of the logs buffers that are read by this reader.
const unsigned int log_mask_;
// If set to non-zero, only pids equal to this are read by the reader.
const pid_t pid_;
// When a reader is referencing (via start_) old elements in the log buffer, and the log
// buffer's size grows past its memory limit, the log buffer may request the reader to skip
// ahead a specified number of logs.
unsigned int skip_ahead_[LOG_ID_MAX];
// Used for distinguishing 'dropped' messages for duplicate logs vs chatty drops
pid_t last_tid_[LOG_ID_MAX];
// These next three variables are used for reading only the most recent lines aka `adb logcat
// -t` / `adb logcat -T`.
// tail_ is the number of most recent lines to print.
unsigned long tail_;
// count_ is the result of a first pass through the log buffer to determine how many total
// messages there are.
unsigned long count_;
// index_ is used along with count_ to only start sending lines once index_ > (count_ - tail_)
// and to disconnect the reader (if it is dumpAndClose, `adb logcat -t`), when index_ >= count_.
unsigned long index_;
// A pointer to the socket for this reader.
SocketClient* client_;
// When a reader requests logs starting from a given timestamp, its stored here for the first
// pass, such that logs before this time stamp that are accumulated in the buffer are ignored.
log_time start_time_;
// The point from which the reader will read logs once awoken.
uint64_t start_;
// CLOCK_MONOTONIC based timeout used for log wrapping. If this timeout expires before logs
// wrap, then wake up and send the logs to the reader anyway.
timespec timeout_;
// If this reader is 'dumpAndClose' and will disconnect once it has read its intended logs.
const bool non_block_;
// Whether or not this reader can read logs from all UIDs or only its own UID. See
// clientHasLogCredentials().
bool privileged_;
// Whether or not this reader can read security logs. See CanReadSecurityLogs().
bool can_read_security_logs_;
};
typedef std::list<std::unique_ptr<LogReaderThread>> LastLogTimes;

View file

@ -1,253 +0,0 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <errno.h>
#include <string.h>
#include <sys/prctl.h>
#include "LogBuffer.h"
#include "LogReader.h"
#include "LogTimes.h"
pthread_mutex_t LogTimeEntry::timesLock = PTHREAD_MUTEX_INITIALIZER;
LogTimeEntry::LogTimeEntry(LogReader& reader, SocketClient* client, bool nonBlock,
unsigned long tail, log_mask_t logMask, pid_t pid, log_time start_time,
uint64_t start, uint64_t timeout, bool privileged,
bool can_read_security_logs)
: leadingDropped(false),
mReader(reader),
mLogMask(logMask),
mPid(pid),
mCount(0),
mTail(tail),
mIndex(0),
mClient(client),
mStartTime(start_time),
mStart(start),
mNonBlock(nonBlock),
privileged_(privileged),
can_read_security_logs_(can_read_security_logs) {
mTimeout.tv_sec = timeout / NS_PER_SEC;
mTimeout.tv_nsec = timeout % NS_PER_SEC;
memset(mLastTid, 0, sizeof(mLastTid));
pthread_cond_init(&threadTriggeredCondition, nullptr);
cleanSkip_Locked();
}
bool LogTimeEntry::startReader_Locked() {
pthread_attr_t attr;
if (!pthread_attr_init(&attr)) {
if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
if (!pthread_create(&mThread, &attr, LogTimeEntry::threadStart,
this)) {
pthread_attr_destroy(&attr);
return true;
}
}
pthread_attr_destroy(&attr);
}
return false;
}
void* LogTimeEntry::threadStart(void* obj) {
prctl(PR_SET_NAME, "logd.reader.per");
LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
SocketClient* client = me->mClient;
LogBuffer& logbuf = me->mReader.logbuf();
me->leadingDropped = true;
wrlock();
uint64_t start = me->mStart;
while (!me->mRelease) {
if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
if (pthread_cond_clockwait(&me->threadTriggeredCondition, &timesLock, CLOCK_MONOTONIC,
&me->mTimeout) == ETIMEDOUT) {
me->mTimeout.tv_sec = 0;
me->mTimeout.tv_nsec = 0;
}
if (me->mRelease) {
break;
}
}
unlock();
if (me->mTail) {
logbuf.flushTo(client, start, nullptr, me->privileged_, me->can_read_security_logs_,
FilterFirstPass, me);
me->leadingDropped = true;
}
start = logbuf.flushTo(client, start, me->mLastTid, me->privileged_,
me->can_read_security_logs_, FilterSecondPass, me);
// We only ignore entries before the original start time for the first flushTo(), if we
// get entries after this first flush before the original start time, then the client
// wouldn't have seen them.
// Note: this is still racy and may skip out of order events that came in since the last
// time the client disconnected and then reconnected with the new start time. The long term
// solution here is that clients must request events since a specific sequence number.
me->mStartTime.tv_sec = 0;
me->mStartTime.tv_nsec = 0;
wrlock();
if (start == LogBufferElement::FLUSH_ERROR) {
break;
}
me->mStart = start + 1;
if (me->mNonBlock || me->mRelease) {
break;
}
me->cleanSkip_Locked();
if (!me->mTimeout.tv_sec && !me->mTimeout.tv_nsec) {
pthread_cond_wait(&me->threadTriggeredCondition, &timesLock);
}
}
LogReader& reader = me->mReader;
reader.release(client);
client->decRef();
LastLogTimes& times = reader.logbuf().mTimes;
auto it =
std::find_if(times.begin(), times.end(),
[&me](const auto& other) { return other.get() == me; });
if (it != times.end()) {
times.erase(it);
}
unlock();
return nullptr;
}
// A first pass to count the number of elements
int LogTimeEntry::FilterFirstPass(const LogBufferElement* element, void* obj) {
LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
LogTimeEntry::wrlock();
if (me->leadingDropped) {
if (element->getDropped()) {
LogTimeEntry::unlock();
return false;
}
me->leadingDropped = false;
}
if (me->mCount == 0) {
me->mStart = element->getSequence();
}
if ((!me->mPid || me->mPid == element->getPid()) && me->isWatching(element->getLogId()) &&
(me->mStartTime == log_time::EPOCH || me->mStartTime <= element->getRealTime())) {
++me->mCount;
}
LogTimeEntry::unlock();
return false;
}
// A second pass to send the selected elements
int LogTimeEntry::FilterSecondPass(const LogBufferElement* element, void* obj) {
LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
LogTimeEntry::wrlock();
me->mStart = element->getSequence();
if (me->skipAhead[element->getLogId()]) {
me->skipAhead[element->getLogId()]--;
goto skip;
}
if (me->leadingDropped) {
if (element->getDropped()) {
goto skip;
}
me->leadingDropped = false;
}
// Truncate to close race between first and second pass
if (me->mNonBlock && me->mTail && (me->mIndex >= me->mCount)) {
goto stop;
}
if (!me->isWatching(element->getLogId())) {
goto skip;
}
if (me->mPid && (me->mPid != element->getPid())) {
goto skip;
}
if (me->mStartTime != log_time::EPOCH && element->getRealTime() <= me->mStartTime) {
goto skip;
}
if (me->mRelease) {
goto stop;
}
if (!me->mTail) {
goto ok;
}
++me->mIndex;
if ((me->mCount > me->mTail) && (me->mIndex <= (me->mCount - me->mTail))) {
goto skip;
}
if (!me->mNonBlock) {
me->mTail = 0;
}
ok:
if (!me->skipAhead[element->getLogId()]) {
LogTimeEntry::unlock();
return true;
}
// FALLTHRU
skip:
LogTimeEntry::unlock();
return false;
stop:
LogTimeEntry::unlock();
return -1;
}
void LogTimeEntry::cleanSkip_Locked(void) {
memset(skipAhead, 0, sizeof(skipAhead));
}

View file

@ -1,109 +0,0 @@
/*
* Copyright (C) 2012-2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _LOGD_LOG_TIMES_H__
#define _LOGD_LOG_TIMES_H__
#include <pthread.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <list>
#include <memory>
#include <log/log.h>
#include <sysutils/SocketClient.h>
typedef unsigned int log_mask_t;
class LogReader;
class LogBufferElement;
class LogTimeEntry {
static pthread_mutex_t timesLock;
bool mRelease = false;
bool leadingDropped;
pthread_cond_t threadTriggeredCondition;
pthread_t mThread;
LogReader& mReader;
static void* threadStart(void* me);
const log_mask_t mLogMask;
const pid_t mPid;
unsigned int skipAhead[LOG_ID_MAX];
pid_t mLastTid[LOG_ID_MAX];
unsigned long mCount;
unsigned long mTail;
unsigned long mIndex;
public:
LogTimeEntry(LogReader& reader, SocketClient* client, bool nonBlock, unsigned long tail,
log_mask_t logMask, pid_t pid, log_time start_time, uint64_t sequence,
uint64_t timeout, bool privileged, bool can_read_security_logs);
SocketClient* mClient;
log_time mStartTime;
uint64_t mStart;
struct timespec mTimeout; // CLOCK_MONOTONIC based timeout used for log wrapping.
const bool mNonBlock;
// Protect List manipulations
static void wrlock(void) {
pthread_mutex_lock(&timesLock);
}
static void rdlock(void) {
pthread_mutex_lock(&timesLock);
}
static void unlock(void) {
pthread_mutex_unlock(&timesLock);
}
bool startReader_Locked();
void triggerReader_Locked(void) {
pthread_cond_signal(&threadTriggeredCondition);
}
void triggerSkip_Locked(log_id_t id, unsigned int skip) {
skipAhead[id] = skip;
}
void cleanSkip_Locked(void);
void release_Locked(void) {
// gracefully shut down the socket.
shutdown(mClient->getSocket(), SHUT_RDWR);
mRelease = true;
pthread_cond_signal(&threadTriggeredCondition);
}
bool isWatching(log_id_t id) const {
return mLogMask & (1 << id);
}
bool isWatchingMultiple(log_mask_t logMask) const {
return mLogMask & logMask;
}
// flushTo filter callbacks
static int FilterFirstPass(const LogBufferElement* element, void* me);
static int FilterSecondPass(const LogBufferElement* element, void* me);
private:
bool privileged_;
bool can_read_security_logs_;
};
typedef std::list<std::unique_ptr<LogTimeEntry>> LastLogTimes;
#endif // _LOGD_LOG_TIMES_H__

View file

@ -16,7 +16,7 @@
#include <string>
#include "../LogBuffer.h"
#include "../LogTimes.h"
#include "../LogReaderThread.h"
// We don't want to waste a lot of entropy on messages
#define MAX_MSG_LENGTH 5