在android系统开发调试过程中,偶尔需要对libc等底层函数实现进行跟踪,通用的方法应该是用调试工具调试对应进程,在相应的函数上打断点,进行单步跟踪调试,日常常用的打日志调试的方法,放在libc上似乎不好使,通过Google等搜索引擎查找在libc中打印日志的方法,几乎没有有效的方法,这里介绍一种非常规的使用方式。
init进程如何打印日志
在android系统中,绝大部分进程打印日志,都是通过liblog这个库进行处理,将日志输入到log dev中再由logcat命令进行读取,但init进程是个例外。作为系统启动过程中的第一个进程,其日志式写入到内核的printk buffer中,通过dmesg命令即可得到其日志信息。具体实现可参考系统源码中system/core/libcutils/klog.c
文件。主要实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| void klog_init(void) { static const char *name = "/dev/__kmsg__"; if (klog_fd >= 0) return; /* Already initialized */ if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) { klog_fd = open(name, O_WRONLY); if (klog_fd < 0) return; fcntl(klog_fd, F_SETFD, FD_CLOEXEC); unlink(name); } } #define LOG_BUF_MAX 512 void klog_vwrite(int level, const char *fmt, va_list ap) { char buf[LOG_BUF_MAX]; if (level > klog_level) return; if (klog_fd < 0) klog_init(); if (klog_fd < 0) return; vsnprintf(buf, LOG_BUF_MAX, fmt, ap); buf[LOG_BUF_MAX - 1] = 0; write(klog_fd, buf, strlen(buf)); }
|
可以看到其过程先用mknod在dev下创建一个/dev/__kmsg__的字符设备,而后打开它获取文件描述符klog_fd,再将这个设备删除,需要写日志的时候只需将日志内容写入klog_fd中即可。
在bionic中打印日志
参考init进程中打印日志的实现,我们可以在需要打印日志的库函数中定义如下宏:
1 2 3 4 5 6 7 8 9
| #define klog(...) { \ int kfd = open("/dev/kmsg",O_RDWR); \ if ( kfd > 0 ){ \ char buf[512]; \ sprintf(buf,__VA_ARGS__); \ write(kfd,buf,strlen(buf)); \ close(kfd); \ } \ }
|
确保<unistd.h> <sys/types.h> <sys/stat.h>三个头文件引入。在需要输入日志的地方,直接插入klog("something output");
即可。使用过程中要注意/dev/kmsg
设备的权限。
kmsg说明