logd: start cleaning up LogReaderThread
1) We can use real member functions with std::thread and
std::function, so use those instead of the 'me' pointer.
2) Don't expose member variables directly.
3) Rename and document member variables, since all of their references
are being touched anyway.
Test: logging unit tests
Change-Id: I9a357a3ea8691433d58687c95356b984b83e9c36
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index d9cc0db..37067bd 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -277,7 +277,7 @@
: 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)];
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 4fe14e7..a7323e8 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -584,9 +584,9 @@
// 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 @@
// 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);
}
}
@@ -657,10 +657,9 @@
LastLogTimes::iterator times = mTimes.begin();
while (times != mTimes.end()) {
LogReaderThread* entry = times->get();
- if (entry->isWatching(id) &&
- (!oldest || (oldest->mStart > entry->mStart) ||
- ((oldest->mStart == entry->mStart) &&
- (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec)))) {
+ 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 @@
continue;
}
- if (oldest && oldest->mStart <= element->getSequence()) {
+ if (oldest && oldest->start() <= element->getSequence()) {
busy = true;
kickMe(oldest, id, pruneRows);
break;
@@ -772,7 +771,7 @@
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 @@
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 @@
continue;
}
- if (oldest && oldest->mStart <= element->getSequence()) {
+ if (oldest && oldest->start() <= element->getSequence()) {
busy = true;
kickMe(oldest, id, pruneRows);
break;
@@ -981,10 +980,10 @@
while (times != mTimes.end()) {
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++;
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index edd326a..5788ada 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -819,7 +819,7 @@
// 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;
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index ba61042..138ab28 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -108,7 +108,7 @@
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;
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index 8816269..4702de5 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -39,15 +39,15 @@
// 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;
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();
@@ -76,7 +76,7 @@
// send another.
LogReaderThread::wrlock();
for (const auto& entry : mLogbuf.mTimes) {
- if (entry->mClient == cli) {
+ if (entry->client() == cli) {
entry->release_Locked();
LogReaderThread::unlock();
return false;
@@ -227,7 +227,7 @@
LastLogTimes::iterator it = times.begin();
while (it != times.end()) {
LogReaderThread* entry = it->get();
- if (entry->mClient == cli) {
+ if (entry->client() == cli) {
entry->release_Locked();
break;
}
diff --git a/logd/LogReader.h b/logd/LogReader.h
index e50ca8e..f00cc21 100644
--- a/logd/LogReader.h
+++ b/logd/LogReader.h
@@ -29,7 +29,7 @@
public:
explicit LogReader(LogBuffer* logbuf);
- void notifyNewLog(log_mask_t logMask);
+ void notifyNewLog(unsigned int logMask);
LogBuffer& logbuf(void) const {
return mLogbuf;
diff --git a/logd/LogReaderThread.cpp b/logd/LogReaderThread.cpp
index 8efc282..5413c4d 100644
--- a/logd/LogReaderThread.cpp
+++ b/logd/LogReaderThread.cpp
@@ -20,6 +20,8 @@
#include <string.h>
#include <sys/prctl.h>
+#include <thread>
+
#include "LogBuffer.h"
#include "LogReader.h"
@@ -28,82 +30,71 @@
pthread_mutex_t LogReaderThread::timesLock = PTHREAD_MUTEX_INITIALIZER;
LogReaderThread::LogReaderThread(LogReader& reader, SocketClient* client, bool non_block,
- unsigned long tail, log_mask_t log_mask, pid_t pid,
+ 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)
- : leadingDropped(false),
- mReader(reader),
- mLogMask(log_mask),
- mPid(pid),
- mCount(0),
- mTail(tail),
- mIndex(0),
- mClient(client),
- mStartTime(start_time),
- mStart(start),
- mNonBlock(non_block),
+ : 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) {
- mTimeout.tv_sec = timeout / NS_PER_SEC;
- mTimeout.tv_nsec = timeout % NS_PER_SEC;
- memset(mLastTid, 0, sizeof(mLastTid));
- pthread_cond_init(&threadTriggeredCondition, nullptr);
+ 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() {
- pthread_attr_t attr;
-
- if (!pthread_attr_init(&attr)) {
- if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
- if (!pthread_create(&mThread, &attr, LogReaderThread::threadStart, this)) {
- pthread_attr_destroy(&attr);
- return true;
- }
- }
- pthread_attr_destroy(&attr);
- }
-
- return false;
+ auto thread = std::thread{&LogReaderThread::ThreadFunction, this};
+ thread.detach();
+ return true;
}
-void* LogReaderThread::threadStart(void* obj) {
+void LogReaderThread::ThreadFunction() {
prctl(PR_SET_NAME, "logd.reader.per");
- LogReaderThread* me = reinterpret_cast<LogReaderThread*>(obj);
+ SocketClient* client = client_;
- SocketClient* client = me->mClient;
+ LogBuffer& logbuf = reader_.logbuf();
- LogBuffer& logbuf = me->mReader.logbuf();
-
- me->leadingDropped = true;
+ leading_dropped_ = true;
wrlock();
- uint64_t start = me->mStart;
+ uint64_t start = start_;
- while (!me->mRelease) {
- if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
- if (pthread_cond_clockwait(&me->threadTriggeredCondition, ×Lock, CLOCK_MONOTONIC,
- &me->mTimeout) == ETIMEDOUT) {
- me->mTimeout.tv_sec = 0;
- me->mTimeout.tv_nsec = 0;
+ while (!release_) {
+ if (timeout_.tv_sec || timeout_.tv_nsec) {
+ if (pthread_cond_clockwait(&thread_triggered_condition_, ×Lock, CLOCK_MONOTONIC,
+ &timeout_) == ETIMEDOUT) {
+ timeout_.tv_sec = 0;
+ timeout_.tv_nsec = 0;
}
- if (me->mRelease) {
+ if (release_) {
break;
}
}
unlock();
- if (me->mTail) {
- logbuf.flushTo(client, start, nullptr, me->privileged_, me->can_read_security_logs_,
- std::bind(&LogReaderThread::FilterFirstPass, _1, me));
- me->leadingDropped = true;
+ 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, me->mLastTid, me->privileged_,
- me->can_read_security_logs_,
- std::bind(&LogReaderThread::FilterSecondPass, _1, me));
+ 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
@@ -111,8 +102,8 @@
// 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;
+ start_time_.tv_sec = 0;
+ start_time_.tv_nsec = 0;
wrlock();
@@ -120,58 +111,54 @@
break;
}
- me->mStart = start + 1;
+ start_ = start + 1;
- if (me->mNonBlock || me->mRelease) {
+ if (non_block_ || release_) {
break;
}
- me->cleanSkip_Locked();
+ cleanSkip_Locked();
- if (!me->mTimeout.tv_sec && !me->mTimeout.tv_nsec) {
- pthread_cond_wait(&me->threadTriggeredCondition, ×Lock);
+ if (!timeout_.tv_sec && !timeout_.tv_nsec) {
+ pthread_cond_wait(&thread_triggered_condition_, ×Lock);
}
}
- LogReader& reader = me->mReader;
+ LogReader& reader = reader_;
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; });
+ [this](const auto& other) { return other.get() == this; });
if (it != times.end()) {
times.erase(it);
}
unlock();
-
- return nullptr;
}
// A first pass to count the number of elements
-int LogReaderThread::FilterFirstPass(const LogBufferElement* element, void* obj) {
- LogReaderThread* me = reinterpret_cast<LogReaderThread*>(obj);
-
+int LogReaderThread::FilterFirstPass(const LogBufferElement* element) {
LogReaderThread::wrlock();
- if (me->leadingDropped) {
+ if (leading_dropped_) {
if (element->getDropped()) {
LogReaderThread::unlock();
return false;
}
- me->leadingDropped = false;
+ leading_dropped_ = false;
}
- if (me->mCount == 0) {
- me->mStart = element->getSequence();
+ if (count_ == 0) {
+ start_ = element->getSequence();
}
- if ((!me->mPid || me->mPid == element->getPid()) && me->isWatching(element->getLogId()) &&
- (me->mStartTime == log_time::EPOCH || me->mStartTime <= element->getRealTime())) {
- ++me->mCount;
+ if ((!pid_ || pid_ == element->getPid()) && IsWatching(element->getLogId()) &&
+ (start_time_ == log_time::EPOCH || start_time_ <= element->getRealTime())) {
+ ++count_;
}
LogReaderThread::unlock();
@@ -180,62 +167,60 @@
}
// A second pass to send the selected elements
-int LogReaderThread::FilterSecondPass(const LogBufferElement* element, void* obj) {
- LogReaderThread* me = reinterpret_cast<LogReaderThread*>(obj);
-
+int LogReaderThread::FilterSecondPass(const LogBufferElement* element) {
LogReaderThread::wrlock();
- me->mStart = element->getSequence();
+ start_ = element->getSequence();
- if (me->skipAhead[element->getLogId()]) {
- me->skipAhead[element->getLogId()]--;
+ if (skip_ahead_[element->getLogId()]) {
+ skip_ahead_[element->getLogId()]--;
goto skip;
}
- if (me->leadingDropped) {
+ if (leading_dropped_) {
if (element->getDropped()) {
goto skip;
}
- me->leadingDropped = false;
+ leading_dropped_ = false;
}
// Truncate to close race between first and second pass
- if (me->mNonBlock && me->mTail && (me->mIndex >= me->mCount)) {
+ if (non_block_ && tail_ && index_ >= count_) {
goto stop;
}
- if (!me->isWatching(element->getLogId())) {
+ if (!IsWatching(element->getLogId())) {
goto skip;
}
- if (me->mPid && (me->mPid != element->getPid())) {
+ if (pid_ && pid_ != element->getPid()) {
goto skip;
}
- if (me->mStartTime != log_time::EPOCH && element->getRealTime() <= me->mStartTime) {
+ if (start_time_ != log_time::EPOCH && element->getRealTime() <= start_time_) {
goto skip;
}
- if (me->mRelease) {
+ if (release_) {
goto stop;
}
- if (!me->mTail) {
+ if (!tail_) {
goto ok;
}
- ++me->mIndex;
+ ++index_;
- if ((me->mCount > me->mTail) && (me->mIndex <= (me->mCount - me->mTail))) {
+ if (count_ > tail_ && index_ <= (count_ - tail_)) {
goto skip;
}
- if (!me->mNonBlock) {
- me->mTail = 0;
+ if (!non_block_) {
+ tail_ = 0;
}
ok:
- if (!me->skipAhead[element->getLogId()]) {
+ if (!skip_ahead_[element->getLogId()]) {
LogReaderThread::unlock();
return true;
}
@@ -251,5 +236,5 @@
}
void LogReaderThread::cleanSkip_Locked(void) {
- memset(skipAhead, 0, sizeof(skipAhead));
+ memset(skip_ahead_, 0, sizeof(skip_ahead_));
}
diff --git a/logd/LogReaderThread.h b/logd/LogReaderThread.h
index b6a489d..39a8b63 100644
--- a/logd/LogReaderThread.h
+++ b/logd/LogReaderThread.h
@@ -27,38 +27,17 @@
#include <log/log.h>
#include <sysutils/SocketClient.h>
-typedef unsigned int log_mask_t;
-
class LogReader;
class LogBufferElement;
class LogReaderThread {
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:
LogReaderThread(LogReader& reader, SocketClient* client, bool non_block, unsigned long tail,
- log_mask_t log_mask, pid_t pid, log_time start_time, uint64_t sequence,
+ unsigned int log_mask, 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() { pthread_mutex_lock(×Lock); }
static void rdlock() { pthread_mutex_lock(×Lock); }
@@ -66,26 +45,81 @@
bool startReader_Locked();
- void triggerReader_Locked() { pthread_cond_signal(&threadTriggeredCondition); }
+ void triggerReader_Locked() { pthread_cond_signal(&thread_triggered_condition_); }
- void triggerSkip_Locked(log_id_t id, unsigned int skip) { skipAhead[id] = skip; }
+ 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(mClient->getSocket(), SHUT_RDWR);
- mRelease = true;
- pthread_cond_signal(&threadTriggeredCondition);
+ shutdown(client_->getSocket(), SHUT_RDWR);
+ release_ = true;
+ pthread_cond_signal(&thread_triggered_condition_);
}
- bool isWatching(log_id_t id) const { return mLogMask & (1 << id); }
- bool isWatchingMultiple(log_mask_t log_mask) const { return mLogMask & log_mask; }
- // flushTo filter callbacks
- static int FilterFirstPass(const LogBufferElement* element, void* me);
- static int FilterSecondPass(const LogBufferElement* element, void* me);
+ 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_;
};