内核头文件 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 部分的更多相关文章

  1. 使用 ftrace 调试 Linux 内核【转】

    转自:http://blog.csdn.net/adaptiver/article/details/7930646 使用 ftrace 调试 Linux 内核,第 1 部分 http://blog.c ...

  2. 使用 ftrace 调试 Linux 内核,第 1 部分【转】

    转自:http://www.ibm.com/developerworks/cn/linux/l-cn-ftrace1/index.html ftrace 是 Linux 内核中提供的一种调试工具.使用 ...

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

    ftrace 是 Linux 内核中提供的一种调试工具.使用 ftrace 可以对内核中发生的事情进行跟踪,这在调试 bug 或者分析内核时非常有用.本系列文章对 ftrace 进行了介绍,分为三部分 ...

  4. 使用 ftrace 调试 Linux 内核,第 2 部分

    ftrace 操作概述 使用 ftrace 提供的跟踪器来调试或者分析内核时需要如下操作: 切换到目录 /sys/kernel/debug/tracing/ 下 查看 available_tracer ...

  5. 使用QEMU调试Linux内核代码

    http://blog.chinaunix.net/uid-20729583-id-1884617.html http://www.linuxidc.com/Linux/2014-08/105510. ...

  6. 【转】TI-Davinci开发系列之六CCS5.2调试Linux内核

    上转博文<TI-Davinci开发系列之五CCS5.2使用gdbserver远程调试应用程序> 使用CCS5.2远程调试内核时,只需导入Linux内核源码,而不需要编译内核,也就不会用到交 ...

  7. Gdb远程调试Linux内核遇到的Bug

    知识共享许可协议本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/gdb-bug 本博客同步在http://www.cn ...

  8. VELT-0.1.5开发:使用kgdb调试Linux内核【转】

    转自:http://demo.netfoucs.com/lights_joy/article/details/44106589 VELT的全称是Visual EmbedLinuxTools,它是一个与 ...

  9. 跟踪调试Linux内核的启动过程

    跟踪调试Linux内核的启动过程---使用gdb 符钰婧 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/UST ...

随机推荐

  1. Codeforces Round #275 (Div. 2)-A. Counterexample

    http://codeforces.com/contest/483/problem/A A. Counterexample time limit per test 1 second memory li ...

  2. C++简单年月日的功能实现

    // C++年月日判断初步代码 #include <iostream> using namespace std; class Data { int year; int month; int ...

  3. 五分钟入门 Dingo API

    基于 https://laravel-china.org/doc... 文档更简洁的描述Dingo,直戳重点,注重实践 Django-Book 概述 Dingo API帮助您轻松快速地构建自己的API ...

  4. InnoDB体系架构总结(二)

    事务 确保事务内的SQL都可以同步执行 要么一起成功 要么一起失败.事务有四个特性原子性 一致性,隔离性,持久性 实现方式 开始事务的时候回家记录记录一个LSN日志序列 当事务执行的时候 会首先在In ...

  5. biological clock

    '''this application aimed to cauculate people's biological block about emotional(28), energy(23),int ...

  6. 我的Python分析成长之路9

    pandas入门 统计分析是数据分析的重要组成部分,它几乎贯穿整个数据分析的流程.运用统计方法,将定量与定性结合,进行的研究活动叫做统计分析.而pandas是统计分析的重要库. 1.pandas数据结 ...

  7. shell 中exec、source以及bash的区别

    在bash shell中,source.exec以及sh都可以用来执行shell script,但是它们的差别在哪里呢? sh:父进程会fork一个子进程,shell script在子进程中执行 so ...

  8. '>>' should be '> >' within a nested template argument list

    在编译关于opencv相机标定的工程的时候出现了这个问题 vector<vector<Point3f>>  objectPoints;  error: 'objectPoint ...

  9. linux下如何编译运行c程序

    GCC是Linux操作系统下一个非常重要的源代码编译工具,有着许多重要的选项,支持许多不同语言的编译,如C.C++.Ada.Fortran.Objective.Perl.Python.Ruby以及Ja ...

  10. Cplex: MIP Callback Interface

    *本文主要记录和分享学习到的知识,算不上原创 *参考文献见链接 这篇文章主要记录一些Cplex的Callback的使用方法,采用Java语言. https://www.ibm.com/support/ ...