ftrace内核跟踪技术

当我们定位到进程热点函数问题时,Ftrace是内核自带的调试工具,可以辅助定位内核问题。

从名字来看,ftrace = function trace,表示可以进行函数级的trace,随着发展Ftrace已经进化成通用的调试框架,可以实现多种trace跟踪的目的。

学习ftrace最好的还是官方文档,其地址如下所示:

https://www.kernel.org/doc/html/latest/trace/ftrace.html

一、Ftrace使用方法

1、Ftrace三板斧

1、设置 tracer 类型

2、设置 tracer 参数

3、开启tracer

目录介绍

文件描述
available_tracers可用跟踪器,hwlat blk function_graph wakeup_dl wakeup_rt wakeup function nop,nop 表示不使用跟踪器
current_tracer当前使用的跟踪器
function_profile_enabled启用函数性能分析器
available_filter_functions可跟踪的完整函数列表
set_ftrace_filter选择跟踪函数的列表,支持批量设置,例如 tcp、tcp 和 tcp 等
set_ftrace_notrace设置不跟踪的函数列表
set_event_pid设置跟踪的 PID,表示仅跟踪 PID 程序的函数或者其他跟踪
tracing_on是否启用跟踪,1 启用跟踪 0 关闭跟踪
trace_options设置跟踪的选项
trace_stat(目录)函数性能分析的输出目录
kprobe_events启用 kprobe 的配置
uprobe_events启用 uprobe 的配置
events ( 目录 )事件(Event)跟踪器的控制文件:tracepoint、kprobe、uprobe
trace跟踪的输出 (Ring Buffer)
trace_pipe跟踪的输出;提供持续不断的数据流,适用于程序进行读取

后续的目录,如无特殊说明,都默认位于 /sys/kernel/debug/tracing/ 根目录。

2、内核函数调用跟踪

进入 ftrace 工作目录:

cd /sys/kernel/debug/tracing

设置 tracer 类型为 function:

echo function > current_tracer

set_ftrace_filter 表示要跟踪的函数,这里我们只跟踪 dev_attr_show 函数:

echo dev_attr_show > set_ftrace_filter

开启全局的 tracer:

echo 1 > tracing_on

查看trace结果

cat trace

跟踪结果中标识了函数调用的任务名称、PID、CPU、标记位、时间戳及函数名字。上图中函数名称为dev_attr_show。

关闭跟踪

echo 0 > /sys/kernel/debug/tracing/tracing_on
echo nop > /sys/kernel/debug/tracing/current_tracer

从上图可以看到 function trace 一个函数的方法基本就是三板斧:

  1. 设置 current_tracer 为 function
  2. 设置要 trace 的函数
  3. 打开 trace 开关,开始 trace
  4. 提取 trace 结果

3、查看函数的调用栈

使用场景:

前面我们获得了内核函数的调用,但是有些场景我们更可能希望获取调用该内核函数的流程(即该函数是在何处被调用),这需要通过设置 options/func_stack_trace 选项实现。

设置 echo 1 > options/func_stack_trace后 即可在 trace 结果中获取追踪函数的调用栈。

#进入目录
cd /sys/kernel/debug/tracing

#先关闭跟踪
echo 0 > tracing_on

#设置跟踪类型以及跟踪的函数名称
echo function > current_tracer
echo dev_attr_show > set_ftrace_filter

#开启跟踪函数的调用栈
# echo 1 > options/func_stack_trace

#开启全局的跟踪
# echo 1 > tracing_on

其cat trace查看跟踪结果,如下图所示:

关闭

# 关闭
#进入目录
cd /sys/kernel/debug/tracing

#重置当前跟踪器为nop
sudo echo nop > current_tracer

#重置当前跟踪器的过滤条件
sudo echo  > set_ftrace_filter

#关闭内核函数堆栈跟踪开关
sudo echo 0 > options/func_stack_trace

1、4 函数调用关系跟踪

使用场景:

如果想要分析内核函数调用的子流程(即本函数调用了哪些子函数,处理的流程如何),这时需要用到 function_graph 跟踪器,从字面意思就可看出这是函数调用关系跟踪。

设置 tracer 类型为 function_graph:

echo function_graph > current_tracer

set_graph_function 表示要跟踪的函数

echo dev_attr_show > set_graph_function

