4. function tracer:函数跟踪原理与过滤机制

function tracer 是 ftrace 中最基础的跟踪器之一,它通过动态插桩技术在内核函数的入口和出口插入探测点,从而记录函数的调用序列。在实时性测试中,function tracer 可以帮助我们精确定位调度延迟发生的函数路径,是联合分析的关键工具。

4.1 函数跟踪原理

function tracer 的核心机制基于 mcount(或 fentry)调用。GCC 编译器在编译内核时,会在每个非内联函数的入口处预留一个 5 字节的 NOP 指令(或 call mcount)。当 ftrace 启用 function tracer 时,内核会动态地将这些 NOP 替换为跳转指令,指向 ftrace 的跟踪回调函数。

关键概念:动态插桩 vs 静态插桩。function tracer 采用动态插桩,只在需要跟踪时修改指令,对性能影响最小;而静态插桩(如早期的 tracepoint)需要预先定义,灵活性较低。
function tracer 动态插桩原理 编译阶段:NOP 预留 函数入口处 5 字节 NOP 启用 tracer:动态替换 NOP → JMP ftrace_caller 跟踪阶段:记录调用 ftrace_caller → trace_function 说明: • 编译时:GCC 在每个函数入口插入 5 字节 NOP(或 call mcount),不产生额外开销。 • 启用时:ftrace 将 NOP 替换为跳转指令,指向 ftrace_caller 处理函数。 • 跟踪时:ftrace_caller 调用 trace_function 记录函数名、时间戳、进程信息。

4.2 set_ftrace_filter:精确控制跟踪范围

set_ftrace_filter 是 function tracer 最强大的过滤接口,它允许我们指定只跟踪特定的内核函数,从而避免海量无关数据淹没关键信息。在实时性测试中,我们通常只关注调度器、中断处理、锁操作等关键路径上的函数。

4.2.1 基本用法

# 启用 function tracer
echo function > /sys/kernel/tracing/current_tracer

# 只跟踪 schedule 函数
echo schedule > /sys/kernel/tracing/set_ftrace_filter

# 跟踪多个函数(用空格分隔)
echo "schedule __schedule try_to_wake_up" > /sys/kernel/tracing/set_ftrace_filter

# 使用通配符匹配
echo "sched_*" > /sys/kernel/tracing/set_ftrace_filter

# 查看当前过滤列表
cat /sys/kernel/tracing/set_ftrace_filter
实时性测试技巧:在分析调度延迟时,建议先使用 echo "sched_*" 跟踪所有调度相关函数,然后逐步缩小范围到 __schedule 和 try_to_wake_up,以减少数据量。

4.2.2 过滤规则与优先级

过滤规则 示例 匹配行为
精确匹配 schedule 只跟踪名为 schedule 的函数
通配符 * sched_* 匹配所有以 sched_ 开头的函数
通配符 ? sched_??? 匹配 sched_ 后跟 3 个任意字符的函数
多规则组合 schedule try_to_wake_up 同时匹配多个独立函数(空格分隔)
取反(不跟踪) !schedule 排除 schedule,跟踪其他所有函数
set_ftrace_filter 过滤流程 用户写入过滤规则 ftrace 解析规则 匹配 → 记录跟踪 不匹配 → 跳过 规则支持精确匹配、通配符 *、? 以及取反操作符 !,多个规则用空格分隔。

4.3 set_ftrace_notrace:排除干扰函数

set_ftrace_notrace 是 set_ftrace_filter 的互补机制,用于明确指定不跟踪的函数。这在实时性测试中非常有用,例如我们可以排除所有中断处理函数,只关注调度路径。

4.3.1 使用场景

  • 排除高频函数:如 printk、console_unlock 等可能产生大量跟踪记录的函数。
  • 排除中断上下文:使用 irq_* 排除中断处理函数,聚焦进程调度路径。
  • 排除调试函数:排除 ftrace 自身相关的函数,避免递归跟踪。
# 排除所有中断相关函数
echo "irq_*" > /sys/kernel/tracing/set_ftrace_notrace

# 排除多个函数
echo "printk console_unlock" > /sys/kernel/tracing/set_ftrace_notrace

# 查看当前排除列表
cat /sys/kernel/tracing/set_ftrace_notrace
注意:set_ftrace_notrace 的优先级高于 set_ftrace_filter。如果一个函数同时出现在 filter 和 notrace 中,它将被排除(不跟踪)。这是 ftrace 的硬规则,使用时需谨慎。

4.4 跟踪特定内核函数:实战案例

在实时性测试中,我们通常需要跟踪调度延迟的关键路径。以下是一个完整的实战案例,展示如何配置 function tracer 来跟踪调度器核心函数。

4.4.1 配置步骤

  1. 清空之前的配置:
echo > /sys/kernel/tracing/set_ftrace_filter
echo > /sys/kernel/tracing/set_ftrace_notrace
  1. 设置跟踪函数:
echo "__schedule try_to_wake_up dequeue_task enqueue_task" > /sys/kernel/tracing/set_ftrace_filter
  1. 排除干扰函数:
echo "irq_* printk" > /sys/kernel/tracing/set_ftrace_notrace
  1. 启用 function tracer:
echo function > /sys/kernel/tracing/current_tracer
  1. 开始跟踪:
echo 1 > /sys/kernel/tracing/tracing_on
# 运行你的实时性测试负载
echo 0 > /sys/kernel/tracing/tracing_on
  1. 查看结果:
cat /sys/kernel/tracing/trace | head -50

4.4.2 输出示例解读

# tracer: function
#
# entries-in-buffer/entries-written: 1423/1423   #P:4
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| / _-=> migrate-disable
#                            |||| /     delay
#           TASK-PID     CPU#  |||||  TIMESTAMP  FUNCTION
#              | |         |   |||||     |         |
          swapper/0-0     [000] d..2.  123.456789: __schedule
          swapper/0-0     [000] d..2.  123.456790: dequeue_task
          swapper/0-0     [000] d..2.  123.456791: enqueue_task
          kworker/0:1-123 [000] d..2.  123.456792: try_to_wake_up
分析要点:在实时性测试中,关注 delay 字段(即函数执行时间)和 TIMESTAMP 字段的时间间隔。如果 __schedule 到 try_to_wake_up 之间的时间差超过预期(如 100μs),则表明存在调度延迟。
调度延迟跟踪路径(function tracer) __schedule 入口 dequeue_task pick_next_task enqueue_task context_switch try_to_wake_up 延迟分析: • 从 __schedule 入口到 context_switch 完成的时间 = 调度延迟。 • 如果 try_to_wake_up 出现在 __schedule 之前,说明唤醒事件触发了调度。

4.5 性能影响与注意事项

因素 影响 优化建议
跟踪函数数量 每多跟踪一个函数,增加一次函数调用开销(约 10-20 ns) 只跟踪关键路径函数,使用 set_ftrace_filter 精确控制
跟踪缓冲区大小 缓冲区过小会导致数据丢失,过大占用内存 根据测试时长调整 buffer_size_kb,通常 4096 KB 足够
递归跟踪 ftrace 自身函数被跟踪可能导致递归死锁 始终将 ftrace_* 加入 set_ftrace_notrace
中断上下文 在中断中启用 function tracer 可能增加中断延迟 使用 irq_* 排除中断函数,或使用 function_graph 替代
最佳实践:在实时性测试环境中,建议先使用 function_graph tracer 进行全局观察,然后使用 function tracer 配合 set_ftrace_filter 进行定点分析。这样可以平衡数据完整性和性能开销。