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

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

ftrace是Linux内核中的一种跟踪工具,用于分析和调试内核和应用程序的性能问题。它可以帮助开发人员跟踪函数调用、系统调用、中断事件、定时器事件等各种事件,并生成相应的跟踪数据。

ftrace 最常见的用途之一是事件跟踪。整个内核中有数百个静态事件点,可以通过 Tracefs 文件系统启用这些事件点,以查看内核某些部分发生的情况。

通过分析这些数据,开发人员可以了解代码执行路径、函数耗时、资源使用情况等信息,从而进行性能优化或故障排查。ftrace提供了多个功能组件,如函数图谱(function graph)、事件追踪(event tracing)和动态追踪(dynamic tracing),使得它在不同场景下都有广泛的应用价值。

一、ftrace介绍

ftrace功能 :帮助了解Linux内核的运行时行为,可以查看系统调用情况,以及某个函数的调用流程。2.6内核之后引入内核的。以便进行故障调试或性能分析。

Ftrace 跟踪工具由性能分析器(profiler)和跟踪器(tracer)两部分组成

性能分析器:用来提供统计和直方图数据(需要 CONFIG_ FUNCTION_PROFILER=y)

  • 函数性能分析
  • 直方图

跟踪器:提供跟踪事件的详情

  • 函数跟踪(function)
  • 点跟踪(tracepoint)
  • kprobe
  • uprobe
  • 函数调用关系(function_graph)
  • hwlat等

ftrace构成

(1)提供函数钩子的基础设施;

(2)基于tracefs文件系统的trace框架;

除了原生操作,还有一些基于ftrace的前端工具,比如perf-tools和trace-cmd,关系图如下:

Ftrace能做什么

Ftrace 可用来快速排查以下相关问题:

  • 特定内核函数调用的频次 (function)
  • 内核函数在被调用的过程中路径(调用栈) (function + stack)
  • 内核函数调用的子函数流程(子调用栈)(function graph)
  • 由于抢占导致的高延时路径等

二、ftrace的使用

1)内核支持ftrace

想让内核支持 ftrace 功能,需要将其编译进入内核,需要打开内核配置项:

kernel hacking ----> Tracers (执行make menuconfig)

目的:选择开启相应的追踪器功能

ftrace 通过 debugfs 向用户态提供了访问接口,所以还需要将 debugfs 编译进内核。激活对 debugfs 的支持,可以直接编辑内核配置文件 .config ,设置 CONFIG_DEBUG_FS=y

2)挂载debugfs文件系统

3)ftrace用户层接口

ftrace的目录:/sys/kernel/debug/tracing/ ,常用文件介绍:

  1. tracing_on,启用/禁用向追踪缓冲区写入功能。1为启用,0为禁用。
  2. trace,以文本格式输出内核中追踪缓冲区的内容,是查看trace日志的接口。
  3. current_tracer,通过该接口指定当前ftrace要使用的tracer,也就是要追踪的函数/时间。
  4. available_tracers,当前内核中可用的插件追踪器。
  5. tracing_cpumask,以十六进制的位掩码指定要作为追踪对象的处理器,例如,指定0xb时仅在处理器0、1、3上进行追踪。
  6. dynamic tracing,动态trace进行过滤的接口,是需要在编译时支持该功能,需要打开对应的宏开关:
  7. set_ftrace_pid,指定作为追踪对象的进程的PID号。
  8. trace_pipe,与trace相同,但是运行时像管道一样,可以在每次事件发生时读出追踪信息,但是读出的内容不能再次读出。
  9. buffer_size_kb,以KB为单位指定各个CPU追踪缓冲区的大小。系统追踪缓冲区的总大小就是这个值乘以CPU的数量。设置buffer_size_kb时,必须设置current_tracer为nop追踪器。
  10. set_ftrace_filter,指定要追踪的函数名称,函数名称仅可以包含一个通配符
  11. set_ftrace_notrace,指定不要追踪的函数名称。

