3、ftrace核心概念:tracer、trace event、filter、trigger,以及它们之间的关系

说实话,刚接触ftrace的时候,我也被这些概念搞得有点晕。tracer、trace event、filter、trigger,每个词都认识,但放在一起就不知道谁是谁了。今天咱们就把这几个核心概念彻底捋清楚。

3.1 tracer:内核的"监控探头"

tracer,说白了就是ftrace里预置好的"监控模式"。每个tracer都盯着内核的某个特定方面,比如函数调用、调度行为、中断响应等等。

我个人习惯把tracer分成两类:

  • function tracer:跟踪函数调用,最常用
  • function_graph tracer:带调用关系的函数跟踪,能看到谁调了谁
  • irqsoff tracer:跟踪中断关闭时间,排查延迟问题
  • preemptoff tracer:跟踪抢占关闭时间
  • wakeup tracer:跟踪进程唤醒延迟
  • nop tracer:空跟踪器,啥也不干,但可以配合event用

怎么选?我一般这样:

快速定位问题:先用function_graph看调用链,再用irqsoff或preemptoff查延迟。

举个例子,你想看某个驱动函数的调用过程:

# 切换到function_graph tracer
echo function_graph > /sys/kernel/tracing/current_tracer

# 只跟踪你关心的函数
echo 'my_driver_function' > /sys/kernel/tracing/set_graph_function

# 开始跟踪
echo 1 > /sys/kernel/tracing/tracing_on
# ... 执行你的操作 ...
echo 0 > /sys/kernel/tracing/tracing_on

# 查看结果
cat /sys/kernel/tracing/trace

3.2 trace event:内核的"日志点"

trace event跟tracer不一样。tracer是"模式",event是"点"。内核代码里到处都埋了trace event,就像打了一堆日志点。你只需要决定开哪些点、关哪些点。

我记得有一次排查网络丢包问题,就是靠打开net:netif_receive_skb这个event,才看到数据包在哪个环节被丢弃了。

查看所有可用的event:

ls /sys/kernel/tracing/events/

你会看到一大堆分类:

分类 说明
sched 调度相关,如进程切换、唤醒
irq 中断相关,如中断进入/退出
syscalls 系统调用相关
block 块设备IO相关
net 网络协议栈相关
kmem 内存分配相关

启用一个event很简单:

echo 1 > /sys/kernel/tracing/events/sched/sched_switch/enable

或者一次启用整个分类:

echo 1 > /sys/kernel/tracing/events/sched/enable

3.3 filter:只抓你想要的

你想想看,如果不加filter,ftrace会输出海量数据。我见过有人开了所有event,结果一秒生成了几百MB的日志,系统直接卡死。

filter就是用来做"数据裁剪"的。它支持基于字段的表达式过滤。

举个例子,我只想看PID为1234的进程调度事件:

echo 'next_pid == 1234' > /sys/kernel/tracing/events/sched/sched_switch/filter

filter支持的表达式:

  • ==!=:等于、不等于
  • <><=>=:比较
  • &&||:逻辑与、或
  • 支持通配符:prev_comm ~ "sshd*"

小技巧:先用cat /sys/kernel/tracing/events/sched/sched_switch/format查看event的字段名,再写filter表达式。

3.4 trigger:让事件"动起来"

trigger是ftrace里最灵活、也最容易让人懵的概念。它允许你在某个event触发时,执行一个动作。

我曾经用trigger解决过一个棘手的性能问题:某个函数被频繁调用,但不知道是谁调用的。用trigger的stacktrace动作,每次调用都打印调用栈,问题一目了然。

常用的trigger动作:

动作 说明
traceon/traceoff 开启/停止跟踪
snapshot 保存当前跟踪快照
stacktrace 打印内核调用栈
enable_event/disable_event 启用/禁用另一个event

举个例子,当sched_switch事件发生时,同时打印调用栈:

echo 'stacktrace if next_pid == 1234' > /sys/kernel/tracing/events/sched/sched_switch/trigger

嗯,这里要注意:trigger的语法是动作 [if 条件]。条件部分是可选的,不加条件就是每次event都触发。

3.5 它们之间的关系

好了,四个概念都讲完了。它们之间到底是什么关系?我画个简单的逻辑图给你看:

tracer 是"监控模式",决定跟踪什么类型的数据。
trace event 是"数据源",提供具体的跟踪数据点。
filter 是"筛子",从数据源里挑出你关心的数据。
trigger 是"触发器",在数据满足条件时执行额外动作。

说白了,tracer和event是"数据生产者",filter是"数据筛选器",trigger是"数据消费者"。它们配合起来,才能完成一次高效的调试。

我常用的组合方式:

  • function_graph + filter:只看某个函数的调用链
  • event + filter:只抓特定进程的调度事件
  • event + trigger(stacktrace):定位谁触发了某个事件
  • nop tracer + event + trigger:最灵活的组合,完全自定义

避坑指南:我曾经在线上环境同时开了多个tracer和event,结果系统负载飙升。记住一个原则——只开你需要的,关掉所有不需要的。调试完立刻关掉跟踪。

最后说一句,这些概念刚开始可能觉得抽象,但用几次就熟了。下次遇到内核问题,先想想:我要用哪个tracer?开哪些event?要不要加filter?要不要加trigger?想清楚这四步,调试思路就清晰了。