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。在這個目錄中,執行 makesudo 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_filterkprobe
  • 可用的 eBPF 地圖型別,例如 hasharray

啟用 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 時,可以使用 bpftooldump 子命令。以下命令用於 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 程式表示,包括指令跳轉,可以使用 bpftooldump 子命令並加上 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事件和網路封包上工作的程式。cgroupperfnet子命令可以幫助您追蹤這些介面上的附加。

  • 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 提供內建函式,如 printfstr,用於印出資訊和將指標轉換為字串。
  • 過濾: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 技術的普及應用。