ftrace 原理详细分析

ftrace工具的介绍及使用 ftrace 工具使用的笔记文档 阅读详情

ftrace 原理分析

1 简介

ftrace 是一个内核内部跟踪程序,用于帮助系统开发者和设计者观察内核内部正在发生的事情。它可以用于调试和分析用户空间之外的延迟和性能问题。

ftrace 的本意是指function tracer,这是因为它最开始主要是为了跟踪与采集内核运行时,函数的调用与执行情况。经过不断发展,ftrace逐渐提供了各类跟踪功能。例如,查看进程上下文切换的相关信息、系统中断被禁用的时间以及高优先级进程从被唤醒到被系统调度执行期间的最大延迟时间等等。这些功能可以很好的辅助内核开发和研究人员对内核进行调试, 并及时发现内核中的各类问题。另外,ftrace也具有很好的拓展性, 它提供了一些简洁易用的接口来允许开发者以插件的形式添加和使用更多种类的跟踪器(tracer) , 正因如此, 其逐渐发展成为了一个内核跟踪框架。一些常见的ftrace跟踪器如下:

跟踪器 相关功能描述
function 函数调用跟踪程序来跟踪所有内核函数。
function_graph 与函数跟踪程序相似,不同之处在于函数跟踪程序在函数的入口探测函数,而函数图跟踪程序在函数的入口和出口都进行跟踪。然后,它提供了绘制函数调用图的能力,类似于C源代码。
blk 阻塞式输入输出跟踪器, 跟踪记录Block I/O 相关信息
irqsoff 跟踪禁用中断的区域,并以最长的最大延迟保存跟踪。看tracing_max_latency文件。当记录新的max时,它将替换旧的跟踪值。最好在启用"latency-format"选项的情况下查看此跟踪,这在选择跟踪程序时自动设置。
preemptoff 类似于irqsoff,但是跟踪和记录抢占被禁用的时间量
preemptirqsoff 类似于irqsoff和preemptoff,但是跟踪和记录irqs和/或抢占被禁用的最大时间。
wakeup 跟踪和记录在最高优先级任务被唤醒后实际调度它所需要的最大延迟。按照一般开发人员的预期跟踪所有任务。
wakeup_rt 跟踪和记录RT任务所需要的最大延迟。这对于那些对RT任务的唤醒时间感兴趣的人很有用。
wakeup_dl 跟踪和记录唤醒SCHED_DEADLINE任务所需的最大延迟(与“wakeup”和“wakeup_rt”一样)。
mmiotrace 一种用于跟踪二进制模块的特殊跟踪程序。它将跟踪一个模块对硬件的所有调用。它也从I/O中写入和读取所有内容。
branch 这个跟踪程序可以在跟踪内核中可likely/unlikley的调用时配置。它将跟踪何时命中可能和不可能的分支,以及它的预测是否正确。

同样的,Ftrace还有个最常见的用途是 event 跟踪。内核有数百个静态事件点,可以通过tracefs启用这些事件点,以便查看内核的某些部分正在发生什么。

由于 ftrace 是一个大的框架,这里基于 function trace 进行分析。

2 ftrace 的编译器支持

2.1 HAVE_FUNCTION_TRACER 选项对 ftrace 的支持

首先对应 arch 要使用 ftrace 需要满足下面两个基础特性:

  • STACKTRACE_SUPPORT - 实现save_stack_trace()
  • TRACE_IRQFLAGS_SUPPORT - 实现include/asm/irqflags.h

当 arch 支持 ftrace 时,需要在 Kconfig 中选中 HAVE_FUNCTION_TRACER 选项,该选项将会激活 ftrace 功能。

该功能将会在编译阶段为代码添加 -pg选项,该选项将会为每个函数开头生成mcount/__mcount函数,这对于用户空间不是难事,libc 库定义了 mcount函数来完成相关工作。而对于内核则需要架构代码去实现mcount和ftrace_stub函数。不同架构可能生成的函数名不是 mcount而是_mcount/__mcount,这取决于具体架构。

