应用程序打印kernel log

需求:

有时候需要监控应用程序关键动作,因此需要将应用程序的log写入到kernel log的缓冲区去。另外,有时候为了查看应用程序和kernel的时序,需要结合应用程序打印的log和kernel的log来查看前后关系。关于打印输出,https://elinux.org/Debugging_by_printing 做了详细的介绍。

kernel log输出介绍

1、dmesg命令官方说明的用法是:

dmesg is used to examine or control the kernel ring buffer. The default action is to read all messages from kernel ring buffer.

-n参数可以设置loglevel,详见:

1
2
3
4
5
6
7
8
-n, --console-level level
Set the level at which logging of messages is done to the console.
The level is a level number or abbreviation of the level name.
For all supported levels see dmesg --help output.
For example, -n 1 or -n alert prevents all messages, except emergency (panic) messages,
from appearing on the console. All levels of messages are still written to /proc/kmsg,
so syslogd(8) can still be used to control exactly where kernel messages appear.
When the -n option is used, dmesg will not print or clear the kernel ring buffer.

include/linux/kern_levels.h文件中定义了内核loglevel:

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
31
32
33
34
35
36
37
38
#ifndef __KERN_LEVELS_H__
#define __KERN_LEVELS_H__

#define KERN_SOH "\001" /* ASCII Start Of Header */
#define KERN_SOH_ASCII '\001'

#define KERN_EMERG KERN_SOH "0" /* system is unusable */
#define KERN_ALERT KERN_SOH "1" /* action must be taken immediately */
#define KERN_CRIT KERN_SOH "2" /* critical conditions */
#define KERN_ERR KERN_SOH "3" /* error conditions */
#define KERN_WARNING KERN_SOH "4" /* warning conditions */
#define KERN_NOTICE KERN_SOH "5" /* normal but significant condition */
#define KERN_INFO KERN_SOH "6" /* informational */
#define KERN_DEBUG KERN_SOH "7" /* debug-level messages */

#define KERN_DEFAULT KERN_SOH "d" /* the default kernel loglevel */

/*
* Annotation for a "continued" line of log printout (only done after a
* line that had no enclosing \n). Only to be used by core/arch code
* during early bootup (a continued line is not SMP-safe otherwise).
*/
#define KERN_CONT KERN_SOH "c"

/* integer equivalents of KERN_<LEVEL> */
#define LOGLEVEL_SCHED -2 /* Deferred messages from sched code
* are set to this special level */
#define LOGLEVEL_DEFAULT -1 /* default (or last) loglevel */
#define LOGLEVEL_EMERG 0 /* system is unusable */
#define LOGLEVEL_ALERT 1 /* action must be taken immediately */
#define LOGLEVEL_CRIT 2 /* critical conditions */
#define LOGLEVEL_ERR 3 /* error conditions */
#define LOGLEVEL_WARNING 4 /* warning conditions */
#define LOGLEVEL_NOTICE 5 /* normal but significant condition */
#define LOGLEVEL_INFO 6 /* informational */
#define LOGLEVEL_DEBUG 7 /* debug-level messages */

#endif

在终端中可以查看当前设置的loglevel:

1
2
3
# cat /proc/sys/kernel/printk
7 4 1 7
current default minimum boot-time-default

关于dmesg的具体实现,可以在androidxref.com中查找或者查看busyboxtoybox中关于dmesg命令的实现,最终都是调用klogctl()函数获取到kernel message。
查看klogctl函数的帮助文档man klogctl得到关于这个函数的介绍和用法:

syslog, klogctl - read and/or clear kernel message ring buffer; set console_loglevel

2、cat /proc/kmsg打印kernel log

1
2
3
4
5
The kernel log buffer is accessible for reading from userspace by /proc/kmsg. 
/proc/kmsg behaves more or less like a FIFO and blocks until new messages appear.
Please note - reading from /proc/kmsg consumes the messages
in the ring buffer so they may not be available for other programs.
It is usually a good idea to let klogd or syslog do this job and read the content of the buffer via dmesg.

3、/dev/kmsg设备节点
应用程序可以通过/dev/kmsg节点将打印信息写入到kernel log中。关于这个节点详见官方的介绍:https://www.kernel.org/doc/Documentation/ABI/testing/dev-kmsg
因此,下面的程序就是将log输出到kernel log中。

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>

#define PRINTFK_LOG_BUF_MAX 1024

enum LogLevel
{
LOGLEVEL_EMERG = 0, /* system is unusable */
LOGLEVEL_ALERT, /* action must be taken immediately */
LOGLEVEL_CRIT, /* critical conditions */
LOGLEVEL_ERR, /* error conditions */
LOGLEVEL_WARNING, /* warning conditions */
LOGLEVEL_NOTICE, /* normal but significant condition */
LOGLEVEL_INFO, /* informational */
LOGLEVEL_DEBUG, /* debug-level messages */
};

void printfk(enum LogLevel level, const char *fmt, ...)
{
va_list args;
int log_fd;
int ret;
char buf[PRINTFK_LOG_BUF_MAX] = {0};

if ((level < LOGLEVEL_EMERG) || (level > LOGLEVEL_DEBUG) || (NULL == fmt)) {
return;
}

buf[0] = '<';
buf[1] = '0' + level;
buf[2] = '>';

va_start(args, fmt);
vsnprintf(&buf[3], (PRINTFK_LOG_BUF_MAX - 3), fmt, args);
log_fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
if (-1 != log_fd) {
ret = write(log_fd, buf, sizeof(buf));
close(log_fd);
}
va_end(args);
}

int main(void)
{
int a = 10;
char *s = "abcdefgh";
printfk(LOGLEVEL_DEBUG, "[xxx] bbbbbbbbb");
printfk(LOGLEVEL_DEBUG, "%d, %s", a, s);
}

参考资料

https://github.com/karelzak/util-linux/blob/master/sys-utils/dmesg.c
https://github.com/brgl/busybox/blob/master/util-linux/dmesg.c