上述dev_attr_show是我们此次要跟踪的函数。

开启全局的tracer:

echo 1 > tracing_on

查看trace结果

cat trace

关闭跟踪

echo 0 > /sys/kernel/debug/tracing/tracing_on
echo nop > /sys/kernel/debug/tracing/current_tracer

通过简单的例子,看到了 function/function_graph tracer 的基本用法,但是在实际应用中会有比较棘手的问题和需求。比如:

  • 某个函数被谁调用?调用栈是什么?
  • 如何跟踪某个进程?如何跟踪一个命令,但是这个命令执行时间很短?
  • 用户态的行为轨迹如何与内核中的 trace 联系到一起?
  • 如何跟踪过滤多个进程?多个函数?
  • 如何灵活控制 trace 的开关?

参考链接

https://www.cnblogs.com/wsg1100/p/17020703.html

二、Ftrace高级用法

1、基于ftrace的tracepoint跟踪

可基于 ftrace 跟踪内核静态跟踪点,可跟踪的完整列表可通过 available_events 查看。

#available_events 文件中包括全部可用于跟踪的静态跟踪点
$ cat available_events | grep openat
syscalls:sys_exit_openat2
syscalls:sys_enter_openat2
syscalls:sys_exit_openat
syscalls:sys_enter_openat

#在路径events/syscalls/sys_enter_openat 中查看sys_enter_openat跟踪点相关的选项
$ ls /sys/kernel/debug/tracing/events/syscalls/sys_enter_openat
enable  filter  format  hist  id  inject  trigger

enable:是否启用跟踪

filter:跟踪过滤

format:跟踪点输出结果的格式

示例:使用tracepoint跟踪sys_openat 系统调用

$ echo 1 > events/syscalls/sys_enter_openat/enable
$ echo 1 > tracing_on
$ cat trace

# 跟踪结果输出如下
EasyMonitor-1043    [003] ..... 39762.091723: sys_openat(dfd: ffffff9c, filename: d19fa0, flags: 0, mode: 0)
             cat-28619   [000] ..... 39762.170322: sys_openat(dfd: ffffff9c, filename: 7f5d0c69821b, flags: 80000, mode: 0)
             cat-28619   [000] ..... 39762.170340: sys_openat(dfd: ffffff9c, filename: 7f5d0c6684b0, flags: 80000, mode: 0)
             cat-28619   [000] ..... 39762.170599: sys_openat(dfd: ffffff9c, filename: 7f5d0c5e1570, flags: 80000, mode: 0)
             cat-28619   [000] ..... 39762.170659: sys_openat(dfd: ffffff9c, filename: 7ffce744f70c, flags: 0, mode: 0)
    systemd-oomd-846     [000] ..... 39762.191236: sys_openat(dfd: ffffff9c, filename: 561b721612c3, flags: 80000, mode: 0)

# 关闭
$ sudo echo 0 > events/syscalls/sys_enter_openat/enable

我们通过设置 events/syscalls/sys_enter_openat/enable 开启对于 sys_enter_openat 的跟踪,trace 文件中的跟踪记录格式与 sys_enter_openat/format 中的 print 的格式一致。

2、基于ftrace的kprobe 跟踪

kprobe 为内核提供的动态跟踪机制。kprobe机制允许跟踪函数的任意位置,可用于获取函数参数与结果返回值。

但是,使用 kprobe 机制跟踪函数须是 available_filter_functions 列表中的子集。

基于ftracek设置probe 跟踪的相关文件如下所示,其中部分文件为设置 kprobe 跟踪函数后,Ftrace 自动创建:

  • kprobe_events

设置 kprobe 跟踪的事件属性;

完整的设置格式如下,其中 GRP 用户可以直接定义,如果不设定默认为 kprobes

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

  • kprobes/<GRP>/<EVENT>/enabled

设置后动态生成,用于控制是否启用该内核函数的跟踪;

  • kprobes/<GRP>/<EVENT>/filter

设置后动态生成,kprobe 函数跟踪过滤器,与上述的跟踪点 fliter 类似;

  • kprobes/<GRP>/<EVENT>/format

设置后动态生成,kprobe 事件显示格式;

上面描述的有点抽象,我们举例说明下。

