本篇定位:对于gdb 重度用户(stat_snap),本篇是你的 Linux 调试工具箱。从 printk(基础日志)到 ftrace(函数跟踪)、perf(性能剖析)、kprobe(动态插桩)、eBPF(现代神器)、oops/crash(死机分析)、lockdep(死锁检测)。读完能在 Linux 上达到你裸机 gdb 的调试精度,甚至更强。
Linux 调试工具远超裸机 gdb
Linux 全套系统化:① printk(日志);② ftrace(函数跟踪,不用改代码);③ perf(性能剖析,找热点);④ kprobe(动态插桩,你的 stat_snap 系统版);⑤ eBPF(可编程跟踪,现代神器);⑥ oops/panic 分析(addr2line,RISC-V 专题 10 篇那套);⑦ lockdep(死锁自动检测)。调试方法论直接迁移,工具升级。
目录
一、printk:基础日志
1.1 printk 用法
printk(KERN_ERR "fail: %d\n", ret);
printk(KERN_INFO "probe ok\n");
pr_err("error: %d\n", ret); // 推荐
pr_info("info\n");
pr_warn("warn\n");
pr_debug("debug\n"); // 默认不打印,开 DEBUG 才打
dev_info(&pdev->dev, "probe\n"); // 带设备信息
dev_err(&pdev->dev, "fail\n");
1.2 日志级别
| 级别 | 宏 | 数值 |
|---|---|---|
| KERN_EMERG | pr_emerg | 0(最高) |
| KERN_ALERT | pr_alert | 1 |
| KERN_CRIT | pr_crit | 2 |
| KERN_ERR | pr_err | 3 |
| KERN_WARNING | pr_warn | 4 |
| KERN_NOTICE | pr_notice | 5 |
| KERN_INFO | pr_info | 6 |
| KERN_DEBUG | pr_debug | 7(最低) |
1.3 console_loglevel
- 控制台只显示 >= console_loglevel 的
dmesg看全部
dmesg # 看所有内核日志
dmesg | tail # 最近
dmesg -w # 实时(follow)
# 改 console_loglevel
echo 8 > /proc/sys/kernel/printk # 全打印
cat /proc/sys/kernel/printk # 当前 4 个级别
1.4 printk 的特点(对照 printf)
- 在内核态(不是用户态 printf)
- 带级别
- 可在中断上下文调(但 try_lock console,忙时缓冲)
- 不是异步的紧急情况用 printk_safe(防死锁)
- earlycon(02 篇):早期 printk,console 初始化前用
1.5 printk 时间戳
dmesg | head
# [ 0.000000] Booting Linux...
# [ 1.234567] ...
- 时间戳(秒.微秒),自启动
dmesg -T转人类时间
嵌入式视角:printk 是你的串口调试升级版
裸机 printf 到串口。Linux printk 同理,但:① 带级别;② 全局环形缓冲(不丢,可 dmesg 回看);③ 可动态调级别。调试驱动,printk + dmesg 是第一工具,和 printf 习惯一致。
二、oops 和 panic:崩溃分析⭐
2.1 oops 是什么
- 内核遇到错误(空指针/非法地址/页错误)
- 打印寄存器 + 栈 + 调用栈
- 若可恢复(进程上下文)→ 杀进程,内核继续;若不可恢复(中断/关键)→ panic
2.2 典型 oops 输出
Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000
Oops[#1]:
CPU: 0 PID: 1234 Comm: myapp
pc : my_driver_probe+0x34/0x100
lr : platform_probe+0x20/0x40
Call trace:
my_driver_probe+0x34/0x100
platform_probe+0x20/0x40
really_probe+0x60/0x100
...
epc : ffffff8001234034 ra : ffffff8001235020 sp : ffffffc08001fee0
RISC-V 寄存器:
ra : ffffff8001235020 sp : ffffffc08001fee0 gp : ...
a0-a7 : ...
Code: xxxx xxxx (出错处指令)
---[ end trace ]---
2.3 oops 分析五步法(对照你 RISC-V 10 篇)
1. 看错误类型
"NULL pointer dereference" / "Unable to handle paging request"
→ 空指针 / 非法地址
2. 看 PC(出错指令地址)
pc : my_driver_probe+0x34/0x100
→ my_driver_probe 函数,偏移 0x34
3. addr2line 定位源码行
riscv64-linux-gnu-addr2line -e vmlinux ffffff8001234034
/path/my_driver.c:42
→ 第 42 行
4. 看调用栈(Call trace)
my_driver_probe ← platform_probe ← really_probe
→ 怎么调用进来的
5. 看寄存器(a0-a7 参数)
a0 = 0 → 传入了空指针
2.4 addr2line(你 10 篇那套)
# 内核符号地址 → 源码行
riscv64-linux-gnu-addr2line -e vmlinux ffffff8001234034
# /home/me/kernel/drivers/my/my_driver.c:42
# 反汇编
riscv64-linux-gnu-objdump -d vmlinux | grep -A 20 "my_driver_probe"
2.5 panic
- oops 不可恢复 → panic(整内核停)
- 配
panic=10内核参数:panic 后 10 秒重启 - kdump:panic 时启动捕获内核,dump 内存
2.6 crash dump(kdump)
CONFIG_CRASH_DUMP:panic 时切到捕获内核- dump 内存到文件
crash工具分析(vmcore)
# 配 kdump
# 内核 cmdline: crashkernel=128M
# 启动 kdump 服务
systemctl start kdump
# 分析 vmcore
crash /usr/lib/debug/vmlinux /var/crash/xxx/vmcore
crash> bt # 看崩溃栈
crash> ps # 看进程
crash> struct task_struct ffffffc0xxx
oops 分析五步法和RISC-V 10 篇死机分析同构
RISC-V 专题 10 篇的"死机分析五步法"(mepc→addr2line/mcause/mtval/MPP/栈回溯)——Linux oops 分析完全同构:PC(=mepc)→ addr2line / 错误类型(=mcause) / 出错地址(=mtval) / 进程上下文(=MPP) / Call trace(=栈回溯)。RISC-V 死机分析经验直接迁移到 Linux oops。
三、ftrace:函数跟踪⭐
3.1 ftrace 是什么
- Function Tracer,不用改代码,运行时跟踪函数调用
- 内核 CONFIG_FTRACE + CONFIG_FUNCTION_TRACER
- 基于编译器
-pg插桩(mcount),运行时动态开关
3.2 tracefs 接口
# tracefs(常挂 /sys/kernel/tracing)
cd /sys/kernel/tracing
# 看可用 tracer
cat available_tracers
# hwlat blk function_graph wakeup_dl wakeup_rt wakeup function nop
# 选 tracer
echo function > current_tracer
# 跟踪某函数
echo my_driver_probe > set_ftrace_filter
# 开始跟踪
echo 1 > tracing_on
# ... 触发操作 ...
echo 0 > tracing_on
# 看结果
cat trace
# myapp-1234 [000] 1234.567: my_driver_probe <- platform_probe
3.3 function_graph tracer(调用图)
echo function_graph > current_tracer
echo my_driver_probe > set_graph_function
cat trace
# my_driver_probe() {
# ioremap+0x10/0x20 () {
# ...
# }
# request_irq+0x20 () {
# }
# }
- 看函数调用嵌套 + 耗时
- 类似你 stat_snap 的调用栈展开
3.4 trace events(事件跟踪)
# 看可用事件
ls events/
# sched/ irq/ net/ block/ ...
# 启用某事件
echo 1 > events/sched/sched_switch/enable
# 跟踪进程切换
cat trace
# sched_switch: prev=myapp pid=1234 next=swapper/0
- 预定义事件(sched/irq/net/block…)
- 细粒度跟踪
3.5 trace-cmd(前端工具)
# trace-cmd 包装 ftrace
trace-cmd record -e sched ls # 跟踪 sched 事件,跑 ls
trace-cmd report # 看结果
# KernelShark:GUI 看 trace
ftrace 系统化跟踪所有函数调用,不用改代码,运行时开关。function_graph 看调用嵌套+耗时,events 看子系统事件(sched_switch/irq_entry)。
四、perf:性能剖析⭐
4.1 perf 是什么
- 性能分析神器
- 采样 CPU 周期/缓存/分支/软件事件
- 找热点(哪个函数耗 CPU)
4.2 perf 基础
# 统计程序事件
perf stat ./myapp
# cycles / instructions / cache-misses / branch-misses
# 采样 CPU 热点
perf record ./myapp # 生成 perf.data
perf report # 交互看热点
perf report --stdio # 文本
# 实时 top
perf top # 实时看 CPU 热点函数
4.3 perf 事件
| 事件 | 含义 |
|---|---|
cycles | CPU 周期 |
instructions | 指令数 |
cache-misses | 缓存未命中 |
branch-misses | 分支预测失败 |
task-clock | 任务占 CPU 时间 |
page-faults | 缺页 |
context-switches | 上下文切换 |
sched:sched_switch | 调度事件(Tracepoint) |
4.4 perf record 采样
# 采样 CPU 周期,调用栈
perf record -g ./myapp # -g 记录调用栈
perf report -g graph # 看调用图
# 采样特定事件
perf record -e cache-misses ./myapp
# 采样所有 CPU
perf record -a sleep 5 # 采 5 秒系统全局
4.5 perf 输出
35.23% myapp myapp [.] process_data
20.10% myapp libc [.] memcpy
10.05% myapp myapp [.] compute_hash
...
- 按函数占比找热点
- process_data 占 35%,优化它收益最大
4.6 perf 应用场景
| 场景 | 用什么 |
|---|---|
| CPU 热点 | perf record -g + report |
| 缓存性能 | perf stat -e cache-misses |
| 调度分析 | perf sched |
| 内存分配 | perf kmem |
| 块 IO | perf stat -e block:… |
| 火焰图 | perf record -g + FlameGraph |
4.7 火焰图
# 生成火焰图
perf record -F 99 -ag sleep 30 # 99Hz 采样 30s
perf script > out.perf
git clone https://github.com/brendangregg/FlameGraph
./stackcollapse-perf.pl out.perf > out.folded
./flamegraph.pl out.folded > flame.svg
# 浏览器打开 flame.svg
- 可视化 CPU 热点,宽的函数 = 占 CPU 多
- 调优必备
perf 是你性能分析主力
裸机性能靠 cycle 计数器(RISC-V mcycle)。Linux perf 全系统采样,找 CPU 热点/缓存/调度问题。性能问题先 perf record + report,火焰图直观。
五、kprobe:动态插桩⭐
5.1 kprobe 是什么
- Kernel Probe,运行时在任意指令插桩
- 不用改内核源码,不用重编
- 插桩点抓寄存器/改寄存器
5.2 kprobe 类型
| 类型 | 触发 |
|---|---|
| kprobe | 任意指令 |
| kretprobe | 函数返回 |
| jprobe | 函数入口(弃用,用 kprobe) |
5.3 kprobe API
struct kprobe kp = {
.symbol_name = "do_sys_open",
.pre_handler = my_pre, // 执行前
.post_handler = my_post, // 执行后
};
register_kprobe(&kp);
int my_pre(struct kprobe *p, struct pt_regs *regs) {
// 抓参数(regs->a0, a1, ...)
printk("open: %s\n", (char *)regs->a1);
return 0;
}
5.4 kprobe 用途
- 跟踪任意内核函数(不用改源码)
- 抓函数参数/返回值
- perf/ftrace 底层用 kprobe
5.5 通过 ftrace 用 kprobe
# 在 do_sys_open 插桩
echo 'p:my_open do_sys_open' > /sys/kernel/tracing/kprobe_events
echo 1 > /sys/kernel/tracing/events/kprobes/my_open/enable
cat trace
# my_open: do_sys_open ...
gdb 在断点抓状态——kprobe 运行时在内核任意函数插桩抓寄存器/参数,不用 gdb attach(生产可用)。perf/ftrace 的动态跟踪底层就是 kprobe。
六、eBPF:现代神器⭐
6.1 eBPF 是什么
- Extended Berkeley Packet Filter
- 在内核运行沙箱程序(安全),不改内核
- 现代 Linux 可观测性/网络/安全核心
6.2 eBPF 能力
| 领域 | 用途 |
|---|---|
| 跟踪 | 替代 kprobe/ftrace(更灵活) |
| 网络 | XDP(网卡层)/TC/cgroup |
| 安全 | LSM 钩子,审计 |
| 观测 | 性能/事件/统计 |
6.3 eBPF 程序
- 用 C(受限子集)写,编译成 BPF 字节码
- 经验证器(verifier)安全检查
- JIT 编译成本地指令
- 挂到钩子点(kprobe/tracepoint/XDP/socket…)
// 简化 eBPF 程序(跟踪 do_sys_open)
SEC("kprobe/do_sys_open")
int my_open(struct pt_regs *ctx) {
char comm[16];
bpf_get_current_comm(&comm, sizeof(comm));
bpf_trace_printk("open by %s\n", comm);
return 0;
}
6.4 eBPF 工具链
| 工具 | 作用 |
|---|---|
bcc | Python + C 写 eBPF(易用) |
bpftrace | 类 awk 脚本写 eBPF(极简) |
libbpf | C 库(生产) |
6.5 bpftrace 示例
# 跟踪所有 open
bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s open %s\n", comm, str(args->filename)); }'
# 统计函数调用次数
bpftrace -e 'kprobe:do_sys_open { @[comm] = count(); }'
# 函数耗时
bpftrace -e 'kprobe:my_func { @start[tid] = nsecs; } kretprobe:my_func /@start[tid]/ { @us[comm] = (nsecs - @start[tid]) / 1000; delete(@start[tid]); }'
6.6 eBPF 工具集(bcc tools)
# 一堆现成工具
execsnoop # 跟踪进程执行
opensnoop # 跟踪 open
biolatency # 块 IO 延迟分布
biosnoop # 块 IO 事件
tcpconnect # TCP 连接
runqlat # 调度延迟
funccount # 函数调用计数
eBPF 是现代内核调试/观测的核武器
裸机调试受限于静态代码。eBPF 运行时动态注入观测逻辑,安全(验证器)且高效(JIT)。bpftrace 几行脚本跟踪任意函数/事件。不用改代码,运行时观测,生产可用。你学 Linux 必学 eBPF。
七、lockdep:死锁检测
7.1 lockdep 是什么
- Lock Dependency Detector
- CONFIG_LOCKDEP
- 运行时跟踪所有锁的获取顺序,检测潜在死锁
7.2 lockdep 检测
- AB-BA 死锁(锁顺序冲突)
- 持锁睡(原子上下文 schedule)
- 自递归锁
- 中断里持同一锁
7.3 开启
# 内核配置 CONFIG_LOCKDEP=y CONFIG_PROVE_LOCKING=y
# 开启后跑测试,自动报告
dmesg | grep -i lockdep
# [ BUG: possible deadlock ]
# [ BUG: sleeping in atomic context ]
7.4 lockdep 报告
=============================================
WARNING: possible recursive locking detected
task: myapp/1234 is trying to acquire lock:
lock(&lockA){...}, at: my_func+0x20/0x40
but task is already holding lock:
lock(&lockA){...}, at: other_func+0x10/0x30
...
lockdep 是你 07 篇死锁的自动检测
你 07 篇学的死锁类型(AB-BA/持锁睡/自递归)——lockdep 自动检测,不用你肉眼看。开发期开 lockdep 跑测试,大部分死锁自动暴露。你写驱动必开 lockdep 调试,省大量排查时间。
八、其他调试工具
8.1 kgdb(内核 gdb)
- 内核版 gdb,远程调试
- 配
kgdboc=ttyS0,115200cmdline - 主机 gdb 连接,断点/单步/读寄存器
- 和你裸机 gdb 几乎一样,只是调试内核
# 目标(内核 cmdline)
kgdbwait kgdboc=ttyS0,115200
# 主机
gdb vmlinux
(gdb) target remote /dev/ttyUSB0
(gdb) b my_driver_probe
(gdb) c
8.2 kdb
- kgdb 的简单版,串口直接交互(不用主机 gdb)
kdb> ps/bt/md(内存)
8.3 /proc 和 /sys
/proc/<pid>/:进程信息(status/maps/fd/…)/proc/meminfo/proc/interrupts/proc/cpuinfo/sys/:设备模型(10 篇)
8.4 sysrq(魔术键)
- SysRq 组合键,紧急调试
echo 1 > /proc/sys/kernel/sysrq启用Alt+SysRq+键:t(栈)/m(内存)/w(阻塞任务)/c(crash dump)/b(重启)/o(关机)
echo t > /proc/sysrq-trigger # 打印所有任务栈
echo c > /proc/sysrq-trigger # 触发 crash(测 kdump)
8.5 debugfs
- 11 篇 / 12 篇提过,驱动调试信息放这
/sys/kernel/debug/
九、网络/IO 调试工具
9.1 网络
| 工具 | 作用 |
|---|---|
tcpdump | 抓包 |
wireshark | 图形分析包 |
ss | 看 socket |
ethtool | 网卡统计 |
iperf3 | 带宽测 |
dropwatch | 丢包点定位 |
9.2 IO
| 工具 | 作用 |
|---|---|
iostat | IO 统计 |
iotop | 进程 IO |
blktrace | 块层事件 |
fio | IO 压测 |
9.3 内存
| 工具 | 作用 |
|---|---|
free | 内存概况 |
cat /proc/meminfo | 详细 |
slabtop | slab 占用 |
pmap | 进程内存映射 |
valgrind | 用户态内存检测 |
十、调试方法论(系统化)
10.1 调试层次
1. 现象捕获:printk/dmesg + 复现
2. 错误定位:oops + addr2line(死机)
或:perf/ftrace(性能)
3. 深入:eBPF/kprobe 动态观测
4. 验证:改代码 + 重测
10.2 各场景首选工具
| 场景 | 首选 |
|---|---|
| 死机/panic | oops + addr2line + 栈 |
| 性能慢(找热点) | perf record + report / 火焰图 |
| 函数调用跟踪 | ftrace function_graph |
| 动态观测参数 | bpftrace / kprobe |
| 死锁 | lockdep |
| 内存泄漏 | kmemleak / slabtop |
| 中断问题 | /proc/interrupts + ftrace irq events |
| 网络 | tcpdump + ss + dropwatch |
| IO 慢 | iostat + blktrace |
| 进程卡 | /proc//status + cat /proc//wchan |
10.3 调试内核配置
开发期开启(生产关,影响性能):
CONFIG_DEBUG_INFO=y # 调试符号(addr2line)
CONFIG_FRAME_POINTER=y # 栈回溯准确
CONFIG_LOCKDEP=y # 死锁检测
CONFIG_DEBUG_ATOMIC_SLEEP=y # 原子上下文睡检测
CONFIG_KASAN=y # 内存安全(地址越界)
CONFIG_KMSAN=y # 内存未初始化
CONFIG_UBSAN=y # 未定义行为
CONFIG_FAULT_INJECTION=y # 故障注入
CONFIG_KMEMLEAK=y # 内存泄漏检测
CONFIG_FTRACE=y # 跟踪
CONFIG_KPROBES=y # 动态插桩
CONFIG_BPF=y # eBPF
开发开 debug 配置,生产关
调试内核要开一堆 CONFIG_DEBUG_*(符号/lockdep/KASAN),自动抓很多 bug。生产关掉(性能损失)。这是裸机"Debug 编译/Release 编译"的内核版。
十一、对照总表
| 概念 | 裸机 gdb | Linux 调试 |
|---|---|---|
| 日志 | printf | printk + dmesg |
| 死机分析 | mepc/addr2line/栈 | oops + addr2line + Call trace |
| 函数跟踪 | 断点单步 | ftrace(不改代码) |
| 性能剖析 | cycle 计数 | perf + 火焰图 |
| 动态插桩 | gdb 断点 | kprobe / eBPF |
| 死锁检测 | 肉眼 | lockdep 自动 |
| 远程调试 | gdb | kgdb |
| 紧急调试 | 复位 | sysrq |
| 内存检测 | 自己加 | KASAN/kmemleak |
| 可观测 | stat_snap | eBPF |
十二、本篇小结
- printk:内核日志,分级(pr_err/info/debug),dmesg 看,console_loglevel 控制台显示
- oops/panic:崩溃分析五步法(错误类型/PC/addr2line/Call trace/寄存器),和你 RISC-V 10 篇死机分析同构;kdump + crash 分析 vmcore
- ftrace:函数跟踪(不改代码),function/function_graph/events,trace-cmd 前端
- perf:性能剖析,perf record/report 找热点,火焰图可视化,事件(cycles/cache-misses/sched)
- kprobe:动态插桩任意函数,抓寄存器/参数,perf/ftrace 底层
- eBPF:现代神器,沙箱程序运行时观测,bpftrace 几行脚本,bcc 工具集(execsnoop/biolatency…)
- lockdep:CONFIG_LOCKDEP 自动检测死锁(AB-BA/持锁睡/自递归),开发必开
- kgdb/kdb:内核 gdb 远程调试,和你裸机 gdb 几乎一样
- sysrq:魔术键紧急调试(t 栈/m 内存/c crash)
- /proc /sys debugfs:状态导出
- 网络工具:tcpdump/ss/ethtool/dropwatch;IO:iostat/blktrace;内存:slabtop/kmemleak
- 方法论:现象→定位→深入→验证;开发开 DEBUG 配置,生产关
速查表
| 想干啥 | 用什么 |
|---|---|
| 看内核日志 | dmesg / dmesg -w |
| 打印 | pr_err/pr_info/dev_info |
| 改日志级别 | echo N > /proc/sys/kernel/printk |
| 死机定位 | oops + riscv64-…-addr2line -e vmlinux ADDR |
| 反汇编 | riscv64-…-objdump -d vmlinux | grep -A 20 func |
| 跟踪函数 | echo function > current_tracer;echo func > set_ftrace_filter |
| 调用图 | echo function_graph > current_tracer |
| 跟踪事件 | echo 1 > events/xxx/enable |
| 性能热点 | perf record -g ./app;perf report |
| 火焰图 | perf record -ag + FlameGraph |
| 实时热点 | perf top |
| 缓存性能 | perf stat -e cache-misses |
| 动态插桩 | bpftrace -e ‘kprobe:func {…}’ |
| 跟踪 open | bpftrace -e ‘tracepoint:syscalls:sys_enter_openat {…}’ |
| 死锁检测 | CONFIG_LOCKDEP;dmesg | grep lockdep |
| 内存越界 | CONFIG_KASAN |
| 内存泄漏 | CONFIG_KMEMLEAK / slabtop |
| 远程调试 | kgdb + gdb vmlinux;target remote |
| 紧急栈 | echo t > /proc/sysrq-trigger |
| 触发 crash | echo c > /proc/sysrq-trigger |
| 看 socket | ss -tlnp |
| 抓包 | tcpdump -i eth0 |
| IO 统计 | iostat -x 1 |
| 进程内存 | cat /proc//maps;pmap PID |
| 调试符号 | CONFIG_DEBUG_INFO=y |
Linux 内核专题 16 篇全部完成!
从 01 心智模型到 16 调试性能,覆盖 Linux 内核全部核心模块。每篇都用"双对照法"(Linux vs FreeRTOS / Linux vs RISC-V bare-metal)对接已有知识。** MCU/RTOS/RISC-V 经验是加速器,不是包袱**。回到 链接: link看全图,按需回查。
💡技术之路漫漫,分享是为了更好地交流。如果本文的内容对你有启发,希望能得到你的 点赞 👍 和 收藏 ⭐。
如果你在调试过程中遇到了其他问题,欢迎在 评论区 💬 留言,我们一起探讨。也欢迎 关注 👀 我,一起交流底层开发的那些事儿。
172



被折叠的 条评论
为什么被折叠?



