用Ftrace跟踪内核模块

来源:互联网 发布:js base64转换成file 编辑:程序博客网 时间:2024/06/05 22:52

 

创建时间:2010-05-23

作者:杨民强 Steven Yang

E-mail:mqyoung@gmail.com

转载请注明出处:http://blog.csdn.net/defeattroy/archive/2010/05/23/5618099.aspx

 

 

如果你需要分析一个内核模块的函数调用数或者希望得到各语句段的执行时间,那么这篇文章很适合您。

 

Ftrace抛开内核代码跟踪领域的对手如KFT等进入Linux官方内核,正说明其功能强大性能稳定。Ftrace 的作用是帮助开发人员了解 Linux 内核的运行时行为,以便进行故障调试或性能分析。网上有很多关于ftrace的介绍及使用的文章,在此就不赘述,本文主要就使用ftrace跟踪分析内核模块,说明其详细操作过程和一些技巧。

 

 

 

 

对一个名为epl.ko的内核模块进行trace的过程如下:

 

  1 首先确保你打开相应的内核选项: kernel_hacking/Trace下面。如果需要重编内核。

    注意重编内核后重编你的模块。

 

  2 设置ftrace

    mount -t debugfs debugfs /sys/kernel/debug   #要使用 ftrace首先需要mount这个debugfs

    ln -s /sys/kernel/debug /debug  #方便期间做个链接

    cd /debug

    echo function_graph > current_tracer       #输出函数调用图,需要打开内核选项FUNCTION_GRAPH_TRACER

 

  3 提取模块里的函数列表

     如果没有设置过滤表(默认加载debugfs后),available_filter_functions文件包含了所有可追踪的函数名称,默认情况下这个链表覆盖了所有内核的可trace函数。如果我们插入一个模块,那么ftrace会自动把我们我们这个模块的函数符号加到available_filter_functions中。提取一个内核模块的函数可以有两种方法,一种是用nm命令,一种是比较插入模块前后available_filter_functions的差异,两种方法都提供给读者如下:

    1)比较插入模块前后available_filter_functions的差异

    cat available_filter_functions > /tmp/funcs_without_epl
    insmod epl.ko
    cat available_filter_functions > /tmp/funcs_with_epl
    diff /tmp/funcs_without_epl /tmp/funcs_with_epl > epl_funcs
    删除 epl_funcs文件里由diff产生的无用信息。

    2) 使用nm命令导出epl.ko里的符号

    参考  [附表:nm基本用法]

    nm导出的是ko里的所有符号,我们只需要函数符号

    nm -s epl.ko | grep " T " | awk '{ print $3 }' > nm_funcs  
    nm -s epl.ko | grep " t " | awk '{ print $3 }' >> nm_funcs

 

    为了比较两者产生的差别,首先排下序

    sort nm_funcs > nm_funcs_sorted
    sort epl_funcs > epl_funcs_sorted
    diff -Nur nm_funcs_sorted epl_funcs_sorted

    我们可能发现nm_funcs包含了一些epl_funcs没有的函数,这些函数正式kernel里面本来有的内核函数,所以如果你只想trace你的模块的函数,那么推荐使用方法1。

  4 开始trace

    insmod epl.ko

    cat /tmp/epl_funcs > set_ftrace_filter
    echo 1 >tracing_enabled                           #开始trace
    cat trace_pipe > /tmp/ftrace_result.txt &    #设置一个管道重定向,防止溢出

    tracing & waiting....

    echo 0 >tracing_enabled                          #结束trace

 

    说明:一个ko插入kernel以后才可以给 set_ftrace_filter设置函数过滤表,因为没插入以前内核没有epl.ko的函数符号,故设置set_ftrace_filter失败,所以需要先插入模块以后再enable trace,但是这样又有一个问题:我们也想trace模块插入时候的执行情况时,我们还不能在插入模块前设置过滤表,那么怎么办呢?需要得到这个答案需要首先确认:在默认情况下,也就是设置ftrace为trace所有可trace的函数后,我们插入模块时候ftrace是不是马上把这个ko的函数表加进可trace列表,同时也能够trace这些函数?如果可以,那我们可以先预先准备好filter list(即我们文中所述的epl_funcs),插入模块后然后再设置set_ftrace_filter过滤表,这样会在ftrace的结果的前一段会有我们不希望trace的函数(内核函数),或者删除,或者提取出我们模块初始化部分即可。作者在实践过程中发现有时候ftrace的结果输出没有显示函数符号表,这个问题还有待进一步确认...希望对这方便有深入了解的网友不吝赐教,mqyoung at gmail.com。

 

 

 

