本文档介绍了Linux内核中的追踪点及其使用方法。文档中提供的例子详细说明了如何在内核中插入追踪点,如何将探测函数与追踪点联系起来。同时也提供了一些探测函数的例子。
Ø 追踪点的目的
代码中的追踪点提供了在运行时调用探测函数的钩子。追踪点可以打开(已连接探测函数)或关闭(没有连接探测函数)。处于关闭状态的追踪点不会引发任何效果,除了增加了一点时间开销(检查一条分支语句的条件)和空间开销(在instrumented function[不知道如何翻译合适]的尾部增加几条函数调用的代码,在独立区域增加一个数据结构)。如果一个追踪点被打开,那么每次追踪点被执行时都会调用连接的探测函数,而且在调用者的执行上下文中。探测函数执行结束后,将返回到调用者(从追踪点继续执行)。
可以在代码中的重要位置安放追踪点。它们是轻量级的钩子函数,能够传递任意个数的参数,原型可以在定义追踪点的头文件中找到。
追踪点可以用来进行系统追踪并进行性能统计。
Ø 使用方法
每个追踪点都包含2个元素:
- 追踪点定义。位于头文件中。
- 追踪点声明。位于C文件中。
使用追踪点时,需要包含头文件linux/tracepoint.h.
例如,在文件[include/trace/subsys.h]中:
#include <linux/tracepoint.h>
DECLARE_TRACE(subsys_eventname,
TP_PROTO(int firstarg, struct task_struct *p),
TP_ARGS(firstarg, p));
在文件[subsys/file.c](添加追踪声明的地方)中:
#include <trace/subsys.h>
DEFINE_TRACE(subsys_eventname);
void somefct(void)
{
...
trace_subsys_eventname(arg, task);
...
}
在代码中:
- subsys_eventname是被追踪事件的描述符。
- subsys是子系统的名字。
- eventname是待追踪事件的名字。
- TP_PROTO(int firstarg, struct task_struct *p)是此追踪点调用的探测函数的原型。
- TP_ARGS(firstarg, p)表示参数的名称,与原型中的相同。
通过函数register_trace_subsys_eventname()可以为追踪点绑定探测函数。而unregister_trace_subsys_eventname()则可以移除一个探测函数。
在模块的推出函数中必须调用tracepoint_synchronize_unregister(),以确保没有代码继续使用这个探测函数。这个操作,再加上在探测函数附近禁止抢占,可以保证探测函数的移除与模块的卸载是安全的。
追踪点机制允许插入同一个追踪点的多个实例。但是任何一个追踪点的名字在整个内核中都必须是唯一的,否则就可能会发生类型冲突。可以使用原型追踪点的名称重整[name mangling],这样可以保证类型的正确性。对探测函数的类型正确性的验证由编译器在探测函数注册时进行。追踪点可以位于内联函数、内联静态函数、unrolled循环以及常规的函数中。
我们推荐使用名称框架“subsys_event”,这会有效地限制住冲突。追踪点名称在内核中是全局变量:无论在核心内核映像中,还是在模块中,相同名称的追踪点都被认为是同一个追踪点。
如果追踪点必须在内核模块中使用,可以使用EXPORT_TRACEPOINT_SYMBOL_GPL()或EXPORT_TRACEPOINT_SYMBOL()导出这些追踪点。
Ø 探测/追踪点实例
参考在samples/tracepoints中提供的例子。
需要与内核一起编译它们。如果选项CONFIG_SAMPLE_TRACEPOINTS=m,这些示例将在执行make操作时被编译(而不是’make modules’).
以root身份进入系统:
modprobe tracepoint-sample (insmod order is not important)
modprobe tracepoint-probe-sample
cat /proc/tracepoint-sample (returns an expected error)
rmmod tracepoint-sample tracepoint-probe-sample
dmesg