打开驱动中的动态打印方法
什么是动态打印
动态打印跟 printk 的主要区别在于动态打印在进入系统后可以手动控制是否开启打印,能具体到某个驱动甚至驱动中的某个函数的打印。
如何使用
调试驱动的时候,有些中断中的打印用printk很容易造成内核崩溃,但是有些人家已经加好的打印,之前不知道怎么使用,这次就介绍一下使用方法。
在调试全志T717 openEuler内核的uart驱动的时候,想看下串口接收到的数据,人家驱动中(sunxi-uart.c)其实已经加好了打印了,只需要咱们把他打开就能通过 “dmesg -w” 来看了,
先看下驱动源码中是怎么写的:
static unsigned int sw_uart_handle_rx(struct sw_uart_port *sw_uport, unsigned int lsr) { unsigned char ch = 0; int max_count = 256; char flag; /* * * */ //在这会通过SERIAL_DBG将收到的每个字符打印出来 SERIAL_DBG(sw_uport->port.dev, "receive data 0x%x\n", ch); uart_insert_char(&sw_uport->port, lsr, SUNXI_UART_LSR_OE, ch, flag); lsr = serial_in(&sw_uport->port, SUNXI_UART_LSR); return lsr; }然后看下 “SERIAL_DBG” 的宏定义:
#define SERIAL_DBG(dev, fmt, arg...) \ do { \ if (!sw_is_console_port(&sw_uport->port)) \ sunxi_debug(dev, "%s()%d - "fmt, __func__, __LINE__, ##arg); \ } while (0) /* 再看下sunxi_debug宏定义 */ /* void sunxi_debug(struct device *dev, char *fmt, ...); */ #define sunxi_debug(dev, fmt, ...) \ do { if (dev) \ pr_debug("-%s:[DEBUG]: "fmt, sunxi_log_dev_name(dev), ## __VA_ARGS__); \ else \ pr_debug(":[DEBUG]: "fmt, ## __VA_ARGS__); \ } while (0) /* CONFIG_AW_LOG_VERBOSE */可以看到要想使用“sunxi_debug”这个宏要先打开CONFIG_AW_LOG_VERBOSE,
然后还要打开内核的动态打印功能,所以一共需要在defconfig中添加三个宏定义:
# 打开全志的日志打印 CONFIG_AW_LOG_VERBOSE=y #打开内核动态打印支持 CONFIG_DYNAMIC_DEBUG=y CONFIG_DYNAMIC_DEBUG_CORE=y打开完了重新编译并烧录内核以后就可以在 /sys/kernel/debug/dynamic_debug/control 中查看动态打印都支持写什么打印了,
这个动态打印默认是关闭的,需要手动去修改下 control 文件,将打印打开,打开方法如下:
# 打开bsp/drivers/uart/sunxi-uart.c 这个驱动中的所有动态打印echo'file bsp/drivers/uart/sunxi-uart.c +p'>/sys/kernel/debug/dynamic_debug/control# 修改下内核的打印登记,直接拉满echo8>/proc/sys/kernel/printk# 使用dmesg -w 动态更新打印,这个就跟tail -F 一样的dmesg-w自己驱动中怎么使用debug
这些打印的宏定义在 openeuler-kernel-t717/include/linux/printk.h 中,其中
pr_emerg() pr_alert() pr_crit() pr_err() pr_warn() pr_notice() pr_info()这些都是printk + level 的封装,如:
#define pr_err(fmt, ...) \ printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)只有pr_debug()会用到动态打印,他的宏定义如下:
/* If you are writing a driver, please use dev_dbg instead */ #if defined(CONFIG_DYNAMIC_DEBUG) || \ (defined(CONFIG_DYNAMIC_DEBUG_CORE) && defined(DYNAMIC_DEBUG_MODULE)) #include <linux/dynamic_debug.h> /** * pr_debug - Print a debug-level message conditionally * @fmt: format string * @...: arguments for the format string * * This macro expands to dynamic_pr_debug() if CONFIG_DYNAMIC_DEBUG is * set. Otherwise, if DEBUG is defined, it's equivalent to a printk with * KERN_DEBUG loglevel. If DEBUG is not defined it does nothing. * * It uses pr_fmt() to generate the format string (dynamic_pr_debug() uses * pr_fmt() internally). */ #define pr_debug(fmt, ...) \ dynamic_pr_debug(fmt, ##__VA_ARGS__) #elif defined(DEBUG) #define pr_debug(fmt, ...) \ printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__) #else #define pr_debug(fmt, ...) \ no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__) #endif如果打开了CONFIG_DYNAMIC_DEBUG,那么pr_debug()就会调用dynamic_pr_debug(),否则如果定义了DEBUG宏,那么pr_debug()就会调用printk(),否则就会调用no_printk()。
但是你看最开始一行写着如果你在写一个驱动,请使用dev_dbg(),下面来看下dev_dbg()的宏定义:
openeuler-kernel-t717/include/linux/dev_printk.h
#ifndef dev_fmt #define dev_fmt(fmt) fmt #endif #define dev_emerg(dev, fmt, ...) \ _dev_emerg(dev, dev_fmt(fmt), ##__VA_ARGS__) #define dev_crit(dev, fmt, ...) \ _dev_crit(dev, dev_fmt(fmt), ##__VA_ARGS__) #define dev_alert(dev, fmt, ...) \ _dev_alert(dev, dev_fmt(fmt), ##__VA_ARGS__) #define dev_err(dev, fmt, ...) \ _dev_err(dev, dev_fmt(fmt), ##__VA_ARGS__) #define dev_warn(dev, fmt, ...) \ _dev_warn(dev, dev_fmt(fmt), ##__VA_ARGS__) #define dev_notice(dev, fmt, ...) \ _dev_notice(dev, dev_fmt(fmt), ##__VA_ARGS__) #define dev_info(dev, fmt, ...) \ _dev_info(dev, dev_fmt(fmt), ##__VA_ARGS__) #if defined(CONFIG_DYNAMIC_DEBUG) || \ (defined(CONFIG_DYNAMIC_DEBUG_CORE) && defined(DYNAMIC_DEBUG_MODULE)) #define dev_dbg(dev, fmt, ...) \ dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__) #elif defined(DEBUG) #define dev_dbg(dev, fmt, ...) \ dev_printk(KERN_DEBUG, dev, dev_fmt(fmt), ##__VA_ARGS__) #else #define dev_dbg(dev, fmt, ...) \ ({ \ if (0) \ dev_printk(KERN_DEBUG, dev, dev_fmt(fmt), ##__VA_ARGS__); \ }) #endifdev的打印会自动给加设备名,会传入dev嘛,就是struct device * 类型的设备指针,
这个会打印设备名,所以在写驱动的时候优先使用这个。