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 內建指令,代表進程名;filename 是 t: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 / kretprobe、tracepoints、software 和 hardware 探針可以通過這個命令列出:

$ sudo bpftrace -l

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

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

統計系統調用數

映射 是保存計數、統計數據和柱狀圖的特殊 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中國

    Linux中國

    捐贈 Let's Encrypt,共建安全的互聯網

    隨著 Mozilla、蘋果和谷歌對沃通和 StartCom 這兩家 CA 公司處罰落定,很多使用這兩家 CA 所簽發證書的網站紛紛尋求新的證書籤發商。有一個非盈利組織可以為大家提供了免費、可靠和安全的 SSL 證書服務,這就是 Let's Encrypt 項目。現在,它需要您的幫助
    Linux中國

    關於Linux防火牆iptables的面試問答

    Nishita Agarwal是Tecmint的用戶,她將分享關於她剛剛經歷的一家公司(印度的一家私人公司Pune)的面試經驗。在面試中她被問及許多不同的問題,但她是iptables方面的專家,因此她想分享這些關於iptables的問題和相應的答案給那些以後可能會進行相關面試的人。 所有的問題和相應的答案都基於Nishita Agarwal的記憶並經過了重寫。 嗨,朋友!我叫Nishita Agarwal。我已經取得了理學學士學位,我的專業集中在UNIX和它的變種(BSD,Linux)。它們一直深深的吸引著我。我在存儲方面有1年多的經驗。我正在尋求職業上的變化,並將供職於印度的P
    Linux中國

    Lets Encrypt 已被所有主流瀏覽器所信任

    旨在讓每個網站都能使用 HTTPS 加密的非贏利組織 Lets Encrypt 已經得了 IdenTrust的交叉簽名,這意味著其證書現在已經可以被所有主流的瀏覽器所信任。從這個裡程碑事件開始,訪問者訪問使用了Lets Encrypt 證書的網站不再需要特別配置就可以得到 HTTPS 安全保護了。 Lets Encrypt 的兩個中級證書 ...