参数定义
GRP组名,如忽略会采用用缺省值kprobes
EVENT事件名,如忽略会基于SYMBOL[+offs]或MEMADDR生成
MOD含有欲跟踪符号SYM的模块名
SYM[+offs]在 符号+偏移 处插入探针
SYM%return在 符号的返回地址 处插入探针
MEMADDR在地址MEMADDR处插入探针
MAXACTIVE可以同时探测的指定函数的最大实例数,否则为缺省值 0,见 Documentation/trace/kprobes.rst section 1.3.1
FETCHARGS探测点的参数,每个探测点可以有最多 128 个参数

跟踪函数入口参数

这里仍然以 __x64_sys_openat 函数为例,演示使用 kpboe 机制进行跟踪:

# p[:[GRP/]EVENT] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS]
# GRP=my_grp EVENT=x64_sys_openat
# SYM=__x64_sys_openat
# FETCHARGS = dfd=$arg1 flags=$arg3 mode=$arg4
# 参数解释如上所示
$ sudo echo 'p:my_grp/x64_sys_openat __x64_sys_openat dfd=$arg1 flags=$arg3 mode=$arg4' >> kprobe_events

# 启用x64_sys_openat事件的跟踪,并启用堆栈跟踪
$ sudo echo 1 > events/my_grp/x64_sys_openat/enable
$ sudo echo 1 > options/stacktrace 

#获取跟踪结果
$ cat trace

             cat-29679   [005] ..... 41534.009037: <stack trace>
 => __x64_sys_openat
 => entry_SYSCALL_64_after_hwframe
             cat-29679   [005] ..... 41534.009053: x64_sys_openat: (__x64_sys_openat+0x0/0xa0) dfd=0xffffb53610443f58 flags=0xffffffffffffffff mode=0x0
             cat-29679   [005] ..... 41534.009053: <stack trace>
 => __x64_sys_openat
 => entry_SYSCALL_64_after_hwframe
             cat-29679   [005] ..... 41534.009234: x64_sys_openat: (__x64_sys_openat+0x0/0xa0) dfd=0xffffb53610443f58 flags=0xffffffffffffffff mode=0x0
             cat-29679   [005] ..... 41534.009235: <stack trace>
 => __x64_sys_openat
 => entry_SYSCALL_64_after_hwframe
             cat-29679   [005] ..... 41534.009291: x64_sys_openat: (__x64_sys_openat+0x0/0xa0) dfd=0xffffb53610443f58 flags=0xffffffffffffffff mode=0x0
             cat-29679   [005] ..... 41534.009291: <stack trace>

# 关闭,注意需要先 echo 0 > enable 停止跟踪
# 然后再使用 "-:my_grp/x64_sys_openat" 停止,否则会正在使用或者忙的错误
$ sudo echo 0 > events/my_grp/x64_sys_openat/enable
$ sudo echo '-:my_grp/x64_sys_openat' >> kprobe_events

跟踪函数返回值

kprobe 可用于跟踪函数返回值,格式如下:

r[:[GRP/]EVENT] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS]

例如:

$ sudo echo 'r:my_grp/x64_sys_openat __x64_sys_openat ret=$retval' >> kprobe_events

变量 $retval 参数表示函数返回值,其他的使用格式与 kprobe 类似。

# p[:[GRP/]EVENT] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS]
# GRP=my_grp_ret EVENT=x64_sys_openat
# SYM=__x64_sys_openat
# FETCHARGS = ret=$retval
# 参数解释如上所示
$ sudo echo 'r:my_grp_ret/x64_sys_openat __x64_sys_openat ret=$retval' >> kprobe_events

# 启用x64_sys_openat事件的跟踪,并启用堆栈跟踪
$ sudo echo 1 > events/my_grp_ret/x64_sys_openat/enable

#获取跟踪结果
$ cat trace


# 关闭,注意需要先 echo 0 > enable 停止跟踪
# 然后再使用 "-:my_grp_ret/x64_sys_openat" 停止,否则会正在使用或者忙的错误
$ sudo echo 0 > events/my_grp_ret/x64_sys_openat/enable
$ sudo echo '-:my_grp_ret/x64_sys_openat' >> kprobe_events

当同时开启函数入口跟踪和函数返回值跟踪时:

跟踪结果如下所示:

