欢迎来到三一办公! | 帮助中心 三一办公31ppt.com(应用文档模板下载平台)
三一办公
全部分类
  • 办公文档>
  • PPT模板>
  • 建筑/施工/环境>
  • 毕业设计>
  • 工程图纸>
  • 教育教学>
  • 素材源码>
  • 生活休闲>
  • 临时分类>
  • ImageVerifierCode 换一换
    首页 三一办公 > 资源分类 > DOCX文档下载  

    Android日志系统Logcat源代码简要分析.docx

    • 资源ID:3152600       资源大小:48.36KB        全文页数:30页
    • 资源格式: DOCX        下载积分:6.99金币
    快捷下载 游客一键下载
    会员登录下载
    三方登录下载: 微信开放平台登录 QQ登录  
    下载资源需要6.99金币
    邮箱/手机:
    温馨提示:
    用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)
    支付方式: 支付宝    微信支付   
    验证码:   换一换

    加入VIP免费专享
     
    账号:
    密码:
    验证码:   换一换
      忘记密码?
        
    友情提示
    2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
    3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
    4、本站资源下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。
    5、试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。

    Android日志系统Logcat源代码简要分析.docx

    Android日志系统Logcat源代码简要分析 在前面两篇文章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 bufLOGGER_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 msg0; /* 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 <count>,可以指定只显示最新count条记录,超过count的记录将被丢弃,在这里的实现中,就是要把排在队列前面的多余日记记录丢弃了,因为排在前面的日志记录是最旧的,默认是显示所有的日志记录。在下面的代码中,我们还会继续分析这个过程。 二. 打开日志设备文件。 Logcat工具的入口函数main,打开日志设备文件和一些初始化的工作也是在这里进行。main函数的内容也比较多,前面的逻辑都是解析命令行参数。这里假设我们使用logcat工具时,不带任何参数。这不会影响我们分析logcat读取日志的主线,有兴趣的读取可以自行分析解析命令行参数的逻辑。 分析完命令行参数以后,就开始要创建日志设备文件上下文结构体struct log_device_t了: view plain 1. if (!devices) 2. devices = new log_device_t(strdup("/dev/"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("/dev/"LOGGER_LOG_SYSTEM, accessmode) 9. devices->next = new log_device_t(strdup("/dev/"LOGGER_LOG_SYSTEM), false, '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 "log/main" 2. #define LOGGER_LOG_SYSTEM "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 ("couldn't open output file"); 14. exit(-1); 15. 16. 17. fstat(g_outFD, &statbuf); 18. 19. g_outByteCount = statbuf.st_size; 20. 21. 如果我们在执行logcat命令时,指定了-f <filename>选项,日志内容就输出到filename文件中,否则,就输出到标准输出控制台去了。 再接下来,就是打开日志设备文件了: view plain 1. dev = devices; 2. while (dev) 3. dev->fd = open(dev->device, mode); 4. if (dev->fd < 0) 5. fprintf(stderr, "Unable to open log device '%s': %sn", 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("ioctl"); 15. exit(EXIT_FAILURE); 16. 17. 18. 19. if (getLogSize) 20. int size, readable; 21. 22. size = android:getLogSize(dev->fd); 23. if (size < 0) 24. perror("ioctl"); 25. exit(EXIT_FAILURE); 26. 27. 28. readable = android:getLogReadableSize(dev->fd); 29. if (readable < 0) 30. perror("ioctl"); 31. exit(EXIT_FAILURE); 32. 33. 34. printf("%s: ring buffer is %dKb (%dKb consumed), " 35. "max entry is %db, max payload is %dbn", dev->device, 36. size / 1024, readable / 1024, 37. (int) LOGGER_ENTRY_MAX_LEN, (int) LOGGER_ENTRY_MAX_PAYLOAD); 38. 39. 40. dev = dev->next; 41. 如果执行logcat命令的目的是清空日志,即clearLog为true,则调用android:clearLog函数来执行清空日志操作: view plain 1. static int clearLog(int logfd) 2. 3. return ioctl(logfd, LOGGER_FLUSH_LOG); 4. 这里是通过标准的文件函数ioctl函数来执行日志清空操作,具体可以参考logger驱动程序的实现。 如果执行logcat命令的目的是获取日志内存缓冲区的大小,即getLogSize为true,通过调用android:getLogSize函数实现: view plain 1. /* returns the total size of the log's ring buffer */ 2. static int getLogSize(int logfd) 3. 4. return ioctl(logfd, LOGGER_GET_LOG_BUF_SIZE); 5. 如果为负数,即size < 0,就表示出错了,退出程序。 接着验证日志缓冲区可读内容的大小,即调用android:getLogReadableSize函数: view plain 1. /* returns the readable size of the log's ring buffer (that is, amount of the log consumed) */ 2. static int getLogReadableSize(int logfd) 3. 4. return ioctl(logfd, LOGGER_GET_LOG_LEN); 5. 如果返回负数,即readable < 0,也表示出错了,退出程序。 接下去的printf语句,就是输出日志缓冲区的大小以及可读日志的大小到控制台去了。 继续看下看代码,如果执行logcat命令的目的是清空日志或者获取日志的大小信息,则现在就完成使命了,可以退出程序了: view plain 1. if (getLogSize) 2. return 0; 3. 4. if (clearLog) 5. return 0; 6. 否则,就要开始读取设备文件的日志记录了: view plain 1. android:readLogLines(devices); 至此日志设备文件就打开并且初始化好了,下面,我们继续分析从日志设备文件读取日志记录的操作,即readLogLines函数。 三. 读取日志设备文件。 读取日志设备文件内容的函数是readLogLines函数: view plain 1. static void readLogLines(log_device_t* devices) 2. 3. log_device_t* dev; 4. int max = 0; 5. int ret; 6. int queued_lines = 0; 7. bool sleep = true; 8. 9. int result; 10. fd_set readset; 11. 12. for (dev=devices; dev; dev = dev->next) 13. if (dev->fd > max) 14. max = dev->fd; 15. 16. 17. 18. while (1) 19. do 20. timeval timeout = 0, 5000 /* 5ms */ ; / If we oversleep it's ok, i.e. ignore EINTR. 21. FD_ZERO(&readset); 22. for (dev=devices; dev; dev = dev->next) 23. FD_SET(dev->fd, &readset); 24. 25. result = select(max + 1, &readset, NULL, NULL, sleep ? NULL : &timeout); 26. while (result = -1 && errno = EINTR); 27. 28. if (result >= 0) 29. for (dev=devices; dev; dev = dev->next) 30. if (FD_ISSET(dev->fd, &readset) 31. queued_entry_t* entry = new queued_entry_t; 32. /* NOTE: driver guarantees we read exactly one full entry */ 33. ret = read(dev->fd, entry->buf, LOGGER_ENTRY_MAX_LEN); 34. if (ret < 0) 35. if (errno = EINTR) 36. delete entry; 37. goto next; 38. 39. if (errno = EAGAIN) 40. delete entry; 41. break; 42. 43. perror("logcat read"); 44. exit(EXIT_FAILURE); 45. 46. else if (!ret) 47. fprintf(stderr, "read: Unexpected EOF!n"); 48. exit(EXIT_FAILURE); 49. 50. 51. entry->entry.msgentry->entry.len = '0' 52. 53. dev->enqueue(entry); 54. +queued_lines; 55. 56. 57. 58. if (result = 0) 59. / we did our short timeout trick and there's nothing new 60. / print everything we have and wait for more data 61. sleep = true; 62. while (true) 63. chooseFirst(devices, &dev); 64. if (dev = NULL) 65. break; 66. 67. if (g_tail_lines = 0 | queued_lines <= g_tail_lines) 68. printNextEntry(dev); 69. else 70. skipNextEntry(dev); 71. 72. -queued_lines; 73. 74. 75. / the caller requested to just dump the log and exit 76. if (g_nonblock) 77. exit(0); 78. 79. else 80. / print all that aren't the last in their list 81. sleep = false; 82. while (g_tail_lines = 0 | queued_lines > g_tail_lines) 83. chooseFirst(devices, &dev); 84. if (dev = NULL | dev->queue->next = NULL) 85. break; 86. 87. if (g_tail_lines = 0) 88. printNextEntry(dev); 89. else 90. skipNextEntry(dev); 91. 92. -queued_lines; 93. 94. 95. 96. next: 97. ; 98. 99. 由于可能同时打开了多个日志设备文件,这里使用select函数来同时监控哪个文件当前可读: view plain 1. do 2. timeval timeout = 0, 5000 /* 5ms */ ; / If we oversleep it's ok, i.e. ignore EINTR. 3. FD_ZERO(&readset); 4. for (dev=devices; dev; dev = dev->next) 5. FD_SET(dev->fd, &readset); 6. 7. result = select(max + 1, &readset, NULL, NULL, sleep ? NULL : &timeout);8. while (result = -1 && errno = EINTR); 如果result >= 0,就表示有日志设备文件可读或者超时。接着,用一个for语句检查哪个设备文件可读,即FD_ISSET(dev->fd, &readset)是否为true,如果为true,表明可读,就要进一步通过read函数将日志读出,注意,每次只读出一条日志记录: view plain 1. for (dev=devices; dev; dev = dev->next) 2. if (FD_ISSET(dev->fd, &readset) 3. queued_entry_t* entry = new queued_entry_t; 4. /* NOTE: driver guarantees we read exactly one full entry */ 5. ret = read(dev->fd, entry->buf, LOGGER_ENTRY_MAX_LEN); 6. if (ret < 0) 7. if (errno = EINTR) 8. delete entry; 9. goto next; 10. 11. if (errno = EAGAIN) 12. delete entry; 13. break; 14. 15. perror("logcat read"); 16. exit(EXIT_FAILURE); 17. 18. else if (!ret) 19. fprintf(stderr, "read: Unexpected EOF!n"); 20. exit(EXIT_FAILURE); 21. 22. 23. entry->entry.msgentry->entry.len = '0' 24. 25. dev->enqueue(entry); 26. +queued_lines; 27. 28. 调用read函数之前,先创建一个日志记录项entry,接着调用read函数将日志读到entry->buf中,最后调用dev->enqueue(entry)将日志记录加入到日志队例中去。同时,把当前的日志记录数保存在queued_lines变量中。 继续进一步处理日志: view plain 1. if (result = 0) 2. / we did our short timeout trick and there's nothing new 3. / print everything we have and wait for more data 4. sleep = true; 5. while (true) 6. chooseFirst(devices, &dev); 7. if (dev = NULL) 8. break; 9. 10. if (g_tail_lines = 0 | queued_lines <= g_tail_lines) 11. printNextEntry(dev); 12. else 13. skipNextEntry(dev); 14. 15. -queued_lines; 16. 17. 18. / the caller requested to just dump the log and exit 19. if (g_nonblock) 20. exit(0); 21. 22. else 23. / print all that aren't the last in their list 24. sleep = false; 25. while (g_tail_lines = 0 | queued_lines > g_tail_lines) 26. chooseFirst(devices, &dev); 27. if (dev = NULL | dev->queue->next = NULL) 28. break; 29. 30. if (

    注意事项

    本文(Android日志系统Logcat源代码简要分析.docx)为本站会员(牧羊曲112)主动上传,三一办公仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知三一办公(点击联系客服),我们立即给予删除!

    温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载不扣分。




    备案号:宁ICP备20000045号-2

    经营许可证:宁B2-20210002

    宁公网安备 64010402000987号

    三一办公
    收起
    展开