Ftrace 是 Linux 内核自带的调试工具自 2.6 内核版本起就已支持。它最初是用于函数级别的跟踪function trace但随着发展 Ftrace 已经演变成一个通用的调试框架能够实现多种跟踪目的。ftrace 通过debugfs虚拟文件系统向用户空间提供访问接口。debugfs 默认挂载在/sys/kernel/debug目录下。而ftrace的相关控制和输出文件就位于该目录下的tracing子目录中完整路径为/sys/kernel/debug/tracing。以观测 do_sys_open 调用栈为例ftrace 的使用如下# 设置跟踪器类型为 function_graph sudo sh -c echo function_graph /sys/kernel/debug/tracing/current_tracer # 设置要观测调用栈的函数 sudo sh -c echo do_sys_open /sys/kernel/debug/tracing/set_graph_function # 设置观测选项启用进程TASK/PID打印 sudo sh -c echo funcgraph-proc /sys/kernel/debug/tracing/trace_options # 开启跟踪 sudo sh -c echo 1 /sys/kernel/debug/tracing/tracing_on # 等待一段时间 # 关闭跟踪 sudo sh -c echo 1 /sys/kernel/debug/tracing/tracing_on # 查看 trace sudo cat /sys/kernel/debug/tracing/tracetrace 的内容如下指定 ftrace 跟踪器Ftrace支持多种追踪类型包括函数调用、函数图、硬件延迟、中断关闭、抢占关闭等我们可以通过/sys/kernel/debug/相关的文件来查看 ftrace 支持的跟踪类型。sudo cat /sys/kernel/debug/tracing/available_tracers hwlat blk mmiotrace function_graph wakeup_dl wakeup_rt wakeup function nop其中比较常用的是 function 和 function_graph。如要要设置跟踪器类型需要把类型写入到current_tracer文件。比如设置类型为 function_graph 可以这样操作sudo sh -c echo function_graph /sys/kernel/debug/tracing/current_tracer设置要 trace 的函数set_ftrace_filter表示要跟踪的函数比如追踪epoll_wait可以这样操作sudo sh -c echo SyS_epoll_wait /sys/kernel/debug/tracing/set_ftrace_filterset_graph_function用于设置 function_graph 跟踪器的触发函数。它不仅跟踪指定的函数还跟踪该函数调用的所有子函数。ftrace 的开关ftrace 的开关是通过tracing_on文件来控制的。# 关闭 trace sudo sh -c echo 0 /sys/kernel/debug/tracing/tracing_on # 开启 trace sudo sh -c echo 1 /sys/kernel/debug/tracing/tracing_on查看 tracesudo cat /sys/kernel/debug/tracing/trace # tracer: function # # entries-in-buffer/entries-written: 2972/2972 #P:12 # # _----- irqs-off # / _---- need-resched # | / _--- hardirq/softirq # || / _-- preempt-depth # ||| / delay # TASK-PID CPU# |||| TIMESTAMP FUNCTION # | | | |||| | | redis-server-2831 [007] .... 8269637.719334: SyS_epoll_wait -do_syscall_64 redis-server-2830 [010] .... 8269637.720688: SyS_epoll_wait -do_syscall_64通过这个 trace 我可以看到进程名、进程号、进程运行的 CPU、执行函数的时间戳等信息。从上面的例子看出使用 ftrace 还是挺麻烦的真正使用时实际上使用 trace-cmd 更多一点。trace-cmd 是一个用户空间的命令行工具用于与 ftrace 进行交互。它提供了一个更方便的接口来配置和使用 ftrace避免了直接操作 debugfs 文件系统的麻烦。trace-cmd 的使用trace-cmd 的常见命令如下trace-cmd record记录实时跟踪数据并将其写入 trace.dat 文件。trace-cmd report读取 trace.dat 文件并将二进制数据转换为可读的 ASCII 文本格式。trace-cmd start开始跟踪但不记录到 trace.dat 文件。trace-cmd stop停止跟踪。trace-cmd extract从内核缓冲区提取数据并创建 trace.dat 文件。trace-cmd reset禁用所有跟踪并恢复系统性能。接下来我们用 trace-cmd 来实现前面 ftrace 观测do_sys_open函数调用图的效果。首先使用 record 记录 trace 数据trace-cmd record -p function_graph -g do_sys_open注意 trace-cmd 默认开启了 funcgraph-proc 这个 trace-option不需要手动指定。使用ctrl-c退出这个 trace-cmd 时会在当前目录生成 trace.dat 文件。接下来使用 report 读取 trace.dat 生成可读的文本trace-cmd 的常用选项查看可以跟踪的事件当我们不知道可以跟踪哪些事件时我们可以使用trace-cmd list列举当前系统上所有可用的事件trace-cmd list -e它还可以带一个可选的参数使用正则表达式进行过滤trace-cmd list -e ^sched.* # 列出所有以 sched 开头的事件跟踪特定进程的函数调用如果只想跟踪特定进程的函数调用可以使用 -P 选项指定进程的 PID。例如要跟踪 PID 为 10885 的进程可以使用以下命令trace-cmd record -p function_graph -P 2830函数过滤-g 选项用于 function_graph 插件-g do_sys_open表示只跟踪do_sys_open函数及其调用的所有子函数。-l 选项指定要跟踪的函数。例如要跟踪所有以ext4_开头的函数可以使用以下命令trace-cmd record -p function_graph -l ext4_*-l 和 -g 的区别也比较显而易见-l 不会跟踪其内部的调用子函数-g 会跟踪函数内部调用的子函数。限制跟踪深度默认情况下trace-cmd 的 function_graph 会记录所有嵌套的函数调用。可以通过设置--max-graph-depth来限制跟踪深度。例如要将深度设置为 2可以使用以下命令trace-cmd record -p function_graph --max-graph-depth 2 -P 2830追踪特定事件可以结合事件追踪-e来获取更详细的信息比如-e sched:sched_switch将指定追踪调度切换事件。还可以使用正则表达式过滤比如追踪 nfs 相关的事件trace-cmd record -e nfs:*ftrace 与 kprobe、kretprobe通过向 /sys/kernel/debug/tracing/kprobe_events 文件写入特定格式的字符串来添加 Kprobe 事件。例如要跟踪 do_sys_open 函数的调用可以使用如下命令sudo sh -c echo p:myopen do_sys_open filename0(%si):string /sys/kernel/debug/tracing/kprobe_events其中p:myopen do_sys_open指定了一个名为 myopen 的 kprobe 事件目标追踪函数是do_sys_open。do_sys_open 这个函数的定义如下/** * vfs_open - open the file at the given path * path: path to open * file: newly allocated file with f_flag initialized * cred: credentials to use */ int vfs_open(const struct path *path, struct file *file) { file-f_path *path; return do_dentry_open(file, d_backing_inode(path-dentry), NULL); }file 是它的第二个参数在 x86-64 的调用规约中使用 6 个寄存器RDI、RSI、RDX、RCX、R8、R9传递函数前 6 项参数用 RAX 存储返回值。因此第二个参数 file 采用寄存器 SI 来传递。接下来我们使用 trace-cmd 来完成 kprobe 事件的记录sudo trace-cmd record -e myopen生成的 trace 日志如下ftrace 是一个很强大的框架推荐大家好好研究一下对于我们分析内核调用作用非常有用调用作用非常大。