android log丢失(一)使用logd丢失log原理

来源:互联网 发布:淘宝托管多少钱一个月 编辑:程序博客网 时间:2024/06/04 20:07

之前我们分析过关于Android log机制,在这里我们再详细说下,log丢失的原理。

一、统计log

logd监听了logdw的socket来保存从log打印函数通过logdw socket传过来的log,最后会调用LogBuffer::log函数,在log函数最后会调用如下两个函数。

[cpp] view plain copy
  1. stats.add(elem);  
  2. maybePrune(log_id);  

这里的log_id就是radio,main,event等。

我们先来看LogStatistics::add函数

[cpp] view plain copy
  1. void LogStatistics::add(LogBufferElement *e) {  
  2.     log_id_t log_id = e->getLogId();  
  3.     unsigned short size = e->getMsgLen();//获取消息长度  
  4.     mSizes[log_id] += size;//对每个log_id做长度累计  
  5.     ++mElements[log_id];//对每个log_id做消息个数累计  
  6.   
  7.     mSizesTotal[log_id] += size;  
  8.     ++mElementsTotal[log_id];  
  9.   
  10.     if (log_id == LOG_ID_KERNEL) {  
  11.         return;  
  12.     }  
  13.   
  14.     uidTable[log_id].add(e->getUid(), e);  
  15.   
  16.     if (!enable) {  
  17.         return;  
  18.     }  
  19.   
  20.     pidTable.add(e->getPid(), e);  
  21.     tidTable.add(e->getTid(), e);  
  22.   
  23.     uint32_t tag = e->getTag();  
  24.     if (tag) {  
  25.         tagTable.add(tag, e);  
  26.     }  
  27. }  

这个函数,对每个log_id的消息长度做统计,消息数量也做了统计。


二、删除log判定

我们再来看下这个maybePrune函数

[cpp] view plain copy
  1. // Prune at most 10% of the log entries or 256, whichever is less.  
  2. //  
  3. // mLogElementsLock must be held when this function is called.  
  4. void LogBuffer::maybePrune(log_id_t id) {  
  5.     size_t sizes = stats.sizes(id);//每个log_id统计的所有消息长度  
  6.     unsigned long maxSize = log_buffer_size(id);//每个log_id缓存的最大值,之前在init函数里面初始化的  
  7.     if (sizes > maxSize) {  
  8.         size_t sizeOver = sizes - ((maxSize * 9) / 10);//超过90%size的那部分  
  9.         size_t elements = stats.elements(id);  
  10.         size_t minElements = elements / 10;//10%的elements  
  11.         unsigned long pruneRows = elements * sizeOver / sizes;//超过90%size的elements  
  12.         if (pruneRows <= minElements) {  
  13.             pruneRows = minElements;  
  14.         }  
  15.         if (pruneRows > 256) {  
  16.             pruneRows = 256;  
  17.         }  
  18.         prune(id, pruneRows);  
  19.     }  
  20. }  

在之前的博客中我们分析过了,每个log_id的size是如何而来的。可以通过属性获取。

这里保存elements的是mLogElements,只是保存的LogBufferElement 的指针而已,实际不会占用多大的内存。

