1 dynamic_debug介绍
这里强烈推荐驱动开发者用这种方式输出log。linux kernel space中有pr_debug
及dev_dbg
来使用dynamic debug。可以看到当用户define DEBUG
后,pr_debug
和dev_dbg
就等于printk的KERN_DEBUG级别输出了;否则什么也不打印。
1.1 开启dynamic debug
要使用dynamic_debug需要在kernel的defconfig中开启。
CONFIG_DEBUG_FS=y
CONFIG_DYNAMIC_DEBUG=y
用menuconfig去配置的话如下图:
1.2 dynamic debug使用
编译好image后,需要挂载debugfs(不挂载的话将不会创建debugfs,那么/sys/kernel/debug/
下是空的)。
修改etc/fstab文件,追加下面这段字符:
nodev /sys/kernel/debug debugfs defaults 0 0
可以用cat /sys/kernel/debug/dynamic_debug/control | grep xxx.c
来查看自己想要查看的log所在文件有没有包含进去。
这里可以看到该文件所有用dev_dbg()
打印出的讯息。
那如果不开启CONFIG_DYNAMIC_DEBUG
,将不会产生/sys/kernel/debug/dynamic_debug
目录, 是不能进行动态打印的。
1.2.1 开启dynamic debug
echo "module xxx_mipi_rx +p" > /sys/kernel/debug/dynamic_debug/control
echo "file xxx_vip_cif.c +p" >/sys/kernel/debug/dynamic_debug/control
这两种方式都是开dynamic debug,第一种是对模块开启,第二种只对文件开启。
下面举一个栗子:
开启之后,可以看到dev_dbg()
打印的log都会输出。
反之,关闭dynamic debug
:
echo "module xxx_mipi_rx -p" > /sys/kernel/debug/dynamic_debug/control
echo "file xxx_vip_cif.c -p" >/sys/kernel/debug/dynamic_debug/control
除了上面的两种方式还有一种可以只开启某个function:
echo "func _init_resource +p" > /sys/kernel/debug/dynamic_debug/control
2 dev_err/dev_info/dev_warn
在Linux驱动代码中,有大量的调试信息,那么推荐使用dev_err/dev_info/dev_warn
这一系列函数族。这一系列函数族定义在include/linux/device.h
。
其实这些函数族本质上和下面printk.h中的定义也是完全一致的。
#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。
如何设置printk console level可以看上一篇Linux日志管理-printk和dmesg。
3 可变参数宏
##__VA_ARGS__表示可变参数宏,可以用来传递多个参数,如:
#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);
结果如下:
那和下面这种写法呢本质上是完全一样的。
4 模块打印等级控制
4.1 按照打印等级控制
#ifndef _VPSS_DEBUG_H_
#define _VPSS_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 精确控制打印等级
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 用户态模块打印等级控制
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__ */