分类: LINUX
2010-08-03 15:30:20
如果你需要分析一个内核模块的函数调用数或者希望得到各语句段的执行时间,那么这篇文章很适合您。
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
|
该符号为
common
。
common 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
”分配于只读存储器中,
test
在
rodata 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
。
|
?
|
该符号类型没有定义
|