如下:

// x86,有 -pg
// echo 'main(){}' | gcc -x c -S -o - - -pg
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        endbr64
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
1:      call    *mcount@GOTPCREL(%rip)	// 调用 mcount
        movl    $0, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
...
// x86,没有 -pg
// echo 'main(){}' | gcc -x c -S -o - -
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        endbr64
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6	// 没有 mcount 相关函数生成调用
        movl    $0, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
...
    
// aarch64,有 -pg
// echo 'main(){}' | aarch64-linux-gnu-gcc -x c -S -o - - -pg
        .text
        .global _mcount
        .align  2
        .global main
        .type   main, %function
main:
.LFB0:
        .cfi_startproc
        stp     x29, x30, [sp, -16]!
        .cfi_def_cfa_offset 16
        .cfi_offset 29, -16
        .cfi_offset 30, -8
        mov     x29, sp
        mov     x0, x30
        mov     x30, x0
        hint    7 // xpaclri
        mov     x0, x30
        bl      _mcount	// 生成了 _mcount
        mov     w0, 0
        ldp     x29, x30, [sp], 16
        .cfi_restore 30
        .cfi_restore 29
        .cfi_def_cfa_offset 0
        ret
        .cfi_endproc
.LFE0:

ftrace 对于 mcount 的内部调用进行了,以便满足 ftrace 的要求。下面描述的是一个正确的mcount 应该做的事:

mcount应该检查函数指针ftrace_trace_function,看看它是否被设置为 ftrace_stub,如果是,那么无事可做,直接返回,如果不是,则进行调用ftrace_trace_function,第一个参数是 frompc,第二个参数时 selfpc。例如:foo() 调用 bar(),当 bar() 调用 mcount() 时:

  • frompc - 地址 bar() 将被用来返回到 foo()
  • selfpc - 地址 bar() (mcount() 大小校正)
// 理论流程
void ftrace_stub(void)
{
   
   
    return;
}

void mcount(void)
{
   
   
    /* save any bare state needed in order to do initial checking */

    extern void (*ftrace_trace_function)(unsigned long, unsigned long);
    if (ftrace_trace_function != ftrace_stub)
        goto do_trace;

    /* restore any bare state */

    return;

    do_trace:

    /* save all state needed by the ABI (see paragraph above) */

    unsigned long frompc = ...;
    unsigned long selfpc = <return address> - MCOUNT_INSN_SIZE;
    ftrace_trace_function(frompc, selfpc);

    /* restore all state needed by the ABI */
}

// arm64 实现
ENTRY(_mcount)
	mcount_enter

	ldr_l	x2, ftrace_trace_function
	adr	x0, ftrace_stub
	cmp	x0, x2			// if (ftrace_trace_function
	b.eq	skip_ftrace_call	//     != ftrace_stub) {
   
   

	mcount_get_pc	x0		//       function's pc
	mcount_get_lr	x1		//       function's lr (= parent's pc)
	blr	x2			//   (*ftrace_trace_function)(pc, lr);

skip_ftrace_call:			// }
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
	ldr_l	x2, ftrace_graph_return
	cmp	x0, x2			//   if ((ftrace_graph_return
	b.ne	ftrace_graph_caller	//        != ftrace_stub)

	ldr_l	x2, ftrace_graph_entry	//     || (ftrace_graph_entry
	adr_l	x0, ftrace_graph_entry_stub //     != ftrace_graph_entry_stub))
	cmp	x0, x2
	b.ne	ftrace_graph_caller	//     ftrace_graph_caller();
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
	mcount_exit
ENDPROC(_mcount)
EXPORT_SYMBOL(_mcount)
NOKPROBE(_mcount)

基于HAVE_FUNCTION_TRACER我们还可以选择HAVE_FUNCTION_GRAPH_TRACER它会开启上述的CONFIG_FUNCTION_GRAPH_TRACER选项,以便于可以根据栈绘制调用表关系图(未仔细研究该部分)。