gsd-housekeepin-1697    [003] .....   345.964115: x64_sys_openat: (__x64_sys_openat+0x0/0xa0) dfd=0xffffaf3444d0ff58 flags=0xffffffffffffffff mode=0x0
 gsd-housekeepin-1697    [003] .....   345.964115: <stack trace>
 => __x64_sys_openat
 => entry_SYSCALL_64_after_hwframe
 gsd-housekeepin-1697    [003] .....   345.964116: x64_sys_openat: (do_syscall_64+0x59/0x90 <- __x64_sys_openat) ret=0xa
 gsd-housekeepin-1697    [003] .....   345.964116: <stack trace>
 => do_syscall_64
 => entry_SYSCALL_64_after_hwframe

同时能看到跟踪了函数的入口参数以及函数的返回值。

2、3 基于ftrace的uprobe 跟踪

uprobe 为用户空间的动态跟踪机制,格式和使用方式与 kprobe 的方式类似,但是由于是用户态程序跟踪需要指定跟踪的二进制文件和偏移量。

p[:[GRP/]EVENT]] PATH:OFFSET [FETCHARGS]  # 跟踪函数入口
r[:[GRP/]EVENT]] PATH:OFFSET [FETCHARGS]  # 跟踪函数返回值
-:[GRP/]EVENT]                            # 删除跟踪点

这里以跟踪 /bin/bash 二进制文件中的 readline() 函数为例:

$ readelf -s /bin/bash | grep -w readline
   920: 00000000000d6070   208 FUNC    GLOBAL DEFAULT   13 readline

$ echo 'p:my_grp/readline /bin/bash:0xd6070' >> uprobe_events
$ echo 1 > events/my_grp/readline/enable

$ cat trace
# tracer: nop
#
# entries-in-buffer/entries-written: 1/1   #P:4
#
#                                _-----=> irqs-off
#                               / _----=> need-resched
#                              | / _---=> hardirq/softirq
#                              || / _--=> preempt-depth
#                              ||| /     delay
#           TASK-PID     CPU#  ||||   TIMESTAMP  FUNCTION
#              | |         |   ||||      |         |
            bash-14951   [003] .... 54570.055093: readline: (0xaaaab3ce6070)

#关闭跟踪
$ echo 0 > events/my_grp/readline/enable
$ echo '-:my_grp/readline' >> uprobe_events

uprobe 跟踪是跟踪用户态的函数,因此需要指定二进制文件+符号偏移量才能进行跟踪。不同系统中的二进制版本或者编译方式不同,会导致函数符号表的位置不同,因此需要跟踪前进行确认。

设置的所有基于ftrace的tracepoint,kprobe,uprobe跟踪点在linux系统重启后会丢失吗?

会的!

三、Ftrace 前端工具 trace-cmd 使用

https://mp.weixin.qq.com/s?__biz=MzA5NDQzODQ3MQ==&mid=2648186333&idx=1&sn=b3b79cebd8780461fd790c5af8411e7a&chksm=886228f5bf15a1e3ce8d82cd99cd93131af660d8bba4ddb43ccd0fd26290c4ef3ded981d5a92&cur_album_id=1364814422627287041&scene=189#wechat_redirect

1、安装

ubuntu系统

apt install trace-cmd

源代码编译安装

源代码下载地址: https://github.com/rostedt/trace-cmd

2、使用帮助手册

$ trace-cmd record -h

trace-cmd version 2.3.1

usage:
 trace-cmd record [-v][-e event [-f filter]][-p plugin][-F][-d][-D][-o file] \
           [-s usecs][-O option ][-l func][-g func][-n func] \
           [-P pid][-N host:port][-t][-r prio][-b size][-B buf][command ...]
           [-m max]
          -e run command with event enabled #开启trace事件
          -f filter for previous -e event
          -p run command with plugin enabled #开启插件后运行命令
          -F filter only on the given process //只抓取给定进程的 trace
          -P trace the given pid like -F for the command
          -c also trace the childen of -F
          -T do a stacktrace on all events
          -l filter function name   //指定跟踪的函数
          -g set graph function
          -n do not trace function
          -m max size per CPU in kilobytes
          -M set CPU mask to trace
          -v will negate all -e after it (disable those events)
          -d disable function tracer when running
          -D Full disable of function tracing (for all users)
          -o data output file [default trace.dat]
          -O option to enable (or disable)
          -r real time priority to run the capture threads
          -s sleep interval between recording (in usecs) [default: 1000]
          -N host:port to connect to (see listen)
          -t used with -N, forces use of tcp in live trace
          -b change kernel buffersize (in kilobytes per CPU)
          -B create sub buffer and folling events will be enabled here
          -k do not reset the buffers after tracing.
          -i do not fail if an event is not found
          --func-stack perform a stack trace for function tracer  //为函数跟踪器执行堆栈跟踪
             (use with caution)
  • -p:指定当前的 tracer
  • -l:指定跟踪的函数
  • --func-stack:记录被跟踪函数的调用栈

