使用 ftrace 调试 Linux 内核,第 3 部分

ftrace 提供的工具函数 html

王 生辉, 软件工程师, EMC
李 骅宸, 实习生, EMC

简介: ftrace 是 Linux 内核中提供的一种调试工具。使用 ftrace 能够对内核中发生的事情进行跟踪,这在调试 bug 或者分析内核时很是有用。本系列文章对 ftrace 进行了介绍,分为三部分。本文是第三部分,经过示例代码介绍如何在代码中使用 ftrace 提供的工具函数,以与 ftrace 交互。经过本文的讲解,读者能够在实际代码中使用 ftrace,方便了调试和分析。 linux

发布日期: 2010 年 6 月 10 日 
级别: 初级 
访问状况 : 4274 次浏览 
评论: 0 (查看 | 添加评论 - 登陆) 框架

平均分 5 星 共 15 个评分 平均分 (15个评分)
为本文评分


内容

内核头文件 include/linux/kernel.h 中描述了 ftrace 提供的工具函数的原型,这些函数包括 trace_printk、tracing_on/tracing_off 等。本文经过示例模块程序向读者展现如何在代码中使用这些工具函数。 less

使用 trace_printk 打印跟踪信息 jsp

ftrace 提供了一个用于向 ftrace 跟踪缓冲区输出跟踪信息的工具函数,叫作 trace_printk(),它的使用方式与 printk() 相似。能够经过 trace 文件读取该函数的输出。从头文件 include/linux/kernel.h 中能够看到,在激活配置 CONFIG_TRACING 后,trace_printk() 定义为宏: 函数

#define trace_printk(fmt, args...)   		 \ 
        ...

下面经过一个示例模块 ftrace_demo 来演示如何使用 trace_printk() 向跟踪缓冲区输出信息,以及如何查看这些信息。这里的示例模块程序中仅提供了初始化和退出函数,这样读者不会由于须要为模块建立必要的访问接口好比设备文件而分散注意力。注意,编译模块时要加入 -pg 选项。 工具


清单 1. 示例模块 ftrace_demo
/*                                                     
 * ftrace_demo.c 
 */                                                    
 #include <linux/init.h> 
 #include <linux/module.h> 
 #include <linux/kernel.h> 

 MODULE_LICENSE("GPL"); 

 static int ftrace_demo_init(void) 
 { 
	 trace_printk("Can not see this in trace unless loaded for the second time\n"); 
	 return 0; 
 } 

 static void ftrace_demo_exit(void) 
 { 
	 trace_printk("Module unloading\n"); 
 } 

 module_init(ftrace_demo_init); 
 module_exit(ftrace_demo_exit);

示例模块很是简单,仅仅是在模块初始化函数和退出函数中输出信息。接下来要对模块的运行进行跟踪,如清单 2 所示。 学习


清单 2. 对模块 ftrace_demo 进行跟踪
[root@linux tracing]# pwd 
 /sys/kernel/debug/tracing 
 [root@linux tracing]# echo 0 > tracing_enabled 
 [root@linux tracing]# echo 1 > /proc/sys/kernel/ftrace_enabled 
 [root@linux tracing]# echo function_graph > current_tracer 

 # 事先加载模块 ftrace_demo 

 [root@linux tracing]# echo ':mod:ftrace_demo' > set_ftrace_filter 
 [root@linux tracing]# cat set_ftrace_filter 
 ftrace_demo_init 
 ftrace_demo_exit 

 # 将模块 ftrace_demo 卸载

 [root@linux tracing]# echo 1 > tracing_enabled 

 # 从新进行模块 ftrace_demo 的加载与卸载操做

 [root@linux tracing]# cat trace 
 # tracer: function_graph 
 # 
 # CPU  DURATION                  FUNCTION CALLS 
 # |     |   |                     |   |   |   | 
 1)               |  /* Can not see this in trace unless loaded for the second time */ 
 0)               |  /* Module unloading */

在这个例子中,使用 mod 指令显式指定跟踪模块 ftrace_demo 中的函数,这须要提早加载该模块,不然在写文件 set_ftrace_filter 时会由于找不到该模块报错。这样在第一次加载模块时,其初始化函数 ftrace_demo_init 中调用 trace_printk 打印的语句就跟踪不到了。所以这里会将其卸载,而后激活跟踪,再从新进行模块 ftrace_demo 的加载与卸载操做。最终能够从文件 trace 中看到模块在初始化和退出时调用 trace_printk() 输出的信息。 测试

这里仅仅是为了以简单的模块进行演示,故只定义了模块的 init/exit 函数,重复加载模块也只是为了获取初始化函数输出的跟踪信息。实践中,能够在模块的功能函数中加入对 trace_printk 的调用,这样能够记录模块的运做状况,而后对其特定功能进行调试优化。还能够将对 trace_printk() 的调用经过宏来控制编译,这样能够在调试时将其开启,在最终发布时将其关闭。 优化

回页首

使用 tracing_on/tracing_off 控制跟踪信息的记录

