昨天我們介紹了 eBPF 的基本概念,知道它可以透過各種 hook points 來追蹤系統行為。但面對 Tracepoints、Kprobes、Uprobes 這些不同的追蹤機制,該如何選擇?今天我們就來深入理解這些機制的差異與使用時機。
在介紹追蹤機制之前,我們會使用 bpftrace 作為實作範例,因此前面會先花些篇幅解釋 bpftrace 是怎樣的一個工具。
在深入討論追蹤機制之前,我們先介紹 bpftrace,一個能讓我們快速實踐 eBPF 追蹤的工具。
bpftrace 是一個能讓我們快速實踐 eBPF 追蹤的工具,它是一個高階的 eBPF 追蹤語言,其特色包含了:
awk 語法,容易上手如果不使用 bpftrace,又不想直接撰寫 bytecode的話,C 語言加上使用 libbpf library 是常見用來撰寫 eBPF 程式語言之一。bpftrace 因為語法相對簡單,相較於前者,其開發速度較快,適合用於快速分析與除錯。本篇也將以 bpftrace 作為 eBPF 操作的範例。
詳細的環境需求與安裝步驟可以參考 官方 repo。在這邊筆者將使用 Ubuntu 24.04 作為實驗環境進行安裝。
sudo apt-get install -y bpftrace
安裝完成後,我們就可以開始使用一些 bpftrace 的簡單語法。
sudo bpftrace -e 'BEGIN { printf("Hello, bpftrace!\n"); }'
運行後,會顯示一個 probe 被動態載入,並且印出 Hello, bpftrace 字樣
Attaching 1 probe...
Hello, bpftrace!
bpftrace 提供了豐富的語法和內建函數。如果想深入了解或查詢特定語法,可以參考以下資源:
或者 bpftrace 本身也提供說明指令:
# 查看所有可用的內建函數和變數
man bpftrace
# 列出所有 probe 類型
sudo bpftrace -l
# 查看特定 tracepoint 的參數
sudo bpftrace -lv tracepoint:syscalls:sys_enter_openat
eBPF 提供四種追蹤機制,讓我們可以根據不同情境使用不同的追蹤方法:
kernel 開發者在程式碼中預先埋入的追蹤點。這些追蹤點允許 eBPF 程式掛載到特定的 kernel event,藉此補貨相關的資料進行分析和監控。
我們也可以使用 bpftrace 找到埋藏在 kernel 中的 tracepoint:
# 列出所有 tracepoints
sudo bpftrace -l 'tracepoint:*' | head
# 查看 syscalls 相關的
sudo bpftrace -l 'tracepoint:syscalls:*' | head
類似 kernel tracepoint,只不過是在應用程式程式碼中預先埋入追蹤點,例如 Python 就有自己內建的 USDT probes,可以追蹤函式呼叫、GC 事件等。
即便是沒有埋入 tracepoint 的內部函式,也可以將 eBPF 動態掛載在幾乎任何 kernel 函式上。至於有哪些 kernel 函式是可以被動態掛載,可以使用以下指令進行確認:
sudo bpftrace -l 'kprobe:*tcp*' | head
接著則會印出所有可用 kprobes 追蹤的函式
kprobe:__arm64_sys_getcpu
kprobe:__bpf_tcp_ca_init
kprobe:__bpf_tcp_ca_release
kprobe:__mptcp_check_push
kprobe:__mptcp_clean_una
kprobe:__mptcp_close
kprobe:__mptcp_close_ssk
kprobe:__mptcp_data_acked
kprobe:__mptcp_destroy_sock
kprobe:__mptcp_error_report
例如,我們可以使用 kprobe 掛載在 kernel 內部的 tcp_connect 函數上,可以看到比 syscall 層更深入的網路行為。
sudo bpftrace -e '
kprobe:tcp_connect {
printf("%s (PID %d) initiated TCP connection\n", comm, pid);
}'
在另個終端機使用 telnet 發起 TCP 連線後,eBPF 便可捕捉到相關資訊
Attaching 1 probe...
telnet (PID 2976) initiated TCP connection
Uprobes 與 Kprobes 類似,差別在與後者會掛載在運行在 kernel space 的函式上,而 Uprobes 則是掛載在 user space 的函式。這樣,我們就可以使用 Uprobes 追蹤應用程式或者 library。
常見的應用場景包含資料庫的查詢追蹤(直接 hook database 的查詢query)、追蹤 OpenSSL 的加密函式來做 SSL/TLS 監控、以及追蹤 malloc/free 函式來做記憶體分析等。
例如,我們可以使用 uprobe 追蹤 bash 的 readline 函式,就可以看到使用者輸入的完整指令
sudo bpftrace -e '
uretprobe:/bin/bash:readline {
printf("Command: %s\n", str(retval));
}'
當我在另個終端機輸入指令時,uprobe 便能成功捕捉:
Attaching 1 probe...
Command: ls
Command: pwd
透過 bpftrace,我們可以用簡潔的語法快速驗證想法,不需要寫 C、也不需要編譯。再來,我們了解了 eBPF 的四種追蹤機制,了解其差異與使用時機。
明天,我們會探討如何將 bpftrace 收集到的資料轉換成 OTLP format,完成本系列文的最後一塊拼圖。
eBPF学习实践系列(六) -- bpftrace学习和使用