偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Linux 內(nèi)核靜態(tài)追蹤技術(shù)的實(shí)現(xiàn)

系統(tǒng) Linux
本文簡(jiǎn)單分享一下內(nèi)核的靜態(tài)追蹤技術(shù)的實(shí)現(xiàn)。追蹤,其實(shí)就是收集代碼在執(zhí)行時(shí)的一些信息,以便協(xié)助排查問(wèn)題。

[[434855]]

前言:最近在探索 Node.js 調(diào)試和診斷方向的內(nèi)容,因?yàn)?Node.js 提供的能力有時(shí)候可能無(wú)法解決問(wèn)題,比如堆內(nèi)存沒(méi)有變化,但是 rss 一直上漲。所以需要深入一點(diǎn)去了解更多的排查問(wèn)題方式。而這些方向往往都涉及到底層的東西,所以就自然需要去了解內(nèi)核提供的一些技術(shù),內(nèi)核提供的能力,經(jīng)過(guò)多年的發(fā)展,可謂是百花齊放,而且非常復(fù)雜。本文簡(jiǎn)單分享一下內(nèi)核的靜態(tài)追蹤技術(shù)的實(shí)現(xiàn)。追蹤,其實(shí)就是收集代碼在執(zhí)行時(shí)的一些信息,以便協(xié)助排查問(wèn)題。

1 Tracepoint

Tracepoints 是一種靜態(tài)插樁的技術(shù),實(shí)現(xiàn)雖然復(fù)雜,但是概念上比較簡(jiǎn)單。比如我們打日志的時(shí)候,就類(lèi)似這種情況,我們?cè)跇I(yè)務(wù)代碼里,寫(xiě)了很多 log 用來(lái)記錄進(jìn)程在運(yùn)行時(shí)的信息。Tracepoints 則是內(nèi)核提供的一種基于鉤子的插樁技術(shù)。不過(guò)和打日志不一樣的是,我們想在哪里打就在哪里加對(duì)應(yīng)的代碼,而 Tracepoints 則幾乎是依賴(lài)于內(nèi)核決定哪里可以插樁,說(shuō)幾乎是因?yàn)槲覀円部梢詫?xiě)內(nèi)核模塊注冊(cè)到內(nèi)核來(lái)通知插樁點(diǎn)。下面來(lái)通過(guò)一個(gè)例子看一下 Tracepoint 的使用和實(shí)現(xiàn)(例子來(lái)自?xún)?nèi)核文檔 tracepoints.rst)。分析之前先看一下兩個(gè)非常重要的宏。第一個(gè)是 DECLARE_TRACE。

  1. #define DECLARE_TRACE(name, proto, args)                \ 
  2.     __DECLARE_TRACE(name, PARAMS(proto), PARAMS(args),      \ 
  3.             cpu_online(raw_smp_processor_id()),     \ 
  4.             PARAMS(void *__data, proto),            \ 
  5.             PARAMS(__data, args)) 

