Linux printk和动态输出
1. printk输出函数
多年工程经验看,printk()最简单有效的调试方法。
printk默认的配置实在配置文件中CONFIG_MESSAGE_LOGLEVEL_DEFAULT 4
通常高于这个值才会输出到控制台或者串口。可以通过如下几种方式修改:
可以手动修改内核为:
<arch/arm64/configs/debian_defconfig>
CONFIG_MESSAGE_LOGLEVEL_DEFAULT 8 // 将默认输出等级设置为8,表示代开所有信息
也可以通过修改启动传递参数:
loglevel=8
系统运行时修改:
#echo 8 > /proc/sys/kernel/printk // 打开所有输出信息
使用举例:printk(KERN_EMERG "figo:%s, %d", __func__, __LINE__)
1.1 printk输出格式
数据类型 | printk格式 |
---|---|
int | %d或%x |
unsigned int | %u或%x |
long | %ld或%lx |
long long | %lld或%llx |
unsigned long long | %llu或%llx |
size_t | %zu或%zx |
ssize_t | %zd或%zx |
函数指针 | %pf |
1.2 有趣的输出
内核数据的输出输出函数:print_hex_dump()
栈输出函数dump_stack()
2.动态输出
动态输出是内核子系统最喜欢的输出手段之一。
内核使用大量的pr_debug()/dev_dbg()
函数来输出信息,使用了动态输出的技术。记住需要挂载debugfs文件系统。
debugfs文件系统对应control节点,记录所有使用动态输出技术的文件名路径、输出语句所在的行号、模块号和将要输出的语句等。
benshushu:dynamic_debug# less control
# filename:lineno [module]function flags format
init/main.c:770 [main]initcall_blacklist =p "blacklisting initcall %s\012"
init/main.c:803 [main]initcall_blacklisted =p "initcall %s blacklisted\012"
使用举例:
# 打开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
其中flags信息,还能输出一些额外的信息:
- p:打开输出动态输出语句
- f:输出函数名
- l:输出行号
- m:输出模块名
- t:输出线程ID
对于一些在shell之前就启动模块,调试起来,使用动态输出的方法:
先找到topology
benshushu:dynamic_debug# cat control | grep topology
arch/arm64/kernel/topology.c:291 [topology]store_cpu_topology =_ "CPU%u: cluster %d core %d thread %d mpidr %#016llx\012"
然后再进行在启动参数上添加:topology.dyndgb=+plft字符串
还可以在子系统Makefile中打开ccflags来打开动态输出功能:
ccflags-y := -DDEBUG
ccflags-y += -DVERBOSE_DEBUG