linux-kernel-dynamic-debug
Linux kernel dynamic debug
1.前提:使能动态输出宏开关
要使用动态打印,必须在内核配置时打开 CONFIG_DYNAMIC_DEBUG 宏。CONFIG_DYNAMIC_DEBUG 是配置动态输出,它依赖于 CONFIG_DEBUG_FS,而 CONFIG_DEBUG_FS 是 debugfs 文件系统。
CONFIG_DEBUG_FS=y
CONFIG_DYNAMIC_DEBUG=y
debugfs默认会挂载到/sys/kernel/debug,如果没有挂载,可以执行以下命令挂载
#mount -t debugfs none /sys/kernel/debug/
# mount
/dev/root on / type squashfs (ro,relatime)
devtmpfs on /dev type devtmpfs (rw,relatime,size=44068k,nr_inodes=11017,mode=755)
proc on /proc type proc (rw,relatime)
devpts on /dev/pts type devpts (rw,relatime,gid=5,mode=620,ptmxmode=000)
tmpfs on /dev/shm type tmpfs (rw,relatime,mode=777)
tmpfs on /tmp type tmpfs (rw,relatime)
sysfs on /sys type sysfs (rw,relatime)
tmpfs on /run type tmpfs (rw,relatime)
ubi0:ubi0_conf on /skybell/config type ubifs (rw,relatime)
ubi1:ubi1_misc on /skybell/misc type ubifs (rw,relatime)
none on /sys/kernel/debug type debugfs (rw,relatime)
#
编译新内核,挂载debugfs文件系统后,可以查看动态打印调试节点control节点内容:
cat /sys/kernel/debug/dynamic_debug/control
cat /sys/kernel/debug/dynamic_debug/control | tail -n 2
相关信息说明:
net/mac80211/mlme.c:1418 [mac80211]ieee80211_handle_pwr_constr =p "%s: Limiting TX power to %d (%d - %d) dBm as advertised by %pM\012"
net/mac80211/mlme.c:1427 [mac80211]ieee80211_handle_pwr_constr =p "%s: Limiting TX power to %d dBm as advertised by %pM\012"
绝对路径下的文件名:net/mac80211/mlme.c
行号:1427
模块:譬如这里是 "[mac80211]"
函数名:ieee80211_handle_pwr_constr
打印开关状态:"=_" 表示关闭,如果被打开了会变成 "=p"
打印的内容:"%s: Limiting TX power to %d dBm as advertised by %pM
2.设置动态打印调试节点
2.1在SHELL终端设置
#打开svcsock.c文件中所有动态输出语句
echo 'file svcsock.c +p' > /sys/kernel/debug/dynamic_debug/control
#打开usbcore模块所有动态输出语句
echo 'module usbcore +p' > /sys/kernel/debug/dynamic_debug/control
#打开svc_process()函数中所有的动态输出语句
echo 'func svc_process +p' > /sys/kernel/debug/dynamic_debug/control
#关闭svc_process()函数中所有的动态输出语句
echo 'func svc_process -p' > /sys/kernel/debug/dynamic_debug/control
#打开文件路径中包含usb的文件里所有的动态输出语句
echo -n '*usb* +p' > /sys/kernel/debug/dynamic_debug/control
#打开系统所有的动态输出语句
echo -n '+p' > /sys/kernel/debug/dynamic_debug/control
上面是打开动态输出语句的例子,除了能输出 pr_debug()/dev_dbg()函数中定义的输出外,还能输出一些额外信息,例如函数名、行号、模块名字和线程ID等。
p:打开动态打印语句。
f:打印函数名。
l:打印行号。
m:打印模块名字。
t:打印线程ID。
2.2在内核启动参数设置
对于调试一些系统启动方面的代码,例如SMP(对称多处理器结构 Symmetrical Multi-Processing)初始化、USB核心初始化等,这些代码在系统进入 shell 终端时已经初始化完成,因此无法及时打开动态输出语句。这时可以在内核启动时传递参数给内核,在系统初始化时动态打开它们,这是实际工程中非常好用的一个技巧。
例如调试SMP初始化的代码,查询到ARM SMP模块有一些动态输出语句。
在内核命令行中添加“smp.dyndbg=+plft”字符串
例如,在内核命令行中添加 usbnet.dyndbg=+plft ,就可以在启动时打开 usbnet的动态输出。
在内核启动后,通过 dmesg | grep "usbnet" 即可看到输出的调试信息。
2.3在Makefile 设置
还可以在各个子系统的Makefile中添加ccflags来打开动态输出语句
#<../Makefile>
ccflags-y += -DDEBUG
ccflags-y += -DVERBOSE_DEBUG
3.dynamic debug 的使用
3.1pr_debug定义
//pr_debug 的定义在文件 include/linux/printk.h
/* If you are writing a driver, please use dev_dbg instead */
#if defined(CONFIG_DYNAMIC_DEBUG)
#include <linux/dynamic_debug.h>
/* dynamic_pr_debug() uses pr_fmt() internally so we don't need it here */
#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
注意的是KERN_DEBUG,默认的 console 打印级别是 7(在 kernel/printk/printk.c 中定义了 #define DEFAULT_CONSOLE_LOGLEVEL 4)。只有那些级别 "小于4" 的调试信息才能打印出来,而 pr_devel() 对应的 KERN_DEBUG 的级别是 7,那就还需要提高 console 打印级别到8
如果是 runtime,procfs 起来了,可以直接通过上篇说的 printk() 的 sys 接口调整打印级别
# cat /proc/sys/kernel/printk
7 4 1 7
# echo 8 > /proc/sys/kernel/printk
# cat /proc/sys/kernel/printk
8 4 1 7
#
或者dmesg方法调整打印级别:
dmesg -n 8
4.测试验证
4.1测试环境
# uname -a
Linux buildroot 4.9.0-1 #1 4.9.257-1-gf822cc068a18 2024-08-31 21:23:36 armv7l GNU/Linux
#
4.2测试代码
//file:pr_debug.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/printk.h>
static int test_init(void) {
pr_debug("test_init!\n");
return 0;
}
static void test_exit(void) {
pr_debug("test_exit!\n");
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
#Makefile
KERNELDIR := /home/young/RCK/kernel-v49257
obj-m += pr_debug.o
#一定要加 不然dmesg不出打印信息
ccflags-y := -DDEBUG
all:
make -C $(KERNELDIR) SUBDIRS=$(PWD) modules
clean:
make -C $(KERNELDIR) SUBDIRS=$(PWD) clean
没加载pr_debug.ko之前,/sys/kernel/debug/dynamic_debug/control 是没有pr_debug的动态调试信息,加载
pr_debug.ko后才有,且默认打开“=p”。


但因为打印等级没设置,也就没打印出来,执行dmesg -n 8,就有log打印。
# dmesg -n 8
#
# rmmod pr_debug
test_exit!
#
卸载模块后,/sys/kernel/debug/dynamic_debug/control 也没有pr_debug的动态调试信息。


测试时,可以加载pr_debug驱动模块后,设置/sys/kernel/debug/dynamic_debug/control 信息,再去卸载pr_debug驱动模块,此时卸载时没打印。
echo 'module pr_debug -p' > /sys/kernel/debug/dynamic_debug/control


学习参考:
Linux内核调试 | 动态输出的使用 - https://mp.weixin.qq.com/s/hb5VEsouKF25s3J2MREE9Q
Linux内核基础篇——动态输出调试 - https://mp.weixin.qq.com/s/iLCTuUjlArMEhrgw_S18gw
内核调试信息printk/pr_debug()/dev_dbg()_内核态诊断printk-CSDN博客 - https://blog.csdn.net/yueni_zhao/article/details/127654005
Linux Dynamic Debug - 简书 - https://www.jianshu.com/p/30bd834934d7
Linux debug - hammerqiu - 博客园 - https://www.cnblogs.com/hammerqiu/p/10761942.html
pr_debug、dev_dbg等动态调试三 - 摩斯电码 - 博客园 - https://www.cnblogs.com/pengdonglin137/p/4622460.html
END!
浙公网安备 33010602011771号