专栏名称: 嵌入式微处理器
关注这个时代最火的嵌入式微处理器,你想知道的都在这里。
目录
相关文章推荐
浙江大学  ·  86年前,“ASIA”杂志怎么评价浙大? ·  昨天  
苏州新闻  ·  正在公示!恭喜苏州这些高校! ·  昨天  
苏州新闻  ·  正在公示!恭喜苏州这些高校! ·  昨天  
四川大学本科招生  ·  川大新学期,昂扬起航! ·  2 天前  
四川大学本科招生  ·  川大新学期,昂扬起航! ·  2 天前  
兰州大学萃英在线  ·  成绩焦虑?学业迷茫?别担心!“向学”学业咨询 ... ·  4 天前  
51好读  ›  专栏  ›  嵌入式微处理器

一文秒懂!Ftrace系统调试工具使用终极指南

嵌入式微处理器  · 公众号  ·  · 2024-05-28 12:00

正文

1、Ftrace是什么?

Ftrace Function Trace 的简写,由 Steven Rostedt 开发的,从 2008 年发布的内核 2.6.27 中开始就内置了。

Ftrace 是一个系统内部提供的追踪工具,旨在帮助内核设计和开发人员去追踪系统内部的函数调用流程。

随着 Ftrace 的不断完善,除了追踪函数调用流程的作用外,还可以用来调试和分析系统的延迟和性能问题,并发展成为一个追踪类调试工具的框架。

除了 Ftrace 外,追踪类调试工具还包括:

Tracing overview

2、Ftrace的实现原理

为了帮助我们更好的使用 Ftrace ,我们有必要简单了解 Ftrace 的实现原理。

2.1 Ftrace框架图

Ftrace 的框架图如下:

在这里插入图片描述

由框架图我们可以知道:

  • ftrace 包括多种类型的 tracers ,每个 tracer 完成不同的功能
  • 将这些不同类型的 tracers 注册进入 ftrace framework
  • 各类 tracers 收集不同的信息,并放入到 Ring buffer 缓冲区以供调用。

2.2 Ftrace是如何记录信息的

Ftrace 采用了静态插桩和动态插桩两种方式来实现。

静态插桩

我们在 Kernel 中打开了 CONFIG_FUNCTION_TRACER 功能后,会增加一个 -pg 的一个编译选项,这个编译选项的作用就是为每个函数入口处,都会插入 bl mcount 跳转指令,使得每个函数运行时都会进入 mcount 函数。

Ftrace 一旦使能,对 kernel 中所有的函数插桩,这带来的性能开销是惊人的,有可能导致人们弃用 Ftrace 功能。

为了解决这个问题,开发者推出了 Dynamic ftrace ,以此来优化整体的性能。

动态插桩

这里的动态,是指的动态修改函数指令。

  1. 编译时,记录所有被添加跳转指令的函数,这里表示所有支持追踪的函数。
  2. 内核将所有跳转指令替换为 nop 指令,以实现非调试状态性能零损失。
  3. 根据 function tracer 设置,动态将被调试函数的 nop 指令,替换为跳转指令,以实现追踪。

总而言之, Ftrace 记录数据可以总结为以下几个步骤

  1. 打开编译选项 -pg ,为每个函数都增加跳转指令
  2. 记录这些可追踪的函数,并为了减少性能消耗,将跳转函数替换为 nop 指令
  3. 通过 flag 标志位来动态管理,将需要追踪的函数预留的 nop 指令替换回追踪指令,记录调试信息。

3、如何使用Ftrace?

3.1 配置详解

CONFIG_FTRACE=y        # 启用了 Ftrace
CONFIG_FUNCTION_TRACER=y     # 启用函数级别的追踪器
CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y   # 表示内核支持图形显示
CONFIG_FUNCTION_GRAPH_TRACER=y    # 以图形的方式显示函数追踪过程
CONFIG_STACK_TRACER=y      # 启用堆栈追踪器,用于跟踪内核函数调用的堆栈信息。
CONFIG_DYNAMIC_FTRACE=y      # 启用动态 Ftrace,允许在运行时启用和禁用 Ftrace 功能。
CONFIG_HAVE_FTRACE_NMI_ENTER=y    # 表示内核支持非屏蔽中断(NMI)时进入 Ftrace 的功能
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y   # 表示内核支持通过 mcount 记录函数调用关系。
CONFIG_FTRACE_NMI_ENTER=y                   # 表示内核支持通过 mcount 记录函数调用关系。   
CONFIG_FTRACE_SYSCALLS=y     # 系统调用的追踪
CONFIG_FTRACE_MCOUNT_RECORD=y    # 启用 mcount 记录函数调用关系。
CONFIG_SCHED_TRACER=y      # 支持调度追踪
CONFIG_FUNCTION_PROFILER=y     # 启用函数分析器,主要用于记录函数的执行时间和调用次数
CONFIG_DEBUG_FS=y       # 启用 Debug 文件系统支持

上面只是介绍了部分配置,更多详细配置可自行了解。

并且上述配置不一定全部打开,勾选自己需要的即可,通常我们选择 CONFIG_FUNCTION_TRACER CONFIG_HAVE_FUNCTION_GRAPH_TRACER 即可,然后编译烧录到开发板。

3.2 挂载debugfs文件系统

Ftrace 是基于 debugfs 调试文件系统的,所以我们的第一步就是先挂载 debugfs

mount -t debugfs none /sys/kernel/debug

此时我们能够在 /sys/kernel/debug 下看到内核支持的所有的调试信息了。

