3. ftrace简介:ftrace的架构、tracefs文件系统、主要跟踪器
ftrace(Function Tracer)是Linux内核内置的跟踪框架,自2.6.27版本起被主线内核采纳。它能够在几乎零开销(静态插桩)到可接受开销(动态插桩)之间切换,是分析调度延迟、抢占点行为、中断关闭时间等内核性能问题的首选工具。本章将拆解ftrace的软件架构、用户态接口(tracefs)以及五种核心跟踪器的工作原理与适用场景。
3.1 ftrace软件架构
ftrace采用前端-后端-环形缓冲区三层架构。前端是用户态通过tracefs发出的跟踪请求;后端是内核中的各种跟踪器(tracer),负责挂接跟踪点或动态插桩;环形缓冲区是所有跟踪事件落地的存储介质,支持每CPU缓冲、覆盖写、时间戳等特性。
架构要点:
- 用户态接口层:通过 tracefs 文件系统暴露控制文件和输出文件,无需系统调用即可切换跟踪器、设置过滤条件、读取跟踪结果。
- 跟踪器层:每种跟踪器是一个内核模块或内置组件,通过
register_ftrace_function()注册回调函数,在函数入口/出口、调度点、中断开关点等位置采集数据。 - 环形缓冲区层:采用 per-CPU 设计,避免锁竞争;支持覆盖写(overwrite)和丢弃(discard)两种模式;时间戳精度可达纳秒级。
3.2 tracefs文件系统
tracefs 是 ftrace 的用户态控制接口,通常挂载在 /sys/kernel/tracing/(旧内核为 /debug/tracing/)。通过读写该目录下的文件,可以完成跟踪器的选择、过滤、启动、停止和数据读取。
# 标准挂载
mount -t tracefs tracefs /sys/kernel/tracing/
# 如果已经挂载 debugfs,tracefs 通常自动挂载在 /sys/kernel/debug/tracing/
mount -t debugfs debugfs /sys/kernel/debug/
下表列出 tracefs 中最核心的控制与数据文件:
| 文件路径 | 读写类型 | 功能说明 |
|---|---|---|
current_tracer |
读写 | 设置或查看当前激活的跟踪器(如 function、wakeup_rt) |
available_tracers |
只读 | 列出当前内核编译支持的跟踪器列表 |
tracing_on |
读写 | 全局跟踪开关:1 开启,0 关闭 |
trace |
只读 | 读取当前环形缓冲区中的跟踪结果(文本格式) |
trace_pipe |
只读 | 阻塞读取跟踪结果,支持流式输出 |
set_ftrace_filter |
读写 | 设置函数过滤白名单(仅跟踪指定函数) |
set_ftrace_notrace |
读写 | 设置函数过滤黑名单(排除指定函数) |
buffer_size_kb |
读写 | 设置每 CPU 环形缓冲区大小(单位 KB) |
tracing_cpumask |
读写 | 限制跟踪只在指定 CPU 上运行 |
trace 文件前,建议先执行 echo 0 > tracing_on 停止跟踪,否则可能读到不完整的数据。流式场景应使用 trace_pipe 配合 cat 命令。
3.3 主要跟踪器详解
ftrace 提供了多种跟踪器,每种跟踪器针对不同的分析场景。在调度延迟分析中,wakeup_rt 和 preemptirqsoff 是最常用的两个跟踪器。下面逐一介绍五种核心跟踪器。
3.3.1 function 跟踪器
function 跟踪器在内核函数的入口处插入回调,记录函数调用事件。它是最轻量的跟踪器之一,适合统计函数调用频率、定位热点函数。
- 原理:利用
-pg编译选项生成的mcount调用,或通过fentry动态插桩。 - 输出格式:每行包含任务名、PID、CPU、时间戳、函数名。
- 典型用法:
echo function > current_tracer后查看trace文件。
3.3.2 function_graph 跟踪器
function_graph 在 function 的基础上增加了函数出口回调,能够展示函数调用的嵌套层级和执行耗时。它是分析内核代码执行路径的首选工具。
- 原理:在函数入口和出口分别插入回调,通过缩进表示调用深度。
- 输出格式:使用
}表示函数返回,行首显示执行耗时(微秒级)。 - 典型用法:
echo function_graph > current_tracer,配合set_ftrace_filter缩小范围。
3.3.3 wakeup 跟踪器
wakeup 跟踪器监控普通任务的调度延迟。它记录从唤醒(wakeup)事件发生到任务实际获得 CPU 执行之间的时间间隔。
- 原理:在
try_to_wake_up()和context_switch()点插桩,计算延迟。 - 输出:显示最大唤醒延迟、对应的唤醒任务和被唤醒任务。
- 适用场景:分析 CFS 调度器的延迟表现。
3.3.4 wakeup_rt 跟踪器
wakeup_rt 是调度延迟分析中最核心的跟踪器。它专门监控实时任务(SCHED_FIFO / SCHED_RR)的调度延迟,记录从实时任务被唤醒到它开始执行的时间差。
- 原理:在
try_to_wake_up()中标记唤醒时间,在finish_task_switch()中计算延迟。 - 输出:包含最大延迟值、延迟分布直方图、导致延迟的抢占点函数。
- 典型用法:
echo wakeup_rt > current_tracer,然后运行实时任务,查看trace中的max_latency字段。
3.3.5 preemptirqsoff 跟踪器
preemptirqsoff 跟踪器用于测量抢占关闭(preempt_disable)和中断关闭(local_irq_disable)的持续时间。它是分析调度延迟根源——即“为什么实时任务不能及时获得 CPU”——的关键工具。
- 原理:在
preempt_disable()/preempt_enable()和local_irq_save()/local_irq_restore()点插桩,记录最长关闭时长及调用栈。 - 输出:显示最大关闭时长、关闭起始函数、关闭结束函数、调用栈。
- 适用场景:定位导致调度延迟的临界区,如自旋锁持有、中断上下文处理。
3.4 跟踪器选择决策表
在实际调优中,根据分析目标快速选择合适的跟踪器至关重要。下表总结了五种跟踪器的选择依据:
| 分析目标 | 推荐跟踪器 | 关键输出字段 | 开销等级 |
|---|---|---|---|
| 函数调用频率统计 | function | 函数名、命中次数 | 低 |
| 函数调用路径与耗时 | function_graph | 缩进层级、函数耗时 | 中 |
| 普通任务调度延迟 | wakeup | 最大延迟、唤醒任务 | 中 |
| 实时任务调度延迟 | wakeup_rt | max_latency、抢占点 | 中 |
| 抢占/中断关闭时长 | preemptirqsoff | 最大关闭时长、调用栈 | 中高 |
wakeup_rt → 发现延迟超标 → preemptirqsoff 定位临界区 → function_graph 深入分析临界区代码路径。
3.5 本章小结
- ftrace 采用用户态接口 → 跟踪器后端 → 环形缓冲区三层架构,每层职责清晰。
- tracefs 文件系统提供了统一的控制接口,核心文件包括
current_tracer、tracing_on、trace、set_ftrace_filter等。 - 五种主要跟踪器中,wakeup_rt 用于测量实时任务调度延迟,preemptirqsoff 用于定位抢占关闭/中断关闭导致的延迟根源,两者是调度延迟分析的核心工具。
- function_graph 作为辅助工具,用于深入分析临界区内的函数执行路径。