Android日志系统Logcat源代码简要分析
更新时间:2023-11-12 13:10:01 阅读量: 教育文库 文档下载
在前面两篇文章Android日志系统驱动程序Logger源代码分析和Android应用程序框架层和系统运行库层日志系统源代码中,介绍了Android内核空间层、系统运行库层和应用程序框架层日志系统相关的源代码,其中,后一篇文章着重介绍了日志的写入操作。为了描述完整性,这篇文章着重介绍日志的读取操作,这就是我们在开发Android应用程序时,经常要用到日志查看工具Logcat了。
Logcat工具内置在Android系统中,可以在主机上通过adb logcat命令来查看模拟机上日志信息。Logcat工具的用法很丰富,因此,源代码也比较多,本文并不打算完整地介绍整个Logcat工具的源代码,主要是介绍Logcat读取日志的主线,即从打开日志设备文件到读取日志设备文件的日志记录到输出日志记录的主要过程,希望能起到一个抛砖引玉的作用。
Logcat工具源代码位于system/core/logcat目录下,只有一个源代码文件logcat.cpp,编译后生成的可执行文件位于out/target/product/generic/system/bin目录下,在模拟机中,可以在/system/bin目录下看到logcat工具。下面我们就分段来阅读logcat.cpp源代码文件。
一. Logcat工具的相关数据结构。
这些数据结构是用来保存从日志设备文件读出来的日志记录:
view plain
1. struct queued_entry_t { 2. union {
3. unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4
)));
4. struct logger_entry entry __attribute__((aligned(4))); 5. };
6. queued_entry_t* next; 7.
8. queued_entry_t() { 9. next = NULL; 10. } 11. }; 12.
13. struct log_device_t { 14. char* device; 15. bool binary; 16. int fd; 17. bool printed;
18. char label; 19.
20. queued_entry_t* queue; 21. log_device_t* next; 22.
23. log_device_t(char* d, bool b, char l) { 24. device = d; 25. binary = b; 26. label = l; 27. queue = NULL; 28. next = NULL; 29. printed = false; 30. } 31.
32. void enqueue(queued_entry_t* entry) { 33. if (this->queue == NULL) { 34. this->queue = entry; 35. } else {
36. queued_entry_t** e = &this->queue; 37. while (*e && cmp(entry, *e) >= 0) { 38. e = &((*e)->next); 39. }
40. entry->next = *e; 41. *e = entry; 42. } 43. } 44. };
其中,宏LOGGER_ENTRY_MAX_LEN和struct logger_entry定义在system/core/include/cutils/logger.h文件中,在Android应用程序框架层和系统运行库层日志系统源代码分析一文有提到,为了方便描述,这里列出这个宏和结构体的定义:
view plain
1. struct logger_entry {
2. __u16 len; /* length of the payload */
3. __u16 __pad; /* no matter what, we get 2 bytes of padding */ 4. __s32 pid; /* generating process's pid */ 5. __s32 tid; /* generating process's tid */ 6. __s32 sec; /* seconds since Epoch */ 7. __s32 nsec; /* nanoseconds */
8. char msg[0]; /* the entry's payload */ 9. }; 10.
11. #define LOGGER_ENTRY_MAX_LEN (4*1024)
从结构体struct queued_entry_t和struct log_device_t的定义可以看出,每一个log_device_t都包含有一个queued_entry_t队列,queued_entry_t就是对应从日志设备文件读取出来的一条日志记录了,而log_device_t则是对应一个日志设备文件上下文。在Android日志系统驱动程序Logger源代码分析一文中,我们曾提到,Android日志系统有三个日志设备文件,分别是/dev/log/main、/dev/log/events和/dev/log/radio。
每个日志设备上下文通过其next成员指针连接起来,每个设备文件上下文的日志记录也是通过next指针连接起来。日志记录队例是按时间戳从小到大排列的,这个log_device_t::enqueue函数可以看出,当要插入一条日志记录的时候,先队列头开始查找,直到找到一个时间戳比当前要插入的日志记录的时间戳大的日志记录的位置,然后插入当前日志记录。比较函数cmp的定义如下:
view plain
1. static int cmp(queued_entry_t* a, queued_entry_t* b) { 2. int n = a->entry.sec - b->entry.sec; 3. if (n != 0) { 4. return n; 5. }
6. return a->entry.nsec - b->entry.nsec; 7. }
为什么日志记录要按照时间戳从小到大排序呢?原来,Logcat在使用时,可以指定一个参数-t
Logcat工具的入口函数main,打开日志设备文件和一些初始化的工作也是在这里进行。main函数的内容也比较多,前面的逻辑都是解析命令行参数。这里假设我们使用logcat工具时,不带任何参数。这不会影响我们分析logcat读取日志的主线,有兴趣的读取可以自行分析解析命令行参数的逻辑。
分析完命令行参数以后,就开始要创建日志设备文件上下文结构体struct log_device_t了:
view plain
1. if (!devices) {
2. devices = new log_device_t(strdup(\LOGGER_LOG_MAIN), false, 'm');
3. android::g_devCount = 1; 4. int accessmode =
5. (mode & O_RDONLY) ? R_OK : 0 6. | (mode & O_WRONLY) ? W_OK : 0; 7. // only add this if it's available
8. if (0 == access(\LOGGER_LOG_SYSTEM, accessmode)) {
9. devices->next = new log_device_t(strdup(\LOGGER_LOG_SYSTEM), f
alse, 's');
10. android::g_devCount++; 11. } 12. }
由于我们假设使用logcat时,不带任何命令行参数,这里的devices变量为NULL,因此,就会默认创建/dev/log/main设备上下文结构体,如果存在/dev/log/system设备文件,也会一并创建。宏LOGGER_LOG_MAIN和
LOGGER_LOG_SYSTEM也是定义在system/core/include/cutils/logger.h文件中:
view plain
1. #define LOGGER_LOG_MAIN \ 2. #define LOGGER_LOG_SYSTEM \
我们在Android日志系统驱动程序Logger源代码分析一文中看到,在Android日志系统驱动程序Logger中,默认是不创建/dev/log/system设备文件的。 往下看,调用setupOutput()函数来初始化输出文件:
view plain
1. android::setupOutput();
setupOutput()函数定义如下:
view plain
1. static void setupOutput() 2. { 3.
4. if (g_outputFileName == NULL) {
5. g_outFD = STDOUT_FILENO; 6.
7. } else {
8. struct stat statbuf; 9.
10. g_outFD = openLogFile (g_outputFileName); 11.
12. if (g_outFD < 0) {
13. perror (\); 14. exit(-1); 15. } 16.
17. fstat(g_outFD, &statbuf); 18.
19. g_outByteCount = statbuf.st_size; 20. } 21. }
如果我们在执行logcat命令时,指定了-f
view plain
1. dev = devices; 2. while (dev) {
3. dev->fd = open(dev->device, mode); 4. if (dev->fd < 0) {
5. fprintf(stderr, \, 6. dev->device, strerror(errno)); 7. exit(EXIT_FAILURE); 8. } 9.
10. if (clearLog) { 11. int ret;
12. ret = android::clearLog(dev->fd); 13. if (ret) {
14. perror(\); 15. exit(EXIT_FAILURE); 16. } 17. } 18.
19. if (getLogSize) {
- exercise2
- 铅锌矿详查地质设计 - 图文
- 厨余垃圾、餐厨垃圾堆肥系统设计方案
- 陈明珠开题报告
- 化工原理精选例题
- 政府形象宣传册营销案例
- 小学一至三年级语文阅读专项练习题
- 2014.民诉 期末考试 复习题
- 巅峰智业 - 做好顶层设计对建设城市的重要意义
- (三起)冀教版三年级英语上册Unit4 Lesson24练习题及答案
- 2017年实心轮胎现状及发展趋势分析(目录)
- 基于GIS的农用地定级技术研究定稿
- 2017-2022年中国医疗保健市场调查与市场前景预测报告(目录) - 图文
- 作业
- OFDM技术仿真(MATLAB代码) - 图文
- Android工程师笔试题及答案
- 生命密码联合密码
- 空间地上权若干法律问题探究
- 江苏学业水平测试《机械基础》模拟试题
- 选课走班实施方案
- 简要
- 源代码
- Android
- 分析
- Logcat
- 系统
- 日志
- 反违章管理办法
- SDH-中级
- 苏教版必修一象山那样思考
- 2014届浙江省温州中学高三3月月考理科数学试题(含答案解析)
- 冷却塔用水量(环评)
- 乌海德晟煤焦化有限公司安康杯安全活动方案
- 统编人教版七年级语文下册第一单元整体教学导学案
- 1口语3看图说话-1
- 2011年全国各地中考数学试卷分类汇编第33章直线与圆的位置关系
- 计算机图形学 试卷
- 供应链与物流管理试题
- 烟塔合一技术在火力发电厂的应用
- 2019高中数学 第三章 函数的应用 3.1.1 方程的根和函数的零点校本作业 新人教A版必修1
- 中企动力-网站策划方案 - 图文
- 2014内蒙古公务员考试笔试内容
- 2013年12月21日一级机试试题卷
- 在全市加强作风建设提高行政效能推进大会上讲话
- PScs3之鞋类设计效果图步骤及技巧
- 河南省郑州市郑州四中2010届高三第五次调考(物理) 命题人李永升
- 化工原理(下)课后习题解答 天津大学化工学院 柴诚敬