基於 eBPF 的新型追蹤工具:bpftrace
導讀 | bpftrace 是一個 基於 eBPF 的新型追蹤工具,在 Fedora 28 第一次引入。Brendan Gregg、Alastair Robertson 和 Matheus Marchini 在網上的一個鬆散的駭客團隊的幫助下開發了 bpftrace。它是一個允許你分析系統在幕後正在執行的操作的追蹤工具,可以告訴你程式碼中正在被呼叫的函式、傳遞給函式的引數、函式的呼叫次數等。 |
這篇文章的內容涉及了 bpftrace 的一些基礎,以及它是如何工作的,請繼續閱讀獲取更多的資訊和一些有用的例項。
eBPF 是一個微型虛擬機器,更確切的說是一個位於 核心中的虛擬 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 world\n"); }'
注意,出於特權級的需要,你必須使用 root 執行 bpftrace,使用 -e 選項指明一個程式,構建一個所謂的“單行程式”。這個例子只會列印 “hello world”,接著等待你按下 Ctrl+C。
BEGIN 是一個特殊的探針名,只在執行一開始生效一次;每次探針命中時,大括號 {} 內的操作(這個例子中只是一個 printf)
都會執行。
現在讓我們轉向一個更有用的例子:
$ sudo bpftrace -e 't:syscalls:sys_enter_execve { printf("%s called %s\n", 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 的一個核心概念是探針點,即 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
你也可以在建立新的工具時參考這些指令碼。
原文來自:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69955379/viewspace-2787006/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 基於 OpenCv 和 Python 的手指識別及追蹤OpenCVPython
- 基於知識圖譜的APT組織追蹤治理APT
- Tockler for Mac時間追蹤工具Mac
- PHP 效能追蹤及分析工具(XHPROF)PHP
- 如何做路由追蹤?【免工具】路由
- 基於Python-sqlparse的SQL表血緣追蹤解析實現PythonSQL
- Linux基礎命令---tracepath追蹤路由Linux路由
- Linux基礎命令---traceroute追蹤路由Linux路由
- 【eBPF-01】初見:基於 BCC 框架的第一個 eBPF 程式eBPF框架
- 基於上下文感知計算的APT攻擊組織追蹤方法APT
- 基於 eBPF 的 Kubernetes 可觀測實踐eBPF
- 一文搞懂基於zipkin的分散式追蹤系統原理與實現分散式
- 基於SLF4J MDC機制實現日誌的鏈路追蹤
- go的鏈路追蹤Go
- DHorse的鏈路追蹤
- 日誌追蹤
- 程式碼追蹤
- 鏈路追蹤
- Map、Debug追蹤
- Debug追蹤eclipseEclipse
- 微服務追蹤SQL(支援Isto管控下的gorm查詢追蹤)微服務SQLGoORM
- OpenTelemetry分散式追蹤分散式
- skywalking鏈路追蹤
- 【eBPF-02】入門:基於 BCC 框架的程式進階eBPF框架
- 追蹤原始碼的方式歸納原始碼
- 追蹤解析 Disruptor 原始碼原始碼
- 追蹤解析 ThreadPoolExecutor 原始碼thread原始碼
- Spring Cloud 鏈路追蹤SpringCloud
- 如何追蹤laravel動態Laravel
- 如何追蹤Go動態Go
- 如何追蹤Java動態Java
- 如何追蹤Python動態Python
- 如何追蹤vue動態Vue
- 微服務追蹤SQL上報至Jaeger(支援Istio管控下的gorm查詢追蹤)微服務SQLGoORM
- 萬字詳解!搜狐智慧媒體基於 Zipkin 和 StarRocks 的微服務鏈路追蹤實踐微服務
- 深度解密|基於 eBPF 的 Kubernetes 問題排查全景圖釋出解密eBPF
- eBPF Cilium實戰(1) - 基於團隊的網路隔離eBPF
- 分散式鏈路追蹤的利器——Zipkin分散式