0%

Linux基础复学(5)- 调试手段一

前言

复习一下Linux内核开发使用的调试手段,主要包含如下方法:

  • printk及pr_xxx
  • 动态输出
  • procfs文件系统
  • sysfs文件系统
  • debugfs文件系统

printk

printk是最常用的调试方法,可以在代码中加入一些打印信息,了解内核模块执行的情况。类似于printf,不过printk是区分了打印等级的,可以通过配置让内核打印相应的等级日志。

printk提供8个打印等级,分别为 DEBUG, INFO, NOTICE, WARNING, ERROR, CRITICAL, ALERT, EMERGE,等级越来越高,对应7-0,值越小等级越高。

内核编译配置可以通过CONFIG_MESSAGE_LOGLEVEL_DEFAULT设置默认打印等级,默认是4(WARNING)。

还可以通过系统启动参数配置打印等级 loglevel=8, 也可以在启动后动态修改等级,修改 /proc/sys/kernel/printk的值。

1
2
3
4
5
root@keep-Inspiron-3020-S:/home/keep/code/raw_blog# cat /proc/sys/kernel/printk
4 4 1 7

# 分别对于 控制台等级/默认消息等级/最低打印等级/默认控制台打印等级

pr_xxx是对printk的一个包装,printk是基础的接口,而pr_xxx可以通过名称体现出等级。

还有2个常用的打印接口

  • print_hex_dump 打印内存数据
  • dump_stack 打印调用栈

动态输出

内核配置CONFIG_DYNAMIC_DEBUG配置使能动态输出功能,可以在内核运行时动态配置信息的输出,而printk属于全局的静态配置。

使用动态输出需要在代码中使用pr_debug/dev_dbg函数打印信息。

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
# 查看内核动态输出代码分布情况
cat /sys/kernel/debug/dynamic_debug/control

# filename:lineno [module]function flags format
init/main.c:1113 [main]initcall_blacklist =p "blacklisting initcall %s\n"
init/main.c:1152 [main]initcall_blacklisted =p "initcall %s blacklisted\n"
init/main.c:1347 [main]run_init_process =p " with arguments:\n"
init/main.c:1349 [main]run_init_process =p " %s\n"
...

# 通过往contrl文件写入内容,打开对于模块的打印

echo 'file init/main.c +p' > /sys/kernel/debug/dynamic_debug/control
echo 'module main +p' > /sys/kernel/debug/dynamic_debug/control
echo 'func run_init_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

#对一些需要在启动时查看的打印可以在启动参数中添加开关, main.dyndbg=+plft
# 也可以在子模块的Makefile 设置ccflags

ccflags += -DDEBUG -DVERBOSE_DEBUG

proc和sys文件系统

proc系统提供了一种内核和内核模块向用户态发送消息的机制。这个虚拟文件系统主要提供内核内部的数据结构信息,也可以用来查看系统的信息。

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
# 查看系统启动的参数

cat /proc/cmdline

# 查看cpu信息
cat /proc/cpuinfo

# 查看内存信息
cat /proc/meminfo
cat /proc/slabinfo

# 查看设备io内存占用信息
cat /proc/iomem

# 查看对于id的进程信息
cat /proc/xxx/*

# 查看misc设备模块
cat /proc/misc

# 查看内核加密算法
cat /proc/crypto

# 查看中断
cat /proc/interrupts

proc文件系统主要用于显示系统相关的信息,对于一般模块而言,则使用sys文件系统,提供了一套统一的设备驱动模型。
现在很多模块都是用sys作为与用户空间的友好接口,比如uio模块。系统默认创建的字符设备等,也会在sys下创建对应的文件。

1
2
3
4
# /dev/kvm有对应的目录
xxx# ls /sys/devices/virtual/misc/kvm/
dev power subsystem uevent

debugfs文件系统

debugfs则是一个纯粹用来调试内核的文件系统, 而proc和sys则是作为用户接口来使用的,不适合暴露私有的调试信息。最基础的内核调试是打印日志,但是如果涉及到数据修改的,就可以使用debugfs文件系统了,否则只能修改代码重新编译加载。

debugfs 一般挂在到/sys/kernel/debug目录下,比如dma模块就有相关的文件在该目录下。之前看的dma-api使用接口,当开启了CONFIG_DMA_API_DEBUG就会在/sys/kernel/debug目录下创建一个dma-api的目录。

1
2
3
4
root@keep-Inspiron-3020-S:~# ls /sys/kernel/debug/dma-api/
all_errors driver_filter error_count nr_total_entries num_free_entries
disabled dump min_free_entries num_errors
root@keep-Inspiron-3020-S:~#

类似proc和sys, debugfs也提供了api来读写文件的接口来实现数据通信,用于调试。

本文相关测试代码: https://gitee.com/fishmwei/blog_code/blob/master/linux-kernel/dbgm/dbgm.c


行动,才不会被动!

欢迎关注个人公众号 微信 -> 搜索 -> fishmwei,沟通交流。

欢迎关注

博客地址: https://fishmwei.github.io

掘金主页: https://juejin.cn/user/2084329776486919