2. Ftrace 基础:架构概览与核心文件

Ftrace(Function Tracer)是 Linux 内核内置的追踪框架,专为调试实时性、中断响应延迟和函数调用路径而设计。它通过静态插桩和动态改写指令,以极低开销记录内核函数的进入/退出、调度点与中断上下文切换。理解 Ftrace 的架构层次与用户态接口,是后续进行中断上下文驱动调试的基石。

2.1 Ftrace 架构概览

Ftrace 采用分层 + 插件化设计,从上到下分为三层:用户态接口层、核心框架层和底层插桩层。核心框架负责管理追踪器(tracer)的注册、环形缓冲区(ring buffer)的分配以及过滤规则的匹配;底层插桩则通过 mcount/fentry 或 kprobe 机制捕获函数执行流。

架构要点:所有追踪数据最终写入统一的 per-CPU 环形缓冲区,用户通过 trace 文件读取;不同 tracer 只是对原始数据的解析和呈现方式不同。
Ftrace 三层架构概览 Ftrace 架构概览 用户态接口层 tracefs 文件系统 (available_tracers, current_tracer, trace, set_ftrace_filter ...) 核心框架层 Tracer 注册 / 回调分发 / 过滤引擎 / per-CPU 环形缓冲区 (function_tracer, irqsoff, preemptoff, wakeup, nop ...) 底层插桩层 mcount / fentry 静态插桩 · kprobe 动态插桩 · tracepoint 说明:用户通过 tracefs 写入 current_tracer 选择追踪器,数据经环形缓冲区后由 trace 文件输出

2.2 tracefs 文件系统挂载

Ftrace 的用户态接口通过 tracefs(Trace File System)暴露。在大多数现代发行版中,tracefs 会自动挂载到 /sys/kernel/tracing;若未自动挂载,可手动执行以下命令:

# 挂载 tracefs 到标准路径
mount -t tracefs tracefs /sys/kernel/tracing

# 验证挂载
ls /sys/kernel/tracing/
兼容性提示:旧版内核(< 3.10)将 Ftrace 接口放在 /sys/kernel/debug/tracing 下,需先挂载 debugfs。建议统一使用 /sys/kernel/tracing 路径。

挂载成功后,/sys/kernel/tracing/ 目录下会出现数十个控制文件和状态文件。下表列出最核心的三个文件:

文件名称 类型 功能描述
available_tracers 只读 列出当前内核支持的所有 tracer 名称(如 function、irqsoff、preemptoff、wakeup、nop 等)
current_tracer 读写 写入 tracer 名称以激活对应追踪器;读取可查看当前激活的 tracer
trace 只读 输出当前环形缓冲区中的追踪结果(人类可读格式);写入 echo 0 > trace 可清空缓冲区

2.3 核心文件详解

2.3.1 available_tracers —— 查看可用追踪器

该文件列出内核编译时开启的所有 tracer 插件。不同内核配置输出的列表可能不同,典型输出如下:

# cat /sys/kernel/tracing/available_tracers
function function_graph irqsoff preemptoff preemptirqsoff wakeup wakeup_dl wakeup_rt nop

各 tracer 的用途简要说明:

  • function —— 追踪所有内核函数的调用,输出函数名与 PID。
  • function_graph —— 以 C 调用图风格显示函数嵌套关系,适合分析调用链深度。
  • irqsoff —— 追踪中断被关闭的最大时长,用于检测关中断导致的调度延迟。
  • preemptoff —— 追踪抢占被关闭的最大时长,用于分析内核态不可抢占路径。
  • preemptirqsoff —— 同时追踪关中断和关抢占的叠加延迟。
  • wakeup / wakeup_rt / wakeup_dl —— 追踪高优先级任务从唤醒到获得 CPU 的调度延迟。
  • nop —— 空追踪器,不记录任何数据,用于关闭追踪。
注意:若 available_tracers 中缺少 irqsoff 或 preemptoff,说明内核未开启 CONFIG_IRQSOFF_TRACER 或 CONFIG_PREEMPT_TRACER,需要重新编译内核。

2.3.2 current_tracer —— 激活追踪器

通过向 current_tracer 写入 tracer 名称来启动追踪。写入 nop 则停止追踪。典型操作流程:

# 1. 查看当前激活的 tracer(默认 nop)
cat /sys/kernel/tracing/current_tracer
nop

# 2. 激活 function tracer
echo function > /sys/kernel/tracing/current_tracer

# 3. 验证激活状态
cat /sys/kernel/tracing/current_tracer
function

# 4. 停止追踪(恢复 nop)
echo nop > /sys/kernel/tracing/current_tracer
最佳实践:切换 tracer 前务必先写入 nop 清空状态,避免残留数据干扰新 tracer 的输出。

2.3.3 trace —— 读取追踪结果

trace 文件是 Ftrace 最主要的输出接口。激活 tracer 后,内核将追踪数据写入 per-CPU 环形缓冲区,用户读取 trace 即可获得格式化文本。以下是一个 function tracer 的示例输出:

# tracer: function
#
# entries-in-buffer/entries-written: 1423/1423   #P:4
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| /     delay
#           TASK-PID   CPU#  ||||   TIMESTAMP  FUNCTION
#              | |       |   ||||      |         |
           <idle>-0     [000] d..2.  1234.567890: _raw_spin_unlock_irqrestore
           <idle>-0     [000] d..2.  1234.567891: __do_softirq
           <idle>-0     [000] .ns2.  1234.567892: run_timer_softirq
           kworker-123   [001] d..1.  1234.567900: mutex_lock
           kworker-123   [001] d..1.  1234.567901: __mutex_lock_slowpath

输出字段含义:

字段 说明
TASK-PID 任务名称和进程 ID
CPU# 运行在哪个 CPU 核心上
irqs-off d 表示关中断,. 表示开中断
need-resched N 表示需要重新调度,. 表示不需要
hardirq/softirq h 硬中断上下文,s 软中断上下文,. 进程上下文
preempt-depth 抢占锁嵌套深度(数值越大越不可抢占)
TIMESTAMP 事件发生的时间戳(秒.微秒)
FUNCTION 被追踪的内核函数名称
trace 文件数据流与字段解析 trace 文件数据流与字段解析 per-CPU 环形缓冲区 事件记录 (函数名 + 元数据) 格式化引擎 解析字段 生成文本行 trace 文件 用户态读取 (cat /trace) trace 输出字段说明 TASK-PID → 任务名与进程ID CPU# → 运行CPU编号 irqs-off → d=关中断 .=开中断 FUNCTION → 内核函数名

2.4 快速上手:第一个 Ftrace 实验

以下步骤演示如何挂载 tracefs、激活 function tracer 并读取追踪结果:

  1. 挂载 tracefs(如未自动挂载):
    mount -t tracefs tracefs /sys/kernel/tracing
  2. 查看可用 tracer:
    cat /sys/kernel/tracing/available_tracers
  3. 激活 function tracer:
    echo function > /sys/kernel/tracing/current_tracer
  4. 触发一些内核活动(例如执行 ls 或 ping):
    ls /tmp
  5. 读取追踪结果:
    cat /sys/kernel/tracing/trace | head -30
  6. 停止追踪:
    echo nop > /sys/kernel/tracing/current_tracer
调试技巧:若 trace 输出为空,检查是否已写入 current_tracer 且内核配置开启了 CONFIG_FUNCTION_TRACER。也可先写入 echo 1 > tracing_on 确保追踪开关已打开。