以下选项也很有用。

-n do not trace function: 指定不跟踪的函数

  • 比如:trace-cmd record -p function -l 'dev*' -n dev_attr_show
  • 设置跟踪所有 dev 开头的函数,但是不跟踪 dev_attr_show

-g set graph function:指定 function_graph tracer 跟踪的 函数,类似 echo function_name > set_graph_function

-O option to enable (or disable): 设置 options,比如设置 options/func_stack_trace 可以用 -O func_stack_trace,在 optoin 名称前加上 no 就是将 option 清 0

-P:设置跟踪的进程

3、 trace-cmd实战

3.1  Function Tracer

trace-cmd使用方式:

  • trace-cmd record子命令将结果记录到本地文件中
  • trace-cmd report子命令对结果进行提取

还以 dev_attr_show 为例,查看其调用栈的 trace-cmd 命令为:

trace-cmd record -p function -l dev_attr_show --func-stack

命令解释:

  • -p:指定当前的 tracer,类似 echo function > current_tracer,可以是支持的 tracer 中的任意一个
  • -l:指定跟踪的函数,可以设置多个,类似 echo function_name > set_ftrace_filter
  • --func-stack:记录被跟踪函数的调用栈

提取 trace 结果的命令为:

trace-cmd report

先通过 record 子命令将结果记录到 trace.dat,再通过 report 命令进行结果提取。

3.2 Trace Event

命令示例:

  • 跟踪 sched_switch :trace-cmd record -e sched:sched_switch
  • 跟踪某个 event 的同时记录调用栈:trace-cmd record -e xxx -T

实例:

  • 跟踪 sched_swtich events

顾名思义,sched_switch 可以监控系统内进程切换事件。

trace-cmd record -e sched_switch
  • 查看跟踪结果
trace-cmd report

  • 查看 sched_switch 消息格式
cat /sys/kernel/debug/tracing/events/sched/sched_switch/format
  • report 结果的时候基于消息格式进行过滤
trace-cmd report -F "sched_switch: prev_comm == 'vmtoolsd'"

  • 抓取过程中过滤使用 -f,注意,-f 过滤参数中没有 event 名称
trace-cmd record -e sched_switch -f "prev_comm == 'containerd'"

使用过程中报错

trace-cmd: Permission denied can't create recorder

四、Ftrace 可视化工具kernelshark

apt install trace-cmd   #KernelShark需要使用trace-cmd来收集跟踪数据
apt install kernelshark #安装KernelShark
sudo kernelshark

项目源代码的地址在https://github.com/yordan-karadzhov/kernel-shark

kernelshark是采用QT框架来实现的。

三个重要的文件

available_tracers

available_filter_functions

available_events

五、参考资料

网上不错的讲解ftrace的资料

https://github.com/freelancer-leon/notes/blob/master/kernel/trace/ftrace.md

https://mp.weixin.qq.com/s?src=11&timestamp=1696238093&ver=4810&signature=9Ihtt3QXWFIXejdC59EET*BOJcON9OkcgR-cnbzN9Fe325i4r3J6K55i70IPPazIZV-dwB1-VSwX7WnwqYrPlHPf5QBr4qDwoD4EThRJOiiZ2L4Vo8k9bRA0eVZS4TMg&new=1

Linux内核跟踪:ftrace hook入门手册(上)

https://mp.weixin.qq.com/s/H8tpt7aWR6pvMAWi5-qVww

Linux内核跟踪:ftrace hook入门手册(下)

http://blog.nsfocus.net/ftrace-hook/

转载于:1小时掌握ftrace内核跟踪技术 - 知乎

  • 26
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值