Android系统日志架构
日志框架概述
Android提供的Logger日志系统是基于内核中的Logger 日志驱动程序实现的,Logger日志驱动程序根据日志的类型以及日志的输出量来对日志记录进行分类,日志的类型一共有四种,它们分别是main、system、radio和events。在Logger日志驱动程序中,这四种类型的日志分别通过/dev/log/main、/dev/log/system、/dev/log/radio和/dev/log/events四个设备文件来访问。
Android在framework层提供了三个Java接口来往Logger日志驱动程序中写入日志:
- android.util.Log:写入main和radio日志;
- android.utii.Slog:写入system和radio日志;
- android.util.EventLog:写入event日志。
在native层提供了三组C/C++宏来往Logger日志驱动程序中写入日志。
- 宏ALOGV、ALOGD、ALOGI、AL0GW、ALOGE用来写入main类型的日志;
- 宏SLOGV、SLOGD、SLOGI、SLOGW、SLOGE用来写入system类型的日志;
- 宏LOG_EVENT_INT、LOG_EVENT_LONG、LOG_EVENT_STRING用来写入events类型的日志。
无论是Java日志写入接口还是C/C++日志写入接口,它们最终都是通过运行时库层的日志库liblog来往Logger日志驱动程序中写入日志的。 此外,系统还提供了一个Logcat工具来读取和显示Logger日志驱动程序中的日志。
注:图片来自其它博客。
其中其中最常用的缓冲区为main、system、events、radio。这四种类型的日志分别通过/dev/log/main、/dev/log/system、/dev/log/radio和/dev/log/events四个设备文件来访问。
日志分类
(1) 应用日志(main)
源码路径:/frameworks/base/core/java/android/util/Log.java
Java接口:android.util.Log
main存储应用程序级别的日志,应用程序唯一可用的日志缓冲区,在应用框架层提供了android.util.log接口通过liblog库来往logger日志驱动程序中写入日志。
注:大多数参考资料都说android.util.log是写入main缓存区的。但经实际测试,android.util.log写入的日志实际上是在system区的。暂不清楚原因。
(2) 系统日志(system)
源码路径:/frameworks/base/core/java/android/util/SLog.java
Java接口:android.util.SLog
使用android.util.Slog类,来编写具有不同优先级的消息、及其关联消息。
许多Android框架类都能够将系统日志(可能比较杂乱)与应用日志消息区分开来。
格式化的消息是通过C/C++库被传递到内核驱动的,该驱动能够将消息存储到适当的缓冲区(如:系统缓冲区)中。
(3) 事件日志(events)
源码:frameworks/base/core/java/android/util/EventLog.java
Java接口:android.util.EventLog
events的日志是专门用来诊断系统问题的,记录了四大组件、systemui、surfaceflinger等frameworks层导致的问题所产生的Log。应用程序开发者不应该使用此种类型的日志。
(4) 广播日志(radio)
通信系统方面的日志。
如果使用android.util.Log和android.util.Slog接口写入的日志的标签值是以“RIL”开头或者等于 “HTC_RIL”、 “AT”、 “GSM”、 “STK”、 “CDMA”、 “PHONE” 和 “SMS” 时,它们就会被转换为radio类型的日志写入到Logger日志驱动程序中。
日志写入接口
C/C++日志写入接口:
system/core/liblog/include/log/log_main.h
system/core/liblog/include/log/log_system.h
system/core/liblog/include/log/log.h
Java日志写入接口:
frameworks/base/core/java/android/util/Log.java
frameworks/base/core/java/android/util/Slog.java
frameworks/base/core/java/android/util/EventLog.java
Frameworks层日志写入
参考《Android系统源代码情景分析》
(1) android.util.Log
调用native层的println_native方法:
public final class Log {
//......
public static final int VERBOSE = 2;
public static final int DEBUG = 3;
public static final int INFO = 4;
public static final int WARN = 5;
public static final int ERROR = 6;
public static final int ASSERT = 7;
//......
public static int i(@Nullable String tag, @NonNull String msg) {
return println_native(LOG_ID_MAIN, INFO, tag, msg);
}
//......
public static native int println_native(int bufID, int priority, String tag, String msg);
}
println_native()定义在frameworks/base/core/jni/android_util_Log.cpp中:
static jint android_util_Log_println_native(JNIEnv* env, jobject clazz,
jint bufID, jint priority, jstring tagObj, jstring msgObj)
{
const char* tag = NULL;
const char* msg = NULL;
if (msgObj == NULL) {
jniThrowNullPointerException(env, "println needs a message");
return -1;
}
if (bufID < 0 || bufID >= LOG_ID_MAX) {
jniThrowNullPointerException(env, "bad bufID");
return -1;
}
if (tagObj != NULL)
tag = env->GetStringUTFChars(tagObj, NULL);
msg = env->GetStringUTFChars(msgObj, NULL);
int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);
if (tag != NULL)
env->ReleaseStringUTFChars(tagObj, tag);
env->ReleaseStringUTFChars(msgObj, msg);
return res;
}
在JNI函数android_util_Log_println_native中,if (msgObj == NULL)检查写入的日志记录的内容msgObj是否为null。
| if (bufID < 0 | bufID >= LOG_ID_MAX)检查写入的日志记录的类型值是否位于0和LOG_ID_MAX之间,其中,0、1、2和3四个值表示的日志记录的类型分别为main、radio、events和system。 |
通过这两个合法性检查之后,最后第35行就调用日志库liblog提供的函数_android_log_buf_write向Logger日志驱动程序中写入日志记录。
(2) android.util.SLog
public final class Slog {
private Slog() {
}
//......
@UnsupportedAppUsage
public static int i(String tag, String msg) {
return Log.println_native(Log.LOG_ID_SYSTEM, Log.INFO, tag, msg);
}
public static int i(String tag, String msg, Throwable tr) {
return Log.println_native(Log.LOG_ID_SYSTEM, Log.INFO, tag,
msg + '\n' + Log.getStackTraceString(tr));
}
//......
}
接口andrcid.util.Slog写入的日志记录的类型为system,它常用的成员函数有v、d、i、w和e,并且它们都是通过调用接口 android.util.Log的JNI方法println_native来实现的。
(3) android.util.EventLog
public class EventLog {
/** @hide */ public EventLog() {}
//......
public static native int writeEvent(int tag, int value);
public static native int writeEvent(int tag, long value);
public static native int writeEvent(int tag, float value);
public static native int writeEvent(int tag, String str);
public static native int writeEvent(int tag, Object... list);
//......
}
android.util.EventLog提供了5个重载版本的JNI方法writeEvent向Logger日志驱动程序中写入类型为events的日志记录,这些日志记录的内容分别为整型、长整型、浮点型、字符串和列表。
frameworks/base/core/jni/android_util_EventLog.cpp
/*
* JNI registration.
*/
static const JNINativeMethod gRegisterMethods[] = {
/* name, signature, funcPtr */
{ "writeEvent", "(II)I", (void*) ELog::writeEventInteger },
{ "writeEvent", "(IJ)I", (void*) ELog::writeEventLong },
{ "writeEvent", "(IF)I", (void*) ELog::writeEventFloat },
{ "writeEvent", "(ILjava/lang/String;)I", (void*) ELog::writeEventString },
{ "writeEvent", "(I[Ljava/lang/Object;)I", (void*) ELog::writeEventArray },
{ "readEvents",
"([ILjava/util/Collection;)V",
(void*) android_util_EventLog_readEvents
},
{ "readEventsOnWrapping",
"([IJLjava/util/Collection;)V",
(void*) android_util_EventLog_readEventsOnWrapping
},
};
int register_android_util_EventLog(JNIEnv* env) {
ELog::Init(env);
return RegisterMethodsOrDie(
env,
"android/util/EventLog",
gRegisterMethods, NELEM(gRegisterMethods));
}
此处通过注册的方式将Java函数与native层的函数一一对应起来。
参考:Android Framework层的JNI机制(一)
android.util.EventLog中5个不同的重载方法writeEvent对应的native中的函数名分别为writeEventInteger、writeEventLong、writeEventFloat、writeEventString、writeEventArray,这几个函数的实现在frameworks/base/core/jni/eventlog_helper.h中:
static jint writeEventInteger(JNIEnv* env ATTRIBUTE_UNUSED, jobject clazz ATTRIBUTE_UNUSED,
jint tag, jint value) {
android_log_event_list ctx(tag);
ctx << (int32_t)value;
return ctx.write(LogID);
}
static jint writeEventLong(JNIEnv* env ATTRIBUTE_UNUSED, jobject clazz ATTRIBUTE_UNUSED,
jint tag, jlong value) {
android_log_event_list ctx(tag);
ctx << (int64_t)value;
return ctx.write(LogID);
}
static jint writeEventFloat(JNIEnv* env ATTRIBUTE_UNUSED, jobject clazz ATTRIBUTE_UNUSED,
jint tag, jfloat value) {
android_log_event_list ctx(tag);
ctx << (float)value;
return ctx.write(LogID);
}
static jint writeEventString(JNIEnv* env, jobject clazz ATTRIBUTE_UNUSED, jint tag,
jstring value) {
android_log_event_list ctx(tag);
// Don't throw NPE -- I feel like it's sort of mean for a logging function
// to be all crashy if you pass in NULL -- but make the NULL value explicit.
ctx << (value != nullptr ? ScopedUtfChars(env, value).c_str() : "NULL");
return ctx.write(LogID);
}
static jint writeEventArray(JNIEnv* env, jobject clazz ATTRIBUTE_UNUSED, jint tag,
jobjectArray value) {
android_log_event_list ctx(tag);
if (value == nullptr) {
ctx << "[NULL]";
return ctx.write(LogID);
}
jsize copied = 0, num = env->GetArrayLength(value);
for (; copied < num && copied < 255; ++copied) {
if (ctx.status()) break;
ScopedLocalRef<jobject> item(env, env->GetObjectArrayElement(value, copied));
if (item == nullptr) {
ctx << "NULL";
} else if (env->IsInstanceOf(item.get(), gStringClass)) {
ctx << ScopedUtfChars(env, (jstring) item.get()).c_str();
} else if (env->IsInstanceOf(item.get(), gIntegerClass)) {
ctx << (int32_t)env->GetIntField(item.get(), gIntegerValueID);
} else if (env->IsInstanceOf(item.get(), gLongClass)) {
ctx << (int64_t)env->GetLongField(item.get(), gLongValueID);
} else if (env->IsInstanceOf(item.get(), gFloatClass)) {
ctx << (float)env->GetFloatField(item.get(), gFloatValueID);
} else {
jniThrowException(env,
"java/lang/IllegalArgumentException",
"Invalid payload item type");
return -1;
}
}
return ctx.write(LogID);
}
最终它们都要调用ctx.write(LogID),ctx是android_log_event_list对象,所以write()函数的实现位于/system/core/liblog/include/log/log_event_list.h中:
int write(log_id_t id = LOG_ID_EVENTS) {
/* facilitate -EBUSY retry */
if ((ret == -EBUSY) || (ret > 0)) ret = 0;
int retval = android_log_write_list(ctx, id);
/* existing errors trump transmission errors */
if (!ret) ret = retval;
return ret;
}
write()调用android_log_write_list,该函数位于system/core/liblog/log_event_list.cpp,如下:
int android_log_write_list(android_log_context context, log_id_t id) {
const char* msg;
ssize_t len;
if ((id != LOG_ID_EVENTS) && (id != LOG_ID_SECURITY) && (id != LOG_ID_STATS)) {
return -EINVAL;
}
if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
return -EBADF;
}
if (context->list_nest_depth) {
return -EIO;
}
/* NB: if there was overflow, then log is truncated. Nothing reported */
context->storage[1] = context->count[0];
len = context->len = context->pos;
msg = (const char*)context->storage;
/* it's not a list */
if (context->count[0] <= 1) {
len -= sizeof(uint8_t) + sizeof(uint8_t);
if (len < 0) {
len = 0;
}
msg += sizeof(uint8_t) + sizeof(uint8_t);
}
return (id == LOG_ID_EVENTS)
? __android_log_bwrite(context->tag, msg, len)
: ((id == LOG_ID_STATS) ? __android_log_stats_bwrite(context->tag, msg, len)
: __android_log_security_bwrite(context->tag, msg, len));
}
最后调用__android_log_bwrite(context->tag, msg, len)向Logger日志驱动程序中写入日志记录。
Native层日志写入
参考《Android系统源代码情景分析》
(1) ALOGV、ALOGD、ALOGI、AL0GW、ALOGE
system/core/liblog/include/log/log_main.h
/*
* Simplified macro to send a verbose log message using the current LOG_TAG.
*/
#ifndef ALOGV
#define __ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
#if LOG_NDEBUG
#define ALOGV(...) \
do { \
__FAKE_USE_VA_ARGS(__VA_ARGS__); \
if (false) { \
__ALOGV(__VA_ARGS__); \
} \
} while (false)
#else
#define ALOGV(...) __ALOGV(__VA_ARGS__)
#endif
/*
* Simplified macro to send a debug log message using the current LOG_TAG.
*/
#ifndef ALOGD
#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
#endif
/*
* Simplified macro to send an info log message using the current LOG_TAG.
*/
#ifndef ALOGI
#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
#endif
/*
* Simplified macro to send a warning log message using the current LOG_TAG.
*/
#ifndef ALOGW
#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
#endif
/*
* Simplified macro to send an error log message using the current LOG_TAG.
*/
#ifndef ALOGE
#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
#endif
这五个宏是用来写入类型为main的日志记录的,它们写入的日志记录的优先级分别为VERBOSE、DEBUG、INFO、WARN和ERROR。其中,宏LOGV只有在宏LOG_NDEBUG定义为0时,即在程序的调试版本中,才是有效的;否则,它只是一个空定义。
这五个宏是通过使用宏LOG来实现日志写入功能的,它的定义如system/core/liblog/include/log/log_main.h中所示:
/*
* Basic log message macro.
*
* Example:
* ALOG(LOG_WARN, NULL, "Failed with error %d", errno);
*
* The second argument may be NULL or "" to indicate the "global" tag.
*/
#ifndef ALOG
#define ALOG(priority, tag, ...) LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
#endif
/*
* Log macro that allows you to specify a number for the priority.
*/
#ifndef LOG_PRI
#define LOG_PRI(priority, tag, ...) android_printLog(priority, tag, __VA_ARGS__)
#endif
#define android_printLog(prio, tag, ...) \
__android_log_print(prio, tag, __VA_ARGS__)
最终通过调用日志库liblog提供的函数_android_log_print向Logger
日志驱动程序中写入日志记录的。
(2) SLOGV、SLOGD. SLOGI、SLOGW和SLOGE
system/core/liblog/include/log/log_system.h
/*
* Simplified macro to send a verbose system log message using current LOG_TAG.
*/
#ifndef SLOGV
#define __SLOGV(...) \
((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, \
__VA_ARGS__))
#if LOG_NDEBUG
#define SLOGV(...) \
do { \
if (0) { \
__SLOGV(__VA_ARGS__); \
} \
} while (0)
#else
#define SLOGV(...) __SLOGV(__VA_ARGS__)
#endif
#endif
/*
* Simplified macro to send a debug system log message using current LOG_TAG.
*/
#ifndef SLOGD
#define SLOGD(...) \
((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, \
__VA_ARGS__))
#endif
/*
* Simplified macro to send an info system log message using current LOG_TAG.
*/
#ifndef SLOGI
#define SLOGI(...) \
((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, \
__VA_ARGS__))
#endif
/*
* Simplified macro to send a warning system log message using current LOG_TAG.
*/
#ifndef SLOGW
#define SLOGW(...) \
((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, \
__VA_ARGS__))
#endif
/*
* Simplified macro to send an error system log message using current LOG_TAG.
*/
#ifndef SLOGE
#define SLOGE(...) \
((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, \
__VA_ARGS__))
#endif
这五个宏是用来写入类型为system的日志记录的,它们写入的日志记录的优先级分别为VERBOSE、DEBUG、INFO、WARN和ERROR。其中,宏SLOGV只有在宏LOG_NDEBUG定义为0时,即在程序的调试版本中,才是有效的;否则,它只是一个空定义。
这五个宏展开之后,实际上是通过调用日志库liblog提供的函数_android_log_buf_print向Logger日志驱动程序中写入日志记录的。
(3) LOG_EVENT_INT、LOG_EVENT_FLOAT、LOG_EVENT_LONG、LOG_EVENT_STRING
system/core/liblog/include/log/log.h
int __android_log_bwrite(int32_t tag, const void* payload, size_t len);
int __android_log_btwrite(int32_t tag, char type, const void* payload,
size_t len);
int __android_log_bswrite(int32_t tag, const char* payload);
int __android_log_stats_bwrite(int32_t tag, const void* payload, size_t len);
#define android_bWriteLog(tag, payload, len) \
__android_log_bwrite(tag, payload, len)
#define android_btWriteLog(tag, type, payload, len) \
__android_log_btwrite(tag, type, payload, len)
typedef enum {
/* Special markers for android_log_list_element type */
EVENT_TYPE_LIST_STOP = '\n', /* declare end of list */
EVENT_TYPE_UNKNOWN = '?', /* protocol error */
/* must match with declaration in java/android/android/util/EventLog.java */
EVENT_TYPE_INT = 0, /* int32_t */
EVENT_TYPE_LONG = 1, /* int64_t */
EVENT_TYPE_STRING = 2,
EVENT_TYPE_LIST = 3,
EVENT_TYPE_FLOAT = 4,
} AndroidEventLogType;
#ifndef LOG_EVENT_INT
#define LOG_EVENT_INT(_tag, _value) \
{ \
int intBuf = _value; \
(void)android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf, sizeof(intBuf)); \
}
#endif
#ifndef LOG_EVENT_LONG
#define LOG_EVENT_LONG(_tag, _value) \
{ \
long long longBuf = _value; \
(void)android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf, sizeof(longBuf)); \
}
#endif
#ifndef LOG_EVENT_FLOAT
#define LOG_EVENT_FLOAT(_tag, _value) \
{ \
float floatBuf = _value; \
(void)android_btWriteLog(_tag, EVENT_TYPE_FLOAT, &floatBuf, \
sizeof(floatBuf)); \
}
#endif
#ifndef LOG_EVENT_STRING
#define LOG_EVENT_STRING(_tag, _value) \
(void)__android_log_bswrite(_tag, _value);
#endif
宏LOG_EVENT_INT、LOG_EVENT_LONG和LOG_EVENT_FLOAT最终通过调用日志库liblog提供的函数_android_log_btwrite来往Logger日志驱动程序中写入日志记录的。
宏LOG_EVENT_STRING通过__android_log_bswrite向Logger日志驱动程序中写入日志记录。
日志库liblog
参考:《Android系统源代码情景分析》 4.3运行时库层日志库
Framework层及Native层中的各个函数写入日志到Logger日志驱动程序的接口如下:
| Framework/Native调用函数 | 写入到Logger的系统调用 |
|---|---|
| Log.java | _android_log_buf_write |
| SLog.java | _android_log_buf_write |
| EventLog.java | __android_log_bwrite |
| ALOGV,ALOGD,ALOGI,AL0GW,ALOGE | _android_log_print |
| SLOGV,SLOGD,SLOGI,SLOGW,SLOGE | _android_log_buf_print |
| LOG_EVENT_INT LOG_EVENT_LONG LOG_EVENT_FLOAT | _android_log_btwrite |
| LOG_EVENT_STRING | __android_log_bswrite |
BugReport日志解析
BugReport源码
生成Bugreport的方式
(1) adb bugreport
- 手机连接电脑,在终端输入命令:adb bugreport
- 生成的 log 位于手机目录:/data/user_de/0/com.android.shell/files/bugreports/ 下,Log文件为:bugreport-
- .zip,如bugreport-qssi-RKQ1.200826.002-2021-08-27-15-34-18.zip - pull 到 PC 端当前目录下:adb pull /data/user_de/0/com.android.shell/files/bugreports/
(2) adb bugreport > path/bugreport.txt
生成bugreport.txt(记录生成bugreport时的进度)和上面的bugreport-
(3) 拨号界面
手机端拨号界面输入 ##284## 会在手机端生成log,位于“/内部存储设备/MIUI/debug_log”
(4) 连续点击处理器
设置->我的设备->全部参数→处理器(连点5下),之后会生成log。
生成的bugreport位于”/内部存储设备/MIUI/debug_log”
Bugreport目录结构
通过adb命令生成的通常是一个ZIP文件,且该文件命名为bugreport-
- bugreport-
- .txt: 最重要的文件,输出的诊断信息,包含:系统服务(dumpsys),错误日志(dumpstate),和系统日志信息(logcat); - version.txt: Android 的发布号;
- systrace.txt: 如果系统systrace是可用的,则会包含这个文件,关于Systrace tool的介绍见官网;
- FS 文件夹:dumpstate工具会将设备文件系统下的文件拷贝到FS文件夹下。
Bugreport日志格式解析
默认输出的日志格式如下文所示:(不带任何参数的logcat命令)
08-26 14:35:55.673 3085 3085 D NewNitzStateMachineImpl: handleCountryDetected: countryIsoCode=cn, mLatestNitzSignal=null
| 日期 | 时间 | UID | PID | TID | 日志级别 | TAG | 日志内容 |
| (1) 日期 | 时间 |
写下日志时的时间,如上中07-22 20:31:21.565。
| (2) UID | PID | TID |
- UID(APP ID),个别日志中没有该项
- PID(进程ID),如上中993。
- TID(线程ID),如上中1032。
注1:当一个进程只有一个线程时,PID和TID总是相同的
注2:每一个APP在系统中都有一个唯一的UID,每个运行的APP可能有多个进程,每个进程可能有多个线程。每条日志前面的UID、PID、TID是指打印出该条日志的具体ID,而不是指日志正文中进程的ID。如:
07-07 13:41:22.727 1000 1704 14498 E ActivityManager: ANR in com.mfashiongallery.emag
07-07 13:41:22.727 1000 1704 14498 E ActivityManager: PID: 12461
1000、1704及14498分别指打印出该条日志的UID、PID及TID,而不是发生ANR的进程com.mfashiongallery.emag的ID(它的PID在下一行列出,为12461)
(3) 日志级别
| 级别 | 输出方法 | 说明 |
|---|---|---|
| Verbose | Log.v | 啰嗦,最低级别,开发调试中的一些详细信息,仅在开发中使用,不可在发布产品中输出,不是很常见,包含诸如方法名,变量值之类的信息 |
| Debug | Log.d | 调试,用于调试的信息,可以在发布产品中关闭,比较常见,开发中经常选择输出此种级别的日志,有时在beta版应用中出现 |
| Info | Log.i | 信息,该等级日志显示运行状态信息,可在产品出现问题时提供帮助,从该级别开始的日志通常包含完整意义的英语语句和调试信息,是最常见的日志级别 |
| Warn | Log.w | 警告,运行出现异常即将发生错误或表明已发生非致命性错误,该级别日志通常显示出执行过程中的意外情况,例如将try-catch语句块中的异常打印堆栈轨迹之后可输出此种级别日志 |
| Error | Log.e | 错误,已经出现可影响运行的错误,比如应用crash时输出的日志 |
| Fatal | 严重错误,比error级别更高,只在android系统内核发出的日志中看到此种级别。在Android6.0以前表明开发者认为绝对不应该出现的错误,在此以后一般开发场景下绝不应该输出此种级别的日志 | |
| Silent | 寂静,最高级别,没有一条日志会属于这个级别,仅仅作为关闭logcat输出的过滤器参数 | |
| Assert | Log.wtf |
(4) 标签
标明日志发起者和方便日志的过滤筛选,如上中BroadcastQueue,表示该日志关于广播队列。
(5) 正文
日志的主体内容。示例日志中表示一个后台执行被阻止,并显示出了接收到的意图的详细信息。
EventLog日志的正文含义
EventLog格式概述
在Android系统中,通常会打印一些日志以观察特定时间及环境下的系统运行情况。常见的日志格式如下:
07-07 13:41:00.989 1000 2650 2650 I sysui_multi_action: [757,804,799,volume_from_keyguard,801,0,802,1]
- 记录日志的时间,07-07 13:41:00.989。
- PID(进程ID),如上中1000。
- TID(线程ID),如上中2650。
- 日志级别,I表示info
- 标签,标明日志发起者和方便日志的过滤筛选,sysui_multi_action
- 正文,本日志的主体内容。
而我们最关心的就是正文,每一个不同的标签都表示一种不同的事件,而正文就是对这些事件更详细的解释。比如标签sysui_multi_action,其定义位于:
frameworks/base/core/java/com/android/internal/logging/EventLogTags.logtags 文件下:
524292 sysui_multi_action (content|4)
定义事件-EventLogTags.logtags
在源码的frameworks目录下的不同子目录(模块)中还有很多EventLogTags.logtags文件,定义了该模块下的一些事件:
/frameworks/base/core/java/org/chromium/arc/EventLogTags.logtags
/frameworks/native/services/surfaceflinger/EventLog/EventLogTags.logtags
/frameworks/base/core/java/android/webkit/EventLogTags.logtags
/frameworks/base/services/core/java/com/android/server/am/EventLogTags.logtags
/system/core/libsysutils/EventLogTags.logtags
/frameworks/base/core/java/android/net/EventLogTags.logtags
/frameworks/opt/telephony/src/java/com/android/internal/telephony/EventLogTags.logtags
/frameworks/av/media/utils/EventLogTags.logtags
/frameworks/base/core/java/com/android/internal/app/EventLogTags.logtags
/frameworks/base/services/core/java/com/android/server/policy/EventLogTags.logtags
/system/core/storaged/EventLogTags.logtags
/frameworks/base/core/java/android/os/EventLogTags.logtags
/frameworks/base/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags
/packages/services/Telephony/src/com/android/phone/EventLogTags.logtags
/frameworks/base/core/java/android/app/EventLogTags.logtags
/frameworks/base/core/java/com/android/internal/logging/EventLogTags.logtags
/frameworks/base/services/core/java/com/android/server/wm/EventLogTags.logtags
/frameworks/base/core/java/android/speech/tts/EventLogTags.logtags
/frameworks/base/services/core/java/com/android/server/EventLogTags.logtags
/packages/apps/Settings/src/com/android/settings/EventLogTags.logtags
/system/bt/EventLogTags.logtags
/frameworks/base/core/java/android/content/EventLogTags.logtags
这些文件都是定义的某一类的事件,EventLogTags.logtags文件中有对事件的注释说明,所以在定位问题的时候可以参考对应模块的EventLogTags.logtags文件中的注释。
定义数据格式-event.logtags
| 那么,上文中的事件524292 sysui_multi_action (content | 4)是什么意思?event.logtags文件就是对上述各种EventLogTags.logtags文件内容加以解释的文件,路径为system/logging/logcat/event.logtags。 |
# 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 百分比
# s: Number of seconds (monotonic time)
# Default value for data of type int/long is 2 (bytes).
每一种Tag由于功能和模块的不同,括号中的内容不同,但是都遵循以下规则:
(Name|type|unit)
(1) Name
表示这个字段的命名
(2) Type
表示这个字段的数据格式
1: int
2: long
3: string
4: list
5: float
(3) unit
表示数据单位
1: Number of objects(对象个数)
2: Number of bytes(字节数)
3: Number of milliseconds(毫秒)
4: Number of allocations(分配个数)
5: Id
6: Percent(百分比)
当数据格式为string或list时,则没有unit。
EventLog数据含义示例
当应用无响应时,会打印以下日志:
09-13 15:03:37.369 1000 1711 30252 I am_anr : [0,28884,com.android.market,949501509,Input dispatching timed out (Application does not have a focused window)]
am_anr事件,定义该事件的文件位置:
/frameworks/base/services/core/java/com/android/server/am/EventLogTags.logtags
其具体定义为:
# Application Not Responding
30008 am_anr (User|1|5),(pid|1|5),(Package Name|3),(Flags|1|5),(reason|3)
根据定义以及具体的日志,可以从am_anr中得到以下信息:
- User字段,数据为int类型,表示id。此处User=0,表默认用户;
- pid字段,数据为int类型,表示id。此处pid=28884,表示进程号;
- Package Name字段,数据类型为string,此处Package Name=com.android.market,表示APP名;
- Flags字段,数据为int类型,表示id。此处Flags=949501509,含义未知;
- reason字段,数据类型为string。此处reason=Input dispatching timed out (Application does not have a focused window),表示发生ANR的原因。
.logtags生成.java
(1) 生成脚本
源码中的Python脚本build/tools/java-event-log-tags.py根据EventLogTags.logtags中的日志定义生成对应的EventLogTags.java文件
(2) 文件路径
原始文件EventLogTags.logtags路径
out/soong/.intermediates/<module_path>/xxxx/android_common/gen/logtags/<module_path>/<java_path>/EventLogTags.java
如frameworks/base/services/core/java/com/android/server/am/EventLogTags.logtags,其生成位置为:
out/soong/.intermediates/frameworks/base/services/core/services.core.unboosted/android_common/gen/logtags/frameworks/base/services/core/java/com/android/server/am/EventLogTags.java
(3) 生成函数
通过EventLogTags.logtags中的日志标签和定义生成的函数是有规律的,函数名为writeXxXxxXxxxx(),函数参数为EventLogTags.logtags中日志标签的各个字段。
如am_proc_start在EventLogTags.logtags中的定义为:
30014 am_proc_start (User|1|5),(PID|1|5),(UID|1|5),(Process Name|3),(Type|3),(Component|3)
生成函数writeAmProcStart():
public static final int AM_PROC_START = 30014;
public static void writeAmProcStart(int user, int pid, int uid, String processName, String type, String component) {
android.util.EventLog.writeEvent(AM_PROC_START, user, pid, uid, processName, type, component);
}
而这些日志实际都是通过android.util.EventLog.writeEvent()来写入的,writeEvent()有数个重载函数。但最终都是调用__android_log_bwrite(context->tag, msg, len)向Logger日志驱动程序中写入日志记录。
追踪EventLog打印位置
有时候分析日志中的信息时,需要定位到打印日志的代码处,对源码进行进一步的分析。于是根据上述的EventLog的生成规律,可以通过搜索函数名快速定位到源码处。
(1) 方法1:搜索“writeXxXxxXxxxx”
如系统打印了标签为wm_restart_activity的EventLog,于是可以搜索函数名“writeWmRestartActivity”定位到打印该日志的源码处。
源码位置:ActivityStackSupervisor.java
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
boolean andResume, boolean checkConfig) throws RemoteException {
//......
EventLogTags.writeWmRestartActivity(r.mUserId, System.identityHashCode(r),
task.mTaskId, r.shortComponentName);
//......
}
(2) 方法2:搜索“XX_XXX_XXXX”(大写)
但有时候代码中也会对EventLog进行封装,并不会直接调用EventLogTags.writeXxXxxXxxxx,如标签为“wm_create_activity”的日志:
源码位置:ActivityStarter.java
int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, Task inTask,
boolean restrictedBgActivity, NeededUriGrants intentGrants) {
//......
mStartActivity.logStartActivity(
EventLogTags.WM_CREATE_ACTIVITY, mStartActivity.getTask());
//......
}
所以也可以通过搜索EventLogTags.XX_XXX_XXXX(大写)搜索到。