Logcat源码分析(一)

本文编辑整理自:http://www.linuxidc.com/Linux/2011-07/38988.htm
Logcat 工具内置在Android系统中,可以在主机上通过adb logcat命令来查看模拟机上日志信息。如果你还不知道Logcat的使用,请参看logcat命令详解》。 Logcat 工具的用法很丰富,因此,源代码也比较多,本文并不打算完整地介绍整个Logcat工具的源代码,主要是介绍Logcat读取日志的主线,即从打开日志设备文件到读取日志设备文件的日志记录到输出日志记录的主要过程,希望能起到一个抛砖引玉的作用.
Logcat 工具源代码位于 system/core/logcat 目录下,只有一个源代码文件 logcat.cpp ,编译后生成的可执行文件位于 out/target/product/generic/system/bin 目录下,在模拟机中,可以在 /system/bin 目录下看到logcat工具。下面我们就分段来阅读 logcat.cpp 源代码文件。
一、Logcat中的基本数据结构
这些数据结构是用来保存从日志设备文件读出来的日志记录:
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;  
    }  
};  
  
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;  
        }  
    }  
}; 
其中,宏 LOGGER_ENTRY_MAX_LEN 和struct logger_entry定义在system/core/include/cutils/logger.h文件中,
Logger详解(一)一文有提到,为了方便描述,这里列出这个宏和结构体的定义:
struct  logger_entry {  
    __u16       len;     /* length of the payload */  
    __u16       __pad;   /* no matter what, we get 2 bytes of padding */  
    __s32       pid;     /* generating process's pid */  
    __s32       tid;     /* generating process's tid */  
    __s32       sec;     /* seconds since Epoch */  
    __s32       nsec;    /* nanoseconds */  
    char        msg[0];  /* the entry's payload */  
};  
  
#define  LOGGER_ENTRY_MAX_LEN        (4*1024)   

从结构体 queued_entry_t log_device_t 的定义可以看出,每一个 log_device_t 都包含有一个 queued_entry_t 队列, queued_entry_t 就是对应从日志设备文件读取出来的一条日志记录了 ,而 log_device_t 则是对应一个日志设备文件。从logcat来看共有4个日志设备文件,它们分别是 /dev/log/main , /dev/log/system , /dev/log/events /dev/log/radio
       正如《 Logger详解 》文中所述, dev/log/main , /dev/log/system , /dev/log/events /dev/log/radio 其实就是Android日志系统中四个日志设备文件 /dev/log_main /dev/log_system , /dev/log_events /dev/log_radio 的软连接,也就是说只是个映射。
      每个日志设备上下文通过其next成员指针连接起来,每个设备文件上下文的日志记录也是通过next指针连接起来。 日志记录队例是按时间戳从小到大排列的,这个 log_device_t::enqueue 函数可以看出,当要插入一条日志记录的时候,先队列头开始查找,直到找到一个时间戳比当前要插入的日志记录的时间戳大的日志记录的位置,然后插入当前日志记录 。比较函数cmp的定义如下:
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;  
}  
为什么日志记录要按照时间戳从小到大排序呢?原来, Logcat 在使用时,可以指定一个参数 -t <count> ,可以指定只显示最新 count 条记录,超过count的记录将被丢弃,在这里的实现中,就是要把排在队列前面的多余日记记录丢弃了,因为排在前面的日志记录是最旧的,默认是显示所有的日志记录。
二.、打开日志设备文件
Logcat 的入口函数在 logcat.cpp 文件中的 main ,打开日志设备文件和一些初始化的工作也是在这里进行。 main 函数的内容也比较多,前面的逻辑都是解析命令行参数 。这里假设我们使用logcat工具时,不带任何参数。这不会影响我们分析logcat读取日志的主线,有兴趣的读取可以自行分析解析命令行参数的逻辑。
分析完命令行参数以后,就开始要创建日志设备文件上下文结构体 log_device_t 了。
main() 函数中:
if (!devices) {  
    devices = new log_device_t(strdup( "/dev/"LOGGER_LOG_MAIN), false, 'm');  
    Android::g_devCount = 1;  
     int accessmode =  
              (mode & O_RDONLY) ? R_OK : 0  
            | (mode & O_WRONLY) ? W_OK : 0;  
    // only add this if it's available   
     if (0 == access( "/dev/"LOGGER_LOG_SYSTEM, accessmode)) {  
        devices->next = new log_device_t(strdup("/dev/"LOGGER_LOG_SYSTEM), false, 's');  
        Android::g_devCount++;  
    }  
由于我们假设使用 logca t时,不带任何命令行参数,这里的 devices 变量为NULL,因此,就会默认创建 /dev/log/main 设备上下文结构体,如果存在 /dev/log/system 设备文件,也会一并创建。宏 LOGGER_LOG_MAIN LOGGER_LOG_SYSTEM 也是定义在system/core/include/cutils/logger.h文件中:
#define LOGGER_LOG_MAIN     "log/main"   
#define LOGGER_LOG_SYSTEM   "log/system"  
注意:这里的日志文件的名字和 logger.cpp中的名字是不一致的。 正如《 Logger详解 》文中所述, dev/log/main , /dev/log/system , /dev/log/events /dev/log/radio 其实就是Android日志系统中四个日志设备文件 /dev/log_main /dev/log_system , /dev/log_events /dev/log_radio 的软连接,也就是说只是个映射。系统是在启动时,即 system/core/init/devices.c中device_init()->coldboot()->do_coldboot()->handle_device_fd()->handle_device_event() 进行日志的软连接
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值