4)ftrace的插件追踪器

  1. function,函数调用追踪器,可以看出哪个函数何时调用。
  2. function_graph,函数调用图表追踪器,可以看出哪个函数被哪个函数调用,何时返回。
  3. mmiotrace,MMIO( Memory MappedI/O)追踪器,用于Nouveau驱动程序等逆向工程。
  4. blk,block I/O追踪器。
  5. wakeup,进程调度延迟追踪器。
  6. wakeup_rt,与wakeup相同,但以实时进程为对象。
  7. irqsoff,当中断被禁止时,系统无法响应外部事件,造成系统响应延迟,irqsoff跟踪并记录内核中哪些函数禁止了中断,对于其中禁止中断时间最长的,irqsoff将在log文件的第一行标示出来,从而可以迅速定位造成系统响应延迟的原因。
  8. preemptoff,追踪并记录禁止内核抢占的函数,并清晰显示出禁止内核抢占时间最长的函数。
  9. preemptirqsoff,追踪并记录禁止内核抢占和中断时间最长的函数
  10. sched_switch,进行上下文切换的追踪,可以得知从哪个进程切换到了哪个进程。
  11. nop,不执行任何操作。不使用插件追踪器时指定。

5)使用ftrace:分为三步

  1. 设置tracer类型
  2. 设置tracer参数
  3. 使能tracer

function trace函数调用追踪器,跟踪函数调用。默认追踪所有函数,通过设置set_ftrace_filter 来跟踪过滤函数,可以看出哪个函数何时调用

  1. vailable_filter_functions:列出当前可以跟踪的内核函数,不在该文件中列出的函数,无法跟踪其活动
  2. enabled_functions:显示有回调附着的函数名称。
  3. function_profile_enabled:打开此选项,在trace_stat中就会显示function的统计信息。
  4. set_ftrace_filter:用于指定跟踪的函数
  5. set_ftrace_notrace:用于指定不跟踪的函数
  6. set_ftrace_pid:用于指定要跟踪特定进程的函数
使用:echo 0 > tracing_on 清空之前的trace记录
echo function > current_tracer 设置tracer类型为function
echo xxxxxx > set_ftrace_filter 过滤要跟踪的函数,对其进行跟踪
echo 1 > tracing_on 收集调用信息
cat trace 查看对跟踪函数的调用信息
wakeup tracer 追踪普通进程从唤醒到真正得到执行之间的延迟
使用:echo wakeup > current_tracer
 echo 1 > tracing_on
 cat trace | hrad -40
 wakeup_rt tracer

function_graphtrace 可以提供类似 C 代码的函数调用关系信息,通过写文件 set_graph_function可以显示指定要生成调用关系的函数,缺省会对所有可追踪的内核函数生成函数调用关系图

函数图追踪器对函数的进入和退出进行跟踪,有利于观察函数的执行时间,确定代码执行流程。

使用:echo 0 > trace
 echo function_graph > current_tracer 设置 tracer 类型
 echo xxxxxx > set_graph_function 设置要追踪的函数
 echo 1 > tracing_on

trace event实现低性能损耗,对执行流无影响的一种信息输出机制

使用:在 events 目录下有很多的event事件,可以看一下sched事件,sched目录下还有很多具体的事件,
打开sched_wakeup事件下,有:enable、filter、format、hist、id、trigger
enable:使能这个事件
filter:
查看函数调用栈:
常用方法:函数内部添加 WARN_ON(1) 、ftrace
在trace函数的时候,设置 echo 1 > options/func_stack_trace 即可在 trace 结果中获取追踪函数的调用栈
使用:echo 0 > tracing_on
 echo function _ current_tracer
 echo schedule > set_ftrace_filter
 echo 1 > options/func_stack_trace
 echo 1 > tracing_on
跟踪一个命令,但是命令执行时间很短

6)前端工具

使用ftrace功能进行二次封装一些命令来操作,trace-cmd 就是ftrace封装命令的其中一种。

该软件包由两部分组成:

  • trace-cmd:提供数据抓取和数据分析的功能
  • kernelshark:用图形化的方式来详细分析数据,也可以做数据抓取
【文章福利】小编推荐自己的Linux内核技术交流群:【 865977150】整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!

