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)需要预先定义,灵活性较低。
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,跟踪其他所有函数 |
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 配置步骤
- 清空之前的配置:
echo > /sys/kernel/tracing/set_ftrace_filter
echo > /sys/kernel/tracing/set_ftrace_notrace
- 设置跟踪函数:
echo "__schedule try_to_wake_up dequeue_task enqueue_task" > /sys/kernel/tracing/set_ftrace_filter
- 排除干扰函数:
echo "irq_* printk" > /sys/kernel/tracing/set_ftrace_notrace
- 启用 function tracer:
echo function > /sys/kernel/tracing/current_tracer
- 开始跟踪:
echo 1 > /sys/kernel/tracing/tracing_on
# 运行你的实时性测试负载
echo 0 > /sys/kernel/tracing/tracing_on
- 查看结果:
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),则表明存在调度延迟。
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 进行定点分析。这样可以平衡数据完整性和性能开销。