欢迎光临

Linux 动态追踪技术深度解析:ftrace、perf 与 bpftrace 内核观测实战指南

在生产环境中排查性能问题或定位疑难故障时,传统的日志分析和监控往往力不从心。当问题发生在内核态、驱动层或库函数调用链中时,strace 和 lsof 这类传统工具要么开销过大、要么粒度不足。Linux 内核提供了多种动态追踪(Dynamic Tracing)机制,允许你在不修改源码、不重启系统的前提下,实时观测内核和用户态程序的执行细节。本文将系统讲解 ftrace、perf 和 bpftrace 三大主流工具的原理与实战用法,帮助你构建从浅层监控到深层追踪的完整可观测性能力。

Linux 内核动态追踪

一、动态追踪技术全景:为什么需要它,有哪些选择

动态追踪的核心思想是:在运行中的程序里动态插入探针(probe),收集你关心的数据,而不需要重新编译或重启。Linux 生态中有四代追踪技术,它们各有侧重:

工具 引入年代 能力层级 典型场景
ftrace 2.6.27 (2008) 内核函数追踪 函数调用图、事件追踪
perf (perf_events) 2.6.31 (2009) 性能计数 + 采样 CPU 火焰图、缓存命中率
eBPF / bpftrace 4.x+ (2015+) 可编程追踪 + 网络 复杂聚合、网络数据包过滤
SystemTap 2005 脚本化探针 企业级内核脚本(RHEL 生态)

三者的关系可以这样理解:ftrace 是内核自带的「调试开关」,perf 是「性能采样器」,bpftrace 则是「可编程的瑞士军刀」。它们的底层都依赖内核提供的探点机制——主要包括 kprobe(内核函数入口)、uprobe(用户态函数入口)、tracepoint(内核预定义静态探点)和 perf_event(硬件性能计数器)。

二、ftrace:内核自带的函数追踪利器

ftrace 通过 debugfs 暴露控制接口,主目录在

1
/sys/kernel/debug/tracing/

(较新内核为

1
/sys/kernel/tracing/

)。它最基础也最强大的能力是函数调用图追踪,让你看清一个系统调用到底触发了哪些内核函数。

2.1 通过 tracefs 进行函数追踪

以下命令追踪

1
openat

系统调用关联的内核函数调用链:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 确认 ftrace 可用
mount | grep tracefs
# 通常已自动挂载: tracefs on /sys/kernel/tracing type tracefs

# 开启函数图追踪,过滤只看 do_sys_openat2
cd /sys/kernel/tracing
echo function_graph > current_tracer
echo 'do_sys_openat2' > set_graph_function
echo > trace            # 清空缓冲
# 然后在另一个终端执行: cat /etc/hosts

cat trace | head -40
# 输出示例:
# 1)               |  do_sys_openat2() {
# 1)   0.580 us    |    getname() {
# 1)   0.120 us    |      getname_flags();
# 1)   0.090 us    |    }
# 1)   1.230 us    |    do_filp_open() {
# 1)   0.310 us    |      path_openat();
# 1)   0.080 us    |    }
# 1) + 12.450 us   |  }

每行前面的数字是 CPU 编号,

1
us

列是函数耗时。你能直观看到

1
openat

调用了

1
getname

1
do_filp_open

,并能发现哪一层最慢。

2.2 使用 tracepoint 观测事件

内核预定义了大量静态 tracepoint,开销比 kprobe 低且稳定。列出所有可用事件:


1
2
3
4
5
6
7
8
9
10
11
ls /sys/kernel/tracing/events/
# sched/  block/  net/  ext4/  syscalls/  ...

# 启用 sched 调度器切换事件
echo 1 > /sys/kernel/tracing/events/sched/sched_switch/enable
echo 1 > tracing_on
cat trace | head -10
# 输出会显示进程切换:
# <task>       <pid>  [cpu]  sched_switch: prev=bash:1234 next=sshd:5678

