前言
很慚愧,搞了這么久的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\n"); return ret; } return 0; }
void cleanup_module(void) {
printk("failed\n");
}
int init_module(void); void cleanup_module(void); MODULE_LICENSE("GPLv2");