/* * 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 #include #include #include "FlushCommand.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) : leadingDropped(false), mReader(reader), mLogMask(logMask), mPid(pid), mCount(0), mTail(tail), mIndex(0), mClient(client), mStartTime(start_time), mStart(start), mNonBlock(nonBlock) { 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(obj); SocketClient* client = me->mClient; LogBuffer& logbuf = me->mReader.logbuf(); bool privileged = FlushCommand::hasReadLogs(client); bool security = FlushCommand::hasSecurityLogs(client); 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, ×Lock, 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, privileged, security, FilterFirstPass, me); me->leadingDropped = true; } start = logbuf.flushTo(client, start, me->mLastTid, privileged, security, 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, ×Lock); } } 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(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(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)); }