[cpp] view plain copy
  1. typedef std::list<LogBufferElement *> LogBufferElementCollection;  
  2.   
  3. class LogBuffer {  
  4.     LogBufferElementCollection mLogElements;  

而且只有每个element被调用erase,才会被真正销毁内存。

[cpp] view plain copy
  1. LogBufferElementCollection::iterator LogBuffer::erase(  
  2.         LogBufferElementCollection::iterator it, bool engageStats) {  
  3.     LogBufferElement *e = *it;  
  4.     log_id_t id = e->getLogId();  
  5.   
  6.     LogBufferIteratorMap::iterator f = mLastWorstUid[id].find(e->getUid());  
  7.     if ((f != mLastWorstUid[id].end()) && (it == f->second)) {  
  8.         mLastWorstUid[id].erase(f);  
  9.     }  
  10.     it = mLogElements.erase(it);  
  11.     if (engageStats) {  
  12.         stats.subtract(e);  
  13.     } else {  
  14.         stats.erase(e);  
  15.     }  
  16.     delete e;//销毁内存  
  17.   
  18.     return it;  
  19. }  

所以每个log_id设定的值,不是一个缓存,而是一个警戒值。超过这个值,就要对特定log删除。


三、prune函数

prune函数主要就是删除log,在删除log的时候也做了白名单和黑名单的机制。

3.1 白名单 & 黑名单

这里我们先来看看LogBuffer的initPrune函数,这是用来设定白名单和黑名单的。

[cpp] view plain copy
  1. int initPrune(char *cp) { return mPrune.init(cp); }  

至于init这个函数我们就不看了,主要是解析字符串,把uid,pid保存下来。


那么又在哪里设定白名单和黑名单呢?我们可以通过如下命令,最后就调用了initPrune函数来设定白黑名单了。

[cpp] view plain copy
  1. int CommandListener::SetPruneListCmd::runCommand(SocketClient *cli,  
  2.                                          int argc, char **argv) {  
  3.     setname();  
  4.     if (!clientHasLogCredentials(cli)) {  
  5.         cli->sendMsg("Permission Denied");  
  6.         return 0;  
  7.     }  
  8.   
  9.     char *cp = NULL;  
  10.     for (int i = 1; i < argc; ++i) {  
  11.         char *p = cp;  
  12.         if (p) {  
  13.             cp = NULL;  
  14.             asprintf(&cp, "%s %s", p, argv[i]);  
  15.             free(p);  
  16.         } else {  
  17.             asprintf(&cp, "%s", argv[i]);  
  18.         }  
  19.     }  
  20.   
  21.     int ret = mBuf.initPrune(cp);  
  22.     free(cp);  
  23.   
  24.     if (ret) {  
  25.         cli->sendMsg("Invalid");  
  26.         return 0;  
  27.     }  
  28.   
  29.     cli->sendMsg("success");  
  30.   
  31.     return 0;  
  32. }  

而每个白名单和黑名单的匹配就是看uid和pid的。这块就不细看了。


3.2 黑名单处理 & log最多的uid处理

下面我们就来看下prune这个函数的黑名单部分处理:

[cpp] view plain copy
  1. void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {  
  2.   ......  
  3.     // prune by worst offender by uid  
  4.     bool hasBlacklist = mPrune.naughty();//这块是黑名单部分  
  5.     while (pruneRows > 0) {  
  6.         // recalculate the worst offender on every batched pass  
  7.         uid_t worst = (uid_t) -1;  
  8.         size_t worst_sizes = 0;  
  9.         size_t second_worst_sizes = 0;  
  10.   
  11.         if (worstUidEnabledForLogid(id) && mPrune.worstUidEnabled()) {  
  12.             std::unique_ptr<const UidEntry *[]> sorted = stats.sort(2, id);//得到log最多的2个uid  
  13.   
  14.             if (sorted.get()) {  
  15.                 if (sorted[0] && sorted[1]) {  
  16.                     worst_sizes = sorted[0]->getSizes();  
  17.                     // Calculate threshold as 12.5% of available storage  
  18.                     size_t threshold = log_buffer_size(id) / 8;  
  19.                     if ((worst_sizes > threshold)//大于阈值  
  20.                         // Allow time horizon to extend roughly tenfold, assume  
  21.                         // average entry length is 100 characters.  
  22.                             && (worst_sizes > (10 * sorted[0]->getDropped()))) {  
  23.                         worst = sorted[0]->getKey();//最多lod uid的size  
  24.                         second_worst_sizes = sorted[1]->getSizes();  
  25.                         if (second_worst_sizes < threshold) {  
  26.                             second_worst_sizes = threshold;  
  27.                         }  
  28.                     }  
  29.                 }  
  30.             }  
  31.         }  
  32.   
  33.         // skip if we have neither worst nor naughty filters  
  34.         if ((worst == (uid_t) -1) && !hasBlacklist) {//如果没有这样的uid,也没有黑名单,直接跳过  
  35.             break;  
  36.         }  
  37.   
  38.         bool kick = false;  
  39.         bool leading = true;  
  40.         it = mLogElements.begin();  
  41.         // Perform at least one mandatory garbage collection cycle in following  
  42.         // - clear leading chatty tags  
  43.         // - merge chatty tags  
  44.         // - check age-out of preserved logs  
  45.         bool gc = pruneRows <= 1;  
  46.         if (!gc && (worst != (uid_t) -1)) {  
  47.             LogBufferIteratorMap::iterator f = mLastWorstUid[id].find(worst);//查找这uid  
  48.             if ((f != mLastWorstUid[id].end())  
  49.                     && (f->second != mLogElements.end())) {  
  50.                 leading = false;//找到了,leading为false  
  51.                 it = f->second;  
  52.             }  
  53.         }  
  54.         static const timespec too_old = {  
  55.             EXPIRE_HOUR_THRESHOLD * 60 * 60, 0  
  56.         };  
  57.         LogBufferElementCollection::iterator lastt;  
  58.         lastt = mLogElements.end();  
  59.         --lastt;  
  60.         LogBufferElementLast last;  
  61.         while (it != mLogElements.end()) {  
  62.             LogBufferElement *e = *it;  
  63.   
  64.             if (oldest && (oldest->mStart <= e->getSequence())) {  
  65.                 break;  
  66.             }  
  67.   
  68.             if (e->getLogId() != id) {//log_id不对 continue  
  69.                 ++it;  
  70.                 continue;  
  71.             }  
  72.   
  73.             unsigned short dropped = e->getDropped();//是否被free过mMsg  
  74.   
  75.             // remove any leading drops  
  76.             if (leading && dropped) {  
  77.                 it = erase(it);  
  78.                 continue;  
  79.             }  
  80.   
  81.             // merge any drops  
  82.             if (dropped && last.merge(e, dropped)) {// 合并之前删除的element  
  83.                 it = erase(it, false);  
  84.                 continue;  
  85.             }  
  86.   
  87.             if (hasBlacklist && mPrune.naughty(e)) {//如果满足黑名单,删除这条element  
  88.                 last.clear(e);  
  89.                 it = erase(it);  
  90.                 if (dropped) {//如果当前是dropoed,直接continue  
  91.                     continue;  
  92.                 }  
  93.   
  94.                 pruneRows--;//删除log的计数  
  95.                 if (pruneRows == 0) {  
  96.                     break;  
  97.                 }  
  98.   
  99.                 if (e->getUid() == worst) {  
  100.                     kick = true;  
  101.                     if (worst_sizes < second_worst_sizes) {//最差值,小于第二个直接跳过  
  102.                         break;  
  103.                     }  
  104.                     worst_sizes -= e->getMsgLen();//最差的值累减  
  105.                 }  
  106.                 continue;  
  107.             }  
  108.   
  109.             if ((e->getRealTime() < ((*lastt)->getRealTime() - too_old))  
  110.                     || (e->getRealTime() > (*lastt)->getRealTime())) {  
  111.                 break;  
  112.             }  
  113.   
  114.             // unmerged drop message  
  115.             if (dropped) {  
  116.                 last.add(e);  
  117.                 if ((!gc && (e->getUid() == worst))  
  118.                         || (mLastWorstUid[id].find(e->getUid())  
  119.                             == mLastWorstUid[id].end())) {  
  120.                     mLastWorstUid[id][e->getUid()] = it;  
  121.                 }  
  122.                 ++it;  
  123.                 continue;  
  124.             }  
  125.   
  126.             if (e->getUid() != worst) {  
  127.                 leading = false;  
  128.                 last.clear(e);  
  129.                 ++it;  
  130.                 continue;  
  131.             }  
  132.   
  133.             pruneRows--;  
  134.             if (pruneRows == 0) {  
  135.                 break;  
  136.             }  
  137.   
  138.             kick = true;  
  139.   
  140.             unsigned short len = e->getMsgLen();  
  141.   
  142.             // do not create any leading drops  
  143.             if (leading) {  
  144.                 it = erase(it);  
  145.             } else {  
  146.                 stats.drop(e);  
  147.                 e->setDropped(1);//setDropped函数,这里就是普通的是worst这个uid的log,但不是黑名单中。就把它的消息清空  
  148.                 if (last.merge(e, 1)) {//合并  
  149.                     it = erase(it, false);//合并之后,删除现有log  
  150.                 } else {  
  151.                     last.add(e);  
  152.                     if (!gc || (mLastWorstUid[id].find(worst)  
  153.                                 == mLastWorstUid[id].end())) {  
  154.                         mLastWorstUid[id][worst] = it;  
  155.                     }  
  156.                     ++it;  
  157.                 }  
  158.             }  
  159.             if (worst_sizes < second_worst_sizes) {//最差值小于第二差就跳过  
  160.                 break;  
  161.             }  
  162.             worst_sizes -= len;  
  163.         }  
  164.         last.clear();  
  165.   
  166.         if (!kick || !mPrune.worstUidEnabled()) {  
  167.             break// the following loop will ask bad clients to skip/drop  
  168.         }  
  169.     }  
  170.   
  171. .....  

上面就是对黑名单以及log最多的那个uid的处理,我们先来看看每个LogBufferElement的setDropped函数

[cpp] view plain copy
  1. unsigned short setDropped(unsigned short value) {  
  2.     if (mMsg) {  
  3.         free(mMsg);//消息清空  
  4.         mMsg = NULL;  
  5.     }  
  6.     return mDropped = value;//第一次为1  
  7. }  

这个函数直接把消息清空了,然后把mDropped设为1,我们再来看看last.merge(e, 1)函数

[cpp] view plain copy
  1. class LogBufferElementLast {  
  2.   
  3.     typedef std::unordered_map<uint64_t, LogBufferElement *> LogBufferElementMap;  
  4.     LogBufferElementMap map;  
  5.   
  6. public:  
  7.   
  8.     bool merge(LogBufferElement *e, unsigned short dropped) {  
  9.         LogBufferElementKey key(e->getUid(), e->getPid(), e->getTid());  
  10.         LogBufferElementMap::iterator it = map.find(key.getKey());  
  11.         if (it != map.end()) {  
  12.             LogBufferElement *l = it->second;  
  13.             unsigned short d = l->getDropped();  
  14.             if ((dropped + d) > USHRT_MAX) {  
  15.                 map.erase(it);  
  16.             } else {  
  17.                 l->setDropped(dropped + d);//将两个element合并  
  18.                 return true;  
  19.             }  
  20.         }  
  21.         return false;  
  22.     }  
通过merge,element的mDropped可以不为1了。


3.3 白名单处理

下面我们再看下白名单处理:

[cpp] view plain copy
  1. bool whitelist = false;  
  2. bool hasWhitelist = mPrune.nice();  
  3. it = mLogElements.begin();  
  4. while((pruneRows > 0) && (it != mLogElements.end())) {  
  5.     LogBufferElement *e = *it;  
  6.   
  7.     if (e->getLogId() != id) {  
  8.         it++;  
  9.         continue;  
  10.     }  
  11.   
  12.     if (oldest && (oldest->mStart <= e->getSequence())) {  
  13.         if (whitelist) {  
  14.             break;  
  15.         }  
  16.   
  17.         if (stats.sizes(id) > (2 * log_buffer_size(id))) {  
  18.             // kick a misbehaving log reader client off the island  
  19.             oldest->release_Locked();  
  20.         } else {  
  21.             oldest->triggerSkip_Locked(id, pruneRows);  
  22.         }  
  23.         break;  
  24.     }  
  25.   
  26.     if (hasWhitelist && !e->getDropped() && mPrune.nice(e)) { // WhiteListed  
  27.         whitelist = true;  
  28.         it++;  
  29.         continue;  
  30.     }  
  31.   
  32.     it = erase(it);  
  33.     pruneRows--;  
  34. }  

白名单的处理比较简单,只要是白名单的不删除,其他都删除,直到满足条件。


四、logcat取log

之前的博客分析过当logcat进程到logd中取log时,会最终调用LogBufferElement::flushTo函数

[cpp] view plain copy
  1. uint64_t LogBufferElement::flushTo(SocketClient *reader, LogBuffer *parent) {  
  2.     struct logger_entry_v3 entry;  
  3.   
  4.     memset(&entry, 0, sizeof(struct logger_entry_v3));  
  5.   
  6.     entry.hdr_size = sizeof(struct logger_entry_v3);  
  7.     entry.lid = mLogId;  
  8.     entry.pid = mPid;  
  9.     entry.tid = mTid;  
  10.     entry.sec = mRealTime.tv_sec;  
  11.     entry.nsec = mRealTime.tv_nsec;  
  12.   
  13.     struct iovec iovec[2];  
  14.     iovec[0].iov_base = &entry;  
  15.     iovec[0].iov_len = sizeof(struct logger_entry_v3);  
  16.   
  17.     char *buffer = NULL;  
  18.   
  19.     if (!mMsg) {//如果mMsg为null了,就是之前prune里面setPropped函数把mMsg设为null  
  20.         entry.len = populateDroppedMessage(buffer, parent);  
  21.         if (!entry.len) {  
  22.             return mSequence;  
  23.         }  
  24.         iovec[1].iov_base = buffer;  
  25.     } else {  
  26.         entry.len = mMsgLen;  
  27.         iovec[1].iov_base = mMsg;  
  28.     }  
  29.     iovec[1].iov_len = entry.len;  
  30.   
  31.     uint64_t retval = reader->sendDatav(iovec, 2) ? FLUSH_ERROR : mSequence;//发送给调用者  
  32.   
  33.     if (buffer) {  
  34.         free(buffer);  
  35.     }  
  36.   
  37.     return retval;  
  38. }  

调用populateDroppedMessage函数最终会把消息设为类似:

[html] view plain copy
  1. chatty  : uid=1000(system) RenderThread expire 3 lines   



五、总结 & 解决方案

最后总结,在logd中如果有丢失log,可以设置log_id的缓冲设置再大写。如果是调试的话可以增加调试的白名单。而且在logd中丢失log肯定会有类似chatty这样的log,那就是删除了log最多的那个uid的log。而且会合并。


我们可以通过设置系统属性persist.logd.size来设置每个log id的最大缓存值(在开发者选项中也有这个设置,开发者选项中设置就不用重启设备了),或者persist.logd.size.radio设置每个id的最大缓存值。

步骤:


  1. 将手机连上电脑并且进入root

  2. setproppersist.logd.size.radio 1024k

  3. reboot 重启

另外可以用getprop | grep logd查看设置的属性是否生效

logcat -g 可以查看每个id 的缓存大小


当然这是通过属性的方法设置,我们还可以通过logcat的命令,logcat -G 10m是设置所有的id的大小,logcat -b radio -G 10m是设置radio的log的缓存大小

在logcat中有如下代码,处理设置缓存大小

如果logd中没有chatty这样的log,但是又有log丢失,那么就要怀疑在写log时,logdw的socket就有丢失。因为我们看下logdw是dgram类型的,这种socket是一种不可靠的报文传递保证效率但会有丢失。所有这样情况我们可以看把socket改成stream试试,看看是否有效果?

[html] view plain copy
  1. service logd /system/bin/logd  
  2.     class core  
  3.     socket logd stream 0666 logd logd  
  4.     socket logdr seqpacket 0666 logd logd  
  5.     socket logdw dgram 0222 logd logd  
  6.     group root system  
  7.      writepid /dev/cpuset/system-background/tasks  

但是试了以后好像socket在连接的时候就有问题。


后续我们使用android4.4的的机制 kernel的log机制,这样就不会有丢失问题。

原文链接:http://blog.csdn.net/kc58236582/article/details/51506896

阅读全文
0 0
原创粉丝点击