三、ftrace 框架

1)整体流程

kernel:会有很多的桩点,function trace/trace event,每个桩点都在想要被trace的函数里边,如果某个被trace的函数桩点触发,如果该函数的桩点是使能的就会传到之前注册好 tracers 里这个回调函数中,把现场的日志写到ring buffer 里边。

而tracers 都是由ftrace 这个框架来管理的,每个tracer 都由ftrace 框架来注册。ftrace框架的功能一个是管理 tracers ,再一个是当接收到 用户态使能某个函数/event的命令,会动态修改内核的某个函数,使其使能起来。ring buffer的功能是一个 接收tracers的写入,再一个是用户通过debugfs读取ring buffer的内容。

2)以function为例进行插桩

函数插桩,利用gcc -pg选项,在每个函数入口附近插入 mconut 调用,对桩点进行管理scripts/recordmcount.c

在内核编译时,每编译完成一个.c文件,调用recordmcount:对生成的每一个 .o 文件都进行重定位,找到mcount调用的地址,先放在临时缓冲区,等recordmcount找到每个 .o文件的mcount地址后,新增一个_mcount_loc 段,并将 mcount 调用地址写到这个段中。

include/asm-generic/vmlinux.lds.h
通过 vmlinux.lds.h 这个连接脚本,将 .o 中的每个mcount进行收集,在连接脚本中定义变量:
_start_mcount_loc
_stop_mcount_loc

将所有的 mcount_loc 放在 start 、stop之间,最后通过这两个变量获取中间数据

收集好这些插桩点之后,在内核初始化阶段,通过start、stop这两个变量,遍历将所有的都替换成 nop

上述是在不使能的情况下对 mcount进行的管理,接下来就需要使能某个tracer:需要考虑:首先需要知道这个函数插桩点的位置,记录某个tarce点的状态,就需建立新的控制结构

struct dyn_ ftrace {
unsigned long ip; /* address of mcount call-site */
unsigned long flags ;
struct dyn_ arch
ftrace arch;
};

ip:对应的bl那个地址,可以理解成一个ip 就对应一个函数

flags:当前插桩点的状态

arch:这个在当前架构下基本上为空

通过ftrace_pages 来保存dyn_ftrace 数据(ftrace_pages的大小是根据所有的 dyn_ftrace 来分配的)

在将 _ mcount_loc 数据迁移到ftrace_pages后,将其释放

ftrace_pages中数据是有序的,便于查找(根据ip进行排序)

通过dmesg可以看到ftrace_pages 占用的内存

在ftrace_pages填好内容之后,mcount_loc释放,

在用户层 执行 :cat available_filter_functions ,可以看到插桩的函数,这个就是通过ftrace_pages来打印的

dyn_ftrace flag:分成两部分:

在dyn_ftrace中的flag,会置位,通过

根据每个ip对应的flag,给内核函数选择不同的 bl 指令。

接下来是 ftrace_enter 的工作方式,eg:fatrace_caller的实现:当使能 ftrace的时候的过程如下:

首先:mcount_enter 进行压栈。

3)注册回调函数

调用 register_ftrace_function() 注册
 提供 ftrace_ops
 Static ftrace_ops
 function
 function graph
 blk
 kprobe
 dynamic ftrace_ops
 perf

通过下面的数据结构进行回调函数的注册, private 保存 ring buffer的数据内容

到了ftrace_caller这一步时,还没有注册对应的回调函数,通过公共的一个函数 ftrace_ops_list_func() ,在该函数内部会迭代的遍历注册到ftrace 框架中的 ftrace_ops ,这些如果这些ops想对对应的函数进行trace,就会调用注册的回调函数,perf的话只是想trace 当前的函数,而 function tracer的话需要trace所有的函数

4)原理

本质上是一种静态代码插装技术,不需要支持某种编程接口让用户自定义 trace行为。静态代码插装技术更加可靠,不会因为用户的不当使用而导致内核崩溃。 ftrace 代码量很小,稳定可靠。实际上,即使是Dtrace,大多数用户也只使用其静态 trace 功能。因此 ftrace 的设计非常务实。

