前言
很惭愧,搞了这么久的linux开发,之前测试无论是用ftrace还是perf也好,都没有认真的去了解tracepoint的实现,这次正好
开发的代码中需要设计一个tracepoint,便于后期调试使用,所以趁此机会,了解下tracepoint在内核里面的编写。
你真的知道trace的原理吗?
这里大致介绍下tracepoint的大致原理,和kprobe相比,tracepoint是一个安静的乖孩子,只有在内核里面编写好才能使用
而kprobe不一样,可动可静,是一个活力十足的假小子,tracepoint实现是基于hooks的思想,function trace是利用gcc编译器初期
在函数的入口就被放置一个probe点,也俗称打桩,这个probe点就会跟踪调用这个函数的各种信息,例如进程,地址,栈信息等,
并将追踪的信息保存到一个环形队列中去,如果用户希望读取这些内核,就会通过debugfs形式来访问,所以有时候我在想是不是可以
写一个程序,去专门监控trace环形队列占用的内存情况(有点跑偏了),下面从网上找的一张图介绍下这个trace的调用流程。
去实现一个tracepoint
实现一个tracepoint是很简单的事,尤其是有经验的内核开发同学,你只需要看下Documentation/trace/相关文档介绍,在看看
内核里面任何一个tracepoint的实现patch, 基本就可以照葫芦画瓢去弄了,至少我在写tracepoint的时候就是这么弄的,很有意思。
step1: 实现一个tracepoint的头文件
我们需要定义一个tracepint的头文件,最好和你想要跟踪的function所在的目录或者相关头文件放在一起。
例如我这里定义trace_myself.h文件
// trace_myself.h
#undef TRACE_SYSTEM #define TRACE_SYSTEM myself
#if !defined(__TRACE_MYSELF_H__) || defined(TRACE_HEADER_MULTI_READ)
#define __TRACE_MYSELF_H__#include <linux/tracepoint.h> // 此处是非常关键的地方,设计到你要追踪的函数的的相关内容作为参数// 为了方便,这里将参数设置为unsiged short形式TRACE_EVENT(myself_tp,
TP_PROTO(unsigned short dest, unsigned short source), TP_ARGS(dest, source), // 定义两参数名称为dest和source TP_STRUCT__entry( // 此处本人理解为打桩时候分配的环形队列时指定的作用域,说白了就是大小和attr。 __field(unsigned short, dest) __field(unsigned short, source) ), TP_fast_assign( __entry->dest = dest; // 将trace的函数的内容拷贝到环形队列中去 __entry->source = source; ), TP_printk("dest:%d, source:%d", __entry->dest, __entry->source) // 打印你所期望的内容 ); #endif // 此处定义完成后,仅仅是类型定义成功
// 下一步我们需要指定头文件所在的目录,并且定义头文件的名称
#undef TRACE_INCLUDE_PATH #define TRACE_INCLUDE_PATH . #define TRACE_INCLUDE_FILE trace_myself // 这就是该头文件的名字 #include <trace/define_trace.h>
到这一步,只能算是你tracepoint function实现了,但是怎么编译和在代码中添加,又有很多规则需要注意
step2: 加入到makefile中去
ccflags-y += -I$(src) # needed for trace events
就是这么简短的一句,但是非加不可,如果不加,可定是不行的,gcc编译的时候会查找相关头文件,这告诉编译器,此处的头文件也要包含。
step3: 在trace里的函数加入tracepoint
//此处需要知道的是trace的函数所在的文件里必须包含CREATE_TRACE_POINTS
//并且必须在头文件之前,虽然不理解,但是还是的遵守
#define CREATE_TRACE_POINTS
#include "test_tp.h" #include <net/protocol.h> #include <linux/ip.h> #include <linux/udp.h> int test(unsiged short dst, unsiged short src) { dst = src + 5;// 这里增加一个tracepoint点 trace_myself(dst, src);
return 0; } int init_module(void) { int ret = 0, dest = 5, src = 5;
ret = test(dest, src); if (ret) { printk("failed "); return ret; } return 0; }
void cleanup_module(void) {
printk("failed
");
}
int init_module(void); void cleanup_module(void); MODULE_LICENSE("GPLv2");