echo 0 > events/sched/sched_switch/enable  # 关闭

2.3 trace-cmd:ftrace 的友好封装

直接操作 tracefs 文件繁琐,

1
trace-cmd

提供了命令行封装:


1
2
3
4
5
6
7
8
9
10
11
12
# 安装
apt install trace-cmd   # Debian/Ubuntu
yum install trace-cmd   # RHEL/CentOS

# 记录 5 秒的 sched 事件
trace-cmd record -e sched:sched_switch -e sched:sched_wakeup sleep 5

# 交互式查看(类似 perf report 的 TUI)
trace-cmd report

# 导出为文本
trace-cmd report > sched_trace.txt

ftrace 的核心局限是它只能追踪,不能做复杂的统计聚合。当你需要「统计每个函数的平均耗时」「按调用次数排序」「按条件过滤后求和」时,就需要 perf 或 bpftrace 登场了。

三、perf:性能采样的瑞士军刀

perf 基于

1
perf_event

子系统,既能做基于硬件计数器(如 cache miss、branch miss)的精确采样,也能做软件事件(context switch、page fault)统计。它是定位 CPU 热点、缓存问题和系统级性能瓶颈的首选。

3.1 CPU 火焰图:定位热点函数

火焰图是排查 CPU 问题的杀手锏。perf 采集调用栈,再配合 flamegraph 工具可视化:


1
2
3
4
5
6
7
8
9
10
11
12
13
# 1. 采集 30 秒 CPU 采样,频率 99Hz(避开 100Hz 与定时器共振)
perf record -F 99 -g -a -- sleep 30
# -F 99: 采样频率 99Hz
# -g:    记录调用栈
# -a:    采样所有 CPU

# 2. 查看函数级热点排行
perf report --stdio | head -30

# 3. 生成火焰图(需先克隆 flamegraph 仓库)
git clone https://github.com/brendangregg/FlameGraph.git
perf script | FlameGraph/stackcollapse-perf.pl | FlameGraph/flamegraph.pl > cpu.svg
# 用浏览器打开 cpu.svg 即可看到直观的调用栈火焰图

火焰图中越宽的函数占用的 CPU 时间越多,能一眼看出热点瓶颈。例如你可能发现 40% 的时间花在

1
mutex_lock

上,这就指向了锁竞争。

3.2 硬件性能计数器分析

perf 能直接读取 CPU 的硬件性能计数器(PMC),诊断缓存命中、分支预测等微架构问题:


1
2
3
4
5
6
7
8
9
10
11
12
13
# 列出本机支持的硬件事件
perf list hw
# branch-instructions / branch-misses / cache-misses / cache-references / cycles ...

# 测量某程序的 L1 cache miss 率
perf stat -e L1-dcache-loads,L1-dcache-load-misses ./your_program

# 输出示例:
# 12,345,678  L1-dcache-loads
#    456,789  L1-dcache-load-misses  # 3.70% of all loads

# 测量分支预测失败率
perf stat -e branch-instructions,branch-misses ./your_program

缓存 miss 率超过 10% 就值得优化数据布局;分支预测失败率高通常意味着大量随机条件跳转,可考虑用查表替代分支。

<

h3>3.3 软件事件:上下文切换与系统调用


1
2
3
4
5
6
7
8
# 统计 10 秒内每秒的上下文切换次数
perf stat -e context-switches -I 1000 -a sleep 10

# 追踪某个进程的系统调用(类似 strace 但开销低得多)
perf trace -p $(pgrep -f nginx)

# 统计某程序的系统调用次数排行
perf trace -p $(pgrep -f nginx) --summary
1
perf trace

是 strace 的高性能替代品,基于采样而非 ptrace,开销从 strace 的 3-10 倍降低到几乎无感。

四、bpftrace:可编程的现代追踪语言

