Android EventLog

概述

   在分析Android系統或者應用相關的問題時,經常會查看EventLog,它非常簡潔明瞭地展現當前系統以及應用行爲的各種狀態,簡單的看下其原理。


1. EventLogTags.logtags介紹

   在文件frameworks/base/services/core/java/com/android/server/EventLogTags.logtags

   有如下內容:

# ---------------------------
# PowerManagerService.java
# ---------------------------
# This is logged when the device is being forced to sleep (typically by
# the user pressing the power button).
2724 power_sleep_requested (wakeLocksCleared|1|1)
# This is logged when the screen on broadcast has completed
2725 power_screen_broadcast_send (wakelockCount|1|1)
# This is logged when the screen broadcast has completed
2726 power_screen_broadcast_done (on|1|5),(broadcastDuration|2|3),(wakelockCount|1|1)
# This is logged when the screen on broadcast has completed
2727 power_screen_broadcast_stop (which|1|5),(wakelockCount|1|1)
# This is logged when the screen is turned on or off.
2728 power_screen_state (offOrOn|1|5),(becauseOfUser|1|5),(totalTouchDownTime|2|3),(touchCycles|1|1)
# This is logged when the partial wake lock (keeping the device awake
# regardless of whether the screen is off) is acquired or released.
2729 power_partial_wake_state (releasedorAcquired|1|5),(tag|3)

#
# Leave IDs through 2739 for more power logs (2730 used by battery_discharge above)
#

   實際上EventLogTags.logtags類文件還有很多,例如:

  • frameworks/native/services/surfaceflinger/EventLog/EventLogTags.logtag

  • frameworks/base/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags

  • frameworks/base/services/core/java/com/android/server/am/EventLogTags.logtags

2. EventLogTags.logtags內容解析

2.1.system/core/logcat/event.logtags對這類EventLogTags.logtags做了介紹

   根據:

# Tag numbers are decimal integers, from 0 to 2^31.  (Let's leave the
# negative values alone for now.)
#
# Tag names are one or more ASCII letters and numbers or underscores, i.e.
# "[A-Z][a-z][0-9]_".  Do not include spaces or punctuation (the former
# impacts log readability, the latter makes regex searches more annoying).
#
# Tag numbers and names are separated by whitespace.  Blank lines and lines
# starting with '#' are ignored.
#
# Optionally, after the tag names can be put a description for the value(s)
# of the tag. Description are in the format
#    (<name>|data type[|data unit])
# Multiple values are separated by commas.
#
# The data type is a number from the following values:
# 1: int
# 2: long
# 3: string
# 4: list
# 5: float
#
# The data unit is a number taken from the following list:
# 1: Number of objects
# 2: Number of bytes
# 3: Number of milliseconds
# 4: Number of allocations
# 5: Id
# 6: Percent
# Default value for data of type int/long is 2 (bytes).
#
# TODO: generate ".java" and ".h" files with integer constants from this file.

   我們來解析以下這段話的意思

2728 power_screen_state (offOrOn|1|5),(becauseOfUser|1|5),(totalTouchDownTime|2|3),(touchCycles|1|1)

格式 (Tag numbers) (|data type[|data unit])

  1. EventLogTags.logtags類文件,使用 “#” 做開頭,註釋一行;

  2. Tag numbers:2728; Tag numbers 是10進制數,範圍爲0 到 2^31;

  3. Tag name:power_screen_state;Tag name是由一個或者多個ASCII碼加數字或者下劃線組合而成,例如[A-Z][a-z][0-9]_,禁止包括空格和標點符合。影響log的閱讀性;

  4. Tag numbers和Tag name中間使用空格隔開;

  5. 參數: (offOrOn|1|5), name:offOrOn,data type:1(int型),data unit:5(Id);多個參數使用逗號隔開,例如(offOrOn|1|5),(becauseOfUser|1|5),(totalTouchDownTime|2|3),(touchCycles|1|1)

3. 解析生成文件EventLogTags.java

   Android通過build/core/base_rules.mk和build/tools/java-event-log-tags.py。把各個模塊的EventLogTags.logtags文件生成對應的EventLogTags.java文件 參照:

###########################################################
## logtags: Add .logtags files to global list, emit java source
###########################################################

logtags_sources := $(filter %.logtags,$(LOCAL_SRC_FILES))

ifneq ($(strip $(logtags_sources)),)

event_log_tags := $(addprefix $(LOCAL_PATH)/,$(logtags_sources))

# Emit a java source file with constants for the tags, if
# LOCAL_MODULE_CLASS is "APPS" or "JAVA_LIBRARIES".
ifneq ($(filter $(LOCAL_MODULE_CLASS),APPS JAVA_LIBRARIES),)

logtags_java_sources := $(patsubst %.logtags,%.java,$(addprefix $(intermediates.COMMON)/src/, $(logtags_sources)))
logtags_sources := $(addprefix $(TOP_DIR)$(LOCAL_PATH)/, $(logtags_sources))

$(logtags_java_sources): $(intermediates.COMMON)/src/%.java: $(TOPDIR)$(LOCAL_PATH)/%.logtags $(TARGET_OUT_COMMON_INTERMEDIATES)/all-event-log-tags.txt
    $(transform-logtags-to-java)

endif

else
logtags_java_sources :=
event_log_tags :=
endif

   例如,把frameworks/base/services/core/java/com/android/server/EventLogTags.logtags文件解析生成:

