日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux tracepoint例子,tracepoint介绍

發(fā)布時(shí)間:2025/4/16 linux 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux tracepoint例子,tracepoint介绍 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

內(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)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。