如下是我在学习使用过程中做的一些笔记和备忘,也许对您有用


需要了解的几个文件:
===============
  set_ftrace_filter:
    limit the trace to only those functions.

  set_ftrace_notrace:
    An effect opposite to that of set_ftrace_filter. Any function that is
    added here will not be traced. If a function exists in both files,
    the function will _not_ be traced.

  set_ftrace_pid:
    Only trace a single thread.

  available_filter_functions:
    This lists the funtions that ftrace has processed and can trace. These
    are the function names that you can pass to "set_ftrace_filter" or
    "set_ftrace_notrace".


Ftrace 提供的几种Tracer
=================
  "function"
  "function_graph"

  "sched_switch"
    context switches and wakeups between tasks
  "irqsoff"
      disable intrrupts and saves the trace with the longest max latency.
    See tracing_max_latency.
  "preemptoff"
  "preemptirqsoff"

  "wakeup"
      Records the max latency that it takes for the highest priority task to
    get scheduled after it has been woken up.
  "hw-branch-tracer"

  "nop"
      "trace nothing" tracer. To remove all tracers from tracing simply echo
    "nop" into current_tracer.

 

参考资料:

 

[1] nm  http://hi.baidu.com/zzgmtv/blog/item/d021d7437d38d91f72f05d57.html
[2] ftrace kernel_src/Documentation/trace/ftrace.txt

 

 

附表:nm基本用法

对于每一个符号,nm列出其值(the symbol value),类型(the symbol type)和其名字(the symbol name)。


符号
类型
说明
A
该符号的值是绝对的,在以后的链接过程中,不允许进行改变。这样的符号值,常常出现在中断向量表中,例如用符号来表示各个中断向量函数在中断向量表中的位置。
B
该符号的值出现在非初始化数据段(bss)中。例如,在一个文件中定义全局static int test。则该符号test的类型为b,位于bss section中。其值表示该符号在bss段中的偏移。一般而言,bss段分配于RAM
C
该符号为commoncommon symbol是未初始话数据段。该符号没有包含于一个普通section中。只有在链接过程中才进行分配。符号的值表示该符号需要的字节数。例如在一个c文件中,定义int test,并且该符号在别的地方会被引用,则该符号类型即为C。否则其类型为B
D
该符号位于初始话数据段中。一般来说,分配到data section中。例如定义全局int baud_table[5] = {9600, 19200, 38400, 57600, 115200},则会分配于初始化数据段中
G
该符号也位于初始化数据段中。主要用于small object提高访问small data object的一种方式。
I
该符号是对另一个符号的间接引用。
N
该符号是一个debugging符号。
R
该符号位于只读数据区。例如定义全局const int test[] = {123, 123};test就是一个只读数据区的符号。注意在cygwin下如果使用gcc直接编译成MZ格式时,源文件中的test对应_test,并且其符号类型为D,即初始化数据段中。但是如果使用m6812-elf-gcc这样的交叉编译工具,源文件中的test对应目标文件的test,即没有添加下划线,并且其符号类型为R。一般而言,位于rodata section。值得注意的是,如果在一个函数中定义const char *test = “abc”, const char test_int = 3。使用nm都不会得到符号信息,但是字符串“abc”分配于只读存储器中,testrodata section中,大小为4
S
符号位于非初始化数据区,用于small object
T
该符号位于代码区text section
U
该符号在当前文件中是未定义的,即该符号的定义在别的文件中。例如,当前文件调用另一个文件中定义的函数,在这个被调用的函数在当前就是未定义的;但是在定义它的文件中类型是T。但是对于全局变量来说,在定义它的文件中,其符号类型为C,在使用它的文件中,其类型为U
V
该符号是一个weak object
W
The symbol is a weak symbol that has not been specifically tagged as a weak object symbol.
-
该符号是a.out格式文件中的stabs symbol
?
该符号类型没有定义