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”。


image

但因为打印等级没设置,也就没打印出来,执行dmesg -n 8,就有log打印。

# dmesg -n 8
# 
# rmmod pr_debug
test_exit!
#

卸载模块后,/sys/kernel/debug/dynamic_debug/control 也没有pr_debug的动态调试信息。


image

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

echo 'module pr_debug -p' > /sys/kernel/debug/dynamic_debug/control


image


学习参考:

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!

posted @ 2024-09-20 21:27  Yaction  阅读(323)  评论(0)    收藏  举报