在跟踪过程当中,有时候在检测到某些事件发生时,想要中止跟踪信息的记录,这样,跟踪缓冲区中较新的数据是与该事件有关的。在用户态,能够经过向文件 tracing_on 写入 0 来中止记录跟踪信息,写入 1 会继续记录跟踪信息。而在内核代码中,能够经过函数 tracing_on() 和 tracing_off() 来作到这一点,它们的行为相似于对 /sys/kernel/debug/tracing 下的文件 tracing_on 分别执行写 1 和 写 0 的操做。使用这两个函数,会对跟踪信息的记录控制地更准确一些,这是由于在用户态写文件 tracing_on 到实际暂停跟踪,中间因为上下文切换、系统调度控制等可能已经通过较长的时间,这样会积累大量的跟踪信息,而感兴趣的那部分可能会被覆盖掉了。

如今对清单 1 中的代码进行修改,使用 tracing_off() 来控制跟踪信息记录的暂停。


清单 3. 使用 tracing_off 的模块 ftrace_demo
/*                                                     
 * ftrace_demo.c 
 *     modified to demostrate the usage of tracing_off 
 */                                                    
 #include <linux/init.h> 
 #include <linux/module.h> 
 #include <linux/kernel.h> 

 MODULE_LICENSE("GPL"); 

 static int ftrace_demo_init(void) 
 { 		
	 trace_printk("ftrace_demo_init called\n"); 
	 tracing_off(); 
	 return 0; 
 } 

 static void ftrace_demo_exit(void) 
 { 
	 trace_printk("ftrace_demo_exit called\n"); 
	 tracing_off(); 
 } 

 module_init(ftrace_demo_init); 
 module_exit(ftrace_demo_exit);

下面对其进行跟踪,如清单 4 所示。


清单 4. 跟踪
[root@linux tracing]# pwd 
 /sys/kernel/debug/tracing 
 [root@linux tracing]# echo 0 > tracing_enabled 
 [root@linux tracing]# echo 1 > /proc/sys/kernel/ftrace_enabled 
 [root@linux tracing]# echo 1 > tracing_on 
 [root@linux tracing]# echo function > current_tracer 
 [root@linux tracing]# echo 1 > tracing_enabled 

 # 加载模块 ftrace_demo,模块初始化函数 ftrace_demo_init 被调用

 [root@linux tracing]# cat tracing_on 
 0 
 [root@linux tracing]# cat trace | wc -l 
 120210 
 [root@linux tracing]# cat trace | grep -n ftrace_demo_init 
 120187:      insmod-2897  [000]  2610.504611: ftrace_demo_init <-do_one_initcall 
 120193:      insmod-2897  [000]  2610.504667: ftrace_demo_init: ftrace_demo_init called 

 [root@linux tracing]# echo 1 > tracing_on   # 继续跟踪信息的记录

 # 卸载模块 ftrace_demo,模块函数 ftrace_demo_exit 被调用

 [root@linux tracing]# cat tracing_on 
 0 
 [root@linux tracing]# wc -l trace 
 120106 trace 
 [root@linux tracing]# grep -n ftrace_demo_exit trace 
 120106:           rmmod-2992  [001]  3016.884449: : ftrace_demo_exit called

在这个例子中,跟踪开始以前须要确保 tracing_on 的值为 1。跟踪开始后,加载模块 ftrace_demo,其初始化方法 ftrace_demo_init 被调用,该方法会调用 tracing_off() 函数来暂停跟踪信息的记录,这时文件 tracing_on 的值被代码设置为 0。查看文件 trace,能够看到 ftrace_demo_init 相关的记录位于跟踪信息的末端,这是由于从调用 trace_off() 到其生效须要一段时间,这段时间中的内核活动会被记录下来;相比从用户态读写 tracing_on 文件,这段时间开销要小了许多。卸载模块时的状况与此相似。从这里能够看到,在代码中使用 tracing_off() 能够控制将感兴趣的信息保存在跟踪缓冲区的末端位置,不会很快被新的信息所覆盖,便于及时查看。

实际代码中,能够经过特定条件(好比检测到某种异常情况,等等)来控制跟踪信息的记录,函数的使用方式相似以下的形式:

if (condition) 
	 tracing_on() or tracing_off()

跟踪模块运行情况时,使用 ftrace 命令操做序列在用户态进行必要的设置,而在代码中则能够经过 traceing_on() 控制在进入特定代码区域时开启跟踪信息,并在遇到某些条件时经过 tracing_off() 暂停;读者能够在查看完感兴趣的信息后,将 1 写入 tracing_on 文件以继续记录跟踪信息。实践中,能够经过宏来控制是否将对这些函数的调用编译进内核模块,这样能够在调试时将其开启,在最终发布时将其关闭。

用户态的应用程序能够经过直接读写文件 tracing_on 来控制记录跟踪信息的暂停状态,以便了解应用程序运行期间内核中发生的活动。

回页首

小结

本系列文章对 ftrace 的配置和使用进行了介绍。本文是其中的第三部分,经过示例代码介绍了 ftrace 所提供的部分工具函数的使用,包括 trace_printk、tracing_on/tracing_off 。至此,本系列文章的全部内容都已经介绍完毕。经过本系列文章,读者能够了解如何使用 ftrace 来对内核进行调试和分析。ftrace 使用起来很是灵活,而且支持多种跟踪器,并且它的框架结构使得在添加新的跟踪器的时候会比较方便。读者能够在之后的工做和学习中探索 ftrace 的各类用法。


参考资料

做者简介

王生辉,软件工程师。目前在 IBM 系统技术实验室从事 Linux for Power 方面的测试工做。

李骅宸,实习生。目前在 IBM 系统技术实验室从事 Linux for Power 方面的测试工做。

相关文章
相关标签/搜索