# cd /sys/kernel/debug/
# ls
asoc                gpio                regmap
bdi                 ieee80211           sched_debug
block               memblock            sched_features
clk                 mmc0                sleep_time
device_component    mmc1                suspend_stats
devices_deferred    mtd                 tracing
dma_buf             opp                 ubi
extfrag             pinctrl             ubifs
fault_around_bytes  pm_qos              wakeup_sources

3.3 traceing目录介绍

/sys/kernel/debug 目录下,包含的是 kernel 所有的调试信息,本章只关注与 tracing 目录,下面挑选一些比较重要的属性文件来分析。

万变不离其宗,如此复杂的框架,设计人员已经提供了 README 文件,里面详解了各个属性文件的含义,我建议抛弃本文,看 README 吧:)

3.3.1 trace

trace :包含当前追踪的内容,以人类可读的格式展现,通过 echo > trace 来清除。

3.3.2 trace_pipe

trace_pipe trace 一样,都是记录当前的追踪内容,但它和 trace 不一样的是:

  • trace_pipe 的读操作将会阻塞,直到有新的追踪数据进来为止;
  • 当前从 trace_pipe 读取的内容将被消耗掉,再次读 trace_pipe 又会阻塞到新数据进来为止。

简单的来说, cat trace_pipe 是堵塞读取,有数据就读,没数据就等待;而 cat trace 有没有数据都是直接返回的

3.3.3 tracing_on

tracing_on :向 tracing_on 写入 1,启用追踪;向 tracing_on 写入 0,停止追踪。

追踪使用 ring buffer 记录追踪数据。修改 tracing_on 不会影响 ring buffer 当前记录的内容。

3.3.4 current_tracer

current_tracer 表示当前启用的 tracer ,默认为 nop ,即不做任何追踪工作:

# cat current_tracer
nop

3.3.5 available_filter_functions

available_filter_functions :可以被追踪的函数列表,即可以写到 set_ftrace_filter,set_ftrace_notrace,set_graph_function,set_graph_notrace 文件的函数列表。

3.3.6 available_tracers

available_tracers 文件中包含的是当前编译到内核的 tracer 列表,也表示当前内核支持的 tracer 列表。

该列表的内容,就是可以写到 current_tracer tracer 名。

# cat available_tracers
function_graph function nop
  • nop :表示为空,不追踪
  • function :追踪函数调用
  • function_graph :以图形形式追踪函数调用

3.3.7 buffer_size_kb

buffer_size_kb 记录 CPU buffer 的大小,单位为 KB

per_cpu/cpuX/buffer_size_kb 记录 每个CPU buffer 大小,单位为 KB 。可通过写 buffer_size_kb 来改变 CPU buffer 的大小。

3.3.8 buffer_total_size_kb

buffer_total_size_kb 记录所有 CPU buffer 的总大小,即所有 CPU buffer 大小总和。

如有 128 个 CPU buffer ,每个大小 7KB,则 buffer_total_size_kb 记录的总大小为 128 * 7KB = 896。

buffer_total_size_kb 文件是只读的。

3.3.9 set_ftrace_filter

set_ftrace_filter :过滤函数追踪,仅仅追踪写入该文件的函数名。

可填入的参数,可以通过 available_filter_functions 文件查看当前支持的函数名。

该过滤功能,也有很多其他变体,如追踪某个模块的函数调用等。

官方给的示例:

Format: :mod:
example: echo :mod:ext3 > set_ftrace_filter  # 该模块必须是已经加载进去的模块

3.3.10 set_ftrace_notrace

set_ftrace_notrace :和 set_ftrace_filter 刚好相反,系统禁用对其中列举函数的追踪。

3.3.11 set_ftrace_pid

系统对 set_ftrace_pid 文件中指定的 PID 进程进行追踪。

如果开启了 options/function-fork 选项, fork 的子进程的 PID 也会自动加入文件,同时该选项也会引起系统自动将退出进程的 PID 从文件中移除。

3.3.12 set_graph_function

此文件中列出的函数将导致 函数图跟踪器仅跟踪这些函数以及它们调用的函数

但是该跟踪的记录,仍然受 set_ftrace_filter set_ftrace_notrace 的影响。

3.3.12 set_graph_notrace

set_graph_function 类似,但当函数被命中时,将禁用函数图跟踪,直到退出函数。

更多干货可见:高级工程师聚集地,助力大家更上一层楼!

3.4 简单使用示例

一般我们挂载上 debugfs 后, tracing_on 是处于打开状态的。

3.4.1 函数追踪

image-20240110110558815

3.4.2 追踪图形显示

image-20240110110617669

3.4.3 动态过滤追踪

image-20240110110641149

3.4.4 重置追踪

echo 0 > tracing_on   # 关闭trace
echo > trace    # 清空当前trace记录
cat available_tracers   # 查看当前支持的追踪类型
echo function_graph > current_tracer  # 设置当前的追踪类型
echo 1 > tracing_on   # 开启追踪
cat trace     # 查看追踪结果

4、进阶用法

上述章节,只是介绍了 Ftrace 最基本的命令,下面来看一下 Ftrace 在具体问题中的用法!

4.1 追踪任意命令

如何追踪我们执行的命令呢?

Ftrace 支持追踪特定进程,通过 set_ftrace_pid 属性来设置指定进程。然后在该进程中,执行特定的命令。

首先我们需要设置好我们的追踪器

mount -t debugfs none /sys/kernel/debug
cd /sys/kernel/debug/tracing
echo 0 > tracing_on         # 关闭追踪器
echo function > current_tracer      # 设置当前追踪类别

在我们设置好追踪器后,使用如下命令,即可追踪我们执行的命令 your_command







请到「今天看啥」查看全文