【Linux】BPF學習筆記 - BCC工具[6]

本學習筆記來自於閱讀 Brendan Gregg的《BPF Performance Tools》

BPF編譯器集合(BCC)是一個開放源代碼項目,其中包含用於構建BPF軟件的編譯器框架和庫。 它是BPF的主要前端項目,受到BPF開發人員的支持. BCC還包含70多個現成的BPF性能分析和故障排除工具

BCC 組件: BCC包含有關工具,手冊頁和示例文件的文檔,以及有關使用BCC工具的指南,以及有關BCC工具開發的指南和參考指南。 它提供了用於在Python,C ++中開發BCC工具的接口; 將來可能會添加更多接口

BCC工具使用的主要語言是Python(用於用戶級組件)和C(用於內核級BPF)。指南中的建議之一是 “編寫工具即可解決問題,而無需更多”. 這鼓勵在可能的情況下開發單一用途的工具,而不是多功能的工具

一、 單一目的工具

Unix的哲學是做一件事並做好, 其中一種方式是創建較小的高質量工具,可以使用管道將它們連接在一起以完成更復雜的任務,例如grep, cut

考慮一下如何爲跟蹤open系列syscall的一項任務自定義選項和輸出:

$ opensnoop -h
usage: opensnoop [-h] [-T] [-U] [-x] [-p PID] [-t TID] [-u UID]
                 [-d DURATION] [-n NAME] [-e] [-f FLAG_FILTER]
Trace open() syscalls

optional arguments:
  -h, --help            show this help message and exit
  -T, --timestamp       include timestamp on output
  -U, --print-uid       print UID column
  -x, --failed          only show failed opens
  -p PID, --pid PID     trace this PID only
[...]
  • 便於初學者學習:默認輸出通常就足夠了。這意味着初學者可以立即使用這些工具,而無需對命令行使用做出任何決定或知道要檢測哪些事件
  • 易於維護:對於工具開發人員來說,要維護的代碼量應該更少,並且需要更少的測試
  • 代碼示例:每個小型工具都提供了一個簡潔實用的代碼示例
  • 自定義參數和輸出:工具參數,位置參數和輸出不需要容納其他任務,可以針對一個目的進行自定義。 這樣可以提高可用性和可讀性

二、多功能工具

BCC包含可以執行各種不同任務的多功能工具。 這些很難學習,但是一旦學習就會更強大。 如果您僅偶爾使用這些內容,則可能無需深入學習它們。 您可以收集一些單行代碼以在需要時執行

  • 更高的可見性:您無需一次分析單個任務或目標,而是可以一次查看各種組件
  • 減少代碼重複:不用多個具有相似代碼的工具

1. FUNCCOUNT

對事件(尤其是函數調用)進行計數,並且可以回答以下問題:

  • 是否調用了此內核級或用戶級函數?
  • 每秒此函數調用的速率是多少?

爲了提高效率, funccount使用BPF map 在內核上下文中維護事件計數,並且僅將總數發送給用戶空間。 儘管與轉儲和後處理工具相比,這大大減少了funccount的開銷,但如果事件的發生頻率很高,則事件的選擇仍會導致相當大的開銷。 例如,內存分配(malloc(),free())每秒可能發生數百萬次,使用 funccount 進行跟蹤可能會花費超過30%的CPU開銷

語法

funccount [options] eventname

參數解釋: eventname

  • name or p:name: kprobe, 稱爲name()的內核函數
  • lib:name or p:lib:name:: uprobe, 在庫lib中稱爲name()的用戶級函數
  • path:name: uprobe, 在路徑中的文件中稱爲name()的用戶級函數
  • t:system:name: instrument the tracepoint 稱爲 system:name
  • u:lib:name : instrument the USDT probe, 在庫lid中稱爲name
  • *: a: 通配符可匹配任何字符串, -r 選項允許使用正則表達式代替

單行用法

# 計算VFS內核調用次數
funccount 'vfs_*'
# 計算TCP內核調用次數
funccount 'tcp_*'
# 計算每秒TCP發送調用次數
funccount -i 1 'tcp_send*'
# 顯示每秒的塊I/O事件的速率
funccount -i 1 't:block:*'
# 顯示每秒新進程的速率
funccount -i 1 t:sched:sched_process_fork
# 顯示每秒libc getaddrinfo() (名稱解析)的速率
funccount -i 1 c:getaddrinfo
# 計算所有 os.* 在libgo中的調用次數”
funccount 'go:os.*'
# 系統範圍內從libc調用頻率最高的字符串函數
funccount 'c:str*'
# 最頻繁的系統調用是
funccount 't:syscalls:sys_enter_*'
[...]

