Linux Kernel Debugging
CONFIG_DEBUG_FS
如何在 WSL2 上启用 debugfs
确认下内核是否开启了 debugfs
1 | zcat /proc/config.gz | grep CONFIG_DEBUG_FS |
要想使用 debugfs,首先要挂载它到 /sys/kernel/debug
1 | mount -t debugfs none /sys/kernel/debug/ |
如果想让挂载在系统重启后自动挂载,在 /etc/fstab
加下面一行
1 | debugfs /sys/kernel/debug debugfs defaults 0 0 |
在添加后,执行 mount -a
命令将看到 /sys/kernel/debug
文件系统的内容
1 | $ ls -ld ./* | sort -k 1.2 |
/sys/kernel/debug
目录的权限是 700, 只有 root 用户才能进入,sudo 也不行
CONFIG_TRACING
CONFIG_TRACING 是内核 2.6.27 (2008-10-09)* 引入的一个不可配置的(not configurable) 内核选项,不可配置的意思是只能通过别的配置选项的打开来影响它的打开。
它对应的 tracefs 也需要挂载
1 | mount -t tracefs none /sys/kernel/tracing |
像 ftrace, trace-cmd 都需要挂载这个目录。
tracers
要使用 ftrace, 不光要挂载 tracefs, 内核相应的追踪器 (tracer) 也要编译进内核,可用的内核追踪器可以通过 cat /sys/kernel/tracing/available_tracers
查看。
- CONFIG_FUNCTION_TRACER
- CONFIG_FUNCTION_GRAPH_TRACER
- CONFIG_HWLAT_TRACER
- CONFIG_IRQSOFF_TRACER
- CONFIG_OSNOISE_TRACER
- CONFIG_PREEMPT_TRACER
- CONFIG_SCHED_TRACER
- CONFIG_STACK_TRACER
- CONFIG_CONTEXT_SWITCH_TRACER
- CONFIG_NOP_TRACER
ftrace
trace-cmd
trace-cmd 实际上是为了方便使用 ftrace, 比方下面的 trace-cmd 命令与直接 cat /sys/kernel/tracing/available_events
是等价的
1 | trace-cmd list -e |
CONFIG_DYNAMIC_DEBUG
Dynamic Debug 就通过 /sys/kernel/debug/dynamic_debug/control
文件打开或关闭特定文件的某行或函数里的打印,但它只对使用
pr_debug()
dev_dbg()
print_hex_dump_debug()
print_hex_dump_bytes()
这 4 个函数的打印来有用。
CONFIG_DRM_USE_DYNAMIC_DEBUG
这是内核 6.1 (2022-12-11) 才引入的专门控制 drm_dbg()
是否用 Dynamic Debug 来实现的可配置选项。
1 |
CONFIG_DEBUG_ATOMIC_SLEEP
dma_fence_wait_timeout()
会睡眠调用进程直到 fence 被 signaled 或者指定定时器超时。该函数中会调用 might_sleep()
来标识 (annotation) 调用进程可能进入睡眠状态,并打印源文件名和行号,帮助调试。 但只有内核配置了 CONFIG_DEBUG_ATOMIC_SLEEP
才有效,否则 __might_sleep()
是一个空函数。
kern.log
/var/log/kern.log
的一个主要问题是每行前面的 %HOSTNAME%
太长又没什么用,%timegenerated%
和内核原始打印 %msg%
里的时间戳实际上有一个就可以了。查了一下配置方法,实际上就是要在 rsyslog.conf
里定义一个 $template
1 | $template SimpleFormat,"%msg:::drop-last-lf%\n" |
然后在 rsyslog.conf 的规则里加上这个 template
1 | kern.* -/var/log/kern.log;SimpleFormat |
注意,不要直接在 /etc/rsyslog.conf
里直接加上面这条规则,要加在 /etc/rsyslog.d/50-default.conf
, 否则会输出两遍。
最后,就是要重启 rsyslog 服务生效。
内核的 log 实际上都是写入一个 ring buffer 里的,暴露给用户的接口是 /proc/kmsg
和 /dev/kmsg
, rsyslog 服务也是通过这些接口,重新处理 log 后写入 /var/log/kern.log
的。下面的操作可以让 dmesg
多一条 log
1 | echo "<3>HELLO" > /dev/kmsg |