【framework】EventLog分析

 

【framework】EventLog分析

分类: android-analysis   2485人阅读  评论(3)  收藏  举报
在frameworks/base的源码中看到几个很奇怪的EventLogTags.logtags文件,丫竟然还能作为src参与编译,百思不得其解之下只好埋头看源码,记录如下。

system/core/logcat/event.logtags文件是说明每个tag的格式的以及部分测试用的log tag,说明如下:
[python]  view plain copy
  1. 1 # The entries in this file map a sparse set of log tag numbers to tag names.  
  2.   2 # This is installed on the device, in /system/etc, and parsed by logcat.  
  3.   3 #  
  4.   4 # Tag numbers are decimal integers, from 0 to 2^31.  (Let's leave the  
  5.   5 # negative values alone for now.)  
  6.   6 # Tag numbers是十进制的整数,取值从0到2^31  
  7.   7 # Tag names are one or more ASCII letters and numbers or underscores, i.e.  
  8.   8 # "[A-Z][a-z][0-9]_".  Do not include spaces or punctuation (the former  
  9.   9 # impacts log readability, the latter makes regex searches more annoying).  
  10. 10 # Tag names由1到多个ASCII码的字母和下划线组成,为了方便在log中搜索,name中避免使用空格和标点  
  11. 11 # Tag numbers and names are separated by whitespace.  Blank lines and lines  
  12. 12 # starting with '#' are ignored.  
  13. 13 # Tag numbers和names之间用空格隔开,空白和#开头的行会被忽略  
  14. 14 # Optionally, after the tag names can be put a description for the value(s)  
  15. 15 # of the tag. Description are in the format  
  16. 16 #    (<name>|data type[|data unit])   
  17.     #  根据需要,tag names后面可以加上这个tag的values来描述这个tag的打印格式。  
  18. 17 # Multiple values are separated by commas.  
  19. 18 # values之间用逗号隔开,每个value的格式如下  
  20. 19 # The data type is a number from the following values:  
  21. 20 # 1: int  
  22. 21 # 2: long  
  23. 22 # 3: string  
  24. 23 # 4: list  
  25. 24 # "data unit"表示数据格式,相当于data的单位:  
  26. 25 # The data unit is a number taken from the following list:  
  27. 26 # 1: Number of objects  
  28. 27 # 2: Number of bytes  
  29. 28 # 3: Number of milliseconds  
  30. 29 # 4: Number of allocations  
  31. 30 # 5: Id  
  32. 31 # 6: Percent  
  33. 32 # Default value for data of type int/long is 2 (bytes).  
  34. 33 #  
  35. 34 # TODO: generate ".java" and ".h" files with integer constants from this file.  
  36.          ... ...  
  37. 137 # NOTE - the range 1000000-2000000 is reserved for partners and others who  
  38. 138 # want to define their own log tags without conflicting with the core platform.  
  39. # 1000000-2000000的tag number是留给合作厂商或者其他开发者用户扩展用的。  

根据上面的说明文档,分析frameworks/base/services/java/com/android/server/am/EventLogTags.logtags里面的一个tag:

# A service has crashed too many times, it is being stopped
30034 am_service_crashed_too_much (Crash Count|1|1),(Component Name|3),(PID|1|5)

Tag Number: 30034
Tag name: am_service_crashed_too_much
value 1: name="Crash Count", data_type=1->int, data_unit=1->"Number of objects"、
value 2: name="Component Name", data_type=3->string
value 3: name="PID", data_type=1->int, data_unit=5->"Id"

因此在logcat中打印出来的效果为:
I/am_service_crashed_too_much(  359): [2,com.stone.weather/.StoneWeatherService,22234]

/build/tools/java-event-log-tags.py负责将EventLogTags.logtags以及调用转化为java文件,或者是将java文件中的writeEvent调用转为标准的java调用,以及生成system/etc/event-log-tags文件。(对于使用来说,分析这个python文件没有多大意义)

转换以后的.java文件,仍以上述的EventLogTags.logtags为例,生成的文件在out目录中:
out/target/common/obj/JAVA_LIBRARIES/services_intermediates/src/com/android/server/am/EventLogTags.java

节选相关代码:

[java]  view plain copy
  1. package com.android.server.am;  
  2.   
  3. public class EventLogTags {  
  4.     private EventLogTags() { }  // don't instantiate  
  5.   
  6.     /** 30034 am_service_crashed_too_much (Crash Count|1|1),(Component Name|3),(PID|1|5) */  
  7.     public static final int AM_SERVICE_CRASHED_TOO_MUCH = 30034;  
  8.   
  9.     public static void writeAmServiceCrashedTooMuch(int crashCount, String componentName, int pid) {  
  10.         android.util.EventLog.writeEvent(AM_SERVICE_CRASHED_TOO_MUCH, crashCount, componentName, pid);  
  11.     }  
  12. }  