bpftrace 基于 eBPF,提供类似 awk 的脚本语法,能做 ftrace 和 perf 做不到的复杂聚合。它编写的程序经内核验证器(verifier)检查后以 JIT 方式运行,性能开销极低,是当前最有前景的追踪技术。

4.1 基础语法与一键脚本

bpftrace 程序由 probe + filter + action 三部分组成:


1
2
3
4
5
6
7
8
9
// hello.bt — 最简单的探针
BEGIN { printf("Hello eBPF\n"); }

// 统计所有进程的 openat 调用次数,按进程名分组
bpftrace -e 'tracepoint:syscalls:sys_enter_openat { @[comm] = count(); }'
# 输出示例(按调用次数排序):
# @[bash]: 142
# @[sshd]: 38
# @[systemd]: 567
1
comm

是内置变量(进程名),

1
@[key] = count()

是 map 聚合语法,程序结束时自动打印结果。

4.2 追踪函数耗时分布

1
hist()

函数生成直方图,是分析延迟分布的神器:


1
2
3
4
5
// 追踪 read 系统调用的返回值分布(字节数)
bpftrace -e '
tracepoint:syscalls:sys_exit_read {
  @bytes = hist(args-&gt;ret);
}'

输出会是一个 ASCII 直方图,让你看清读操作的字节数分布——是大量小块读还是少量大块读。

4.3 用 kprobe 追踪内核函数并过滤


1
2
3
4
5
6
7
// 统计某个进程打开文件超过 50 次时打印警告
bpftrace -e '
tracepoint:syscalls:sys_enter_openat
/comm == "nginx"/
{
  @opens[comm, str(args-&gt;filename)] = count();
}'

这里

1
/comm == "nginx"/

是过滤器,只在 nginx 进程触发时执行动作。

1
str()

把内核指针转成字符串。

4.4 实战:定位磁盘 I/O 延迟来源

以下脚本追踪 block 层 I/O 提交到完成的耗时,定位慢盘:


1
2
3
4
5
6
7
8
9
10
11
// iolatency.bt — 块设备 I/O 延迟追踪
tracepoint:block:block_rq_issue { @start[args-&gt;dev, args-&gt;sector] = nsecs; }
tracepoint:block:block_rq_complete
/@start[args-&gt;dev, args-&gt;sector]/
{
  $lat = (nsecs - @start[args-&gt;dev, args-&gt;sector]) / 1000;  // 微秒
  @us[args-&gt;dev] = hist($lat);
  delete(@start[args-&gt;dev, args-&gt;sector]);
}

// 运行 30 秒后 Ctrl+C,会打印每个块设备的 I/O 延迟分布直方图

如果某个设备

1
8,0

的延迟集中在 10000us 以上,而其他设备都在 100us 内,说明该盘有问题(可能是机械盘旋转延迟或坏道重读)。

五、生产环境实战:综合排查一例慢请求

假设线上服务出现偶发慢请求,P99 延迟从 50ms 飙升到 2s。我们用三件套联合排查:

第一步:perf 定位 CPU 热点


1
2
3
4
# 采集 30 秒 on-CPU 数据
perf record -F 99 -g -p $(pgrep -f your_service) -- sleep 30
perf script | FlameGraph/stackcollapse-perf.pl | FlameGraph/flamegraph.pl &gt; cpu.svg
# 发现大量时间花在 __schedule 和 try_to_wake_up,指向调度频繁

第二步:bpftrace 追踪调度延迟


1
2
3
4
5
6
7
8
9
# 统计进程被唤醒到实际运行的时间(runqueue latency)
bpftrace -e '
tracepoint:sched:sched_wakeup { @wake[tid] = nsecs; }
tracepoint:sched:sched_switch
/@wake[args-&gt;next_pid]/
{
  @runq_lat[comm] = hist((nsecs - @wake[args-&gt;next_pid]) / 1000);
  delete(@wake[args-&gt;next_pid]);
}' -p $(pgrep -f your_service)

