Linux内核调试Ftrace

时间:2022-08-19 16:56:30

Ftrace能帮助系统开发者了解Linux 内核运行的细节,它能帮助分析系统延时和性能问题。 Ftrace顾名思义是function trace,但除此之外,它还能支持其他类型的tracer,例如进程上下文切换,统计一个高优先级的进程从唤醒到实际执行的时间,中断被屏蔽的时间等等。

Ftrace用debug文件系统来存储它的配置文件和输出文件。如果debug文件系统配置进了kernel,目录“/sys/kernel/debug”就会被创建。用户可以在fstab中加上一行来mount debug文件系统:

debugfs       /sys/kernel/debug          debugfs defaults        0       0

或者手动的运行

mount -t debugfs nodev /sys/kernel/debug
debugfs mount之后,你可以看到一个叫tracing的文件夹,这个文件夹就包含了ftrace的配置和输出文件。

如何为特定体系结构的CPU实现ftrace?

1. 实现最基本的__mount, ftrace_stub, 和static_trace。为了效率考虑,本文讨论的函数都需要用汇编来实现。__mcount这个函数,如果在配置kernel的时候加上CONFIG_FUNCTION_TRACER的支持,编译kernel时会自动加上-pg的选项,该选项会在每个kernel函数的入口处添加对__mcount函数的调用。所以__mcount函数的效率问题至关重要,否则会影响kernel的性能。下面的伪代码是ftrace的一个基本实现,当然实际中需要用汇编代码来实现。

void ftrace_stub(void)
{
return;
}

void mcount(void)
{
/* save any bare state needed in order to do initial checking */
+if (function_trace_stop)
+return;
extern void (*ftrace_trace_function)(unsigned long, unsigned long);
        if (ftrace_trace_function != ftrace_stub)
goto do_trace;
/* restore any bare state */
return;
do_trace:
/* save all state needed by the ABI (see paragraph above) */
unsigned long frompc = ...;
unsigned long selfpc = <return address> - MCOUNT_INSN_SIZE;
ftrace_trace_function(frompc, selfpc);
/* restore all state needed by the ABI */
}
2. 实现函数调用关系call graph的支持,也就是CONFIG_FUNCTION_GRAPH_TRACER。需要实现函数ftrace_graph_caller(), prepare_ftrace_return(), return_to_handler()。
在__mcount()中,添加对ftrace_graph_return()和ftrace_graph_entry()的检查,如果两者有效,则调用ftrace_graph_caller()。如下所示:
void mcount(void)
{
...
if (ftrace_trace_function != ftrace_stub)
goto do_trace;
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+extern void (*ftrace_graph_return)(...);
+extern void (*ftrace_graph_entry)(...);
+if (ftrace_graph_return != ftrace_stub ||
+ ftrace_graph_entry != ftrace_graph_entry_stub)
+ftrace_graph_caller();
+#endif
/* restore any bare state */
...
3.实现dynamic ftrace,也就是CONFIG_DYNAMIC_FTRACE。dynamic ftrace会去掉每个函数开头的__mcount,这样如果ftrace被disable,对系统性能的影响极少。用户可以设置debugfs中配置文件set_ftrace_filter和set_ftrace_notrace,来选择需要trace的函数。需要实现:
- asm/ftrace.h:- MCOUNT_ADDR- ftrace_call_adjust()- struct dyn_arch_ftrace{}- asm code:- mcount() (new stub)- ftrace_caller()- ftrace_call()- ftrace_stub()- C code:- ftrace_dyn_arch_init()- ftrace_make_nop()- ftrace_make_call()- ftrace_update_ftrace_func()
如果需要让call graph也支持dynamic trace,call graph部分还需要实现:
- update:- ftrace_caller()- ftrace_graph_call()- ftrace_graph_caller()- implement:- ftrace_enable_ftrace_graph_caller()- ftrace_disable_ftrace_graph_caller()