android log丢失(一)使用logd丢失log原理
之前我们分析过关于Android log机制,在这里我们再详细说下,log丢失的原理。
一、统计log
logd监听了logdw的socket来保存从log打印函数通过logdw socket传过来的log,最后会调用LogBuffer::log函数,在log函数最后会调用如下两个函数。
- stats.add(elem);
- maybePrune(log_id);
这里的log_id就是radio,main,event等。
我们先来看LogStatistics::add函数
- void LogStatistics::add(LogBufferElement *e) {
- log_id_t log_id = e->getLogId();
- unsigned short size = e->getMsgLen();//获取消息长度
- mSizes[log_id] += size;//对每个log_id做长度累计
- ++mElements[log_id];//对每个log_id做消息个数累计
- mSizesTotal[log_id] += size;
- ++mElementsTotal[log_id];
- if (log_id == LOG_ID_KERNEL) {
- return;
- }
- uidTable[log_id].add(e->getUid(), e);
- if (!enable) {
- return;
- }
- pidTable.add(e->getPid(), e);
- tidTable.add(e->getTid(), e);
- uint32_t tag = e->getTag();
- if (tag) {
- tagTable.add(tag, e);
- }
- }
这个函数,对每个log_id的消息长度做统计,消息数量也做了统计。
二、删除log判定
我们再来看下这个maybePrune函数
- // Prune at most 10% of the log entries or 256, whichever is less.
- //
- // mLogElementsLock must be held when this function is called.
- void LogBuffer::maybePrune(log_id_t id) {
- size_t sizes = stats.sizes(id);//每个log_id统计的所有消息长度
- unsigned long maxSize = log_buffer_size(id);//每个log_id缓存的最大值,之前在init函数里面初始化的
- if (sizes > maxSize) {
- size_t sizeOver = sizes - ((maxSize * 9) / 10);//超过90%size的那部分
- size_t elements = stats.elements(id);
- size_t minElements = elements / 10;//10%的elements
- unsigned long pruneRows = elements * sizeOver / sizes;//超过90%size的elements
- if (pruneRows <= minElements) {
- pruneRows = minElements;
- }
- if (pruneRows > 256) {
- pruneRows = 256;
- }
- prune(id, pruneRows);
- }
- }
在之前的博客中我们分析过了,每个log_id的size是如何而来的。可以通过属性获取。
这里保存elements的是mLogElements,只是保存的LogBufferElement 的指针而已,实际不会占用多大的内存。
- typedef std::list<LogBufferElement *> LogBufferElementCollection;
- class LogBuffer {
- LogBufferElementCollection mLogElements;
而且只有每个element被调用erase,才会被真正销毁内存。
- LogBufferElementCollection::iterator LogBuffer::erase(
- LogBufferElementCollection::iterator it, bool engageStats) {
- LogBufferElement *e = *it;
- log_id_t id = e->getLogId();
- LogBufferIteratorMap::iterator f = mLastWorstUid[id].find(e->getUid());
- if ((f != mLastWorstUid[id].end()) && (it == f->second)) {
- mLastWorstUid[id].erase(f);
- }
- it = mLogElements.erase(it);
- if (engageStats) {
- stats.subtract(e);
- } else {
- stats.erase(e);
- }
- delete e;//销毁内存
- return it;
- }
所以每个log_id设定的值,不是一个缓存,而是一个警戒值。超过这个值,就要对特定log删除。
三、prune函数
prune函数主要就是删除log,在删除log的时候也做了白名单和黑名单的机制。
3.1 白名单 & 黑名单
这里我们先来看看LogBuffer的initPrune函数,这是用来设定白名单和黑名单的。
- int initPrune(char *cp) { return mPrune.init(cp); }
至于init这个函数我们就不看了,主要是解析字符串,把uid,pid保存下来。
那么又在哪里设定白名单和黑名单呢?我们可以通过如下命令,最后就调用了initPrune函数来设定白黑名单了。
- int CommandListener::SetPruneListCmd::runCommand(SocketClient *cli,
- int argc, char **argv) {
- setname();
- if (!clientHasLogCredentials(cli)) {
- cli->sendMsg("Permission Denied");
- return 0;
- }
- char *cp = NULL;
- for (int i = 1; i < argc; ++i) {
- char *p = cp;
- if (p) {
- cp = NULL;
- asprintf(&cp, "%s %s", p, argv[i]);
- free(p);
- } else {
- asprintf(&cp, "%s", argv[i]);
- }
- }
- int ret = mBuf.initPrune(cp);
- free(cp);
- if (ret) {
- cli->sendMsg("Invalid");
- return 0;
- }
- cli->sendMsg("success");
- return 0;
- }
而每个白名单和黑名单的匹配就是看uid和pid的。这块就不细看了。
3.2 黑名单处理 & log最多的uid处理
下面我们就来看下prune这个函数的黑名单部分处理:
- void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
- ......
- // prune by worst offender by uid
- bool hasBlacklist = mPrune.naughty();//这块是黑名单部分
- while (pruneRows > 0) {
- // recalculate the worst offender on every batched pass
- uid_t worst = (uid_t) -1;
- size_t worst_sizes = 0;
- size_t second_worst_sizes = 0;
- if (worstUidEnabledForLogid(id) && mPrune.worstUidEnabled()) {
- std::unique_ptr<const UidEntry *[]> sorted = stats.sort(2, id);//得到log最多的2个uid
- if (sorted.get()) {
- if (sorted[0] && sorted[1]) {
- worst_sizes = sorted[0]->getSizes();
- // Calculate threshold as 12.5% of available storage
- size_t threshold = log_buffer_size(id) / 8;
- if ((worst_sizes > threshold)//大于阈值
- // Allow time horizon to extend roughly tenfold, assume
- // average entry length is 100 characters.
- && (worst_sizes > (10 * sorted[0]->getDropped()))) {
- worst = sorted[0]->getKey();//最多lod uid的size
- second_worst_sizes = sorted[1]->getSizes();
- if (second_worst_sizes < threshold) {
- second_worst_sizes = threshold;
- }
- }
- }
- }
- }
- // skip if we have neither worst nor naughty filters
- if ((worst == (uid_t) -1) && !hasBlacklist) {//如果没有这样的uid,也没有黑名单,直接跳过
- break;
- }
- bool kick = false;
- bool leading = true;
- it = mLogElements.begin();
- // Perform at least one mandatory garbage collection cycle in following
- // - clear leading chatty tags
- // - merge chatty tags
- // - check age-out of preserved logs
- bool gc = pruneRows <= 1;
- if (!gc && (worst != (uid_t) -1)) {
- LogBufferIteratorMap::iterator f = mLastWorstUid[id].find(worst);//查找这uid
- if ((f != mLastWorstUid[id].end())
- && (f->second != mLogElements.end())) {
- leading = false;//找到了,leading为false
- it = f->second;
- }
- }
- static const timespec too_old = {
- EXPIRE_HOUR_THRESHOLD * 60 * 60, 0
- };
- LogBufferElementCollection::iterator lastt;
- lastt = mLogElements.end();
- --lastt;
- LogBufferElementLast last;
- while (it != mLogElements.end()) {
- LogBufferElement *e = *it;
- if (oldest && (oldest->mStart <= e->getSequence())) {
- break;
- }
- if (e->getLogId() != id) {//log_id不对 continue
- ++it;
- continue;
- }
- unsigned short dropped = e->getDropped();//是否被free过mMsg
- // remove any leading drops
- if (leading && dropped) {
- it = erase(it);
- continue;
- }
- // merge any drops
- if (dropped && last.merge(e, dropped)) {// 合并之前删除的element
- it = erase(it, false);
- continue;
- }
- if (hasBlacklist && mPrune.naughty(e)) {//如果满足黑名单,删除这条element
- last.clear(e);
- it = erase(it);
- if (dropped) {//如果当前是dropoed,直接continue
- continue;
- }
- pruneRows--;//删除log的计数
- if (pruneRows == 0) {
- break;
- }
- if (e->getUid() == worst) {
- kick = true;
- if (worst_sizes < second_worst_sizes) {//最差值,小于第二个直接跳过
- break;
- }
- worst_sizes -= e->getMsgLen();//最差的值累减
- }
- continue;
- }
- if ((e->getRealTime() < ((*lastt)->getRealTime() - too_old))
- || (e->getRealTime() > (*lastt)->getRealTime())) {
- break;
- }
- // unmerged drop message
- if (dropped) {
- last.add(e);
- if ((!gc && (e->getUid() == worst))
- || (mLastWorstUid[id].find(e->getUid())
- == mLastWorstUid[id].end())) {
- mLastWorstUid[id][e->getUid()] = it;
- }
- ++it;
- continue;
- }
- if (e->getUid() != worst) {
- leading = false;
- last.clear(e);
- ++it;
- continue;
- }
- pruneRows--;
- if (pruneRows == 0) {
- break;
- }
- kick = true;
- unsigned short len = e->getMsgLen();
- // do not create any leading drops
- if (leading) {
- it = erase(it);
- } else {
- stats.drop(e);
- e->setDropped(1);//setDropped函数,这里就是普通的是worst这个uid的log,但不是黑名单中。就把它的消息清空
- if (last.merge(e, 1)) {//合并
- it = erase(it, false);//合并之后,删除现有log
- } else {
- last.add(e);
- if (!gc || (mLastWorstUid[id].find(worst)
- == mLastWorstUid[id].end())) {
- mLastWorstUid[id][worst] = it;
- }
- ++it;
- }
- }
- if (worst_sizes < second_worst_sizes) {//最差值小于第二差就跳过
- break;
- }
- worst_sizes -= len;
- }
- last.clear();
- if (!kick || !mPrune.worstUidEnabled()) {
- break; // the following loop will ask bad clients to skip/drop
- }
- }
- .....
上面就是对黑名单以及log最多的那个uid的处理,我们先来看看每个LogBufferElement的setDropped函数
- unsigned short setDropped(unsigned short value) {
- if (mMsg) {
- free(mMsg);//消息清空
- mMsg = NULL;
- }
- return mDropped = value;//第一次为1
- }
这个函数直接把消息清空了,然后把mDropped设为1,我们再来看看last.merge(e, 1)函数
- class LogBufferElementLast {
- typedef std::unordered_map<uint64_t, LogBufferElement *> LogBufferElementMap;
- LogBufferElementMap map;
- public:
- bool merge(LogBufferElement *e, unsigned short dropped) {
- LogBufferElementKey key(e->getUid(), e->getPid(), e->getTid());
- LogBufferElementMap::iterator it = map.find(key.getKey());
- if (it != map.end()) {
- LogBufferElement *l = it->second;
- unsigned short d = l->getDropped();
- if ((dropped + d) > USHRT_MAX) {
- map.erase(it);
- } else {
- l->setDropped(dropped + d);//将两个element合并
- return true;
- }
- }
- return false;
- }
通过merge,element的mDropped可以不为1了。
3.3 白名单处理
下面我们再看下白名单处理:
- bool whitelist = false;
- bool hasWhitelist = mPrune.nice();
- it = mLogElements.begin();
- while((pruneRows > 0) && (it != mLogElements.end())) {
- LogBufferElement *e = *it;
- if (e->getLogId() != id) {
- it++;
- continue;
- }
- if (oldest && (oldest->mStart <= e->getSequence())) {
- if (whitelist) {
- break;
- }
- if (stats.sizes(id) > (2 * log_buffer_size(id))) {
- // kick a misbehaving log reader client off the island
- oldest->release_Locked();
- } else {
- oldest->triggerSkip_Locked(id, pruneRows);
- }
- break;
- }
- if (hasWhitelist && !e->getDropped() && mPrune.nice(e)) { // WhiteListed
- whitelist = true;
- it++;
- continue;
- }
- it = erase(it);
- pruneRows--;
- }
白名单的处理比较简单,只要是白名单的不删除,其他都删除,直到满足条件。
四、logcat取log
之前的博客分析过当logcat进程到logd中取log时,会最终调用LogBufferElement::flushTo函数
- uint64_t LogBufferElement::flushTo(SocketClient *reader, LogBuffer *parent) {
- struct logger_entry_v3 entry;
- memset(&entry, 0, sizeof(struct logger_entry_v3));
- entry.hdr_size = sizeof(struct logger_entry_v3);
- entry.lid = mLogId;
- entry.pid = mPid;
- entry.tid = mTid;
- entry.sec = mRealTime.tv_sec;
- entry.nsec = mRealTime.tv_nsec;
- struct iovec iovec[2];
- iovec[0].iov_base = &entry;
- iovec[0].iov_len = sizeof(struct logger_entry_v3);
- char *buffer = NULL;
- if (!mMsg) {//如果mMsg为null了,就是之前prune里面setPropped函数把mMsg设为null
- entry.len = populateDroppedMessage(buffer, parent);
- if (!entry.len) {
- return mSequence;
- }
- iovec[1].iov_base = buffer;
- } else {
- entry.len = mMsgLen;
- iovec[1].iov_base = mMsg;
- }
- iovec[1].iov_len = entry.len;
- uint64_t retval = reader->sendDatav(iovec, 2) ? FLUSH_ERROR : mSequence;//发送给调用者
- if (buffer) {
- free(buffer);
- }
- return retval;
- }
调用populateDroppedMessage函数最终会把消息设为类似:
- 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的最大缓存值。
步骤:
将手机连上电脑并且进入root
setproppersist.logd.size.radio 1024k
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试试,看看是否有效果?
- service logd /system/bin/logd
- class core
- socket logd stream 0666 logd logd
- socket logdr seqpacket 0666 logd logd
- socket logdw dgram 0222 logd logd
- group root system
- writepid /dev/cpuset/system-background/tasks
但是试了以后好像socket在连接的时候就有问题。
后续我们使用android4.4的的机制 kernel的log机制,这样就不会有丢失问题。
原文地址: http://blog.csdn.net/kc58236582/article/details/51506896
android log丢失(一)使用logd丢失log原理相关推荐
- mysql redo log 数据恢复_MySQL 怎么样恢复丢失的数据?redo log 写磁盘的过程
在生活中,你一定有过类似这样的经历: 比如部门发礼品.或者说学校发课本.如果在发放的时候,大家一窝蜂的涌了过来,毕竟双拳双敌四手,渐渐你就招架不过来. 为了工作更好做,你会有几个选择,提前打印个名单, ...
- android 6.0 logcat机制(一)java层写log,logd接受log
第一篇博客,讲的主要是c++,java中打印log,然后通过socket传给logd,然后logd是如何处理接受log的. 一.logcat常用命令 logcat -c 清除已有log信息 logca ...
- android webview 设置cookie时间,解决Android webview设置cookie和cookie丢失的问题
Android页面嵌套了一个h5,H5页面内部有用户登陆页面,发现h5页面的登陆功能无法使用,一直登陆失败.和web那边商量一会,发现js写入的cookie丢失了.所有需要Android这边在重写写入 ...
- logd 删除log
进程Log过多,超过上限,导致log被删除,类似: chatty : uid=1000(system) MountService expire 168 lines logd 删除log的判断依据为 ...
- Android流媒体开发之 自定义一个完备的log模块
Android音视频开发之 自定义一个完备的log模块 前言 基础知识的掌握 Log系统 为什么需要自定义一个log模块呢? 做什么? 怎么做? 确定成员变量: 初始化LogUtil 输出功能的实现 ...
- Android studio 使用androidX后应用启动log中报Didn‘t find class “android.view.View$OnUnhandledKeyEventListener“
Didn't find class "android.view.View$OnUnhandledKeyEventListener" Android studio 使用android ...
- 如何从 Android 手机恢复已删除或丢失的照片
当 Android 上的照片被意外删除时,您一定会感到难过,不知道该怎么办,尤其是那些珍贵的照片.实际上,现在您不必太担心,因为您可以利用多种方法来恢复 Android 手机上已删除或丢失的照片.其实 ...
- android adb命令 抓取系统各种 log
android adb命令 抓取系统各种 log getLog.bat: adb root adb remount adb wait-for-device adb logcat -v time > ...
- Android L日志系统1——logd
在介绍完Android M之前的日志系统的实现之后,我们现在来看看现在最新的Android L的日志机制.Android L与之前版本最大的变化,就是日志保存的位置由Kernel的Ringer Buf ...
最新文章
- 51单片机实现对24C02进行页写、顺序读取并显示验证
- 华胜天成ivcs云系统初体验2
- Python3 基础语法(笔记2)
- 2012 RDS Remote App 对于win7的支持问题
- 检查mysql当前状态
- 从零开始入门 K8s | 应用存储和持久化数据卷:核心知识
- 【狂人小白】如何将Java项目发布到Maven中
- (创建模式 上)设计模式——工厂、抽象工厂 C++/Python3实现
- MySQL数据库和ACID模型
- (转载)WebSphere MQ安装过程
- 简单的用户登录(一)
- DXUT实战2:HLSL(withoutEffect)+D3D9+DXUT(june_2010) .
- c语言 学习手册,c语言学习手册
- 使用boost线程池很好的例子
- RFID 射频识别技术 NFC ISO14443 - A 协议 RC522
- java邮件抄送_JAVA实现邮件抄送,密送,多个附件发送
- 电信机顶盒时中心服务器异常,电信机顶盒常见故障汇总大全
- PAMI-2021:5篇顶级GNN论文
- telnet协议的Wireshark抓包分析
- 华为荣耀android是什么系统,华为荣耀+