tracefs
简介
tracefs 是 Linux 内核自带的调试工具,从久远的 2.6 内核就支持了,可以辅助定位 内核问题,原理参考 tracing_system。
tracefs 有以下 tracer:
ftrace tracer(e.g. function tracer, function_graph tracer)
event tracer(e.g. 静态 tracepoint event, 动态 kprobe event)
ftrace tracer
基于 ftrace tracer,通过 current_tracer
节点来激活使用。
event tracer
默认 event 是静态的,并且使用 tracepoint 实现,而利用 kprobe 机制,可以 动态的插入 event,实现静态 event 同样功能。 换句话说,静态 event 只能在代码中使用 tracepoint 写好后进行编译,动态 event 能够在系统运行中利用 kprobe 增加/删除。
最初 kprobe 是以内核模块形式开发(e.g. samples/kprobes/kprobe_example.c
), 但是开发代码不方便并且容易影响内核运行的稳定性。如何避免?
将 kprobe 封装成动态 kprobe event
通过 eBPF (e.g. bpftrace) 使用 kprobe
基于 event tracer,不需要通过 current_tracer
节点来激活使用。
静态 event 只需要通过
events/xxx/enable
打开 event 即可动态 event,只需要通过
kprobe_events
增加/删除 event,并且通过events/kprobes/xxx/enable
打开 event 即可。
使能 tracefs 功能
CONFIG_FUNCTION_TRACER=y
CONFIG_FUNCTION_GRAPH_TRACER=y
CONFIG_FUNCTION_GRAPH_RETVAL=y
CONFIG_KPROBE_EVENTS=y
tracefs 挂载位置
$ mount -t debugfs none /sys/kernel/debug
$ ls /sys/kernel/debug/tracing
or
$ ls /sys/kernel/tracing
节点解析
trace
输出 ftrace buffter 内容
trace_pipe
以 PIPE 方式输出 ftrace buffter 内容
tracing_on
ftrace 总开关
current_tracer
指定 tracer 类型,默认是nop
available_tracers
所有可用的 tracer 类型
set_graph_function
指定要跟踪的函数,能够输出调用哪些函数
set_ftrace_filter
指定要过滤的函数,只输出单一函数
set_event
指定要跟踪的 event
available_events
所有可用的 events
kprobe_events
添加/删除动态 event
trace_marker
用户空间直接写内容到 ftrace buffer 中
set_ftrace_pid
指定要跟踪的进程 pid
trace_options
所有可选功能的打开/关闭情况
options/trace_printk
控制 trace_printk() 是否能够输出 comment 到 ftrace buffer
options/markers
控制 trace_marker 节点是否可写
options/funcgraph-retval
显示 function_graph tracer 的函数返回值
options/funcgraph-retaddr
显示 function_graph tracer 的函数返回地址
options/funcgraph-proc
显示 function_graph tracer 在哪个进程触发
options/func_stack_trace
显示 function tracer 的函数调用栈
options/stacktrace
显示 event 的函数调用栈
events/xxx/format
显示 event 输出格式
events/xxx/filter
过滤 event,如:echo "pid==123" > filter
只显示 pid 123 的 event
events/xxx/trigger
触发 event 的额外操作,如:echo stacktrace > trigger
打印函数调用栈
events/xxx/enable
使能 event
技巧
设置
function_graph tracer + set_graph_function
跟踪一个函数往下的调用路径设置
function_graph tracer + set_graph_function + funcgraph-retval
跟踪 一个函数返回错误码的调用路径,再加上kprobe_events
打印函数参数设置
function_graph tracer + set_graph_function + funcgraph-retaddr
跟踪 一个函数返回地址,利用faddr2line
获得 inline 函数的调用路径设置
function tracer + set_ftrace_filter + func_stack_trace
跟踪 一个函数往上的调用路径设置
event + stacktrace
跟踪一个函数往上的调用路径
输出格式
# tracer: nop
#
# entries-in-buffer/entries-written: 358096/10552611 #P:6
#
# _-----=> irqs-off/BH-disabled
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / _-=> migrate-disable
# |||| / delay
# TASK-PID CPU# ||||| TIMESTAMP FUNCTION
# | | | ||||| | |
systemd-1 [002] d..1. 127.240361: contention_begin: 00000000610328f2 (flags=SPIN|WRITE)
systemd-1 [002] d..1. 127.240987: contention_end: 00000000610328f2 (ret=0)
第1行 tracer 类型
第2行 缓冲区中的事件数以及总数,如:358096/10552611,代表缓冲区因填满而丢失的 事件数为 10552611−358096=10194515
)。最后是 CPU数量
其它是记录的事件内容:进程名,PID,运行在哪个CPU上,延迟时间戳(格式:<秒>.<微秒>
), 函数名,打印内容
例子
function tracer
$ echo function > current_tracer # 指定tracer类型
$ echo func_name > set_ftrace_filter # 指定要过滤的函数
$ echo 1 > tracing_on
$ cat trace
function_graph tracer
$ echo function_graph > current_tracer # 指定tracer类型
$ echo func_name > set_graph_function # 指定要跟踪的函数,显示 func_name() 调用哪些函数
$ echo 1 > tracing_on
$ cat trace
tracepoint event
$ echo 1 > events/xxx/enable # 使能指定的 event
$ echo stacktrace > events/xxx/trigger # 打印函数调用栈
$ echo 1 > tracing_on
$ cat trace
kprobe event
$ echo 'p vm_mmap_pgoff+296 comm=$comm sem=%x0 count=+0(%x0) owner=+8(%x0)' > kprobe_events # 添加 vm_mmap_pgoff() 偏移 296 位置的动态 event(打印进程名、信号量等)
$ echo 'comm=="ABC"' > events/kprobes/p_vm_mmap_pgoff_296/filter # 进行过滤,只显示进程名为 ABC 的信息
$ echo 1 > options/stacktrace # 打印函数调用栈
$ echo 1 > events/kprobes/p_vm_mmap_pgoff_296/enable # 使能指定的 event
$ echo 1 > tracing_on
$ cat trace
注意:如果结构体的成员变量是 char *
类型,如下:
/* offset | size */ struct zone {
/* 168 | 8 */ const char *name;
使用 name=+0(+168($argN))
来打印。
过滤技巧,跟踪多个函数
## 情景1:函数名类似,使用正则表达式匹配
$ echo 'dev_attr_*' > set_ftrace_filter
$ cat set_ftrace_filter
dev_attr_store
dev_attr_show
## 情景2:追加某个函数
$ echo __kmalloc >> set_ftrace_filter
$ cat set_ftrace_filter
__kmalloc
dev_attr_store
dev_attr_show
## 情景3:基于模块过滤
$ echo 'write*:mod:ext4' >> set_ftrace_filter
$ cat set_ftrace_filter
__kmalloc
dev_attr_store
dev_attr_show
write*:mod:ext4
## 情景4:从过滤列表中删除某个函数,使用 ! 前缀
$ echo '!__kmalloc' >> set_ftrace_filter
$ cat set_ftrace_filter
dev_attr_store
dev_attr_show
write*:mod:ext4
前端工具 trace-cmd
与前面直接读写/sys/kernel/tracing/xxx
一样,如下:
## 查看 func_name() 是否可用
$ trace-cmd list -f func_name
## 过滤 func_name(),trace 数据保存到 trace.dat 文件
$ trace-cmd record -p function -l func_name
$ trace-cmd report
## 过滤 func_name(),trace数据不保存到 trace.dat 文件
$ trace-cmd start -p function -l func_name
$ trace-cmd show
## 显示 func_name() 调用栈
$ trace-cmd start -p function -l func_name --func-stack
$ trace-cmd show
## 显示 func_name() 调用哪些函数
$ trace-cmd start -p function_graph -g func_name
$ trace-cmd show
## 只显示 a.out 执行时,func_name() 调用哪些函数
$ trace-cmd start -p function_graph -g func_name ./a.out
$ trace-cmd show
## 只显示 a.out 执行时,func_name() 调用哪些函数,并且显示每一个函数的返回值
$ trace-cmd start -p function_graph -g func_name -O funcgraph-retval ./a.out
$ trace-cmd show
参数解释:
-p
:指定当前的 tracer,类似echo function > current_tracer
,支持available_tracers
中的任意一个-l
:指定过滤的函数,可以设置多个,类似echo function_name > set_ftrace_filter
-g
:指定跟踪的函数,显示调用哪些函数,类似echo function_name > set_graph_function
--func-stack
:记录被跟踪函数的调用栈,类似echo 1 > options/func_stack_trace
-O
:指定可选功能,如funcgraph-retval
,类似echo 1 > options/funcgraph-retval
阅读 function_graph 输出的日志
在 Linux Kernel 中有一个 vim 配置文件,用于方便阅读 function_graph 输出的日志,如下:
$ vim -u Documentation/trace/function-graph-fold.vim <trace_log>
Last updated
Was this helpful?