这部分代码已经是最终插入的那部分代码,在这之前还有很多流程是在其它文章分析。 log按时间进行排序很重要,时间乱序的的log可能会干扰查问题。所以安卓系统对log的时间戳进行了一些特别的设计
// assumes LogBuffer::wrlock() held, owns elem, look after garbage collection
//这里假设已经加锁了
void LogBuffer::log(LogBufferElement* elem) {
? ? // cap on how far back we will sort in-place, otherwise append
? ?//log插入需要按时间排序,但是如果时间差超过一定限度too_far_back之后就不排序,直接插到队列尾部
? ? static uint32_t too_far_back = 5; ?// five seconds
? ? // Insert elements in time sorted order if possible
? ? //要尽可能的按照时间顺序插入log
? ? // ?NB: if end is region locked, place element at end of list
? ? LogBufferElementCollection::iterator it = mLogElements.end();
? ? LogBufferElementCollection::iterator last = it;
? ? if (__predict_true(it != mLogElements.begin())) --it;
? ? if (__predict_false(it == mLogElements.begin()) ||
? ? ? ? __predict_true((*it)->getRealTime() <= elem->getRealTime()) ||
? ? ? ? __predict_false((((*it)->getRealTime().tv_sec - too_far_back) >
? ? ? ? ? ? ? ? ? ? ? ? ?elem->getRealTime().tv_sec) &&
? ? ? ? ? ? ? ? ? ? ? ? (elem->getLogId() != LOG_ID_KERNEL) &&
? ? ? ? ? ? ? ? ? ? ? ? ((*it)->getLogId() != LOG_ID_KERNEL))) {
? ? ? ? mLogElements.push_back(elem);
? ? } else {
? ? ? ? log_time end(log_time::EPOCH);
????????//log_time::EPOCH = {0, 0};
? ? ? ? bool end_set = false;
? ? ? ? bool end_always = false;
? ? ? ? LogTimeEntry::rdlock();
? ? ? ? LastLogTimes::iterator times = mTimes.begin();
????????//mTimes保存的是reader客户端创建的时间
? ? ? ? while (times != mTimes.end()) {
? ? ? ? ? ? LogTimeEntry* entry = times->get();
? ? ? ? ? ? if (!entry->mNonBlock) {
? ? ? ? ? ? ? ? end_always = true;//只要有一个客户端以堵塞模式读的话就直接插入到尾部,详见后文
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? }
? ? ? ? ? ? // it passing mEnd is blocked by the following checks.
? ? ? ? ? ? if (!end_set || (end <= entry->mEnd)) {
? ? ? ? ? ? ? ? end = entry->mEnd;
? ? ? ? ? ? ? ? end_set = true;
? ? ? ? ? ? }
? ? ? ? ? ? times++;
? ? ? ? }
? ? ? ? if (end_always || (end_set && (end > (*it)->getRealTime()))) {
? ? ? ? ? ? mLogElements.push_back(elem);
? ? ? ? } else {
? ? ? ? ? ? // should be short as timestamps are localized near end()
????????????//找到要插入的位置
? ? ? ? ? ? do {
? ? ? ? ? ? ? ? last = it;
? ? ? ? ? ? ? ? if (__predict_false(it == mLogElements.begin())) {
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? --it;
? ? ? ? ? ? } while (((*it)->getRealTime() > elem->getRealTime()) &&
? ? ? ? ? ? ? ? ? ? ?(!end_set || (end <= (*it)->getRealTime())));
? ? ? ? ? ? mLogElements.insert(last, elem);
? ? ? ? }
? ? ? ? LogTimeEntry::unlock();
? ? }
? ? stats.add(elem);
? ? maybePrune(elem->getLogId());
}
客户端堵塞读的处理 源码中我们看到如果 !entry->mNonBlock 为真,即有客户端在阻塞地读取 log时,就把把新的 log 直接插入列表的末尾,没有进行排序。这主要是考虑这样一种比较极端的情况,它已经读取了所有的 log 并等待新的 log,我们又没有把新的 log 放入列表的末尾,就会导致客户端无法读取新写入的这条 log,毕竟,此时它应该读列表最后面的 log。另一种情况是,有客户端在读 log,并且它读到的最后一条 log 已经超过了我们正要写入的 log。此时最简单的做法就是把新 log 放到末尾,这样客户才能读取到新写入的 log。 其它知识点: __predict_true 和 __predict_false __predict_true 和 __predict_false 用来提示编译器对应的判断很可能是 true/false,类似于 Linux 内核的 likely/unlikely。如果判断正确,可以得到很大的性能提升。
|