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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ ls -ld ./* | sort -k 1.2
--w------- 1 root root 0 May 19 17:03 ./clear_warn_once
--w------- 1 root root 0 May 19 17:03 ./split_huge_pages
-r--r--r-- 1 root root 0 May 19 17:03 ./devices_deferred
-r--r--r-- 1 root root 0 May 19 17:03 ./sleep_time
-rw-r--r-- 1 root root 0 May 19 17:03 ./fault_around_bytes
drwx------ 8 root root 0 May 19 17:03 ./tracing
drwxr-xr-x 2 root root 0 May 19 17:03 ./acpi
drwxr-xr-x 2 root root 0 May 19 17:03 ./btt
drwxr-xr-x 2 root root 0 May 19 17:03 ./ceph
drwxr-xr-x 2 root root 0 May 19 17:03 ./clk
drwxr-xr-x 2 root root 0 May 19 17:03 ./device_component
drwxr-xr-x 2 root root 0 May 19 17:03 ./dma_buf
drwxr-xr-x 2 root root 0 May 19 17:03 ./error_injection
drwxr-xr-x 2 root root 0 May 19 17:03 ./extfrag
drwxr-xr-x 2 root root 0 May 19 17:03 ./hid
drwxr-xr-x 2 root root 0 May 19 17:03 ./kprobes
drwxr-xr-x 2 root root 0 May 19 17:03 ./kvm
drwxr-xr-x 2 root root 0 May 19 17:03 ./ramdisk_pages
drwxr-xr-x 2 root root 0 May 19 17:03 ./ras
drwxr-xr-x 2 root root 0 May 19 17:03 ./swiotlb
drwxr-xr-x 2 root root 0 May 19 17:03 ./usb
drwxr-xr-x 2 root root 0 May 19 17:03 ./virtio-ports
drwxr-xr-x 2 root root 0 May 19 17:03 ./x86
drwxr-xr-x 3 root root 0 May 19 17:03 ./dri
drwxr-xr-x 3 root root 0 May 19 17:03 ./sched
drwxr-xr-x 29 root root 0 May 19 17:03 ./block
drwxr-xr-x 35 root root 0 May 19 17:03 ./bdi

/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
2
3
4
5
6
7
8
#if !defined(CONFIG_DRM_USE_DYNAMIC_DEBUG)
#define drm_dev_dbg(dev, cat, fmt, ...) \
__drm_dev_dbg(NULL, dev, cat, fmt, ##__VA_ARGS__)
#else
#define drm_dev_dbg(dev, cat, fmt, ...) \
_dynamic_func_call_cls(cat, fmt, __drm_dev_dbg, \
dev, cat, fmt, ##__VA_ARGS__)
#endif

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

References