BPF(Berkeley Packet Filter)技術近年來在系統程式設計和效能分析領域得到廣泛應用。理解和分析 BPF 程式對於系統調校和問題排查至關重要。本文將介紹兩個重要的 BPF 工具:BPFTool 和 BPFTrace,它們分別提供了檢視和管理 BPF 程式以及使用高階追蹤語言撰寫 BPF 程式的功能。BPFTool 可以用於顯示 BPF 程式資訊、dump bytecode、啟用 runtime 統計、載入和附加程式,以及管理 BPF 地圖。BPFTrace 則提供了一種更簡潔的方式來撰寫 BPF 程式,並支援過濾和動態地圖等功能。結合使用這兩個工具,開發者可以更有效率地開發、調校和分析 BPF 程式,提升系統效能和穩定性。
步驟 1:下載並安裝 BPFTool
首先,需要下載 Linux 核心的原始碼,並切換到指定的核心版本標籤,例如 v5.1
。然後,導航到 BPFTool 的原始碼目錄,通常位於 tools/bpf/bpftool
。在這個目錄中,執行 make
和 sudo make install
命令來編譯和安裝 BPFTool。
步驟 2:驗證 BPFTool 的版本
安裝完成後,可以使用 bpftool --version
命令來驗證 BPFTool 的版本。正確安裝的 BPFTool 會顯示其版本號,例如 bpftool v5.1.0
。
步驟 3:顯示 BPF 功能
BPFTool 的基本操作之一是掃描系統以瞭解可用的 BPF 功能。這對於記不住哪個核心版本引入了哪種程式或 BPF JIT 編譯器是否啟用非常有用。要檢視這些資訊,可以執行 bpftool feature
命令。
步驟 4:解讀輸出
執行 bpftool feature
命令後,會得到一份詳細的輸出,描述系統中支援的 BPF 功能。這份輸出包括了許多資訊,例如:
- 是否允許非特權使用者執行
bpf
系統呼叫。 - 是否啟用了 JIT 編譯器。
- 可用的 eBPF 程式型別,例如
socket_filter
和kprobe
。 - 可用的 eBPF 地圖型別,例如
hash
和array
。
啟用 JIT 編譯器
如果系統沒有啟用 JIT 編譯器,可以透過特定的命令來啟用它。JIT 編譯器對於編譯 BPF 程式非常重要,尤其是在新版本的核心中,它可以大大提高效能。
BPFTool:檢視BPF程式
BPFTool是一個強大的工具,允許您直接檢視BPF程式在核心中的資訊。它可以幫助您調查系統中已經執行的BPF程式,並且可以載入和固定新編譯的BPF程式。
檢視BPF程式
要開始使用BPFTool來檢視BPF程式,您可以執行bpftool prog show
命令。如果您使用Systemd作為初始化系統,您可能已經有一些BPF程式載入並附加到某些cgroup中。命令的輸出將顯示如下:
52: cgroup_skb tag 7be49e3934a125ba
loaded_at 2019-03-28T16:46:04-0700 uid 0
xlated 296B jited 229B memlock 4096B map_ids 52,53
53: cgroup_skb tag 2a142ef67aaad174
loaded_at 2019-03-28T16:46:04-0700 uid 0
xlated 296B jited 229B memlock 4096B map_ids 52,53
54: cgroup_skb tag 7be49e3934a125ba
loaded_at 2019-03-28T16:46:04-0700 uid 0
xlated 296B jited 229B memlock 4096B map_ids 54,55
左側的數字是程式識別碼,我們稍後會用它們來調查這些程式的詳細資訊。從這個輸出中,您也可以學到系統正在執行哪些型別的程式。在這種情況下,系統正在執行三個附加到cgroup socket buffers的BPF程式。載入時間可能與您啟動系統的時間相匹配,如果這些程式是由玄貓啟動的,您也可以看到這些程式目前使用的記憶體量和相關聯的地圖識別碼。
篩選程式資訊
您可以將程式識別碼作為額外的引數新增到之前的命令中:bpftool prog show id 52
。這樣,BPFTool將只顯示指定程式的資訊,讓您可以篩選出不需要的資訊。這個命令也支援--json
旗標來生成JSON輸出,這對於操縱輸出非常方便。例如,工具如jq
可以給您一個更結構化的格式來檢視這些資料:
{
"id": 52,
"type": "cgroup_skb",
"tag": "7be49e3934a125ba",
"gpl_compatible": false,
"loaded_at": 1553816764,
"uid": 0,
"bytes_xlated": 296,
"jited": true,
"bytes_jited": 229,
"bytes_memlock": 4096,
"map_ids": [
52,
53
]
}
您也可以進行更高階的操縱和篩選,只檢視您感興趣的資訊。在下一個例子中,我們只對BPF程式識別碼、程式型別和載入時間感興趣:
# bpftool prog show --json id 52 | jq '.id,.type,.loaded_at'
這樣,您就可以使用BPFTool來檢視和操縱BPF程式的資訊,進一步瞭解系統中正在執行的程式。
使用 BPFTool 進行 BPF 程式分析
當您已知一個 BPF 程式的識別碼(ID)時,可以使用 bpftool
來顯示該程式的詳細資訊。例如,若要顯示 ID 為 52 的 BPF 程式的 JSON 格式資訊,可以使用以下命令:
bpftool prog show --json id 52 | jq -c '[.id,.type,.loaded_at]'
這將輸出類別似以下的結果:
[52,"cgroup_skb",1553816764]
這個結果顯示了程式的 ID、型別以及載入時間。
BPF 程式 dump
如果您需要對 BPF 程式進行除錯,特別是當您需要檢視由玄貓生成的 BPF bytecode 時,可以使用 bpftool
的 dump
子命令。以下命令用於 dump ID 為 52 的 BPF 程式:
bpftool prog dump xlated id 52
這將輸出類別似以下的結果:
0: (bf) r6 = r1
1: (69) r7 = *(u16 *)(r6 +192)
2: (b4) w8 = 0
3: (55) if r7!= 0x8 goto pc+14
4: (bf) r1 = r6
5: (b4) w2 = 16
6: (bf) r3 = r10
7: (07) r3 += -4
8: (b4) w4 = 4
9: (85) call bpf_skb_load_bytes#7151872
...
這個結果顯示了 BPF 程式的指令序列,可以幫助您瞭解程式的邏輯和執行流程。
視覺化 BPF 程式
如果您想要獲得一個更視覺化的 BPF 程式表示,包括指令跳轉,可以使用 bpftool
的 dump
子命令並加上 visual
選項。這將生成一個可以被工具如 dotty
轉換成圖形表示的輸出格式,從而方便您分析程式的控制流程。
bpftool prog dump xlated visual id 52
這個命令可以幫助您更好地理解 BPF 程式的結構和執行流程,有助於您的除錯和開發工作。
使用 BPFTool 來檢視和管理 BPF 程式
BPFTool 是一個強大的工具,允許您檢視和管理 BPF 程式在核心中的執行情況。下面我們將介紹如何使用 BPFTool 來檢視 BPF 程式的執行情況、載入新程式以及附加程式到 socket 和 cgroup。
檢視 BPF 程式的執行情況
首先,您可以使用 bpftool prog dump
命令來檢視 BPF 程式的執行情況。例如:
bpftool prog dump xlated id 52 visual &> output.out
這個命令會將 BPF 程式的執行情況 dump 到 output.out
檔案中。您可以使用 dot
工具來將這個檔案轉換成圖形化的輸出。例如:
dot -Tpng output.out -o visual-graph.png
這個命令會將 output.out
檔案轉換成一個 PNG 圖片,顯示 BPF 程式的執行情況。
啟用 runtime 統計
如果您正在使用 5.1 或更新版本的核心,您可以啟用 runtime 統計來檢視 BPF 程式的執行時間和次數。首先,您需要執行以下命令來啟用 runtime 統計:
sysctl -w kernel.bpf_stats_enabled=1
啟用 runtime 統計後,您可以使用 bpftool prog show
命令來檢視 BPF 程式的執行情況,包括執行時間和次數。例如:
bpftool prog show
這個命令會顯示 BPF 程式的執行情況,包括執行時間和次數。
載入新程式
您可以使用 bpftool prog load
命令來載入新程式到核心中。例如:
bpftool prog load bpf_prog.o /sys/fs/bpf/bpf_prog
這個命令會將 bpf_prog.o
檔案載入到核心中,並將其附加到 /sys/fs/bpf/bpf_prog
檔案系統中。
附加程式到 socket 和 cgroup
您可以使用 bpftool prog attach
命令來附加程式到 socket 和 cgroup 中。例如:
bpftool prog attach cgroup_skb tag 7be49e3934a125ba
這個命令會將 BPF 程式附加到 cgroup 中,並指定其 tag 為 7be49e3934a125ba
。
圖表翻譯:
下圖顯示了 BPFTool 的架構和功能。
graph LR A[BPFTool] -->|檢視|> B[核心] A -->|載入|> C[新程式] A -->|附加|> D[socket 和 cgroup] B -->|執行情況|> E[runtime 統計] C -->|載入|> F[核心] D -->|附加|> G[socket 和 cgroup]
這個圖表顯示了 BPFTool 的架構和功能,包括檢視核心中的執行情況、載入新程式以及附加程式到 socket 和 cgroup 中。
檢視BPF對映
除了提供存取和操作BPF程式的功能,BPFTool還允許您檢視和操作BPF程式使用的BPF對映。要列出所有對映並篩選對映,您可以使用以下命令:
bpftool map show
這個命令會顯示系統中所有的BPF對映,包括其識別碼、型別、旗標、鍵大小、值大小、最大條目數和記憶體鎖定大小。
篩選對映
您可以使用 bpftool
來篩選對映,方法是指定對映的型別或名稱。例如,要顯示所有 lpm_trie
型別的對映,您可以使用以下命令:
bpftool map show type lpm_trie
建立新的對映
要建立新的對映,您需要指定對映的型別、鍵大小、值大小、最大條目數和名稱。您可以使用以下命令來建立一個新的對映:
bpftool map create /sys/fs/bpf/counter type array key 4 value 4 entries 5 name counter
這個命令會建立一個新的陣列對映,具有指定的鍵大小、值大小、最大條目數和名稱,並將其儲存到 /sys/fs/bpf/
目錄中。
列出對映元素
您可以使用 bpftool
來列出對映中的所有元素。例如,要列出 counter
對映中的所有元素,您可以使用以下命令:
bpftool map dump /sys/fs/bpf/counter
這個命令會顯示 counter
對映中的所有元素,包括其鍵和值。
圖表翻譯:
graph LR A[bpftool] -->|map show|> B[顯示所有對映] A -->|map create|> C[建立新的對映] A -->|map dump|> D[列出對映元素]
這個圖表顯示了 bpftool
的不同命令和其功能,包括顯示所有對映、建立新的對映和列出對映元素。
BPFTool 使用:更新和刪除元素
在建立地圖後,您可以更新和刪除元素,就像在 BPF 程式中一樣。然而,請記住,您無法從固定大小的陣列中移除元素,只能更新它們。但是,您可以從其他地圖中刪除元素,例如雜湊表。
更新元素
若要將新元素新增到地圖或更新現有元素,您可以使用 map update
命令。您可以從前面的示例中取得地圖識別碼:
# bpftool map update id 58 key 1 0 0 0 value 1 0 0 0
如果您嘗試使用無效鍵或值更新元素,BPFTool 將傳回錯誤:
# bpftool map update id 58 key 1 0 0 0 value 1 0 0
Error: value expected 4 bytes got 3
dump 地圖元素
BPFTool 可以提供地圖中所有元素的 dump,如果您需要檢查其值。您可以看到 BPF 初始化所有元素為 null 值時,當您建立固定大小的陣列地圖:
# bpftool map dump id 58
key: 00 00 00 00 value: 00 00 00 00
key: 01 00 00 00 value: 01 00 00 00
key: 02 00 00 00 value: 00 00 00 00
key: 03 00 00 00 value: 00 00 00 00
key: 04 00 00 00 value: 00 00 00 00
附加預建立地圖
BPFTool 提供了一個強大的選項,即您可以將預先建立的地圖附加到新程式並替換它們初始化的地圖。這樣,您可以在程式啟動時提供儲存的資料,即使您沒有編寫程式來讀取 BPF 檔案系統中的地圖。要執行此操作,您需要在載入程式時使用 BPFTool 設定要初始化的地圖。您可以使用地圖名稱或索引位置指定地圖:
# bpftool prog load bpf_prog.o /sys/fs/bpf/bpf_prog_2 \
map name counter /sys/fs/bpf/counter
在此示例中,我們將剛剛建立的地圖附加到新的程式。在這種情況下,我們替換了名為 counter
的地圖,因為我們知道程式初始化了一個名為 counter
的地圖。您也可以使用 idx
詞彙來指定地圖的索引位置,如果這對您更方便。
圖表翻譯
flowchart TD A[建立地圖] --> B[更新元素] B --> C[刪除元素] C --> D[dump 地圖元素] D --> E[附加預建立地圖] E --> F[載入程式]
內容解密
上述過程中,我們展示瞭如何使用 BPFTool 更新和刪除地圖元素,以及如何附加預先建立的地圖到新程式。這些功能使我們可以更有效地管理 BPF 程式中的資料,並提供了一種在程式啟動時提供儲存資料的方法。透過這些步驟,我們可以更好地控制和管理 BPF 程式中的資料流。
存取BPF Maps和檢查程式
存取BPF Maps和檢查程式對於除錯訊息傳遞和理解系統行為至關重要。BPFTool提供了一種方便的方式來直接存取BPF Maps和檢查程式。
檢查附加到特定介面的程式
有時您可能會想知道哪些程式附加到特定介面上。BPF可以載入在cgroup、Perf事件和網路封包上工作的程式。cgroup
、perf
和net
子命令可以幫助您追蹤這些介面上的附加。
perf
子命令列出所有附加到系統追蹤點的程式,例如kprobes、uprobes和tracepoints。net
子命令列出附加到XDP和Traffic Control的程式。cgroup
子命令列出所有附加到cgroup的程式。這個子命令需要您指定要檢查的cgroup路徑。如果您想要列出所有cgroup中的所有附加,則需要使用bpftool cgroup tree
。
使用BPFTool批次模式載入命令
在分析系統行為時,您可能需要反覆執行多個命令。BPFTool的批次模式允許您將多個命令寫入一個檔案並一次執行所有命令。
- 您可以在檔案中寫入所有要執行的命令,並使用
bpftool
執行這個檔案。 - 檔案中可以包含註解,使用
#
符號開始。 - 執行模式不是原子性的,BPFTool會一行一行執行命令,如果有一個命令失敗,則會停止執行並保持系統在最後一個成功命令之後的狀態。
以下是批次模式檔案的一個簡單示例:
# 建立一個新的hash map
map create /sys/fs/bpf/hash_map type hash key 4 value 4 entries 5 name hash_map
這個示例展示瞭如何使用BPFTool批次模式來執行多個命令,從而簡化您的工作流程。
內容解密:
在這個示例中,我們使用map create
命令來建立一個新的hash map。這個命令需要指定map的型別、鍵值大小、值大小、條目數量和名稱。
圖表翻譯:
flowchart TD A[開始] --> B[建立hash map] B --> C[指定map型別] C --> D[指定鍵值大小] D --> E[指定值大小] E --> F[指定條目數量] F --> G[指定名稱] G --> H[完成建立]
這個圖表展示了建立hash map的過程,從開始到完成。每一步都對應到一個特定的動作或決策。
BPFTool 和 BPFTrace 的強大功能
BPFTool 是一個強大的工具,提供了多種功能來幫助您管理和除錯 BPF 程式。其中一個重要功能是批次模式(batch mode),它允許您將多個命令儲存到一個檔案中,並一次性執行。這個功能可以幫助您自動化工作流程,例如建立多個地圖(map)和載入 BPF 程式。
顯示地圖
您可以使用 map show
命令來顯示系統中的所有地圖。這個命令會列出所有已經建立的地圖,包括其名稱、旗標、鍵長度、值長度、最大條目數和記憶體鎖定大小。
批次模式
批次模式允許您將多個命令儲存到一個檔案中,並一次性執行。您可以使用 bpftool batch file
命令來載入批次檔案。批次檔案中的命令會按照順序執行,如果任何命令失敗,批次模式會立即終止。
顯示 BTF 資訊
BPFTool 還可以顯示 BPF Type Format(BTF)資訊,當二進位制物件中存在 BTF 時。BTF 提供了程式結構的中繼資料資訊,幫助您除錯程式。您可以使用 bpftool btf dump
命令來顯示 BTF 資訊。
BPFTrace
BPFTrace 是一個高階別的追蹤語言,允許您使用簡潔的 DSL(Domain-Specific Language)撰寫 BPF 程式。它提供了許多內建功能,例如彙總資訊和建立直方圖。BPFTrace 的語言受到 awk 和 DTrace 的啟發。如果您熟悉 DTrace,您會發現 BPFTrace 是一個很好的替代品。
安裝 BPFTrace
您可以使用多種方式安裝 BPFTrace,包括使用預先建置的套件或從原始碼編譯。開發者建議使用預先建置的套件,以確保安裝過程順暢。您可以在 BPFTrace 的 GitHub 儲存函式庫中找到安裝檔案和相關資訊。
BPFTrace 程式設計語言
BPFTrace 的程式設計語言是一種簡潔的語言,分為三個部分:標頭(header)、動作區塊(action blocks)和尾部(footer)。標頭是 BPFTrace 載入程式時執行的特殊區塊,通常用於印出一些資訊,如前言。尾部則是在程式終止前執行的特殊區塊。這兩個區塊都是可選的,而動作區塊則是必須有的,至少有一個。
程式結構
一個基本的 BPFTrace 程式結構如下:
BEGIN
{
# 標頭區塊
printf("開始 BPFTrace 程式\n")
}
kprobe:do_sys_open
{
# 動作區塊
printf("開啟檔案描述元:%s\n", str(arg1))
}
END
{
# 尾部區塊
printf("結束 BPFTrace 程式\n")
}
語言特點
BPFTrace 的語言特點包括:
- 動態語言:BPFTrace 是一種動態語言,不需要事先知道探針(probe)會接收到的引數數量。
- 引數存取:BPFTrace 提供引數存取的方法,例如
arg1
代表第二個引數。 - 內建函式:BPFTrace 提供內建函式,如
printf
和str
,用於印出資訊和將指標轉換為字串。 - 過濾:BPFTrace 支援過濾功能,可以根據條件過濾動作區塊的執行。
- 動態對映:BPFTrace 支援動態對映,可以根據探針的引數動態建立對映。
過濾
過濾是 BPFTrace 的一項重要功能,可以根據條件過濾動作區塊的執行。過濾的語法如下:
kprobe:do_sys_open /str(arg1) == "/tmp/example.bt"/
{
printf("開啟檔案描述元:%s\n", str(arg1))
}
這個例子中,動作區塊只會在檔案描述元為 /tmp/example.bt
時執行。
動態對映
動態對映是 BPFTrace 的另一項重要功能,可以根據探針的引數動態建立對映。對映的語法如下:
kprobe:do_sys_open
{
@opens[str(arg1)] = count()
}
這個例子中,建立了一個名為 @opens
的對映,根據檔案描述元的路徑進行計數。
kubectl-trace
kubectl-trace 是一個 Kubernetes 的外掛程式,可以幫助使用者在 Kubernetes 叢集中執行 BPFTrace 程式。它可以將 BPFTrace 程式排程在 Kubernetes 叢集中,不需要額外安裝任何套件或模組。
BPF 工具介紹
安裝 kubectl-trace
要使用 kubectl-trace,您需要從其原始碼倉函式庫安裝它,因為其開發者不提供二進位制包。kubectl 的外掛系統會自動偵測這個新外掛,一旦 Go 的工具鏈編譯並安裝完成。kubectl-trace 可以自動下載它需要的 Docker 映象,並在叢集中執行。
檢視 Kubernetes 節點
您可以使用 kubectl-trace 來檢視節點和 Pod 中執行的容器,以及容器中的程式。對於節點,您可以執行任何 BPF 程式,但對於程式,您只能執行附加使用者空間探針的程式。
深入剖析 BPFTool 和 BPFTrace 的核心功能後,我們可以發現,這些工具為開發者提供了強大的 BPF 程式開發、佈署和除錯能力。從底層 bytecode 的檢視到高階 BPFTrace 語言的應用,BPFTool 與 BPFTrace 的結合涵蓋了 BPF 程式開發的完整生命週期。分析比較 BPFTool 與其他 BPF 工具,例如 BCC,可以發現 BPFTool 更專注於底層操作和系統層級的分析,而 BCC 則更偏向於應用層的追蹤和效能分析。這兩種工具各有千秋,在不同場景下可以互補使用。目前的限制主要在於 BPF 程式本身的複雜性和除錯的難度,以及 BPFTool 的某些功能需要較新版本的 Linux 核心支援。對於想要深入探索 BPF 技術的開發者,建議深入學習 BPF 程式設計原理,並結合 BPFTool 和 BPFTrace 進行實踐,才能真正掌握這些工具的精髓。玄貓認為,BPF 技術作為 Linux 核心的一項重要創新,未來將在系統監控、網路安全和效能最佳化等領域發揮更大的作用。隨著 eBPF 技術的持續發展和社群的壯大,我們預見 BPFTool 和 BPFTrace 將成為 BPF 生態系統中不可或缺的工具,並持續推動 BPF 技術的普及應用。