我們只需要關(guān)注主體的實(shí)現(xiàn),而不需要關(guān)注參數(shù),繼續(xù)展開(kāi)。

  1. #define __DECLARE_TRACE(name, proto, args, cond, data_proto, data_args) \ 
  2.     extern struct tracepoint __tracepoint_##name;           \ 
  3.     // 執(zhí)行鉤子函數(shù) 
  4.     static inline void trace_##name(proto)              \ 
  5.     {                               \ 
  6.         if (static_key_false(&__tracepoint_##name.key))     \ 
  7.             __DO_TRACE(&__tracepoint_##name,        \ 
  8.                 TP_PROTO(data_proto),           \ 
  9.                 TP_ARGS(data_args),         \ 
  10.                 TP_CONDITION(cond), 0);         \ 
  11.     }                               \ 
  12.     // 注冊(cè)鉤子函數(shù) 
  13.     static inline int                       \ 
  14.     register_trace_##name(void (*probe)(data_proto), void *data)    \ 
  15.     {                               \ 
  16.         return tracepoint_probe_register(&__tracepoint_##name,  \ 
  17.                         (void *)probe, data);   \ 
  18.     }                               \    
  19.     // 注銷(xiāo)鉤子函數(shù)                    
  20.     static inline int                       \ 
  21.     unregister_trace_##name(void (*probe)(data_proto), void *data)  \ 
  22.     {                               \ 
  23.         return tracepoint_probe_unregister(&__tracepoint_##name,\ 
  24.                         (void *)probe, data);   \ 
  25.     }                               \ 
  26.     static inline bool                      \ 
  27.     trace_##name##_enabled(void)                    \ 
  28.     {                               \ 
  29.         return static_key_false(&__tracepoint_##name.key);  \ 
  30.     } 

__DECLARE_TRACE 主要是實(shí)現(xiàn)了幾個(gè)函數(shù),我們只需要關(guān)注注冊(cè)鉤子和執(zhí)行鉤子函數(shù)(格式是 register_trace_${yourname} 和 trace_${yourame})。接下來(lái)看第二個(gè)宏 DEFINE_TRACE。

  1. #define DEFINE_TRACE_FN(name, reg, unreg)                \ 
  2.     struct tracepoint __tracepoint_##name#define DEFINE_TRACE(name)                      \ 
  3.     DEFINE_TRACE_FN(nameNULLNULL); 

我省略了一些代碼,DEFINE_TRACE 主要是定義了一個(gè) tracepoint 結(jié)構(gòu)體。了解了兩個(gè)宏之后,來(lái)看一下如何使用 Tracepoint。

1.1 使用

include/trace/events/subsys.h

  1. #include <linux/tracepoint.h>DECLARE_TRACE(subsys_eventname, 
  2.     TP_PROTO(int firstarg, struct task_struct *p), 
  3.     TP_ARGS(firstarg, p)); 

首先在頭文件里通過(guò) DECLARE_TRACE 宏定義了一系列函數(shù)。subsys/file.c

  1. #include <trace/events/subsys.h> 
  2.  
  3.  
  4.  
  5. DEFINE_TRACE(subsys_eventname);void somefct(void){ 
  6.  
  7.     ... 
  8.     trace_subsys_eventname(arg, task); 
  9.     ... 
  10.  
  11.  
  12.  
  13.  
  14. // 實(shí)現(xiàn)自己的鉤子函數(shù)并注冊(cè)到內(nèi)核 
  15.  
  16. void callback(...) {} 
  17.  
  18. register_trace_subsys_eventname(callback); 

然后在實(shí)現(xiàn)文件里通過(guò) DEFINE_TRACE 定義一個(gè) tracepoint 結(jié)構(gòu)體。接著調(diào)用 register_trace_subsys_eventname 函數(shù)把自定義的鉤子函數(shù)注冊(cè)到內(nèi)核,然后在需要收集信息的地方調(diào)用處理鉤子的函數(shù) trace_subsys_eventname。

1.2 實(shí)現(xiàn)

了解了使用之后,接下來(lái)看看實(shí)現(xiàn)。首先看一下注冊(cè)鉤子函數(shù)。

  1. int tracepoint_probe_register(struct tracepoint *tp, void *probe, void *data){ 
  2.     return tracepoint_probe_register_prio(tp, probe, data, TRACEPOINT_DEFAULT_PRIO); 
  3.  
  4.  
  5.  
  6.  
  7. int tracepoint_probe_register_prio(struct tracepoint *tp, void *probe, 
  8.  
  9.                    void *data, int prio){ 
  10.     struct tracepoint_func tp_func; 
  11.     int ret; 
  12.  
  13.     mutex_lock(&tracepoints_mutex); 
  14.     tp_func.func = probe; 
  15.     tp_func.data = data; 
  16.     tp_func.prio = prio; 
  17.     ret = tracepoint_add_func(tp, &tp_func, prio); 
  18.     mutex_unlock(&tracepoints_mutex); 
  19.     return ret; 
  20.  

tracepoint_probe_register_prio 中定義了一個(gè) tracepoint_func 結(jié)構(gòu)體用于表示鉤子信息,然后調(diào)用 tracepoint_add_func,其中 tp 就剛才自定義的 tracepoint 結(jié)構(gòu)體。

  1. static int tracepoint_add_func(struct tracepoint *tp, struct tracepoint_func *func, int prio){ 
  2.     struct tracepoint_func *old, *tp_funcs; 
  3.     int ret; 
  4.     // 拿到鉤子列表 
  5.     tp_funcs = rcu_dereference_protected(tp->funcs, lockdep_is_held(&tracepoints_mutex)); 
  6.     // 插入新的鉤子到列表 
  7.     old = func_add(&tp_funcs, func, prio); 
  8.     rcu_assign_pointer(tp->funcs, tp_funcs); 
  9.     return 0;}static struct tracepoint_func * func_add(struct tracepoint_func **funcs, struct tracepoint_func *tp_func, 
  10.      int prio){ 
  11.     struct tracepoint_func *new; 
  12.     int nr_probes = 0; 
  13.     int pos = -1; 
  14.     /* + 2 : one for new probe, one for NULL func */ 
  15.     new = allocate_probes(nr_probes + 2); 
  16.     pos = 0; 
  17.     new[pos] = *tp_func; 
  18.     new[nr_probes + 1].func = NULL
  19.     *funcs = new; 
  20.  

注冊(cè)函數(shù)的邏輯其實(shí)就是往自定義的結(jié)構(gòu)體的隊(duì)列里插入一個(gè)新的節(jié)點(diǎn)。接下來(lái)再看一下處理鉤子的邏輯。

  1. #define __DO_TRACE(tp, proto, args, cond, rcuidle)          \ 
  2.     do {                                \ 
  3.         struct tracepoint_func *it_func_ptr;            \ 
  4.         void *it_func;                      \ 
  5.         void *__data;                       \ 
  6.         int __maybe_unused __idx = 0;               \ 
  7.         // 拿到隊(duì)列 
  8.         it_func_ptr = rcu_dereference_raw((tp)->funcs);     \ 
  9.         // 非空則執(zhí)行里面的節(jié)點(diǎn)的回調(diào) 
  10.         if (it_func_ptr) {                  \ 
  11.             do {                        \ 
  12.                 it_func = (it_func_ptr)->func;      \ 
  13.                 __data = (it_func_ptr)->data;       \ 
  14.                 ((void(*)(proto))(it_func))(args);  \ 
  15.             } while ((++it_func_ptr)->func);        \ 
  16.         }                           \ 
  17.     } while (0) 

邏輯上和我們?cè)趹?yīng)用層的類(lèi)似。在執(zhí)行鉤子,也就是我們的回調(diào)時(shí),我們可以通過(guò)內(nèi)核接口把信息寫(xiě)到 ring buffer,然后應(yīng)用層可以通過(guò) debugfs 獲取到這個(gè)信息。

2 trace event

有了 Tracepoint 機(jī)制后,我們就可以寫(xiě)模塊加載到內(nèi)核中實(shí)現(xiàn)自己的插樁點(diǎn)。但是內(nèi)核也為我們內(nèi)置提供了非常多的插樁點(diǎn)。具體是通過(guò) trace event 來(lái)實(shí)現(xiàn)的。下面看一個(gè)例子。

  1. #define TRACE_EVENT(name, proto, args, struct, assign, print)   \ 
  2.     DECLARE_TRACE(name, PARAMS(proto), PARAMS(args))TRACE_EVENT(consume_skb, 
  3.  
  4.     TP_PROTO(struct sk_buff *skb), 
  5.  
  6.     TP_ARGS(skb), 
  7.  
  8.     TP_STRUCT__entry( 
  9.         __field(    void *, skbaddr ) 
  10.     ), 
  11.  
  12.     TP_fast_assign( 
  13.         __entry->skbaddr = skb; 
  14.     ), 
  15.  
  16.     TP_printk("skbaddr=%p", __entry->skbaddr)); 

上面定義了一個(gè)宏 TRACE_EVENT,它本質(zhì)上是對(duì) DECLARE_TRACE 的封裝,所以這里是定義了一系列的函數(shù)(注冊(cè)鉤子、處理鉤子)。然后在 consume_skb 函數(shù)中處理了注冊(cè)的鉤子。

  1. void consume_skb(struct sk_buff *skb){ 
  2.     trace_consume_skb(skb); 
  3.     __kfree_skb(skb); 
  4.  

3. 總結(jié)

內(nèi)核提供了非常豐富但是也非常復(fù)雜的機(jī)制,從而用戶可以通過(guò)內(nèi)核的能力獲取到更底層的數(shù)據(jù),用以排查問(wèn)題和做性能優(yōu)化。我們可以看到插樁的這種機(jī)制是一種靜態(tài)的機(jī)制,我們通常需要依賴(lài)當(dāng)前版本的內(nèi)核所支持的樁,從而獲得對(duì)應(yīng)的信息,但其實(shí)內(nèi)核也提供了動(dòng)態(tài)追蹤的能力,可以實(shí)現(xiàn)熱插拔獲取信息的能力??偟膩?lái)說(shuō),Linux 下的追蹤技術(shù)多種多樣,雖然非常復(fù)雜,但是上層也提供了各種更方便的工具,這些能力是我們深入排查問(wèn)題的利器。

 

責(zé)任編輯:姜華 來(lái)源: 編程雜技
相關(guān)推薦

2021-11-15 04:00:07

Linux 內(nèi)核動(dòng)態(tài)

2025-04-02 00:33:00

2025-04-01 02:00:22

2016-12-08 09:57:09

LinuxDTrace技術(shù)

2022-03-03 18:18:53

BPF解釋器系統(tǒng)

2021-10-06 09:46:17

trace-cmd追蹤內(nèi)核Linux

2021-07-11 06:45:18

Linux內(nèi)核靜態(tài)

2025-05-15 09:12:27

2025-03-07 08:30:00

pwruLinux網(wǎng)絡(luò)包追蹤

2025-01-02 11:06:22

2023-02-28 09:47:42

2023-03-10 14:56:37

Linuxconnect系統(tǒng)

2023-03-01 23:53:30

Linuxshutdown進(jìn)程

2023-03-01 23:56:11

2024-04-15 11:24:32

庫(kù)存跟蹤技術(shù)NFC藍(lán)牙

2022-05-24 12:34:32

Docker容器Linux容器進(jìn)程

2023-11-24 11:24:16

Linux系統(tǒng)

2021-09-30 09:43:11

Linux內(nèi)核Zstd補(bǔ)丁

2017-01-12 19:15:03

Linux內(nèi)核調(diào)試自構(gòu)proc

2013-09-24 10:48:32

Google追蹤技術(shù)Cookies
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)