Linux日志管理-dynamic_debug

1 dynamic_debug介绍#

img

这里强烈推荐驱动开发者用这种方式输出log。linux kernel space中有pr_debugdev_dbg来使用dynamic debug。可以看到当用户define DEBUG后,pr_debugdev_dbg就等于printk的KERN_DEBUG级别输出了;否则什么也不打印。

img

1.1 开启dynamic debug#

要使用dynamic_debug需要在kernel的defconfig中开启。

1
2
CONFIG_DEBUG_FS=y
CONFIG_DYNAMIC_DEBUG=y

用menuconfig去配置的话如下图:

img

1.2 dynamic debug使用#

编译好image后,需要挂载debugfs(不挂载的话将不会创建debugfs,那么/sys/kernel/debug/下是空的)。

修改etc/fstab文件,追加下面这段字符:

1
nodev      /sys/kernel/debug debugfs   defaults    0   0

img

可以用cat /sys/kernel/debug/dynamic_debug/control | grep xxx.c来查看自己想要查看的log所在文件有没有包含进去。

img

这里可以看到该文件所有用dev_dbg()打印出的讯息。

那如果不开启CONFIG_DYNAMIC_DEBUG,将不会产生/sys/kernel/debug/dynamic_debug目录, 是不能进行动态打印的。

1.2.1 开启dynamic debug#

1
2
echo "module cvi_mipi_rx +p" > /sys/kernel/debug/dynamic_debug/control
echo "file cvi_vip_cif.c +p" >/sys/kernel/debug/dynamic_debug/control

这两种方式都是开dynamic debug,第一种是对模块开启,第二种只对文件开启。

下面举一个栗子:

img

开启之后,可以看到dev_dbg()打印的log都会输出。

img

反之,关闭dynamic debug

1
2
echo "module cvi_mipi_rx -p" > /sys/kernel/debug/dynamic_debug/control
echo "file cvi_vip_cif.c -p" >/sys/kernel/debug/dynamic_debug/control

除了上面的两种方式还有一种可以只开启某个function:

1
echo "func _init_resource +p" > /sys/kernel/debug/dynamic_debug/control

img

2 dev_err/dev_info/dev_warn#

在Linux驱动代码中,有大量的调试信息,那么推荐使用dev_err/dev_info/dev_warn这一系列函数族。这一系列函数族定义在include/linux/device.h

img

其实这些函数族本质上和下面printk.h中的定义也是完全一致的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#define pr_emerg(fmt, ...) \
printk(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__)
#define pr_alert(fmt, ...) \
printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
#define pr_crit(fmt, ...) \
printk(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
#define pr_err(fmt, ...) \
printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
#define pr_warning(fmt, ...) \
printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
#define pr_warn pr_warning
#define pr_notice(fmt, ...) \
printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)
#define pr_info(fmt, ...) \
printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)

下图是示例,可以看到err级别以下的log没有打印,那么设置printk的控制台级别可以把对应的log输出到console。

img

如何设置printk console level可以看上一篇Linux日志管理-printk和dmesg

3 可变参数宏#

##__VA_ARGS__表示可变参数宏,可以用来传递多个参数,如:

