Linux中國

在 Fedora 中用 bpftrace 追蹤代碼

bpftrace 是一個 基於 eBPF 的新型追蹤工具,在 Fedora 28 第一次引入。Brendan Gregg、Alastair Robertson 和 Matheus Marchini 在網上的一個鬆散的黑客團隊的幫助下開發了 bpftrace。它是一個允許你分析系統在幕後正在執行的操作的追蹤工具,可以告訴你代碼中正在被調用的函數、傳遞給函數的參數、函數的調用次數等。

這篇文章的內容涉及了 bpftrace 的一些基礎,以及它是如何工作的,請繼續閱讀獲取更多的信息和一些有用的實例。

eBPF( 擴展的伯克利數據包過濾器 extended Berkeley Packet Filter

eBPF 是一個微型虛擬機,更確切的說是一個位於 Linux 內核中的虛擬 CPU。eBPF 可以在內核空間以一種安全可控的方式載入和運行小型程序,使得 eBPF 的使用更加安全,即使在生產環境系統中。eBPF 虛擬機有自己的指令集架構(ISA),類似於現代處理器架構的一個子集。通過這個 ISA,可以很容易將 eBPF 程序轉化為真實硬體上的代碼。內核即時將程序轉化為主流處理器架構上的本地代碼,從而提升性能。

eBPF 虛擬機允許通過編程擴展內核,目前已經有一些內核子系統使用這一新型強大的 Linux 內核功能,比如網路、安全計算、追蹤等。這些子系統的主要思想是添加 eBPF 程序到特定的代碼點,從而擴展原生的內核行為。

雖然 eBPF 機器語言功能強大,由於是一種底層語言,直接用於編寫代碼很費力,bpftrace 就是為了解決這個問題而生的。eBPF 提供了一種編寫 eBPF 追蹤腳本的高級語言,然後在 clang / LLVM 庫的幫助下將這些腳本轉化為 eBPF,最終添加到特定的代碼點。

安裝和快速入門

在終端 使用 sudo 執行下面的命令安裝 bpftrace:

$ sudo dnf install bpftrace

使用「hello world」進行實驗:

$ sudo bpftrace -e 'BEGIN { printf("hello worldn"); }'

注意,出於特權級的需要,你必須使用 root 運行 bpftrace,使用 -e 選項指明一個程序,構建一個所謂的「單行程序」。這個例子只會列印 「hello world」,接著等待你按下 Ctrl+C

BEGIN 是一個特殊的探針名,只在執行一開始生效一次;每次探針命中時,大括弧 {} 內的操作(這個例子中只是一個 printf)都會執行。

現在讓我們轉向一個更有用的例子:

$ sudo bpftrace -e 't:syscalls:sys_enter_execve { printf("%s called %sn", comm, str(args->filename)); }'

這個例子列印了父進程的名字(comm)和系統中正在創建的每個新進程的名稱。t:syscalls:sys_enter_execve 是一個內核追蹤點,是 tracepoint:syscalls:sys_enter_execve 的簡寫,兩種形式都可以使用。下一部分會向你展示如何列出所有可用的追蹤點。

comm 是一個 bpftrace 內建指令,代表進程名;filenamet:syscalls:sys_enter_execve 追蹤點的一個欄位,這些欄位可以通過 args 內建指令訪問。

追蹤點的所有可用欄位可以通過這個命令列出:

bpftrace -lv "t:syscalls:sys_enter_execve"

示例用法

列出探針

bpftrace 的一個核心概念是 探針點 probe point ,即 eBPF 程序可以連接到的(內核或用戶空間的)代碼中的測量點,可以分成以下幾大類:

  • kprobe——內核函數的開始處
  • kretprobe——內核函數的返回處
  • uprobe——用戶級函數的開始處
  • uretprobe——用戶級函數的返回處
  • tracepoint——內核靜態追蹤點
  • usdt——用戶級靜態追蹤點
  • profile——基於時間的採樣
  • interval——基於時間的輸出
  • software——內核軟體事件
  • hardware——處理器級事件

所有可用的 kprobe / kretprobetracepointssoftwarehardware 探針可以通過這個命令列出:

$ sudo bpftrace -l

uprobe / uretprobeusdt 是用戶空間探針,專用於某個可執行文件。要使用這些探針,通過下文中的特殊語法。

profileinterval 探針以固定的時間間隔觸發;固定的時間間隔不在本文的範疇內。

統計系統調用數

映射 是保存計數、統計數據和柱狀圖的特殊 BPF 數據類型,你可以使用映射統計每個系統調用正在被調用的次數:

$ sudo bpftrace -e 't:syscalls:sys_enter_* { @[probe] = count(); }'

一些探針類型允許使用通配符匹配多個探針,你也可以使用一個逗號隔開的列表為一個操作塊指明多個連接點。上面的例子中,操作塊連接到了所有名稱以 t:syscalls:sysenter_ 開頭的追蹤點,即所有可用的系統調用。

bpftrace 的內建函數 count() 統計系統調用被調用的次數;@[] 代表一個映射(一個關聯數組)。該映射的鍵 probe 是另一個內建指令,代表完整的探針名。

這個例子中,相同的操作塊連接到了每個系統調用,之後每次有系統調用被調用時,映射就會被更新,映射中和系統調用對應的項就會增加。程序終止時,自動列印出所有聲明的映射。

下面的例子統計所有的系統調用,然後通過 bpftrace 過濾語法使用 PID 過濾出某個特定進程調用的系統調用:

$ sudo bpftrace -e 't:syscalls:sys_enter_* / pid == 1234 / { @[probe] = count(); }'

進程寫的位元組數

讓我們使用上面的概念分析每個進程正在寫的位元組數:

$ sudo bpftrace -e 't:syscalls:sys_exit_write /args->ret > 0/ { @[comm] = sum(args->ret); }'

bpftrace 連接操作塊到寫系統調用的返回探針(t:syscalls:sys_exit_write),然後使用過濾器丟掉代表錯誤代碼的負值(/arg->ret > 0/)。

映射的鍵 comm 代表調用系統調用的進程名;內建函數 sum() 累計每個映射項或進程寫的位元組數;args 是一個 bpftrace 內建指令,用於訪問追蹤點的參數和返回值。如果執行成功,write 系統調用返回寫的位元組數,arg->ret 用於訪問這個位元組數。

進程的讀取大小分布(柱狀圖):

bpftrace 支持創建柱狀圖。讓我們分析一個創建進程的 read 大小分布的柱狀圖的例子:

$ sudo bpftrace -e 't:syscalls:sys_exit_read { @[comm] = hist(args->ret); }'

柱狀圖是 BPF 映射,因此必須保存為一個映射(@),這個例子中映射鍵是 comm

這個例子使 bpftrace 給每個調用 read 系統調用的進程生成一個柱狀圖。要生成一個全局柱狀圖,直接保存 hist() 函數到 @(不使用任何鍵)。

程序終止時,bpftrace 自動列印出聲明的柱狀圖。創建柱狀圖的基準值是通過 args->ret 獲取到的讀取的位元組數。

追蹤用戶空間程序

你也可以通過 uprobes / uretprobes 和 USDT(用戶級靜態定義的追蹤)追蹤用戶空間程序。下一個例子使用探測用戶級函數結尾處的 uretprobe ,獲取系統中運行的每個 bash 發出的命令行:

$ sudo bpftrace -e 'uretprobe:/bin/bash:readline { printf("readline: "%s"n", str(retval)); }'

要列出可執行文件 bash 的所有可用 uprobes / uretprobes, 執行這個命令:

$ sudo bpftrace -l "uprobe:/bin/bash"

uprobe 指向用戶級函數執行的開始,uretprobe 指向執行的結束(返回處);readline()/bin/bash 的一個函數,返回鍵入的命令行;retval 是被探測的指令的返回值,只能在 uretprobe 訪問。

使用 uprobes 時,你可以用 arg0..argN 訪問參數。需要調用 str()char * 指針轉化成一個字元串。

自帶腳本

bpftrace 軟體包附帶了許多有用的腳本,可以在 /usr/share/bpftrace/tools/ 目錄找到。

這些腳本中,你可以找到:

  • killsnoop.bt——追蹤 kill() 系統調用發出的信號
  • tcpconnect.bt——追蹤所有的 TCP 網路連接
  • pidpersec.bt——統計每秒鐘(通過 fork)創建的新進程
  • opensnoop.bt——追蹤 open() 系統調用
  • bfsstat.bt——追蹤一些 VFS 調用,按秒統計

你可以直接使用這些腳本,比如:

$ sudo /usr/share/bpftrace/tools/killsnoop.bt

你也可以在創建新的工具時參考這些腳本。

鏈接

via: https://fedoramagazine.org/trace-code-in-fedora-with-bpftrace/

作者:Augusto Caringi 選題:lujun9972 譯者:YungeG 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    More in:Linux中國