log_time start(log_time::EPOCH); staticconstchar _start[] = " start="; cp = strstr(buffer, _start); if (cp) { // Parse errors will result in current time start.strptime(cp + sizeof(_start) - 1, "%s.%q"); }
bool nonBlock = false; if (!fastcmp<strncmp>(buffer, "dumpAndClose", 12)) { // Allow writer to get some cycles, and wait for pending notifications sched_yield(); LogTimeEntry::lock(); LogTimeEntry::unlock(); sched_yield(); nonBlock = true; }
log_time sequence = start; // // This somewhat expensive data validation operation is required // for non-blocking, with timeout. The incoming timestamp must be // in range of the list, if not, return immediately. This is // used to prevent us from from getting stuck in timeout processing // with an invalid time. // // Find if time is really present in the logs, monotonic or real, implicit // conversion from monotonic or real as necessary to perform the check. // Exit in the check loop ASAP as you find a transition from older to // newer, but use the last entry found to ensure overlap. // if (nonBlock && (sequence != log_time::EPOCH) && timeout) { classLogFindStart {// A lambda by another name private: constpid_t mPid; constunsigned mLogMask; bool mStartTimeSet; log_time mStart; // 注意,mSequence 是一个引用 log_time& mSequence; log_time mLast; bool mIsMonotonic;
// When we are notified a new log entry is available, inform // all of our listening sockets. void LogReader::notifyNewLog() { FlushCommand command(*this); runOnEachSocket(&command); }
// runSocketCommand is called once for every open client on the // log reader socket. Here we manage and associated the reader // client tracking and log region locks LastLogTimes list of // LogTimeEntrys, and spawn a transitory per-client thread to // work at filing data to the socket. // // global LogTimeEntry::lock() is used to protect access, // reference counts are used to ensure that individual // LogTimeEntry lifetime is managed when not protected. void FlushCommand::runSocketCommand(SocketClient* client) { LogTimeEntry* entry = NULL; // 每个读者都对应 times 列表里的一个元素 LastLogTimes& times = mReader.logbuf().mTimes;
LogTimeEntry::lock(); LastLogTimes::iterator it = times.begin(); while (it != times.end()) { entry = (*it); // 遍历列表,找到自己 if (entry->mClient == client) { if (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec) { // 等 timeout 结束后就会醒来,所以直接 return if (mReader.logbuf().isMonotonic()) { LogTimeEntry::unlock(); return; } // If the user changes the time in a gross manner that // invalidates the timeout, fall through and trigger. log_time now(CLOCK_REALTIME); // 如果时间被修改,可能会导致 timeout 无效,用当前时间判断 timeout 是否还有效 // 这里其实是一个bug,mEnd 是创建 entry 时的时间,而后面在使用 mTimeout 的时候,直 // 接把它传递给了 pthread_cond_timedwait,也就是说, mTimeout 也是一个绝对时间 if (((entry->mEnd + entry->mTimeout) > now) && (now > entry->mEnd)) { LogTimeEntry::unlock(); return; } } // 唤醒读 log 线程 entry->triggerReader_Locked(); if (entry->runningReader_Locked()) { // 线程已经在运行,直接返回 LogTimeEntry::unlock(); return; } // 只有在创建是 entry 后,创建线程失败才会执行到这些,后面重新尝试启动线程 entry->incRef_Locked(); break; } it++; }
if (it == times.end()) { // Create LogTimeEntry in notifyNewLog() ? if (mTail == (unsignedlong)-1) { LogTimeEntry::unlock(); return; } entry = new LogTimeEntry(mReader, client, mNonBlock, mTail, mLogMask, mPid, mStart, mTimeout); times.push_front(entry); }
client->incRef();
// release client and entry reference counts once done entry->startReader_Locked(); LogTimeEntry::unlock(); }
// A first pass to count the number of elements int LogTimeEntry::FilterFirstPass(const LogBufferElement* element, void* obj) { LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
LogTimeEntry::lock();
if (me->leadingDropped) { if (element->getDropped()) { LogTimeEntry::unlock(); returnfalse; } me->leadingDropped = false; }
if (me->mCount == 0) { me->mStart = element->getRealTime(); }
// system/core/logd/LogTimes.cpp // A second pass to send the selected elements int LogTimeEntry::FilterSecondPass(const LogBufferElement* element, void* obj) { LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
// leading drop 其实是空的 log 项 if (me->leadingDropped) { if (element->getDropped()) { goto skip; } me->leadingDropped = false; }
// Truncate to close race between first and second pass // 总共只有 mCount 条 log,mIndex >= mCount 表示已经没有更多的 log 了 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; }
// 根据 start 参数找到开始迭代的地方 if (start == log_time::EPOCH) { // client wants to start from the beginning it = mLogElements.begin(); } else { // 3 second limit to continue search for out-of-order entries. log_time min = start - pruneMargin;
// Cap to 300 iterations we look back for out-of-order entries. size_t count = 300;
// Client wants to start from some specified time. Chances are // we are better off starting from the end of the time sorted list. LogBufferElementCollection::iterator last; for (last = it = mLogElements.end(); it != mLogElements.begin(); /* do nothing */) { --it; LogBufferElement* element = *it; if (element->getRealTime() > start) { last = it; } elseif (!--count || (element->getRealTime() < min)) { break; } } it = last; }
log_time max = start;
LogBufferElement* lastElement = nullptr; // iterator corruption paranoia staticconstsize_t maxSkip = 4194304; // maximum entries to skip size_t skip = maxSkip; for (; it != mLogElements.end(); ++it) { LogBufferElement* element = *it;
if (!--skip) { android::prdebug("reader.per: too many elements skipped"); break; } if (element == lastElement) { android::prdebug("reader.per: identical elements"); break; } lastElement = element;
if (!privileged && (element->getUid() != uid)) { continue; }
if (!security && (element->getLogId() == LOG_ID_SECURITY)) { continue; }
if (element->getRealTime() <= start) { continue; }
// filter 就是我们前面多次传递进来的函数 // 1. 返回 true 表示写回该 log 项 // 2. false 表示忽略 // 3. 其他值则结束迭代 // NB: calling out to another object with mLogElementsLock held (safe) if (filter) { int ret = (*filter)(element, arg); if (ret == false) { continue; } if (ret != true) { break; } }
bool sameTid = false; if (lastTid) { sameTid = lastTid[element->getLogId()] == element->getTid(); // Dropped (chatty) immediately following a valid log from the // same source in the same log buffer indicates we have a // multiple identical squash. chatty that differs source // is due to spam filter. chatty to chatty of different // source is also due to spam filter. lastTid[element->getLogId()] = (element->getDropped() && !sameTid) ? 0 : element->getTid(); }
pthread_mutex_unlock(&mLogElementsLock);
// 这里把单个 log 项的数据写回客户端 // range locking in LastLogTimes looks after us max = element->flushTo(reader, this, privileged, sameTid);