可以看出多了一个静态常量和一个API可以调用,所以在源码里面可以这样调用(android源码的做法):
EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH,
                            sr.crashCount, sr.shortName, app.pid);
这里只是用了AM_SERVICE_CRASHED_TOO_MUCH这个常量而没有调专用API,或者这样用(测试可以编译通过):
EventLog.writeAmServiceCrashedTooMuch(sr.crashCount, sr.shortName, app.pid);

这个调用通过如下的路径最终写到了/dev/log/events中:
EventLog.writeEvent() [android.util.EventLog]
android_util_EventLog_writeEvent_Array() [libandroid_runtime.so]
android_bWriteLog() -> __android_log_bwrite() [liblog.so]
write_to_log(LOG_ID_EVENTS, vec, 2) -> __write_to_log_kernel() [liblog.so]

====================================================

下面看看logcat -b events是怎么读取并显示event log的:

在system/core/logcat/logcat.cpp的main函数里头,如果参数带-b events的话,needBinary会是true,然后回去读取event tag maps:

android::g_eventTagMap = android_openEventTagMap(EVENT_TAG_MAP_FILE);
其中是EVENT_TAG_MAP_FILE就是system/etc/event-log-tags

接着是例行的android::readLogLines(devices),这个函数是个大循环,源源不断的读取/dev/logs/*文件里的内容并输出到屏幕或文件中,首先使用select和FD_SET/FD_ISSET进行异步读取文件。

结合上图,android使用简单的链表管理log,一个log_device_t结构对应main/radio/events/system之一,每个device又对应一个entry链表,每个entry表示一条完整log信息。根据源码中的注释,每次读取至多4k字节的log并填到entry的buf中,kernel driver会确保每次都会得到一个完整的entry。

对于event log来说,由于读取到的是binary的格式,所以还要“解码”才能得到可读的日志:
printNextEntry(dev) -> processBuffer(dev, &dev->queue->entry) -> android_log_processBinaryLogBuffer(...)

这里又会遇到另外一个结构:
[cpp]  view plain copy
  1. typedef struct AndroidLogEntry_t {  
  2.     time_t tv_sec;  
  3.     long tv_nsec;  
  4.     android_LogPriority priority;  
  5.     pid_t pid;  
  6.     pthread_t tid;  
  7.     const char * tag;  
  8.     size_t messageLen;  
  9.     const char * message;  
  10. } AndroidLogEntry;  

android_log_processBinaryLogBuffer的作用就是解析queue->entry里面的原始数据然后填到这个AndroidLogEntry里面(下面的entry类型都是AndroidLogEntry),最重要的就是tag和message两个成员
tagIndex = get4LE(eventData);
entry->tag =  android_lookupEventTag(map, tagIndex);
两步之后,就从map(也就是上面提到的g_eventTagMap )表中查找到tag。

至于message则是通过android_log_printBinaryEvent(...)解析出来的,这个函数使用了递归的方式来解析list类型的日志:

[cpp]  view plain copy
  1. static int android_log_printBinaryEvent(const unsigned char** pEventData,  
  2.     size_t* pEventDataLen, char** pOutBuf, size_t* pOutBufLen){  
  3.     type = *eventData++; // 第一个字节表示value的type,有下面四种类型  
  4.     switch (type) {  
  5.         case EVENT_TYPE_INT:  
  6.         // 对于int型,直接读取4个字节并转换为int再输出到pOutBuf中。  
  7.             ival = get4LE(eventData);  
  8.             outCount = snprintf(outBuf, outBufLen, "%d", ival);  
  9.         case EVENT_TYPE_LONG:  
  10.         // 对于long型,差不多的手段  
  11.             lval = get8LE(eventData);  
  12.             outCount = snprintf(outBuf, outBufLen, "%lld", lval);  
  13.         case EVENT_TYPE_STRING:  
  14.         // 对于string型,紧跟的四个字节代表字串长度,然后紧跟字串内容  
  15.             strLen = get4LE(eventData);  
  16.             memcpy(outBuf, eventData, strLen); // 实际上能copy多少就看pOutBufLen剩多少空间了  
  17.         case EVENT_TYPE_LIST:  
  18.         // 对于list类,第一个字节是list中value的个数,所以value个数不能超过256吧  
  19.             count = *eventData++;  
  20.         // 递归调用android_log_printBinaryEvent()  
  21.             for(i=0;i<count;i++){  
  22.                 result = android_log_printBinaryEvent(&eventData, &eventDataLen,  
  23.                         &outBuf, &outBufLen);  
  24.                     ... ...  
  25.             }  
  26.     }  
  27. }  

可知logger_entry->msg[0]的结构如下,以string类型的log为例,单位为字节:
|--tag(4)--|--type(1)--|--str_len(4)--|--str_data(str_len)--|

解析之后的输出过程,这里就略过了。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值