【framework】EventLog分析
16lz
2021-01-23
在frameworks/base的源码中看到几个很奇怪的EventLogTags.logtags文件,丫竟然还能作为src参与编译,百思不得其解之下只好埋头看源码,记录如下。
system/core/logcat/event.logtags文件是说明每个tag的格式的以及部分测试用的log tag,说明如下:
根据上面的说明文档,分析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
节选相关代码:
可以看出多了一个静态常量和一个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(...)
这里又会遇到另外一个结构:
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类型的日志:
可知logger_entry->msg[0]的结构如下,以string类型的log为例,单位为字节: |--tag(4)--|--type(1)--|--str_len(4)--|--str_data(str_len)--|
解析之后的输出过程,这里就略过了。
system/core/logcat/event.logtags文件是说明每个tag的格式的以及部分测试用的log tag,说明如下:
1 # The entries in this file map a sparse set of log tag numbers to tag names. 2 # This is installed on the device, in /system/etc, and parsed by logcat. 3 # 4 # Tag numbers are decimal integers, from 0 to 2^31. (Let's leave the 5 # negative values alone for now.) 6 # Tag numbers是十进制的整数,取值从0到2^31 7 # Tag names are one or more ASCII letters and numbers or underscores, i.e. 8 # "[A-Z][a-z][0-9]_". Do not include spaces or punctuation (the former 9 # impacts log readability, the latter makes regex searches more annoying).10 # Tag names由1到多个ASCII码的字母和下划线组成,为了方便在log中搜索,name中避免使用空格和标点11 # Tag numbers and names are separated by whitespace. Blank lines and lines12 # starting with '#' are ignored.13 # Tag numbers和names之间用空格隔开,空白和#开头的行会被忽略14 # Optionally, after the tag names can be put a description for the value(s)15 # of the tag. Description are in the format16 # (<name>|data type[|data unit]) # 根据需要,tag names后面可以加上这个tag的values来描述这个tag的打印格式。17 # Multiple values are separated by commas.18 # values之间用逗号隔开,每个value的格式如下19 # The data type is a number from the following values:20 # 1: int21 # 2: long22 # 3: string23 # 4: list24 # "data unit"表示数据格式,相当于data的单位:25 # The data unit is a number taken from the following list:26 # 1: Number of objects27 # 2: Number of bytes28 # 3: Number of milliseconds29 # 4: Number of allocations30 # 5: Id31 # 6: Percent32 # Default value for data of type int/long is 2 (bytes).33 #34 # TODO: generate ".java" and ".h" files with integer constants from this file. ... ...137 # NOTE - the range 1000000-2000000 is reserved for partners and others who138 # want to define their own log tags without conflicting with the core platform.# 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
节选相关代码:
package com.android.server.am;public class EventLogTags { private EventLogTags() { } // don't instantiate /** 30034 am_service_crashed_too_much (Crash Count|1|1),(Component Name|3),(PID|1|5) */ public static final int AM_SERVICE_CRASHED_TOO_MUCH = 30034; public static void writeAmServiceCrashedTooMuch(int crashCount, String componentName, int pid) { android.util.EventLog.writeEvent(AM_SERVICE_CRASHED_TOO_MUCH, crashCount, componentName, pid); }}
可以看出多了一个静态常量和一个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(...)
这里又会遇到另外一个结构:
typedef struct AndroidLogEntry_t { time_t tv_sec; long tv_nsec; android_LogPriority priority; pid_t pid; pthread_t tid; const char * tag; size_t messageLen; const char * message;} 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类型的日志:
static int android_log_printBinaryEvent(const unsigned char** pEventData, size_t* pEventDataLen, char** pOutBuf, size_t* pOutBufLen){ type = *eventData++; // 第一个字节表示value的type,有下面四种类型 switch (type) { case EVENT_TYPE_INT: // 对于int型,直接读取4个字节并转换为int再输出到pOutBuf中。 ival = get4LE(eventData); outCount = snprintf(outBuf, outBufLen, "%d", ival); case EVENT_TYPE_LONG: // 对于long型,差不多的手段 lval = get8LE(eventData); outCount = snprintf(outBuf, outBufLen, "%lld", lval); case EVENT_TYPE_STRING: // 对于string型,紧跟的四个字节代表字串长度,然后紧跟字串内容 strLen = get4LE(eventData); memcpy(outBuf, eventData, strLen); // 实际上能copy多少就看pOutBufLen剩多少空间了 case EVENT_TYPE_LIST: // 对于list类,第一个字节是list中value的个数,所以value个数不能超过256吧 count = *eventData++; // 递归调用android_log_printBinaryEvent() for(i=0;i<count;i++){ result = android_log_printBinaryEvent(&eventData, &eventDataLen, &outBuf, &outBufLen); ... ... } }}
可知logger_entry->msg[0]的结构如下,以string类型的log为例,单位为字节: |--tag(4)--|--type(1)--|--str_len(4)--|--str_data(str_len)--|
解析之后的输出过程,这里就略过了。
更多相关文章
- android camera 源码分析(基于应用)
- android 源码下java文件的路径
- 后台动态添加布局文件、控件与动态设置属性2
- Android 之 使用File类在SD卡中读取数据文件
- Android之如何安装(卸载)apk文件到模拟器