2. ftrace简介:ftrace的架构、tracefs文件系统、主要跟踪器概览

ftrace(Function Tracer)是Linux内核内置的跟踪框架,用于帮助开发者和调试人员分析内核内部的行为。它能够记录函数调用、事件发生以及系统状态变化,且开销极低,非常适合在生产环境中进行性能分析和问题定位。本章将介绍ftrace的核心架构、用户接口以及三种最常用的跟踪器。

2.1 ftrace架构概览

ftrace的架构采用分层设计,从上到下依次为:用户空间接口层、核心框架层、跟踪器层以及数据采集层。这种分层结构使得ftrace既灵活又易于扩展。

架构要点: ftrace通过tracefs文件系统向用户空间暴露控制接口,用户通过读写这些文件来启用/禁用跟踪器、设置过滤条件以及读取跟踪结果。
ftrace 分层架构 用户空间接口层 tracefs 文件系统(/sys/kernel/tracing/) 核心框架层 ftrace core:ring buffer、trace_array、回调管理 跟踪器层 function | function_graph | events | wakeup | irqsoff ... 每个跟踪器注册自己的回调函数到 ftrace core 数据采集层 动态插桩(ftrace mcount / fentry) | 静态 tracepoint Linux 内核函数 / 驱动代码

2.2 tracefs文件系统

ftrace通过tracefs文件系统与用户空间交互。在大多数现代Linux发行版中,tracefs被挂载在/sys/kernel/tracing/目录下。如果该目录不存在,可以手动挂载:

# 挂载 tracefs
mount -t tracefs tracefs /sys/kernel/tracing/

# 或者挂载 debugfs(旧接口,仍兼容)
mount -t debugfs debugfs /sys/kernel/debug/
# 此时 tracefs 位于 /sys/kernel/debug/tracing/

下表列出了/sys/kernel/tracing/目录下最常用的控制文件及其作用:

文件名称 作用描述 读写类型
available_tracers 列出当前内核支持的所有跟踪器 只读
current_tracer 设置或查看当前激活的跟踪器 读写
trace 读取跟踪结果(人类可读格式) 只读
trace_pipe 以流式方式读取跟踪结果(阻塞读) 只读
tracing_on 全局跟踪开关(1=开启,0=关闭) 读写
set_ftrace_filter 设置函数过滤白名单(仅跟踪指定函数) 读写
set_ftrace_notrace 设置函数过滤黑名单(排除指定函数) 读写
available_events 列出所有可用的 tracepoint 事件 只读
events/ 事件控制目录,可单独启用/禁用每个事件 目录
buffer_size_kb 设置每个 CPU 的 ring buffer 大小(KB) 读写
提示: 在调试 V4L2 驱动时,最常用的操作是:先通过 available_tracers 确认跟踪器支持情况,然后设置 current_tracer 为 function_graph,再通过 set_ftrace_filter 只过滤 V4L2 相关的函数,最后读取 trace 文件获取调用路径。

2.3 主要跟踪器概览

ftrace 提供了多种跟踪器,每种跟踪器适用于不同的调试场景。对于 V4L2 驱动调试,最常用的是 function、function_graph 和 events 三种。

2.3.1 function 跟踪器

function 跟踪器是最基础的跟踪器,它记录每个被调用内核函数的名称和调用顺序。当启用后,每个函数入口都会向 ring buffer 写入一条记录。

# 启用 function 跟踪器
echo function > /sys/kernel/tracing/current_tracer
echo 1 > /sys/kernel/tracing/tracing_on

# 执行需要跟踪的操作(如打开 V4L2 设备)
# ...

# 读取跟踪结果
cat /sys/kernel/tracing/trace | head -20

输出示例:

# 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
#              | |       |   ||||      |         |
  v4l2-test-1234  [000] .... 12345.678901: v4l2_open: v4l2_open+0x0/0x100
  v4l2-test-1234  [000] .... 12345.678902: mutex_lock: mutex_lock+0x0/0x30
  v4l2-test-1234  [000] .... 12345.678903: _raw_spin_lock: _raw_spin_lock+0x0/0x20
  v4l2-test-1234  [000] .... 12345.678904: __mutex_lock_slowpath: __mutex_lock_slowpath+0x0/0x200

2.3.2 function_graph 跟踪器