ftrace依赖的内核特性:tracepoint[3],debugfs[2],kprobe[4],IRQ-Flags[5]

ftrace由两部分组成:framework、一系列的tracer

framework利用debugfs建立tracing目录,并提供一系列的控制文件。一系列的tarcer由framework管理,ftrace的trace信息保存在ring buffer中,也由framework管理。


相关文章
|
5天前
|
算法 Linux 调度
深入理解Linux内核调度器:从基础到优化####
本文旨在通过剖析Linux操作系统的心脏——内核调度器,为读者揭开其高效管理CPU资源的神秘面纱。不同于传统的摘要概述,本文将直接以一段精简代码片段作为引子,展示一个简化版的任务调度逻辑,随后逐步深入,详细探讨Linux内核调度器的工作原理、关键数据结构、调度算法演变以及性能调优策略,旨在为开发者与系统管理员提供一份实用的技术指南。 ####
24 4
|
6天前
|
缓存 负载均衡 Linux
深入理解Linux内核调度器
本文探讨了Linux操作系统核心组件之一——内核调度器的工作原理和设计哲学。不同于常规的技术文章,本摘要旨在提供一种全新的视角来审视Linux内核的调度机制,通过分析其对系统性能的影响以及在多核处理器环境下的表现,揭示调度器如何平衡公平性和效率。文章进一步讨论了完全公平调度器(CFS)的设计细节,包括它如何处理不同优先级的任务、如何进行负载均衡以及它是如何适应现代多核架构的挑战。此外,本文还简要概述了Linux调度器的未来发展方向,包括对实时任务支持的改进和对异构计算环境的适应性。
24 6
|
7天前
|
缓存 Linux 开发者
Linux内核中的并发控制机制:深入理解与应用####
【10月更文挑战第21天】 本文旨在为读者提供一个全面的指南,探讨Linux操作系统中用于实现多线程和进程间同步的关键技术——并发控制机制。通过剖析互斥锁、自旋锁、读写锁等核心概念及其在实际场景中的应用,本文将帮助开发者更好地理解和运用这些工具来构建高效且稳定的应用程序。 ####
25 5
|
7天前
|
算法 Unix Linux
深入理解Linux内核调度器:原理与优化
本文探讨了Linux操作系统的心脏——内核调度器(Scheduler)的工作原理,以及如何通过参数调整和代码优化来提高系统性能。不同于常规摘要仅概述内容,本摘要旨在激发读者对Linux内核调度机制深层次运作的兴趣,并简要介绍文章将覆盖的关键话题,如调度算法、实时性增强及节能策略等。
|
7天前
|
缓存 运维 网络协议
深入Linux内核架构:操作系统的核心奥秘
深入Linux内核架构:操作系统的核心奥秘
24 2
|
存储 Unix Linux
浅入分析Linux
Linux 操作系统必须完成的两个主要目的 与硬件部分交互, 为包含在硬件平台上的所有底层可编程部件提供服务 为运行在计算机系统上的应用程序(即所谓的用户空间)提供执行环境 一些操作系统运行所有的用户程序都直接与硬件部分进行交互, 比如典型的MS-DOS。
1004 0
|
5天前
|
Linux
在 Linux 系统中,“cd”命令用于切换当前工作目录
在 Linux 系统中,“cd”命令用于切换当前工作目录。本文详细介绍了“cd”命令的基本用法和常见技巧,包括使用“.”、“..”、“~”、绝对路径和相对路径,以及快速切换到上一次工作目录等。此外,还探讨了高级技巧,如使用通配符、结合其他命令、在脚本中使用,以及实际应用案例,帮助读者提高工作效率。
23 3
|
5天前
|
监控 安全 Linux
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景,包括 ping(测试连通性)、traceroute(跟踪路由路径)、netstat(显示网络连接信息)、nmap(网络扫描)、ifconfig 和 ip(网络接口配置)。掌握这些命令有助于高效诊断和解决网络问题,保障网络稳定运行。
18 2
|
13天前
|
缓存 监控 Linux
|
16天前
|
Linux Shell 数据安全/隐私保护