成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Linux 內核靜態追蹤技術的實現

系統 Linux
本文簡單分享一下內核的靜態追蹤技術的實現。追蹤,其實就是收集代碼在執行時的一些信息,以便協助排查問題。

[[434855]]

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

1 Tracepoint

Tracepoints 是一種靜態插樁的技術,實現雖然復雜,但是概念上比較簡單。比如我們打日志的時候,就類似這種情況,我們在業務代碼里,寫了很多 log 用來記錄進程在運行時的信息。Tracepoints 則是內核提供的一種基于鉤子的插樁技術。不過和打日志不一樣的是,我們想在哪里打就在哪里加對應的代碼,而 Tracepoints 則幾乎是依賴于內核決定哪里可以插樁,說幾乎是因為我們也可以寫內核模塊注冊到內核來通知插樁點。下面來通過一個例子看一下 Tracepoint 的使用和實現(例子來自內核文檔 tracepoints.rst)。分析之前先看一下兩個非常重要的宏。第一個是 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)) 

我們只需要關注主體的實現,而不需要關注參數,繼續展開。

  1. #define __DECLARE_TRACE(name, proto, args, cond, data_proto, data_args) \ 
  2.     extern struct tracepoint __tracepoint_##name;           \ 
  3.     // 執行鉤子函數 
  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.     // 注冊鉤子函數 
  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.     // 注銷鉤子函數                    
  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 主要是實現了幾個函數,我們只需要關注注冊鉤子和執行鉤子函數(格式是 register_trace_${yourname} 和 trace_${yourame})。接下來看第二個宏 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 主要是定義了一個 tracepoint 結構體。了解了兩個宏之后,來看一下如何使用 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)); 

首先在頭文件里通過 DECLARE_TRACE 宏定義了一系列函數。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. // 實現自己的鉤子函數并注冊到內核 
  15.  
  16. void callback(...) {} 
  17.  
  18. register_trace_subsys_eventname(callback); 

然后在實現文件里通過 DEFINE_TRACE 定義一個 tracepoint 結構體。接著調用 register_trace_subsys_eventname 函數把自定義的鉤子函數注冊到內核,然后在需要收集信息的地方調用處理鉤子的函數 trace_subsys_eventname。

1.2 實現

了解了使用之后,接下來看看實現。首先看一下注冊鉤子函數。

  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 中定義了一個 tracepoint_func 結構體用于表示鉤子信息,然后調用 tracepoint_add_func,其中 tp 就剛才自定義的 tracepoint 結構體。

  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.  

注冊函數的邏輯其實就是往自定義的結構體的隊列里插入一個新的節點。接下來再看一下處理鉤子的邏輯。

  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.         // 拿到隊列 
  8.         it_func_ptr = rcu_dereference_raw((tp)->funcs);     \ 
  9.         // 非空則執行里面的節點的回調 
  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) 

邏輯上和我們在應用層的類似。在執行鉤子,也就是我們的回調時,我們可以通過內核接口把信息寫到 ring buffer,然后應用層可以通過 debugfs 獲取到這個信息。

2 trace event

有了 Tracepoint 機制后,我們就可以寫模塊加載到內核中實現自己的插樁點。但是內核也為我們內置提供了非常多的插樁點。具體是通過 trace event 來實現的。下面看一個例子。

  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)); 

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

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

3. 總結

內核提供了非常豐富但是也非常復雜的機制,從而用戶可以通過內核的能力獲取到更底層的數據,用以排查問題和做性能優化。我們可以看到插樁的這種機制是一種靜態的機制,我們通常需要依賴當前版本的內核所支持的樁,從而獲得對應的信息,但其實內核也提供了動態追蹤的能力,可以實現熱插拔獲取信息的能力。總的來說,Linux 下的追蹤技術多種多樣,雖然非常復雜,但是上層也提供了各種更方便的工具,這些能力是我們深入排查問題的利器。

 

責任編輯:姜華 來源: 編程雜技
相關推薦

2021-11-15 04:00:07

Linux 內核動態

2025-04-01 02:00:22

2025-04-02 00:33:00

2016-12-08 09:57:09

LinuxDTrace技術

2022-03-03 18:18:53

BPF解釋器系統

2021-07-11 06:45:18

Linux內核靜態

2021-10-06 09:46:17

trace-cmd追蹤內核Linux

2025-05-15 09:12:27

2025-03-07 08:30:00

pwruLinux網絡包追蹤

2025-01-02 11:06:22

2023-02-28 09:47:42

2023-03-01 23:56:11

2023-03-10 14:56:37

Linuxconnect系統

2023-03-01 23:53:30

Linuxshutdown進程

2024-04-15 11:24:32

庫存跟蹤技術NFC藍牙

2021-09-30 09:43:11

Linux內核Zstd補丁

2023-11-24 11:24:16

Linux系統

2017-01-12 19:15:03

Linux內核調試自構proc

2022-05-24 12:34:32

Docker容器Linux容器進程

2013-09-24 10:48:32

Google追蹤技術Cookies
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日韩视频精品 | 久久综合av| 最新中文字幕在线 | 欧美一区二区三区的 | 久久国产日韩 | 亚洲精品国产一区 | 在线中文字幕日韩 | 成年免费在线观看 | 天天av天天好逼 | 国产精品一卡二卡三卡 | 精品国产91久久久久久 | 久久久久国产精品午夜一区 | 欧美精品在欧美一区二区少妇 | 久久婷婷av | 一区在线播放 | 国产高清自拍视频在线观看 | 国产精品久久久久久吹潮日韩动画 | 高清欧美性猛交xxxx黑人猛交 | 国产麻豆乱码精品一区二区三区 | www日韩 | 涩涩操 | 国产欧美一区二区在线观看 | 国产成人一区二区 | 日本字幕在线观看 | 亚洲精品视频一区二区三区 | 日韩国产在线 | 天天射影院 | 欧美专区在线 | 国产高清区 | 一区二区三区免费 | 国产精品片 | 午夜一级黄色片 | 久久久久综合 | 国产成人a亚洲精品 | 国产激情视频网站 | 国产精品高潮呻吟久久 | 久久综合色综合 | 91国在线观看| 91美女在线观看 | 国产精品久久久久久久久久久免费看 | 久久网站免费视频 |