function_graph 跟踪器在 function 的基础上增加了函数调用关系的层级缩进和函数执行耗时。它以图形化的方式展示函数的嵌套调用关系,非常适合分析 V4L2 ioctl 的完整调用路径。

# 启用 function_graph 跟踪器
echo function_graph > /sys/kernel/tracing/current_tracer
echo 1 > /sys/kernel/tracing/tracing_on

# 只跟踪 V4L2 相关函数(过滤)
echo 'v4l2_*' > /sys/kernel/tracing/set_ftrace_filter

# 执行 ioctl 操作
# ...

# 读取跟踪结果
cat /sys/kernel/tracing/trace

输出示例(展示层级关系):

# tracer: function_graph
#
# CPU  DURATION                  FUNCTION CALLS
# |     |   |                     |   |   |   |
 0)               |  v4l2_ioctl() {
 0)               |    mutex_lock() {
 0)   0.500 us    |      _raw_spin_lock();
 0)   1.200 us    |    }
 0)               |    video_usercopy() {
 0)               |      v4l2_querycap() {
 0)   0.800 us    |        __check_object_size();
 0)   2.100 us    |      }
 0)               |      v4l2_s_fmt() {
 0)               |        uvc_v4l2_set_format() {
 0)   1.300 us    |          uvc_acquire_privileges();
 0)   0.600 us    |          uvc_video_alloc_buffers();
 0)   4.500 us    |        }
 0)   5.200 us    |      }
 0)   8.000 us    |    }
 0)  10.500 us    |  }
关键优势: function_graph 跟踪器能够直观展示函数调用的深度和每个子函数的耗时,这对于定位 V4L2 ioctl 中哪个子函数执行时间过长、或者哪个调用路径异常中断,具有不可替代的价值。

2.3.3 events 跟踪器

events 跟踪器基于内核预定义的 tracepoint(跟踪点)工作。与 function 系列跟踪器不同,events 不跟踪所有函数,而是只记录开发者预先埋设的特定事件点。每个事件可以携带丰富的上下文数据(如参数值、返回值、设备状态等)。

对于 V4L2 驱动调试,events 跟踪器可以捕获 V4L2 核心层预埋的 tracepoint 事件:

# 列出所有可用的 V4L2 相关事件
ls /sys/kernel/tracing/events/v4l2/

# 启用所有 V4L2 事件
echo 1 > /sys/kernel/tracing/events/v4l2/enable

# 或者只启用特定事件(如 v4l2_ioctl)
echo 1 > /sys/kernel/tracing/events/v4l2/v4l2_ioctl/enable

# 开始跟踪
echo 1 > /sys/kernel/tracing/tracing_on

# 执行 ioctl 操作
# ...

# 读取结果
cat /sys/kernel/tracing/trace

events 输出示例:

# tracer: nop
#
# entries-in-buffer/entries-written: 45/45   #P:4
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| /     delay
#           TASK-PID   CPU#  ||||   TIMESTAMP  FUNCTION
#              | |       |   ||||      |         |
  v4l2-test-1234  [000] .... 12345.678901: v4l2_ioctl: dev=1, cmd=VIDIOC_QUERYCAP, ret=0
  v4l2-test-1234  [000] .... 12345.678905: v4l2_ioctl: dev=1, cmd=VIDIOC_S_FMT, ret=0
  v4l2-test-1234  [000] .... 12345.678910: v4l2_ioctl: dev=1, cmd=VIDIOC_REQBUFS, ret=0

2.4 三种跟踪器对比

下表总结了三种主要跟踪器的特点,帮助你在不同调试场景下做出选择:

跟踪器 跟踪粒度 数据量 性能开销 适用场景
function 函数级(所有函数) 极大 中 快速定位函数调用序列,确认某个函数是否被调用
function_graph 函数级 + 调用层级 + 耗时 极大 中高 分析 ioctl 完整调用路径、定位耗时瓶颈、检查异常分支
events 预定义 tracepoint 小 低 捕获特定事件(如 ioctl 命令码、错误码),适合长期监控
注意事项: function 和 function_graph 跟踪器会记录大量数据,在未设置过滤条件时可能导致 ring buffer 迅速填满,从而丢失早期数据。建议始终配合 set_ftrace_filter 使用,只跟踪与 V4L2 相关的函数(如 v4l2_*、uvc_*、videobuf2_* 等)。

2.5 小结