使用 ftrace 调试 Linux 内核,第 3 部分
内核头文件 include/linux/kernel.h 中描述了 ftrace 提供的工具函数的原型,这些函数包括 trace_printk、tracing_on/tracing_off 等。本文通过示例模块程序向读者展示如何在代码中使用这些工具函数。
使用 trace_printk 打印跟踪信息
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 的各种用法。
使用 ftrace 调试 Linux 内核,第 3 部分的更多相关文章
- 使用 ftrace 调试 Linux 内核【转】
转自:http://blog.csdn.net/adaptiver/article/details/7930646 使用 ftrace 调试 Linux 内核,第 1 部分 http://blog.c ...
- 使用 ftrace 调试 Linux 内核,第 1 部分【转】
转自:http://www.ibm.com/developerworks/cn/linux/l-cn-ftrace1/index.html ftrace 是 Linux 内核中提供的一种调试工具.使用 ...
- 使用 ftrace 调试 Linux 内核,第1部分
ftrace 是 Linux 内核中提供的一种调试工具.使用 ftrace 可以对内核中发生的事情进行跟踪,这在调试 bug 或者分析内核时非常有用.本系列文章对 ftrace 进行了介绍,分为三部分 ...
- 使用 ftrace 调试 Linux 内核,第 2 部分
ftrace 操作概述 使用 ftrace 提供的跟踪器来调试或者分析内核时需要如下操作: 切换到目录 /sys/kernel/debug/tracing/ 下 查看 available_tracer ...
- 使用QEMU调试Linux内核代码
http://blog.chinaunix.net/uid-20729583-id-1884617.html http://www.linuxidc.com/Linux/2014-08/105510. ...
- 【转】TI-Davinci开发系列之六CCS5.2调试Linux内核
上转博文<TI-Davinci开发系列之五CCS5.2使用gdbserver远程调试应用程序> 使用CCS5.2远程调试内核时,只需导入Linux内核源码,而不需要编译内核,也就不会用到交 ...
- Gdb远程调试Linux内核遇到的Bug
知识共享许可协议本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/gdb-bug 本博客同步在http://www.cn ...
- VELT-0.1.5开发:使用kgdb调试Linux内核【转】
转自:http://demo.netfoucs.com/lights_joy/article/details/44106589 VELT的全称是Visual EmbedLinuxTools,它是一个与 ...
- 跟踪调试Linux内核的启动过程
跟踪调试Linux内核的启动过程---使用gdb 符钰婧 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/UST ...
随机推荐
- 树形dp——覆盖所有边的最少费用(Protecting Zonk)
一.问题描述 有一个n(n<=10000)个节点的无根树.有两种装置A,B,每种都有无限多个. 1.在某个节点X使用A装置需要C1(C1<=1000)的花费,并且此时与节点X相连的边都被覆 ...
- lg、ln的表示方法
c语言中 函数 log(x) 表示是以e为底的自然对数,即 ln(x) 函数 log10(x) 以10为底的对数,即 lg(x) 以其它数为底的对数用换底公式来表示 log(a)/log(b) 函数 ...
- ucosii(2.89)semaphore 应用要点
semaphore 的作用:1,允许一个任务与其他任务(中断)同步.2,取得共享资源使用权.3,标志事件的发生.
- 【Codeforces #228】Solutions
http://codeforces.com/contest/389 重新把号刷到Div 1 准备ACM?(我这么菜还是玩玩算了……) 官方题解出的很快 Div2 A: 怎么做都行……随便找俩数减就可以 ...
- struts2的多个文件上传
成功效果图: 上篇文章描述了单个文件的上传和配置,下面主要讲解下不同的地方: index.jsp <head> <script ...
- Java中的线程--线程的互斥与同步通信
Java中的线程之前也提到过,但是还是想再详细的学习一下,跟着张孝祥老师,系统的再学习一下. 一.线程中的互斥 线程安全中的问题解释:线程安全问题可以用银行中的转账 例题描述: 线程A与线程B分别访问 ...
- shell脚本,tee小工具的用法。
解释: tee是个工具 , 它的作用就是把标准输出,复制一份,扔文件里 ,原标准输出还输出,-a就相当于 >> 追加到文件里的意思. 不加就是 > 重定向到文件里去.
- ios runloop学习
今天突然才之间才意识到NSTimer这样的运行方式,是在多线程中实现的循环还是在主线程中去实现的呢.当然不可能是在主线程中的while那么简单,那样什么都干不了,简单看了下NSTimer是以同步方式运 ...
- C# 使用Epplus导出Excel [3]:合并列连续相同数据
C# 使用Epplus导出Excel [1]:导出固定列数据 C# 使用Epplus导出Excel [2]:导出动态列数据 C# 使用Epplus导出Excel [3]:合并列连续相同数据 C# 使用 ...
- 【NOIP2017提高A组冲刺11.6】拆网线
和syq大兄弟吐槽题目不小心yy出了正解.. 最优的选法就是选两个两个相互独立的,欸这不就是最大匹配吗?那多的企鹅就新加一条边呗?不够的就除以2上取整呗? 欸?AC了? 树也是一个二分图,最大匹配=最 ...