linux tracepoint例子,tracepoint介绍
內(nèi)核中的每個(gè)tracepoint提供一個(gè)鉤子來調(diào)用probe函數(shù)。一個(gè)tracepoint可以打開或關(guān)閉。打開時(shí),probe函數(shù)關(guān)聯(lián)到tracepoint;關(guān)閉時(shí),probe函數(shù)不關(guān)聯(lián)到tracepoint。tracepoint關(guān)閉時(shí)對kernel產(chǎn)生的影響很小,只是增加了極少的時(shí)間開銷(一個(gè)分支條件判斷),極小的空間開銷(一條函數(shù)調(diào)用語句和幾個(gè)數(shù)據(jù)結(jié)構(gòu))。當(dāng)一個(gè)tracepoint打開時(shí),用戶提供的probe函數(shù)在每次這個(gè)tracepoint執(zhí)行是都會被調(diào)用。
如果用戶準(zhǔn)備為kernel加入新的tracepoint,每個(gè)tracepoint必須以下列格式聲明:
#include DECLARE_TRACE(tracepoint_name,
TPPROTO(trace_function_prototype),
TPARGS(trace_function_args));
上面的宏定義了一個(gè)新的tracepoint叫tracepoint_name。與這個(gè)tracepoint關(guān)聯(lián)的probe函數(shù)必須與TPPROTO宏定義的函數(shù)prototype一致,probe函數(shù)的參數(shù)列表必須與TPARGS宏定義的一致。
或許用一個(gè)例子來解釋會比較容易理解。Kernel里面已經(jīng)包含了一些tracepoints,其中一個(gè)叫做sched_wakeup,這個(gè)tracepoint在每次scheduler喚醒一個(gè)進(jìn)程時(shí)都會被調(diào)用。它是這樣定義的:
DECLARE_TRACE(sched_wakeup,
TPPROTO(struct rq *rq, struct task_struct *p),
TPARGS(rq, p))
實(shí)際在kernel中插入這個(gè)tracepoint點(diǎn)的是一行如下代碼:
trace_sched_wakeup(rq, p);
注意,插入tracepoint的函數(shù)名就是將trace_前綴添加到tracepoint_name的前面。除非有一個(gè)實(shí)際的probe函數(shù)關(guān)聯(lián)到這個(gè)tracepoint,trace_sched_wakeup()這個(gè)只是一個(gè)空函數(shù)。下面的操作就是將一個(gè)probe函數(shù)關(guān)聯(lián)到一個(gè)tracepoint:
void my_sched_wakeup_tracer(struct rq *rq, struct task_struct *p);
register_trace_sched_wakeup(my_sched_wakeup_tracer);
register_trace_sched_wakeup()函數(shù)實(shí)際上是DEFINE_TRACE()定義的,它把probe函數(shù)my_sched_wakeup_tracer()和tracepoint sched_wakeup關(guān)聯(lián)起來。
當(dāng)需要獲取內(nèi)核的debug信息時(shí),通常你會通過以下printk的方式打印信息:
void trace_func() { //…… printk("輸出信息"); //…… }1
2
3
4
5
6
缺點(diǎn):
內(nèi)核中printk是統(tǒng)一控制的,各個(gè)模塊的printk都會被打印,無法只打印需要關(guān)注的模塊。
如果需要修改/新增打印信息,需要修改所有受影響的printk語句。這些printk分散在代碼多處,每個(gè)地方都需要修改。
嵌入式系統(tǒng)中,如果printk信息量大,console(如果有)有大量的打印輸出,用戶無法在console輸入命令,影響人機(jī)交互。
二、內(nèi)核解決方案
內(nèi)核采用“插樁”的方法抓取log,“插樁”也稱為trace point。每種trace point有一個(gè)name、一個(gè)enable開關(guān)、一系列樁函數(shù)、注冊樁函數(shù)的函數(shù)、卸載樁函數(shù)的函數(shù)。“樁函數(shù)”功能類似于printk,不過“樁函數(shù)”并不會把信息打印到console,而是輸出到內(nèi)核的ring buffer(環(huán)形緩沖區(qū)),緩沖區(qū)中的信息通過debugfs對用戶呈現(xiàn)。
邏輯架構(gòu)如下:
接下來說明涉及到一些內(nèi)核數(shù)據(jù)結(jié)構(gòu),代碼參考:
數(shù)據(jù)結(jié)構(gòu)
代碼路徑
DEFINE_TRACE(name)
DECLARE_TRACE(name, proto, args)
include/linux/tracepoint.h
struct tracepoint
include/linux/tracepoint-defs.h
trace point依次執(zhí)行樁函數(shù),每個(gè)樁函數(shù)實(shí)現(xiàn)不同的debug功能。內(nèi)核通過register_trace_##name將樁函數(shù)添加到trace point中,通過unregister_trace_##name從trace point中移除。(注:##表示字符串連接)。
內(nèi)核通過DEFINE_TRACE(name)定義struct tracepoint變量來描述trace point。
struct tracepoint { const char *name; /* Tracepoint name */ struct static_key key; int (*regfunc)(void); void (*unregfunc)(void); struct tracepoint_func __rcu *funcs; };1
2
3
4
5
6
7
@name* trace point的名字,內(nèi)核中通過hash表管理所有的trace point,找到對應(yīng)的hash slot后,需要通過name來識別具體的trace point。
@keytrace point狀態(tài),1表示disable,0表示enable。
@regfunc添加樁函數(shù)的函數(shù)
@unregfunc卸載樁函數(shù)的函數(shù)
@funcstrace point中所有的樁函數(shù)鏈表
內(nèi)核通過#define DECLARE_TRACE(name, proto, args)定義trace point用到的函數(shù),定義的函數(shù)原型如下(從代碼中摘取了幾個(gè),不止以下3個(gè)):
static inline void trace_##name(proto) register_trace_##name(void (*probe)(data_proto), void *data) unregister_trace_##name(void (*probe)(data_proto), void *data)1
2
3
4
5
#define __DECLARE_TRACE(name, proto, args, cond, data_proto, data_args)//\ extern struct tracepoint __tracepoint_##name; \ static inline void trace_##name(proto) \ { \ if (static_key_false(&__tracepoint_##name.key)) \ __DO_TRACE(&__tracepoint_##name, \ TP_PROTO(data_proto), \ TP_ARGS(data_args), \ TP_CONDITION(cond), 0); \ if (IS_ENABLED(CONFIG_LOCKDEP) && (cond)) { \ rcu_read_lock_sched_notrace(); \ rcu_dereference_sched(__tracepoint_##name.funcs);\ rcu_read_unlock_sched_notrace(); \ } \ } \ __DECLARE_TRACE_RCU(name, PARAMS(proto), PARAMS(args), \ PARAMS(cond), PARAMS(data_proto), PARAMS(data_args)) \ static inline int \
register_trace_##name(void (*probe)(data_proto), void *data) \ { \ return tracepoint_probe_register(&__tracepoint_##name, \ (void *)probe, data); \ } \ static inline int \
register_trace_prio_##name(void (*probe)(data_proto), void *data,\ int prio) \ { \ return tracepoint_probe_register_prio(&__tracepoint_##name, \ (void *)probe, data, prio); \ } \ static inline int \
unregister_trace_##name(void (*probe)(data_proto), void *data) \ { \ return tracepoint_probe_unregister(&__tracepoint_##name,\ (void *)probe, data); \ } \ static inline void \
check_trace_callback_type_##name(void (*cb)(data_proto)) \ { \ } \ static inline bool\
trace_##name##_enabled(void) \ { \ return static_key_false(&__tracepoint_##name.key); \ }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
???第2行聲明一個(gè)外部trace point變量。"static inline"部分定義了一些trace point用到的公共函數(shù)。
???第5行判斷trace point是否disable,如果沒有disable,那么調(diào)用__DO_TRACE遍歷執(zhí)行trace point中的樁函數(shù)(通過“函數(shù)指針”來實(shí)現(xiàn)執(zhí)行樁函數(shù))。
???trace point提供了統(tǒng)一的框架,用void *指向任何函數(shù),所以各個(gè)trace point取出樁函數(shù)指針后,需要轉(zhuǎn)換成自己的函數(shù)指針類型, TP_PROTO(data_proto)傳遞函數(shù)指針類型用于轉(zhuǎn)換,具體的轉(zhuǎn)換在:(–> 這一行)
#define __DO_TRACE(tp, proto, args, cond, rcuidle)//\ do { \ struct tracepoint_func *it_func_ptr; \ void *it_func; \ void *__data; \ //......................... it_func_ptr = rcu_dereference_raw((tp)->funcs); \
\ if (it_func_ptr) { \ do { \
it_func = (it_func_ptr)->func; \
__data = (it_func_ptr)->data; \ --> ((void(*)(proto))(it_func))(args); \ } while ((++it_func_ptr)->func); \ } \ //......................... } while (0)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
樁函數(shù)的proto的傳遞的例
DEFINE_EVENT_CONDITION(f2fs__submit_page_bio, f2fs_submit_page_write, --> TP_PROTO(struct page *page, struct f2fs_io_info *fio), TP_ARGS(page, fio), TP_CONDITION(page->mapping) );1
2
3
4
5
第2行(–>)聲明了樁函數(shù)原型。
#define DEFINE_EVENT_CONDITION(template, name, proto, args, cond) DEFINE_EVENT(template, name, PARAMS(proto), PARAMS(args))1
2
#define DEFINE_EVENT(template, name, proto, args) DECLARE_TRACE(name, PARAMS(proto), PARAMS(args))1
2
#define DECLARE_TRACE(name, proto, args) __DECLARE_TRACE(name, PARAMS(proto), PARAMS(args), cpu_online(raw_smp_processor_id()), PARAMS(void *__data, proto), PARAMS(__data, args))1
2
3
4
5
???至此執(zhí)行到__DECLARE_TRACE宏,參考前面說明,提到了何時(shí)轉(zhuǎn)換成樁函數(shù)指針類型。
???從上面可以看出trace point的機(jī)制很簡單,就是把用于debug的函數(shù)指針組織在一個(gè)struct trace point變量中,然后依次執(zhí)行各個(gè)函數(shù)指針。不過為了避免各個(gè)模塊重復(fù)寫代碼,內(nèi)核用了比較復(fù)雜的宏而已。
???另外我們也可以發(fā)現(xiàn),使用trace point必須要通過register_trace_##name將樁函數(shù)(也就是我們需要的debug函數(shù))添加到trace point中,這個(gè)工作只能通過moudule或者修改內(nèi)核代碼實(shí)現(xiàn),對于開發(fā)者來說,操作比較麻煩。ftrace開發(fā)者們意識到了這點(diǎn),所以提供了trace event功能,開發(fā)者不需要自己去注冊樁函數(shù)了,易用性較好,后面文章會談到trace event是如何實(shí)現(xiàn)的以及如何使用。
總結(jié)
以上是生活随笔為你收集整理的linux tracepoint例子,tracepoint介绍的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c语言用正数的形式求最大值最小值,C语言
- 下一篇: linux 控制台输入命令无效_在控制台