out/target/common/obj/JAVA_LIBRARIES/services.core_intermediates/src/java/com/android/server/EventLogTags.java

   frameworks/base/services/core/java/com/android/server/am/EventLogTags.logtags解析生成:

out/target/common/obj/JAVA_LIBRARIES/services.core_intermediates/src/java/com/android/server/am/EventLogTags.java

4. EventLogTags和EventLog調用:

   查看 out/target/common/obj/JAVA_LIBRARIES/services.core_intermediates/src/java/com/android/server
/EventLogTags.java

   可以看出EventLogTags.logtags的一條內容項:

2728 power_screen_state (offOrOn|1|5),(becauseOfUser|1|5),(totalTouchDownTime|2|3),(touchCycles|1|1)

   被解析爲一個靜態變量POWER_SCREEN_STATE和一個靜態函數writePowerScreenState。

public class EventLogTags {
       ...
       /** 2728 power_screen_state (offOrOn|1|5),(becauseOfUser|1|5),(totalTouchDownTime|2|3),(touchCycles|1|1) */
       public static final int POWER_SCREEN_STATE = 2728;
       ...
       public static void writePowerScreenState(int offoron, int becauseofuser, long totaltouchdowntime, int touchcycles) {
                     android.util.EventLog.writeEvent(POWER_SCREEN_STATE, offoron, becauseofuser, totaltouchdowntime, touchcycles);
       }
       ...
}

   舉例:notifier.java代碼裏面有如下調用:

EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0);

   當然還可以如下使用,效果一樣:

EventLogTags.writePowerScreenState(1, 0, 0, 0);

5. writeEvent的處理流程:

   大致流程如下:

writeEvent -> android_util_EventLog.cpp -> logd_write_kern.c(liblog.so) ->
Uio.c(liblog.so)

6. logcat -b events輸出

   eventlog可以通過以下命令來輸出

adb logcat -b events

   代碼都在:

system/core/logcat/logcat.cpp
system/core/liblog/log_read_kern.c

   最主要的main函數,大致步驟如下:

  1. 解析"-b events";

  2. 準備好log_device_t數據;

  3. 設置輸出地方,例如終端或者log文件;

  4. 打開LOG文件,保存fd;

  5. 設置輸出地方,例如終端或者log文件;

  6. 讀取logfd裏面的內容;

  7. 多個設備監測信息更新,例如多個終端輸出;

  8. 輸出LOG頭,例如:

    ----------Logcat----------

    --------- beginning of system

  9. 輸出LOG;

  10. 重新整理log結構。

   具體代碼如下:

int main(int argc, char **argv)
{
    ...
    for (;;) {
        ...
        switch(ret) {
            ...
            case 'b': {//解析"-b events"
                if (strcmp(optarg, &quot;all&quot;) == 0) {
                    while (devices) {
                        dev = devices;
                        devices = dev-&gt;next;
                        delete dev;
                    }
                    devices = dev = NULL;
                    g_devCount = 0;
                    for(int i = LOG_ID_MIN; i &lt; LOG_ID_MAX; ++i) {
                        const char *name = android_log_id_to_name((log_id_t)i);
                        log_id_t log_id = android_name_to_log_id(name);
                        if (log_id != (log_id_t)i) {
                            continue;
                        }
                        bool binary = strcmp(name, &quot;events&quot;) == 0;
                        log_device_t* d = new log_device_t(name, binary);
                        if (dev) {
                            dev-&gt;next = d;
                            dev = d;
                        } else {
                            devices = dev = d;
                        }
                        g_devCount++;
                    }
                    break;
                }

                bool binary = strcmp(optarg, &quot;events&quot;) == 0;

                if (devices) {
                    dev = devices;
                    while (dev-&gt;next) {
                        dev = dev-&gt;next;
                    }
                    dev-&gt;next = new log_device_t(optarg, binary); //準備好log_device_t數據
                } else {
                    devices = new log_device_t(optarg, binary);
                }
                g_devCount++;
            }
            break;
            case 'B':
                g_printBinary = 1;
            break;
            ...
        }
    }
    ...
    setupOutput();//設置輸出地方,例如終端或者log文件
    ...
    while (dev) {
        ...
        //打開LOG文件,保存fd
        dev-&gt;logger = android_logger_open(logger_list,
                                          android_name_to_log_id(dev-&gt;device));
        ...
    }
    ...
    //以下是LOG輸出
    dev = NULL;
    log_device_t unexpected(&quot;unexpected&quot;, false);
    while (1) {
        ...
        int ret = android_logger_list_read(logger_list, &amp;log_msg);//讀取logfd裏面的內容
        ...
        //多個設備監測。例如多個終端輸出
        for(d = devices; d; d = d-&gt;next) {
            if (android_name_to_log_id(d-&gt;device) == log_msg.id()) {
                break;
            }
        }
        if (!d) {
            g_devCount = 2; // set to Multiple
            d = &amp;unexpected;
            d-&gt;binary = log_msg.id() == LOG_ID_EVENTS;
        }

        /**
        * 輸出LOG頭,例如
        * ----------Logcat----------
        * --------- beginning of system
        **/
        if (dev != d) {
            dev = d;
            maybePrintStart(dev, printDividers);
        }
        //輸出LOG
        if (g_printBinary) {
            printBinary(&amp;log_msg);
        } else {
            processBuffer(dev, &amp;log_msg);
        }
    }

    //整理log結構
    android_logger_list_free(logger_list);
    return EXIT_SUCCESS;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章