这里强烈推荐驱动开发者用这种方式输出log。linux kernel space中有pr_debug及dev_dbg来使用dynamic debug。可以看到当用户define DEBUG后,prdebug和dev_dbg就等于printk的KERN_DEBUG级别输出了;否则什么也不打印。
一. 开启dynamic debug功能
要使用dynamic_debug需要在kernel的defconfig中开启
CONFIG_DEBUG_FS=y
CONFIG_DYNAMIC_DEBUG=y
用menuconfig去配置的话如下图:
二 dynamic debug功能使用
1. 编译好image后,需要挂载debugfs(不挂载的话将不会创建debugfs,那么/sys/kernel/debug/下是空的)。
修改etc/fstab文件,追加下面这段字符。
nodev /sys/kernel/debug debugfs defaults 0 0
2. 可以用cat /sys/kernel/debug/dynamic_debug/control | grep xxx.c来查看自己想要查看的log所在文件有没有包含进去。
那这里可以看到该文件所有用dev_dbg()打印出的讯息。
那如果不开启CONFIG_DYNAMIC_DEBUG,将不会产生/sys/kernel/debug/dynamic_debug目录, 是不能进行动态打印的。
开启dynamic debug:
①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,第一种是对模块开启,第二种只对文件开启。下面举一个栗子:
开启之后,可以看到dev_dbg()打印的log都会输出。
反之,关闭dynamic debug:
①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:
echo "func _init_resource +p" > /sys/kernel/debug/dynamic_debug/control
不过一般不太推荐使用这种做法,因为很多functions都是相同名字但属于不同modules的。
三 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可以看上一篇1. printk & dmesg)。
四 可变参数宏(##__VA_ARGS__)
##__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);
结果如下:
那和下面这种写法呢本质上是完全一样的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#ifndef _VPSS_DEBUG_H_ #define _VPSS_DEBUG_H_ #include <linux/debugfs.h> extern u32 vpss_log_lv; #define CVI_DBG_ERR 1 /* error conditions */ #define CVI_DBG_WARN 2 /* warning conditions */ #define CVI_DBG_NOTICE 3 /* normal but significant condition */ #define CVI_DBG_INFO 4 /* informational */ #define CVI_DBG_DEBUG 5 /* debug-level messages */ #if defined(CONFIG_CVI_LOG) #define CVI_TRACE_VPSS(level, fmt, ...) \ do { \ if (level <= vpss_log_lv) { \ if (level == CVI_DBG_ERR) \ pr_err("%s:%d(): " fmt, __func__, __LINE__, ##__VA_ARGS__); \ else if (level == CVI_DBG_WARN) \ pr_warn("%s:%d(): " fmt, __func__, __LINE__, ##__VA_ARGS__); \ else if (level == CVI_DBG_NOTICE) \ pr_notice("%s:%d(): " fmt, __func__, __LINE__, ##__VA_ARGS__); \ else if (level == CVI_DBG_INFO) \ pr_info("%s:%d(): " fmt, __func__, __LINE__, ##__VA_ARGS__); \ else if (level == CVI_DBG_DEBUG) \ printk(KERN_DEBUG "%s:%d(): " fmt, __func__, __LINE__, ##__VA_ARGS__); \ } \ } while (0) #else #define CVI_TRACE_VPSS(level, fmt, ...) #endif #endif /* _VPSS_DEBUG_H_ */
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)
可以看到这两种log控制级别的差异,第一种简单,当级别高于DBG,即可输出高于DBG的所有级别打印。第二种优点是可以随意开启任意级别打印,比如只开启DBG,只开启WARN。
用户态模块打印等级控制:
CVI_S32 *log_levels; CVI_CHAR const *log_name[8] = { (CVI_CHAR *)"EMG", (CVI_CHAR *)"ALT", (CVI_CHAR *)"CRI", (CVI_CHAR *)"ERR", (CVI_CHAR *)"WRN", (CVI_CHAR *)"NOT", (CVI_CHAR *)"INF", (CVI_CHAR *)"DBG" }; CVI_S32 CVI_LOG_SetLevelConf(LOG_LEVEL_CONF_S *pstConf) { log_levels[pstConf->enModId] = pstConf->s32Level; return CVI_SUCCESS; } CVI_S32 CVI_LOG_GetLevelConf(LOG_LEVEL_CONF_S *pstConf) { pstConf->s32Level = log_levels[pstConf->enModId]; return CVI_SUCCESS; } /* * Copyright (C) Cvitek Co., Ltd. 2019-2020. All rights reserved. * * File Name: include/cvi_debug.h * Description: */ #ifndef __CVI_DEBUG_H__ #define __CVI_DEBUG_H__ #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <syslog.h> #include <string.h> #include <cvi_common.h> #ifdef __cplusplus #if __cplusplus extern "C" { #endif #endif /* End of #ifdef __cplusplus */ /* * Debug Config */ #define CONFIG_CVI_GDB_NO 1 #define CONFIG_CVI_GDB "n" #define CONFIG_CVI_LOG_TRACE_SUPPORT 1 #define CONFIG_CVI_LOG_TRACE_ALL 1 #define CONFIG_CVI_LOG_TRACE_LEVEL 4 #define CVI_DBG_EMERG 0 /* system is unusable */ #define CVI_DBG_ALERT 1 /* action must be taken immediately */ #define CVI_DBG_CRIT 2 /* critical conditions */ #define CVI_DBG_ERR 3 /* error conditions */ #define CVI_DBG_WARN 4 /* warning conditions */ #define CVI_DBG_NOTICE 5 /* normal but significant condition */ #define CVI_DBG_INFO 6 /* informational */ #define CVI_DBG_DEBUG 7 /* debug-level messages */ typedef struct _LOG_LEVEL_CONF_S { MOD_ID_E enModId; CVI_S32 s32Level; char cModName[16]; } LOG_LEVEL_CONF_S; #define CVI_PRINT printf #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" extern CVI_S32 * log_levels; extern CVI_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 CVI_GET_MOD_NAME(id) (id < CVI_ID_BUTT)? MOD_STRING[id] : "UNDEF" /* #ifdef CVI_DEBUG */ #ifdef CONFIG_CVI_LOG_TRACE_SUPPORT #define CVI_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 CVI_TRACE(level, enModId, fmt, ...) \ do { \ CVI_S32 LogLevel = (log_levels == NULL) ? CONFIG_CVI_LOG_TRACE_LEVEL : log_levels[enModId]; \ if (level <= LogLevel) \ syslog(LOG_LOCAL5|level, "[%s-%s] " fmt, CVI_GET_MOD_NAME(enModId), log_name[level], \ ##__VA_ARGS__); \ } while (0) #else #define CVI_TRACE(level, enModId, fmt, ...) \ printf(fmt, ##__VA_ARGS__) #endif #else #define CVI_ASSERT(expr) #define CVI_TRACE(level, enModId, fmt...) #endif #define CVI_TRACE_ID(level, id, fmt, ...) \ CVI_TRACE(level, id, "%s:%d:%s(): " fmt, __FILENAME__, __LINE__, __func__, ##__VA_ARGS__) #define CVI_TRACE_LOG(level, fmt, ...) \ CVI_TRACE(level, CVI_ID_LOG, "%s:%d:%s(): " fmt, __FILENAME__, __LINE__, __func__, ##__VA_ARGS__) #define CVI_TRACE_SYS(level, fmt, ...) \ CVI_TRACE(level, CVI_ID_SYS, "%s:%d:%s(): " fmt, __FILENAME__, __LINE__, __func__, ##__VA_ARGS__) #define CVI_TRACE_VB(level, fmt, ...) \ CVI_TRACE(level, CVI_ID_VB, "%s:%d:%s(): " fmt, __FILENAME__, __LINE__, __func__, ##__VA_ARGS__) #define CVI_TRACE_SNS(level, fmt, ...) \ CVI_TRACE(level, CVI_ID_VI, "%s:%d:%s(): " fmt, __FILENAME__, __LINE__, __func__, ##__VA_ARGS__) #define CVI_TRACE_VI(level, fmt, ...) \ CVI_TRACE(level, CVI_ID_VI, "%s:%d:%s(): " fmt, __FILENAME__, __LINE__, __func__, ##__VA_ARGS__) #define CVI_TRACE_VPSS(level, fmt, ...) \ CVI_TRACE(level, CVI_ID_VPSS, "%s:%d:%s(): " fmt, __FILENAME__, __LINE__, __func__, ##__VA_ARGS__) #define CVI_TRACE_VO(level, fmt, ...) \ CVI_TRACE(level, CVI_ID_VO, "%s:%d:%s(): " fmt, __FILENAME__, __LINE__, __func__, ##__VA_ARGS__) #define CVI_TRACE_GDC(level, fmt, ...) \ CVI_TRACE(level, CVI_ID_GDC, "%s:%d:%s(): " fmt, __FILENAME__, __LINE__, __func__, ##__VA_ARGS__) #define CVI_TRACE_RGN(level, fmt, ...) \ CVI_TRACE(level, CVI_ID_RGN, "%s:%d:%s(): " fmt, __FILENAME__, __LINE__, __func__, ##__VA_ARGS__) #define CVI_TRACE_MISC(level, fmt, ...) \ CVI_TRACE(level, CVI_ID_SYS, "%s:%d:%s(): " fmt, __FILENAME__, __LINE__, __func__, ##__VA_ARGS__) #define CVI_TRACE_DWA(level, fmt, ...) \ CVI_TRACE(level, CVI_ID_DWA, "%s:%d:%s(): " fmt, __FILENAME__, __LINE__, __func__, ##__VA_ARGS__) #define CVI_TRACE_DPU(level, fmt, ...) \ CVI_TRACE(level, CVI_ID_DPU, "%s:%d:%s(): " fmt, __FILENAME__, __LINE__, __func__, ##__VA_ARGS__) #define CVI_TRACE_STITCH(level, fmt, ...) \ CVI_TRACE(level, CVI_ID_STITCH, "%s:%d:%s(): " fmt, __FILENAME__, __LINE__, __func__, ##__VA_ARGS__) #define CVI_TRACE_HDMI(level, fmt, ...) \ CVI_TRACE(level, CVI_ID_HDMI, "%s:%d:%s(): " fmt, __FILENAME__, __LINE__, __func__, ##__VA_ARGS__) #ifdef __cplusplus #if __cplusplus } #endif #endif /* __cplusplus */ #endif /* __CVI_COMM_SYS_H__ */