同样的,在 CONFIG_FUNCTION_GRAPH_TRACER下检查 ftrace_graph_return函数指针如果没有设置特定于ftrace_graph_entry和ftrace_graph_entry_stub函数,则调用特定于架构的ftrace_graph_caller,该函数反过来调用特定于架构的 prepare_ftrace_return,prepare_ftrace_return参数与 ftrace_trace_function 略有不同,第二个参数selfpc是相同的,但第一个参数应该是指向frompc的指针。
通常它位于堆栈上。这允许函数暂时劫持返回地址,使其指向特定于 arch 的函数return_to_handler。

下面是更新后的 mcount 伪代码:

	void mcount(void)
	{
   
   
	...
		if (ftrace_trace_function != ftrace_stub)
			goto do_trace;

	+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
	+	extern void (*ftrace_graph_return)(...);
	+	extern void (*ftrace_graph_entry)(...);
	+	if (ftrace_graph_return != ftrace_stub ||
	+	    ftrace_graph_entry != ftrace_graph_entry_stub)
	+		ftrace_graph_caller();
	+#endif

		/* restore any bare state */
	...

下面是新的 ftrace_graph_caller 汇编函数的伪代码:

	#ifdef CONFIG_FUNCTION_GRAPH_TRACER
	void ftrace_graph_caller(void)
	{
   
   
		/* save all state needed by the ABI */

		unsigned long *frompc = &...;
		unsigned long selfpc = <return address> - MCOUNT_INSN_SIZE;
		/* passing frame pointer up is optional -- see below */
		prepare_ftrace_return(frompc, selfpc, frame_pointer);

		/* restore all state needed by the ABI */
	}
	#endif

下面是新的 return_to_handler 汇编函数的伪代码。
	#ifdef CONFIG_FUNCTION_GRAPH_TRACER
	void return_to_handler(void)
	{
   
   
		
浅析Linux追踪技术之ftrace:综述 Ftrace,全称Function Tracer,是一个内部跟踪器,旨在帮助系统的开发人员和设计人员了解内核内部的情况,可以用来调试或分析在系统中发生的延迟和性能问题。尽管ftrace通常被认为是函数跟踪器,但它实际上是由几个不同的跟踪实用程序组成的框架。 阅读详情

相关推荐

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

在目录下,包含的是kernel所有的调试信息,本章只关注与tracing目录,下面挑选一些比较重要的属性文件来分析。万变不离其宗,如此复杂的框架,设计人员已经提供了README文件,里面详解了各个属性文件的含义,我建议抛弃本文,看README吧:)以上,介绍了Ftrace的由来,实现原理,以及如何使用Ftrace,并最终提供了自动化测试脚本,希望对大家有所帮助。欢迎关注 公号&星球【嵌入式艺术】,董哥原创!

Donge's Blog 5053

深入ftrace function原理

到此我们已经分析完了ftrace的各个阶段的行为,以及钩子函数的替换过程,基本上包含如下过程:编译阶段。通过编译选项 -pg -mrecord-mcount 在每个支持ftrace的函数中插入bl 0 指令链接阶段。会根据重定位段将bl 0 指令地址重定位为_mcount函数地址。运行阶段(1)ftrace_init:会将可trace函数中的bl _mcount替换为nop指令;

奇小葩 3061

深入ftrace function graph原理

我们了解了 ftrace 的两个核心机制 – 动态函数跟踪、动态函数图跟踪,二者分别向用户提供、接口来注册对函数进行跟踪以及对函数入口和返回进行跟踪的跟踪函数。最后我们以一张 ftrace 的架构图来结束本文。

奇小葩 2743

逆向实战:手把手教你用Frida绕过Flutter App的SSL Pinning(附脚本修改指南)

本文详细介绍了如何利用Frida工具绕过Flutter应用的SSL Pinning保护机制,包括静态分析定位关键函数、定制Frida Hook脚本以及流量捕获的完整解决方案。通过实战案例和脚本修改指南,帮助安全研究人员有效应对Flutter应用的安全挑战,特别适用于安卓平台的逆向工程和抓包需求。

weixin_28723171的博客 400

销售预测的方法与模型(三)丨安全库存与再订货(补货)

较高的 Z 值对应较高的服务水平(例如,服务水平 99% 对应 Z 值 2.33),意味着企业希望更低的缺货风险,因此需要更高的安全库存。在再订货点的管理中,考虑到供应商交货的不确定性,通常需要结合安全库存,以防止在交货期间库存用尽。要对L开根号,根号L表示交货周期越长,企业需要的安全库存越多,因为在较长的供应期内,需求波动的累积效应更明显。举例:供应商的交期服从标准差为2件/天的正态分布,顾客需求为50件/天的正态分布,若要客户服务水平达到 90%,安全库存量应该是多少?下面介绍几种核心的模型与公式。

weixin_45675709的博客 1524

改进爬山算法之六:随机重启爬山算法(Random-Restart Hill Climbing,RRHC)

随机重启爬山算法(Random-Restart Hill Climbing,RRHC)是爬山算法的一种改进策略,是一种启发式搜索算法,旨在通过随机重启来克服传统爬山算法容易陷入局部最优解的局限性,并增加找到全局最优解的概率。随机重启爬山算法通过在多次运行中不断尝试不同的初始解,并在每次运行中执行标准的爬山算法,最终选择所有运行中找到的最佳解作为最终结果。(1)计算复杂度:由于需要多次运行爬山算法,因此算法的计算复杂度可能较高,特别是在解空间较大或每次迭代需要较长时间的情况下。

搏博的专栏 2197

滤波器设计实战:Filter Solutions软件从入门到精通

本文是Filter Solutions滤波器设计软件的实战指南,从入门到精通。通过设计一个低通数字滤波器的完整流程,详细讲解了参数设置、FIR/IIR选择、窗函数及逼近函数应用。文章重点介绍了如何利用这款经典工具高效完成滤波器设计、性能权衡,并将设计成果导出至MATLAB等平台进行实际验证与应用,是信号处理工程师提升效率的实用参考。

weixin_29095049的博客 524

第7篇-SAP MM模块-JIT应用实战解析,从计划协议到JIT供应的关键配置与流程

本文深入解析SAP MM模块中的JIT(Just In Time)供应模式,从计划协议到JIT供应的关键配置与流程。通过动态计划行管理、需求聚合与分解等核心功能,实现精确数量、时间和地点的物料供应。文章详细介绍了主数据准备、计划协议配置、MRP参数精调及EDI集成方案,并提供了实施中的典型问题与解决方案,助力企业优化供应链管理。

weixin_30363509的博客 943

华东师范大学2020机试题解

1.统计卡牌的值 #include<iostream> #include<string> #include<vector> //#include<algorithm> using namespace std; int main() { int n; cin>>n; int answer=0; for(int i=0;i<n;++i) { string str; ci

qq_42254342的博客 1897

用Omniverse Kit开发第一个USD插件:从环境配置到实战案例(Python 3.7版)

本文详细介绍了如何使用NVIDIA Omniverse Kit和Python 3.7开发第一个USD插件,涵盖从环境配置到实战案例的全过程。通过创建可实时同步材质属性的USD插件,开发者可以快速掌握Omniverse开发框架,提升3D内容创作效率。

x8y9z0的博客 582

ADS基础教程23 - 有限元电磁仿真(FEM)可视化操作

一、引言 二、FEM可视化操作流程1.打开可视化界面2.查看介质的网格3.设置网格颜色4.选择网格5.传感器选择6.编辑传感器7.选择频率8.动画 三、总结介绍了在ADS中如何进行FEM仿真场视图操作过程。

XZ_ZC的博客 1807

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

上一篇: tracepoint 原理详细分析
下一篇: linux top命令中 cpu 利用率/mem 使用率与load average平均负载计算方式
内核新视界
博客等级 码龄8年 478粉丝 44原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值