Featured image of post 如何使用 ftrace 来调试系统问题

如何使用 ftrace 来调试系统问题

在无法使用 bpftrace 的时候,我们用 ftrace 来分析内核当中函数的调用流程。

ftrace

查看是否支持

ftrace 可以用来跟踪 S3 过程,而 bpftrace 不能

cd /sys/kernel/debug/tracing #进入 ftrace 工作目录
cat available_tracers #查看系统支持的 tracer 类型,eg: blk mmiotrace function_graph function nop
cat available_filter_functions #查看可以跟踪的函数
cat available_events # 查看可以跟踪的事件

查看函数是否调用

echo function > current_tracer #设置 tracer 类型
echo xxx > set_ftrace_filter #设置要跟踪的函数,不设置则跟踪所有
echo 1 > tracing_on #开启跟踪
cat trace #查看结果
echo 0 > tracing_on #关闭跟踪
echo nop > current_tracer

要清空 trace 的话,可以使用 echo /dev/null > trace 。而如果使用 ftrace-cmd 的话,就不用这样做清理了。

function_graph 查看函数执行栈、内部函数执行事件

可以确定函数内部的函数指针

echo function_graph > current_tracer
# 设置前先执行`echo  > set_ftrace_filter #确保可以跟踪所有函数`

# 即执行`echo 0 > tracing_on`,否则设置不生效
echo xxx > set_graph_function #设置要查看的函数
echo 3 > max_graph_depth  # 设置最大层级
echo 1 > tracing_on
cat trace
echo 0 > tracing_on
echo   > set_graph_function
echo nop > current_tracer

查看函数调用栈(被调用流程)

echo function > current_tracer
echo xxx > set_ftrace_filter
echo 1 > options/func_stack_trace #开启调用栈跟踪
cho 1 > tracing_on
cat trace
echo 0 > tracing_on
echo 0 > options/func_stack_trace
echo nop > current_tracer

trace event/tracepoint

trace event 就是利用 ftrace 框架,实现低性能损耗,对执行流无影响的一种信息输出机制。相比 printk, trace event:

  • 不开启没有性能损耗
  • 开启后不影响代码流程
  • 不需要重新编译内核即可获取 debug 信息

系统支持的所有 trace event 都位于/sys/kernel/debug/tracing/events 目录

echo nop > current_tracer #必须确保
echo 1 > events/sched/sched_switch
echo 1 > tracing_on
cat trace
echo 0 > tracing_on
echo 0 > events/sched/sched_switch

kprobe

休眠唤醒过程无法使用 kprobe?

使用 kprobe 机制跟踪函数须是 available_filter_functions 列表中的子集,格式如下:

p[:[GRP/]EVENT] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS] # 设置 probe 探测点
r[:[GRP/]EVENT] [MOD:]SYM[+0] [FETCHARGS] # 函数地址的返回跟踪
-:[GRP/]EVENT # 删除跟踪

以下文件为设置 kprobe 跟踪函数后,Ftrace 自动创建:

  • kprobes/<GRP>/<EVENT>/enabled 用于控制是否启用该内核函数的跟踪
  • kprobes/<GRP>/<EVENT>/filter kprobe 函数跟踪过滤器,与上述的跟踪点 fliter 类似
  • kprobes/<GRP>/<EVENT>/format kprobe 事件显示格式
  • kprobe_profile kprobe 事件统计性能数据
echo 'p:my_grp/arm64_sys_openat __arm64_sys_openat dfd=$arg1 flags=$arg3 mode=$arg4' >> kprobe_events #跟踪函数参数
echo 1 > events/my_grp/arm64_sys_openat/enable
cat trace
echo 0 > events/my_grp/arm64_sys_openat/enable
echo '-:my_grp/arm64_sys_openat' >> kprobe_events #删除跟踪

echo 'r:my_grp/arm64_sys_openat __arm64_sys_openat ret=$retval' >> kprobe_events #跟踪函数返回值

uprobe

ftrace filter

namefunction
set_ftrace_filterfunction tracer 只跟踪某个函数
set_ftrace_notracefunction tracer 不跟踪某个函数
set_graph_functionfunction_graph tracer 只跟踪某个函数
set_graph_notracefunction_graph tracer 不跟踪某个函数
set_event_pidtrace event 只跟踪某个进程
set_ftrace_pidfunction/function_graph tracer 只跟踪某个进程

跟踪某个进程内核态的某个函数:set_event_pid/set_ftrace_pid

使用脚本来跟踪运行时间很短的进程

  • 函数名雷同,可以使用正则匹配
echo 'dev_attr_*' > set_ftrace_filter
  • 追加某个函数
echo 'dev_attr_*' > set_ftrace_filter
echo ip_rcv >> set_ftrace_filter
  • 基于模块过滤

格式为:<function>:<command>:<parameter>,例如,过滤 ext3 module 的 write* 函数:

echo 'write*:mod:ext3' > set_ftrace_filter
  • 从过滤列表中删除某个函数,使用“感叹号”
echo '!ip_rcv' >> set_ftrace_filter

用户态,内核态联动

用户态程序只需要打开 trace_marker 节点可以向其中写入内容,写入的内容会体现在 trace 文件中,与内核态的各种 trace 融合在一起,提供时间线、事件参考。

cd /sys/kernel/debug/tracing
echo 'hello ftrace' > trace_marker
cat trace
...
           <...>-2157    [001] ....  1227.772963: tracing_mark_write: hello ftrace

控制 trace 开关

用户态

通过 tracing_on 可以灵活控制

内核态

可以通过 set_ftrace_filter 实现的,控制范式:function>:<command>:<parameter>

简单示例:遇到 __schedule_bug 函数后关闭 trace

echo '__schedule_bug:traceoff' > set_ftrace_filter

ftrace-cmd

安装

sudo apt install trace-cmd

使用

trace-cmd start -p function_graph --max-graph-depth 2 -g bit_xfer #查看 bit_xfer 内部执行细节
trace-cmd stop #停止 trace
trace-cmd show #查看 trace 的结果
trace-cmd reset #重置

trace-cmd start -p function -l intel_hpd_irq_handler --func-stack

Reference

*Ftrace 基本用法

Ftrace 进阶用法

*Ftrace 前端工具 trace cmd 介绍

*Ftrace 实现原理与开发实践

*Ftrace 官方文档

*高效调试与分析:利用 ftrace 进行 Linux 内核追踪

*1 小时掌握 ftrace 内核跟踪技术

使用 ftrace 跟踪内核

问题排查利器:Linux 原生跟踪工具 Ftrace 必知必会

实践举例

内核当中函数是:amdgpu_gem_create_ioctl ,但是发现在关机阶段没法调试。

bpftrace 的使用

安装

sudo apt install bpfcc-tools python3-bpfcc libbpfcc libbpfcc-dev

使用

sudo funccount-bpfcc '*edid*' #统计edid有关函数的调用次数
sudo stackcount-bpfcc drm_load_edid_firmware #查看drm_load_edid_firmware的调用栈
sudo funcslower-bpfcc -U -K -m 30 '/usr/lib/xorg/Xorg:RRGetInfo' #跟踪用户层函数调用
sudo funcslower-bpfcc -U -K -m 30 '/usr/lib/xorg/modules/drivers/radeon_drv.so:drmmode_handle_uevents' #跟踪.so函数调用

arm 的机器无法使用 bpfcc 的,只能使用 bpftrace :