diff options
Diffstat (limited to 'logd/LogTimes.cpp')
-rw-r--r-- | logd/LogTimes.cpp | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp new file mode 100644 index 0000000..e7e3ec2 --- /dev/null +++ b/logd/LogTimes.cpp @@ -0,0 +1,242 @@ +/* + * 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 <sys/prctl.h> + +#include "FlushCommand.h" +#include "LogBuffer.h" +#include "LogTimes.h" +#include "LogReader.h" + +pthread_mutex_t LogTimeEntry::timesLock = PTHREAD_MUTEX_INITIALIZER; + +const struct timespec LogTimeEntry::EPOCH = { 0, 1 }; + +LogTimeEntry::LogTimeEntry(LogReader &reader, SocketClient *client, + bool nonBlock, unsigned long tail, + unsigned int logMask, pid_t pid, + log_time start) + : mRefCount(1) + , mRelease(false) + , mError(false) + , threadRunning(false) + , threadTriggered(true) + , mReader(reader) + , mLogMask(logMask) + , mPid(pid) + , skipAhead(0) + , mCount(0) + , mTail(tail) + , mIndex(0) + , mClient(client) + , mStart(start) + , mNonBlock(nonBlock) + , mEnd(CLOCK_MONOTONIC) +{ } + +void LogTimeEntry::startReader_Locked(void) { + pthread_attr_t attr; + + threadRunning = true; + + 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; + } + } + pthread_attr_destroy(&attr); + } + threadRunning = false; + if (mClient) { + mClient->decRef(); + } + decRef_Locked(); +} + +void LogTimeEntry::threadStop(void *obj) { + LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj); + + lock(); + + me->threadRunning = false; + if (me->mNonBlock) { + me->error_Locked(); + } + + SocketClient *client = me->mClient; + + if (me->isError_Locked()) { + LogReader &reader = me->mReader; + LastLogTimes × = reader.logbuf().mTimes; + + LastLogTimes::iterator it = times.begin(); + while(it != times.end()) { + if (*it == me) { + times.erase(it); + me->release_Locked(); + break; + } + it++; + } + + me->mClient = NULL; + reader.release(client); + } + + if (client) { + client->decRef(); + } + + me->decRef_Locked(); + + unlock(); +} + +void *LogTimeEntry::threadStart(void *obj) { + prctl(PR_SET_NAME, "logd.reader.per"); + + LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj); + + pthread_cleanup_push(threadStop, obj); + + SocketClient *client = me->mClient; + if (!client) { + me->error(); + pthread_exit(NULL); + } + + LogBuffer &logbuf = me->mReader.logbuf(); + + bool privileged = FlushCommand::hasReadLogs(client); + + lock(); + + me->threadTriggered = true; + + while(me->threadTriggered && !me->isError_Locked()) { + + me->threadTriggered = false; + + log_time start = me->mStart; + + unlock(); + + if (me->mTail) { + logbuf.flushTo(client, start, privileged, FilterFirstPass, me); + } + start = logbuf.flushTo(client, start, privileged, FilterSecondPass, me); + + if (start == LogBufferElement::FLUSH_ERROR) { + me->error(); + } + + if (me->mNonBlock) { + lock(); + break; + } + + sched_yield(); + + lock(); + } + + unlock(); + + pthread_exit(NULL); + + pthread_cleanup_pop(true); + + return NULL; +} + +// A first pass to count the number of elements +bool LogTimeEntry::FilterFirstPass(const LogBufferElement *element, void *obj) { + LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj); + + LogTimeEntry::lock(); + + if (me->mCount == 0) { + me->mStart = element->getMonotonicTime(); + } + + if ((!me->mPid || (me->mPid == element->getPid())) + && (me->mLogMask & (1 << element->getLogId()))) { + ++me->mCount; + } + + LogTimeEntry::unlock(); + + return false; +} + +// A second pass to send the selected elements +bool LogTimeEntry::FilterSecondPass(const LogBufferElement *element, void *obj) { + LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj); + + LogTimeEntry::lock(); + + if (me->skipAhead) { + me->skipAhead--; + goto skip; + } + + me->mStart = element->getMonotonicTime(); + + // Truncate to close race between first and second pass + if (me->mNonBlock && me->mTail && (me->mIndex >= me->mCount)) { + goto skip; + } + + if ((me->mLogMask & (1 << element->getLogId())) == 0) { + goto skip; + } + + if (me->mPid && (me->mPid != element->getPid())) { + goto skip; + } + + if (me->isError_Locked()) { + goto skip; + } + + 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) { + LogTimeEntry::unlock(); + return true; + } + // FALLTHRU + +skip: + LogTimeEntry::unlock(); + return false; +} |