iT邦幫忙

2025 iThome 鐵人賽

DAY 28
0

昨天我們介紹了 eBPF 的基本概念,知道它可以透過各種 hook points 來追蹤系統行為。但面對 Tracepoints、Kprobes、Uprobes 這些不同的追蹤機制,該如何選擇?今天我們就來深入理解這些機制的差異與使用時機。

在介紹追蹤機制之前,我們會使用 bpftrace 作為實作範例,因此前面會先花些篇幅解釋 bpftrace 是怎樣的一個工具。

認識 bpftrace

在深入討論追蹤機制之前,我們先介紹 bpftrace,一個能讓我們快速實踐 eBPF 追蹤的工具。

什麼是 bpftrace?

bpftrace 是一個能讓我們快速實踐 eBPF 追蹤的工具,它是一個高階的 eBPF 追蹤語言,其特色包含了:

  • 語法簡潔:類似 linux 的 awk 語法,容易上手
  • 即時執行:腳本會自動編譯成 eBPF bytecode,不需要手動編譯
  • 支援所有追蹤機制:Tracepoints、Kprobes、Uprobes、USDT 都支援,這些追蹤機制會在後續章節中提及
  • 內建聚合函數:count()、hist()、avg() 等統計功能

如果不使用 bpftrace,又不想直接撰寫 bytecode的話,C 語言加上使用 libbpf library 是常見用來撰寫 eBPF 程式語言之一。bpftrace 因為語法相對簡單,相較於前者,其開發速度較快,適合用於快速分析與除錯。本篇也將以 bpftrace 作為 eBPF 操作的範例。

安裝 bpftrace

詳細的環境需求與安裝步驟可以參考 官方 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 提供了豐富的語法和內建函數。如果想深入了解或查詢特定語法,可以參考以下資源:

或者 bpftrace 本身也提供說明指令:

# 查看所有可用的內建函數和變數
man bpftrace

# 列出所有 probe 類型
sudo bpftrace -l

# 查看特定 tracepoint 的參數
sudo bpftrace -lv tracepoint:syscalls:sys_enter_openat

eBPF 的四種追蹤機制

eBPF 提供四種追蹤機制,讓我們可以根據不同情境使用不同的追蹤方法:
https://ithelp.ithome.com.tw/upload/images/20251012/20177961zS7YgTtImF.png

1. Kernel Tracepoint

kernel 開發者在程式碼中預先埋入的追蹤點。這些追蹤點允許 eBPF 程式掛載到特定的 kernel event,藉此補貨相關的資料進行分析和監控。

我們也可以使用 bpftrace 找到埋藏在 kernel 中的 tracepoint:

# 列出所有 tracepoints
sudo bpftrace -l 'tracepoint:*' | head

# 查看 syscalls 相關的
sudo bpftrace -l 'tracepoint:syscalls:*' | head

2. USDT (User Statically Defined Tracing)

類似 kernel tracepoint,只不過是在應用程式程式碼中預先埋入追蹤點,例如 Python 就有自己內建的 USDT probes,可以追蹤函式呼叫、GC 事件等。

3. Kprobes (Kernel Probes)

即便是沒有埋入 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

4, Uprobes (User Probes)

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,完成本系列文的最後一塊拼圖。

參考資料

bpftrace

eBPF学习实践系列(一) -- 初识eBPF

eBPF学习实践系列(六) -- bpftrace学习和使用


上一篇
Day 27 - eBPF:kernel 層級的可觀測性
下一篇
Day 29 - 將 eBPF 資料串接到 OpenTelemetry
系列文
被稱作Server Restart Engineer的我,也想了解如何實踐可觀測性工程30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言