💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
**struct logger_entry** **system/core/include/cutils/logger.h** ~~~ struct logger_entry { uint16_t len; /* length of the payload */ uint16_t __pad; /* no matter what, we get 2 bytes of padding */ int32_t pid; /* generating process's pid */ int32_t tid; /* generating process's tid */ int32_t sec; /* seconds since Epoch */ int32_t nsec; /* nanoseconds */ char msg[0]; /* the entry's payload */ }; #define LOGGER_ENTRY_MAX_LEN (4*1024) #define LOGGER_ENTRY_MAX_PAYLOAD \ (LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry)) ~~~ 结构体logger_entry和宏LOGGER_ENTRY_MAX_LEN、LOGGER_ENTRY_MAX_PAYLOAD是从Logger日志驱动程序中提取出来的。在前面的4.2.1小节中,我们已经分析过它们的实现了。其中,结构体logger_entry用来描述一条日志记录;宏LOGGER_ENTRY_MAX_LEN用来描述一条日志记录的最大长度,包括日志记录头和日志记录有效负载两部分内容的长度;宏LOGGER_ENTRY_MAX_PAYLOAD用来描述日志记录有效负载的最大长度。 **struct queued_entry_t** **system/core/logcat/logcat.cpp** ~~~ struct queued_entry_t { union { unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4))); struct logger_entry entry __attribute__((aligned(4))); }; queued_entry_t* next; queued_entry_t() { next = NULL; } }; ~~~ 结构体queued_entry_t用来描述一个日志记录队列。每一种类型的日志记录都对应有一个日志记录队列。Logcat工具将相同类型的日志记录按照写入时间的先后顺序保存在同一个队列中,这样在输出日志记录时,沿着这个队列就可以按照时间顺序来依次输出系统中的日志信息了。结构体queued_entry_t的第一个成员变量是一个联合体,用来描述一条日志记录的内容。通过这个联合体,Logcat工具就既可以把一条日志记录内容当作一个缓冲区来处理,也可以把它当作一个logger_entry结构体来处理。结构体queued_entry_t的另外一个成员变量next用来连接下一条日志记录,从而形成一个队列。 **struct log_device_t** **system/core/logcat/logcat.cpp** ~~~ struct log_device_t { char* device; bool binary; int fd; bool printed; char label; queued_entry_t* queue; log_device_t* next; log_device_t(char* d, bool b, char l) { device = d; binary = b; label = l; queue = NULL; next = NULL; printed = false; } void enqueue(queued_entry_t* entry) { if (this->queue == NULL) { this->queue = entry; } else { queued_entry_t** e = &this->queue; while (*e && cmp(entry, *e) >= 0) { e = &((*e)->next); } entry->next = *e; *e = entry; } } }; ~~~ 结构体log_device_t用来描述一个日志设备。成员变量device保存的是日志设备文件名称。我们知道,Logger日志驱动程序在初始化时,会创建四个设备文件/dev/log/main、/dev/log/system、/dev/log/radio和/dev/log/events,分别用来代表四个日志设备。成员变量label用来描述日志设备的标号,其中,日志设备/dev/log/main、/dev/log/system、/dev/log/radio和/dev/log/events对应的标号分别为‘m’、‘s’、‘r’和‘e’。成员变量binary是一个布尔值,表示日志记录的内容是否是二进制格式的。我们知道,只有日志设备/dev/log/events的日志记录内容才是二进制格式的,其余的日志设备的日志记录内容均为文本格式的。成员变量fd是一个文件描述符,它是通过调用函数open来打开相应的日志设备文件得到的,用来从Logger日志驱动程序中读取日志记录。成员变量printed是一个布尔值,用来表示一个日志设备是否已经处于输出状态。成员变量queue用来保存日志设备中的日志记录。成员变量next用来连接下一个日志设备,这样,Logcat工具就可以把所有已经打开的日志设备保存在一个队列中。 结构体log_device_t的成员函数enqueue用来将一条日志记录添加到内部的日志记录队列中。每次往队列中加入一条日志记录时,都会根据它的写入时间来找到它在队列中的位置,然后再将它插入到队列中。两条日志记录的写入时间比较是通过调用函数cmp来实现的,它的定义如下所示。 **system/core/logcat/logcat.cpp** ~~~ static int cmp(queued_entry_t* a, queued_entry_t* b) { int n = a->entry.sec - b->entry.sec; if (n != 0) { return n; } return a->entry.nsec - b->entry.nsec; } ~~~ 它先比较日志写入时间的秒数。如果相等,再比较日志写入时间的毫秒数;否则,就可以得到比较结果。如果函数cmp的返回值大于0,就表示日志记录a的写入时间比日志记录b晚;如果函数cmp的返回值等于0,就表示日志记录a和b的写入时间相等;如果函数cmp的返回值小于0,就表示日志记录a的写入时间比日志记录b早。 **enum android_LogPriority** **system/core/include/android/log.h** ~~~ /* * Android log priority values, in ascending priority order. */ typedef enum android_LogPriority { ANDROID_LOG_UNKNOWN = 0, ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */ ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL, ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */ } android_LogPriority; ~~~ 枚举android_LogPriority用来描述日志记录的优先级,它一共有九个值,分别表示九个优先级。 **enum AndroidLogPrintFormat** **system/core/include/cutils/logprint.h** ~~~ typedef enum { FORMAT_OFF = 0, FORMAT_BRIEF, FORMAT_PROCESS, FORMAT_TAG, FORMAT_THREAD, FORMAT_RAW, FORMAT_TIME, FORMAT_THREADTIME, FORMAT_LONG, } AndroidLogPrintFormat; ~~~ 枚举AndroidLogPrintFormat用来描述日志记录的输出格式,它一共有九个值,分别表示九种不同的输出格式。在分析日志记录的输出过程时,我们再分析它们的含义。 **struct AndroidLogEntry_t** **system/core/include/cutils/logprint.h** ~~~ 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; ~~~ 与结构体logger_entry一样,结构体AndroidLogEntry_t也是用来描述一条日志记录的,不过,它描述的日志记录是经过解析了的,即已经将它的优先级、标签和内容都解析出来了,分别保存在成员变量priority、tag和message中。 **struct FilterInfo_t** **system/core/liblog/logprint.c** ~~~ typedef struct FilterInfo_t { char *mTag; android_LogPriority mPri; struct FilterInfo_t *p_next; } FilterInfo ~~~ 结构体FilterInfo_t用来描述一个日志记录输出过滤器。成员变量mTag和mPri分别表示要过滤的日志记录的标签和优先级。当一条日志记录的标签等于mTag时,如果它的优先级大于等于mPri,那么它就会被输出,否则就会被忽略。成员变量p_next用来连接下一个日志记录输出过滤器,这样就可以将所有的日志记录输出过滤器连接在一起形成一个队列。 **struct AndroidLogFormat_t** **system/core/liblog/logprint.c** ~~~ struct AndroidLogFormat_t { android_LogPriority global_pri; FilterInfo *filters; AndroidLogPrintFormat format; }; ~~~ 结构体AndroidLogFormat_t用来保存日志记录的输出格式以及输出过滤器。其中,成员变量global_pri是一个全局设置的默认日志记录输出过滤优先级,成员变量filters是一个日志记录输出过滤器列表,而成员变量format用来保存具体的日志记录输出格式。当日志记录输出过滤器列表filters中的某一个过滤器的过滤优先级被设置为默认优先级ANDROID_LOG_DEFAULT时,系统就会将它的过滤优先级修改为global_pri。 **struct EventTag** **system/core/liblog/event_tag_map.c** ~~~ typedef struct EventTag { unsigned int tagIndex; const char* tagStr; } EventTag; ~~~ 结构体EventTag用来描述类型为events的日志记录的标签号,每一个标签号(tagIndex)都对应有一个文本描述字符串(tagStr)。这些对应关系是通过解析目标设备上的/system/etc/event-log-tags文件得到的。 **struct EventTagMap** **system/core/liblog/event_tag_map.c** ~~~ struct EventTagMap { /* memory-mapped source file; we get strings from here */ void* mapAddr; size_t mapLen; /* array of event tags, sorted numerically by tag index */ EventTag* tagArray; int numTags; }; ~~~ 结构体EventTagMap用来描述类型为events的日志记录的内容格式,它同样是通过解析目标设备上的/system/etc/event-log-tags文件得到的。Logcat工具在打开目标设备上的/system/etc/event-log-tags文件时,会把它的内容映射到内存中,其中,成员变量mapAddr就指向这块内存的起始地址;成员变量mapLen表示该内存的大小;成员变量tagArray是一个EventTag类型的数组,数组的大小由成员变量numTags来描述。