在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);
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)--|
解析之后的输出过程,这里就略过了。