1
2
3
4
5
6
7
8
9
10
11
#define my_dbg(fmt, ...) \
do { \
printf("[%s] [%d] " fmt, __func__, __LINE__, ##__VA_ARGS__);\
} while(0)
#define my_dbg(fmt...) \
do { \
  printf("[%s] [%d] ", __func__, __LINE__); \
  printf(fmt); \
} while(0)

char *name = "robin"; int age = 18; my_dbg("this is a test. name:%s, age:%d\n", name, age);

结果如下:

img

那和下面这种写法呢本质上是完全一样的。

img

4 模块打印等级控制#

4.1 按照打印等级控制#

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
#ifndef _XXX_DEBUG_H_
#define _XXX_DEBUG_H_

#include <linux/debugfs.h>

extern u32 xxx_log_lv;

#define DBG_ERR 1 /* error conditions */
#define DBG_WARN 2 /* warning conditions */
#define DBG_NOTICE 3 /* normal but significant condition */
#define DBG_INFO 4 /* informational */
#define DBG_DEBUG 5 /* debug-level messages */

#if defined(CONFIG_LOG)
#define TRACE_XXX(level, fmt, ...) \
do { \
if (level <= xxx_log_lv) { \
if (level == DBG_ERR) \
pr_err("%s:%d(): " fmt, __func__, __LINE__, ##__VA_ARGS__); \
else if (level == DBG_WARN) \
pr_warn("%s:%d(): " fmt, __func__, __LINE__, ##__VA_ARGS__); \
else if (level == DBG_NOTICE) \
pr_notice("%s:%d(): " fmt, __func__, __LINE__, ##__VA_ARGS__); \
else if (level == DBG_INFO) \
pr_info("%s:%d(): " fmt, __func__, __LINE__, ##__VA_ARGS__); \
else if (level == DBG_DEBUG) \
printk(KERN_DEBUG "%s:%d(): " fmt, __func__, __LINE__, ##__VA_ARGS__); \
} \
} while (0)
#else
#define TRACE_XXX(level, fmt, ...)
#endif

#endif /* _xxx_DEBUG_H_ */

当级别高于DBG,即可输出高于DBG的所有级别打印。

4.2 精确控制打印等级#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
extern u32 vi_log_lv;
enum vi_msg_pri {
VI_ERR = 0x1,
VI_WARN = 0x2,
VI_NOTICE = 0x4,
VI_INFO = 0x8,
VI_DBG = 0x10,
};
#define vi_pr(level, fmt, arg...) \
do { \
if (vi_log_lv & level) { \
if (level == VI_ERR) \
pr_err("%s:%d(): " fmt, __func__, __LINE__, ## arg); \
else if (level == VI_WARN) \
pr_warn("%s:%d(): " fmt, __func__, __LINE__, ## arg); \
else if (level == VI_NOTICE) \
pr_notice("%s:%d(): " fmt, __func__, __LINE__, ## arg); \
else if (level == VI_INFO) \
pr_info("%s:%d(): " fmt, __func__, __LINE__, ## arg); \
else if (level == VI_DBG) \
pr_debug("%s:%d(): " fmt, __func__, __LINE__, ## arg); \
} \
} while (0)

可以随意开启任意级别打印,比如只开启DBG,只开启WARN。

5 用户态模块打印等级控制#

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
XXX_S32 *log_levels;
XXX_CHAR const *log_name[8] = {
(XXX_CHAR *)"EMG", (XXX_CHAR *)"ALT", (XXX_CHAR *)"CRI", (XXX_CHAR *)"ERR",
(XXX_CHAR *)"WRN", (XXX_CHAR *)"NOT", (XXX_CHAR *)"INF", (XXX_CHAR *)"DBG"
};

XXX_S32 XXX_LOG_SetLevelConf(LOG_LEVEL_CONF_S *pstConf)
{
log_levels[pstConf->enModId] = pstConf->s32Level;
return XXX_SUCCESS;
}

XXX_S32 XXX_LOG_GetLevelConf(LOG_LEVEL_CONF_S *pstConf)
{
pstConf->s32Level = log_levels[pstConf->enModId];
return XXX_SUCCESS;
}

#ifndef __XXX_DEBUG_H__
#define __XXX_DEBUG_H__

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <syslog.h>
#include <string.h>
#include <XXX_common.h>

#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif /* End of #ifdef __cplusplus */

/*
* Debug Config
*/
#define CONFIG_XXX_GDB_NO 1
#define CONFIG_XXX_GDB "n"
#define CONFIG_XXX_LOG_TRACE_SUPPORT 1
#define CONFIG_XXX_LOG_TRACE_ALL 1
#define CONFIG_XXX_LOG_TRACE_LEVEL 4


#define XXX_DBG_EMERG 0 /* system is unusable */
#define XXX_DBG_ALERT 1 /* action must be taken immediately */
#define XXX_DBG_CRIT 2 /* critical conditions */
#define XXX_DBG_ERR 3 /* error conditions */
#define XXX_DBG_WARN 4 /* warning conditions */
#define XXX_DBG_NOTICE 5 /* normal but significant condition */
#define XXX_DBG_INFO 6 /* informational */
#define XXX_DBG_DEBUG 7 /* debug-level messages */

typedef struct _LOG_LEVEL_CONF_S {
MOD_ID_E enModId;
XXX_S32 s32Level;
char cModName[16];
} LOG_LEVEL_CONF_S;

#define XXX_PRINT printf

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"

extern XXX_S32 * log_levels;
extern XXX_CHAR const *log_name[8];

#pragma GCC diagnostic pop

#define _GENERATE_STRING(STRING) (#STRING),
static const char *const MOD_STRING[] = FOREACH_MOD(_GENERATE_STRING);
#define XXX_GET_MOD_NAME(id) (id < XXX_ID_BUTT)? MOD_STRING[id] : "UNDEF"

/* #ifdef XXX_DEBUG */
#ifdef CONFIG_XXX_LOG_TRACE_SUPPORT

#define XXX_ASSERT(expr) \
do { \
if (!(expr)) { \
printf("\nASSERT at:\n" \
" >Function : %s\n" \
" >Line No. : %d\n" \
" >Condition: %s\n", \
__func__, __LINE__, #expr); \
_exit(-1); \
} \
} while (0)

#ifndef FPGA_PORTING

#define XXX_TRACE(level, enModId, fmt, ...) \
do { \
XXX_S32 LogLevel = (log_levels == NULL) ? CONFIG_XXX_LOG_TRACE_LEVEL : log_levels[enModId]; \
if (level <= LogLevel) \
syslog(LOG_LOCAL5|level, "[%s-%s] " fmt, XXX_GET_MOD_NAME(enModId), log_name[level], \
##__VA_ARGS__); \
} while (0)
#else
#define XXX_TRACE(level, enModId, fmt, ...) \
printf(fmt, ##__VA_ARGS__)
#endif
#else
#define XXX_ASSERT(expr)
#define XXX_TRACE(level, enModId, fmt...)
#endif

#define XXX_TRACE_ID(level, id, fmt, ...) \
XXX_TRACE(level, id, "%s:%d:%s(): " fmt, __FILENAME__, __LINE__, __func__, ##__VA_ARGS__)

#define XXX_TRACE_LOG(level, fmt, ...) \
XXX_TRACE(level, XXX_ID_LOG, "%s:%d:%s(): " fmt, __FILENAME__, __LINE__, __func__, ##__VA_ARGS__)

#define XXX_TRACE_SYS(level, fmt, ...) \
XXX_TRACE(level, XXX_ID_SYS, "%s:%d:%s(): " fmt, __FILENAME__, __LINE__, __func__, ##__VA_ARGS__)

#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* __cplusplus */

#endif /* __XXX_COMM_SYS_H__ */