如果发现 runqueue latency 直方图长尾严重,意味着 CPU 被其他进程抢占,你的服务经常「等 CPU」。

第三步:ftrace 定位锁竞争源头


1
2
3
4
5
# 追踪 futex 系统调用的内核路径
echo function_graph &gt; /sys/kernel/tracing/current_tracer
echo 'futex_wait_queue_me' &gt; /sys/kernel/tracing/set_graph_function
echo 1 &gt; /sys/kernel/tracing/tracing_on
# 观察是否大量线程卡在 futex 上 → 说明存在锁竞争

三个工具配合使用,可以完整还原「CPU 热点 → 调度延迟 → 锁竞争」的因果链,比单纯看监控大盘有效得多。

Linux 性能分析工具链

六、开销控制与生产安全要点

动态追踪虽然强大,但在生产环境使用必须控制开销,否则会引入新的问题:

  • 采样频率适度:perf 的
    1
    -F 99

    1
    -F 999

    安全得多。高频采样对高负载系统可能造成 5% 以上的额外 CPU 占用。

  • 过滤先行:用 filter 缩小追踪范围。追踪全部进程的 syscalls 比只追踪目标进程开销高几个数量级。
  • 善用 tracepoint 而非 kprobe:tracepoint 是内核稳定的 ABI,升级不会失效;kprobe 依赖函数符号,内核版本变化可能失效。
  • 设运行时长上限:所有 bpftrace 脚本都应配合
    1
    interval:s:30 { exit(); }

    或在命令行用

    1
    timeout 30 bpftrace ...

    ,避免遗忘导致长时间运行。

  • 权限管理:这些工具大多需要 root 或
    1
    CAP_SYS_ADMIN

    +

    1
    CAP_PERFMON

    。生产环境建议通过 sudo 白名单严格控制可执行的脚本。

  • 开箱即用的 BCC 工具集:如果你不想手写脚本,BCC(BPF Compiler Collection)提供了上百个现成工具,如
    1
    biolatency

    1
    execsnoop

    1
    opensnoop

    1
    tcplife

    ,覆盖绝大多数常见场景。

七、工具选型决策矩阵

面对不同问题,如何选择最合适的工具?以下是实战总结:

问题类型 首选工具 替代方案
定位 CPU 热点函数 perf + 火焰图 ftrace function_graph
分析延迟分布(P99) bpftrace hist() perf sched
追踪系统调用序列 perf trace / strace bpftrace tracepoint
统计函数调用次数 bpftrace count() ftrace + filter
网络数据包分析 bpftrace + kprobe:tcp_* tcpdump + BCC tcplife
磁盘 I/O 延迟 BCC biolatency bpftrace block tracepoint
缓存命中率 perf stat -e cache-misses perf c2c
内核函数调用图 ftrace function_graph trace-cmd

总结

ftrace、perf、bpftrace 构成了 Linux 内核可观测性的三层武器库。ftrace 适合快速查看内核函数调用关系,开销低但能力单一;perf 是性能采样的标准工具,火焰图和硬件计数器能力无可替代;bpftrace 则以其可编程性和低开销代表了未来方向。三者并非互斥,而是层层递进——先用 perf 拍全景定位热点区域,再用 bpftrace 精确追踪特定事件,用 ftrace 补充函数调用图细节。掌握这套工具链,你就能在不依赖额外监控基础设施的前提下,独立诊断从用户态到内核态的绝大多数性能疑难问题。

建议在测试环境先用 BCC 工具集熟悉常见场景,再逐步过渡到自己编写 bpftrace 脚本。Kernel 版本越新(5.10+ 最佳,4.x 功能受限),eBPF 能力越强、可用的 helper 越多。投资几天时间上手这些工具,会在未来的每一次故障排查中获得成倍的回报。

【本站文章皆为原创,未经允许不得转载】:汤不热吧 » Linux 动态追踪技术深度解析:ftrace、perf 与 bpftrace 内核观测实战指南
分享到: 更多 (0)