在frameworks/base的源码中看到几个很奇怪的EventLogTags.logtags文件,丫竟然还能作为src参与编译,百思不得其解之下只好埋头看源码,记录如下。
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进行异步读取文件。 【framework】EventLog分析_第1张图片
结合上图,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)--|
解析之后的输出过程,这里就略过了。

更多相关文章

  1. android camera 源码分析(基于应用)
  2. android 源码下java文件的路径
  3. 后台动态添加布局文件、控件与动态设置属性2
  4. Android 之 使用File类在SD卡中读取数据文件
  5. Android之如何安装(卸载)apk文件到模拟器

随机推荐

  1. Android(安卓)studio gradle配置完整版
  2. android textview设置字体的行距和字间距
  3. Android搭建开发环境
  4. Android(安卓)studio 点击按钮跳转到新的
  5. Android(安卓)的recovery模式分析
  6. Android(安卓)Studio中的六种依赖
  7. Android(安卓)Studio中SVN安装与使用
  8. Android高效加载大图、多图解决方案,有效
  9. 35 个你必看的 Android(安卓)库
  10. 4412开发板Android和LinuxQT烧写方法