示例: 曾經調用過tcp_drop()內核函數嗎?是的, 此調用僅跟蹤tcp_drop()內核函數,在追蹤時,它被調用了三遍

$ funccount tcp_drop
Tracing 1 functions for "tcp_drop"... Hit Ctrl-C to end.
^C
FUNC                                    COUNT
tcp_drop                                    3

2. STACKCOUNT

事件的堆棧跟蹤。 與funccount一樣,事件可以是內核或用戶級函數,跟蹤點或USDT探測器

  • 爲什麼調用這個事件?代碼路徑是什麼?
  • 調用此事件的所有不同代碼路徑及其頻率是什麼?

爲了提高效率,stackcount完全在內核上下文中使用堆棧跟蹤的特殊BPF map 執行此摘要。 用戶空間讀取堆棧ID和計數,然後獲取堆棧跟蹤以進行符號轉換和打印。 與funccount一樣,開銷取決於所檢測事件的發生率,並且隨着stackcount對每個事件進行更多工作:遍歷和記錄堆棧跟蹤,開銷會明顯更高

語法

同FUNCCOUNT一樣

單行用法

# 計算創建塊I/O的堆棧跟蹤
stackcount t:block:block_rq_insert
# 計算導致發送IP數據包的堆棧跟蹤
stackcount ip_output
# 使用相應的PID對導致發送IP數據包的堆棧跟蹤進行計數
stackcount -P ip_output
# 計算導致線程阻塞和移出CPU的堆棧跟蹤
stackcount t:sched:sched_switch
# 計算導致read()系統調用的堆棧跟蹤
stackcount t:syscalls:sys_enter_read

示例

我注意到使用funccount在一個空閒系統上,我似乎有很高的ktime_get()內核函數調用率,每秒超過八千次。 這些獲取時間,但是爲什麼我的空閒系統需要如此頻繁地獲取時間?使用stackcount識別導致ktime_get()的代碼路徑.

如下所示: 輸出長達數百頁,包含一千多個堆棧跟蹤。 每個堆棧軌跡在每個功能上打印一行,然後出現次數.

最頻繁調用ktime_get()的堆棧來自CPU空閒路徑

$ stackcount ktime_get
Tracing 1 functions for "ktime_get"... Hit Ctrl-C to end.
^C
[...]
  ktime_get
  tick_nohz_idle_enter
  do_idle
  cpu_startup_entry
  start_secondary
  secondary_startup_64
    1077
[...]

stackcount火焰圖

有時,您會發現只爲一個事件打印了一個或幾個堆棧跟蹤,可以在stackcount輸出中輕鬆瀏覽。 對於ktime_get()這樣的情況,其中輸出長達數百頁,可以使用火焰圖來可視化輸出. stackcount可以使用 -f 生成此格式。

$ stackcount -f -P -D 10 ktime_get > out.stackcount01.txt
# 使用火焰圖軟件,生成svg
$ git clone http://github.com/brendangregg/FlameGraph
$ cd FlameGraph
$ ./flamegraph.pl --hash --bgcolors=grey < ../out.stackcount01.txt > out.stackcount01.svg

3. TRACE

可從許多不同的來源進行事件跟蹤

  • 調用內核級或用戶級函數時,參數是什麼?
  • 該函數的返回值是多少? 失敗了嗎?
  • 這個函數怎麼調用? 用戶或內核級堆棧跟蹤是什麼?

由於它爲每個事件打印一行輸出,因此它適合於很少調用的事件。 諸如網絡數據包,上下文切換和內存分配之類的非常頻繁的事件每秒可能發生數百萬次,而trace會產生如此多的輸出,以至於將花費大量的開銷。 使用trace的解決方案是使用過濾器表達式僅打印感興趣的事件。 頻繁發生的事件通常更適合於使用其他進行內核彙總的工具進行分析,例如funccount,stackcount和argdist

4. 其他

其他工具可自行查看, 工具文檔

如果您的工具是通過軟件包安裝的,則可能會發現man opensnoop命令有效, 可以使用nroff來格式化手冊頁

$ nroff -man man/man8/opensnoop.8

參考文檔

GIT 地址:可以查看各系統 安裝說明

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章