Linux 中的 DTrace :BPF 進入 4.9 內核
用於 Linux 的追蹤項目有很多,但是這個最終被合併進 Linux 內核的技術從一開始就根本不是一個追蹤項目:它是最開始是用於 伯克利包過濾器 (BPF)的增強功能。這些補丁允許 BPF 重定向數據包,從而創建軟體定義網路(SDN)。久而久之,對事件追蹤的支持就被添加進來了,使得程序追蹤可用於 Linux 系統。
儘管目前 BPF 沒有像 DTrace 一樣的高級語言,但它所提供的前端已經足夠讓我創建很多 BPF 工具了,其中有些是基於我以前的 DTraceToolkit。這個帖子將告訴你怎麼去用這些 BPF 提供的前端工具,以及暢談這項技術將會何去何從。
示例
我已經將基於 BPF 的追蹤工具添加到了開源的 bcc 項目里(感謝 PLUMgrid 公司的 Brenden Blanco 帶領 bcc 項目的發展)。詳見 bcc 安裝 手冊。它會在 /usr/share/bcc/tools
目錄下添加一系列工具,包括接下來的那些工具。
捕獲新進程:
# execsnoop
PCOMM PID RET ARGS
bash 15887 0 /usr/bin/man ls
preconv 15894 0 /usr/bin/preconv -e UTF-8
man 15896 0 /usr/bin/tbl
man 15897 0 /usr/bin/nroff -mandoc -rLL=169n -rLT=169n -Tutf8
man 15898 0 /usr/bin/pager -s
nroff 15900 0 /usr/bin/locale charmap
nroff 15901 0 /usr/bin/groff -mtty-char -Tutf8 -mandoc -rLL=169n -rLT=169n
groff 15902 0 /usr/bin/troff -mtty-char -mandoc -rLL=169n -rLT=169n -Tutf8
groff 15903 0 /usr/bin/grotty
硬碟 I/O 延遲的柱狀圖:
# biolatency -m
Tracing block device I/O... Hit Ctrl-C to end.
^C
msecs : count distribution
0 -> 1 : 96 |************************************ |
2 -> 3 : 25 |********* |
4 -> 7 : 29 |*********** |
8 -> 15 : 62 |*********************** |
16 -> 31 : 100 |**************************************|
32 -> 63 : 62 |*********************** |
64 -> 127 : 18 |****** |
追蹤慢於 5 毫秒的 ext4 常見操作:
# ext4slower 5
Tracing ext4 operations slower than 5 ms
TIME COMM PID T BYTES OFF_KB LAT(ms) FILENAME
21:49:45 supervise 3570 W 18 0 5.48 status.new
21:49:48 supervise 12770 R 128 0 7.55 run
21:49:48 run 12770 R 497 0 16.46 nsswitch.conf
21:49:48 run 12770 R 1680 0 17.42 netflix_environment.sh
21:49:48 run 12770 R 1079 0 9.53 service_functions.sh
21:49:48 run 12772 R 128 0 17.74 svstat
21:49:48 svstat 12772 R 18 0 8.67 status
21:49:48 run 12774 R 128 0 15.76 stat
21:49:48 run 12777 R 128 0 7.89 grep
21:49:48 run 12776 R 128 0 8.25 ps
21:49:48 run 12780 R 128 0 11.07 xargs
21:49:48 ps 12776 R 832 0 12.02 libprocps.so.4.0.0
21:49:48 run 12779 R 128 0 13.21 cut
[...]
追蹤新建的 TCP 活躍連接(connect()
):
# tcpconnect
PID COMM IP SADDR DADDR DPORT
1479 telnet 4 127.0.0.1 127.0.0.1 23
1469 curl 4 10.201.219.236 54.245.105.25 80
1469 curl 4 10.201.219.236 54.67.101.145 80
1991 telnet 6 ::1 ::1 23
2015 ssh 6 fe80::2000:bff:fe82:3ac fe80::2000:bff:fe82:3ac 22
通過跟蹤 getaddrinfo()
/gethostbyname()
庫的調用來追蹤 DNS 延遲:
# gethostlatency
TIME PID COMM LATms HOST
06:10:24 28011 wget 90.00 www.iovisor.org
06:10:28 28127 wget 0.00 www.iovisor.org
06:10:41 28404 wget 9.00 www.netflix.com
06:10:48 28544 curl 35.00 www.netflix.com.au
06:11:10 29054 curl 31.00 www.plumgrid.com
06:11:16 29195 curl 3.00 www.facebook.com
06:11:25 29404 curl 72.00 foo
06:11:28 29475 curl 1.00 foo
按類別劃分 VFS 操作的時間間隔統計:
# vfsstat
TIME READ/s WRITE/s CREATE/s OPEN/s FSYNC/s
18:35:32: 231 12 4 98 0
18:35:33: 274 13 4 106 0
18:35:34: 586 86 4 251 0
18:35:35: 241 15 4 99 0
對一個給定的 PID,通過內核和用戶堆棧軌跡來追蹤 CPU 處理之外的時間(由內核進行統計):
# offcputime -d -p 24347
Tracing off-CPU time (us) of PID 24347 by user + kernel stack... Hit Ctrl-C to end.
^C
[...]
ffffffff810a9581 finish_task_switch
ffffffff8185d385 schedule
ffffffff81085672 do_wait
ffffffff8108687b sys_wait4
ffffffff81861bf6 entry_SYSCALL_64_fastpath
--
00007f6733a6b64a waitpid
- bash (24347)
4952
ffffffff810a9581 finish_task_switch
ffffffff8185d385 schedule
ffffffff81860c48 schedule_timeout
ffffffff810c5672 wait_woken
ffffffff8150715a n_tty_read
ffffffff815010f2 tty_read
ffffffff8122cd67 __vfs_read
ffffffff8122df65 vfs_read
ffffffff8122f465 sys_read
ffffffff81861bf6 entry_SYSCALL_64_fastpath
--
00007f6733a969b0 read
- bash (24347)
1450908
追蹤 MySQL 查詢延遲(通過 USDT 探針):
# mysqld_qslower `pgrep -n mysqld`
Tracing MySQL server queries for PID 14371 slower than 1 ms...
TIME(s) PID MS QUERY
0.000000 18608 130.751 SELECT * FROM words WHERE word REGEXP '^bre.*n$'
2.921535 18608 130.590 SELECT * FROM words WHERE word REGEXP '^alex.*$'
4.603549 18608 24.164 SELECT COUNT(*) FROM words
9.733847 18608 130.936 SELECT count(*) AS count FROM words WHERE word REGEXP '^bre.*n$'
17.864776 18608 130.298 SELECT * FROM words WHERE word REGEXP '^bre.*n$' ORDER BY word
監測 pam 庫並使用多種追蹤工具觀察登錄請求:
# trace 'pam:pam_start "%s: %s", arg1, arg2'
TIME PID COMM FUNC -
17:49:45 5558 sshd pam_start sshd: root
17:49:47 5662 sudo pam_start sudo: root
17:49:49 5727 login pam_start login: bgregg
bcc 項目里的很多工具都有幫助信息(-h
選項),並且都應該包含有示例的 man 頁面和文本文件。
必要性
2014 年,Linux 追蹤程序就有一些內核相關的特性(來自 ftrace
和 pref_events
),但是我們仍然要轉儲並報告進程數據,這種幾十年前的老技術有很多的限制。你不能頻繁地訪問進程名、函數名、堆棧軌跡或內核中的任意的其它數據。你不能在將變數保存到一個監測事件里,又在另一個事件里訪問它們,這意味著你不能在你需要的地方計算延遲(或者說時間增量)。你也不能創建一個內核內部的延遲柱狀圖,也不能追蹤 USDT 探針,甚至不能寫個自定義的程序。DTrace 可以做到所有這些,但僅限於 Solaris 或 BSD 系統。在 Linux 系統中,有些不在主線內核的追蹤器,比如 SystemTap 就可以滿足你的這些需求,但它也有自身的不足。(理論上說,你可以寫一個基於探針的內核模塊來滿足需求-但實際上沒人這麼做。)
2014 年我加入了 Netflix cloud performance 團隊。做了這麼久的 DTrace 方面的專家,轉到 Linux 對我來說簡直不可思議。但我確實這麼做了,而且遇到了巨大的挑戰:在應用快速變化、採用微服務架構和分散式系統的情況下,調優 Netflix cloud。有時要用到系統追蹤,而我之前是用的 DTrace。在 Linux 系統上可沒有 DTrace,我就開始用 Linux 內核內建的 ftrace
和 perf_events
工具,構建了一個追蹤工具(perf-tools)。這些工具很有用,但有些工作還是沒法完成,尤其是延遲柱狀圖以及堆棧蹤跡計數。我們需要的是內核追蹤的可程序化。
發生了什麼?
BPF 將程序化的功能添加到現有的內核追蹤工具中(tracepoints
、kprobes
、uprobes
)。在 Linux 4.x 系列的內核里,這些功能大大加強了。
時間採樣是最主要的部分,它被 Linux 4.9-rc1 所採用(patchset)。十分感謝 Alexei Starovoitov(在 Facebook 致力於 BPF 的開發),他是這些 BPF 增強功能的主要開發者。
Linux 內核現在內建有以下這些特性(自 2.6 版本到 4.9 版本之間增加):
- 內核級的動態追蹤(BPF 對
kprobes
的支持) - 用戶級的動態追蹤(BPF 對
uprobes
的支持) - 內核級的靜態追蹤(BPF 對
tracepoints
的支持) - 時間採樣事件(BPF 的
pref_event_open
) - PMC 事件(BPF 的
pref_event_open
) - 過濾器(通過 BPF 程序)
- 調試輸出(
bpf_trace_printk()
) - 按事件輸出(
bpf_perf_event_output()
) - 基礎變數(全局的和每個線程的變數,基於 BPF 映射)
- 關聯數組(通過 BPF 映射)
- 頻率計數(基於 BPF 映射)
- 柱狀圖(2 的冥次方、線性及自定義,基於 BPF 映射)
- 時間戳和時間增量(
bpf_ktime_get_ns()
,和 BPF 程序) - 內核態的堆棧軌跡(BPF 棧映射)
- 用戶態的堆棧軌跡 (BPF 棧映射)
- 重寫 ring 緩存(
pref_event_attr.write_backward
)
我們採用的前端是 bcc,它同時提供 Python 和 lua 介面。bcc 添加了:
- 用戶級靜態追蹤(基於
uprobes
的 USDT 探針) - 調試輸出(Python 中調用
BPF.trace_pipe()
和BPF.trace_fields()
函數 ) - 按事件輸出(
BPF_PERF_OUTPUT
宏和BPF.open_perf_buffer()
) - 間隔輸出(
BPF.get_table()
和table.clear()
) - 列印柱狀圖(
table.print_log2_hist()
) - 內核級的 C 結構體導航(bcc 重寫器映射到
bpf_probe_read()
函數) - 內核級的符號解析(
ksym()
、ksymaddr()
) - 用戶級的符號解析(
usymaddr()
) - BPF 跟蹤點支持(通過
TRACEPOINT_PROBE
) - BPF 堆棧軌跡支持(包括針對堆棧框架的
walk
方法) - 其它各種輔助宏和方法
- 例子(位於
/examples
目錄) - 工具(位於
/tools
目錄) - 教程(
/docs/tutorial*.md
) - 參考手冊(
/docs/reference_guide.md
)
直到最新也是最主要的特性被整合進來,我才開始寫這篇文章,現在它在 4.9-rc1 內核中。我們還需要去完成一些次要的東西,還有另外一些事情要做,但是現在我們所擁有的已經值得歡呼了。現在 Linux 擁有了內建的高級追蹤能力。
安全性
設計 BPF 及其增強功能時就考慮到生產環境級安全,它被用在大範圍的生產環境里。不過你想的話,你還是可以找到一個掛起內核的方法。這種情況是偶然的,而不是必然,類似的漏洞會被快速修復,尤其是當 BPF 合併入了 Linux。因為 Linux 可是公眾的焦點。
在開發過程中我們碰到了一些非 BPF 的漏洞,它們需要被修復:rcu 不可重入,這可能導致內核由於 funccount 掛起,在 4.6 內核版本中這個漏洞被 「bpf: map pre-alloc」 補丁集所修復,舊版本內核的漏洞 bcc 有個臨時處理方案。還有一個是 uprobe 的內存計算問題,這導致 uprobe 分配內存失敗,在 4.8 內核版本這個漏洞由 「uprobes: Fix the memcg accounting」 補丁所修復,並且該補丁還將被移植到之前版本的內核中(例如,它現在被移植到了 4.4.27 和 4.4.0-45.66 版本中)。
為什麼 Linux 追蹤用了這麼久才加進來?
首要任務被分到了若干追蹤器中間:這些不是某個追蹤器單個的事情。想要了解更多關於這個或其它方面的問題,可以看一看我在 2014 年 tracing summit 上的講話。我忽視了部分方案的反面影響:有些公司發現其它追蹤器(SystemTap 和 LTTng)能滿足他們的需求,儘管他們樂於聽到 BPF 的開發進程,但考慮到他們現有的解決方案,幫助 BPF 的開發就不那麼重要了。
BPF 僅在近兩年里在追蹤領域得到加強。這一過程原本可以更快的,但早期缺少全職從事於 BPF 追蹤的工程師。Alexei Starovoitov (BPF 領導者),Brenden Blanco (bcc 領導者),我還有其它一些開發者,都有其它的事情要做。我在 Netflix 公司花了大量時間(志願地),大概有 7% 的時間是花在 BPF 和 bcc 上。某種程度上這不是我的首要任務,因為我還有自己的工作(包括我的 perf-tools,一個可以工作在舊版本內核上的程序)。
現在BPF 追蹤器已經推出了,已經有科技公司開始尋找會 BPF 的人了。但我還是推薦 Netflix 公司。(如果你為了 BPF 而要聘請我,那我還是十分樂於待在 Netflix 公司的!)
使用簡單
DTrace 和 bcc/BPF 現在的最大區別就是哪個更好使用。這取決於你要用 BPF 追蹤做什麼了。如果你要
- 使用 BPF 工具/度量:應該是沒什麼區別的。工具的表現都差不多,圖形用戶界面都能取得類似度量指標。大部分用戶通過這種方式使用 BPF。
- 開發工具/度量:bcc 的開發可難多了。DTrace 有一套自己的簡單語言,D 語音,和 awk 語言相似,而 bcc 使用已有的語言(C 語言,Python 和 lua)及其類庫。一個用 C 和 Python 寫的 bcc 工具與僅僅用 D 語言寫出來的工具相比,可能要多十多倍行數的代碼,或者更多。但是很多 DTrace 工具用 shell 封裝來提供參數和差錯檢查,會讓代碼變得十分臃腫。編程的難處是不同的:重寫 bcc 更需要巧妙性,這導致某些腳本更加難開發。(尤其是
bpf_probe_read()
這類的函數,需要了解更多 BPF 的內涵知識)。當計劃改進 bcc 時,這一情形將得到改善。 - 運行常見的命令:十分相近。通過
dtrace
命令,DTrace 能做很多事,但 bcc 有各種工具,trace
、argdist
、funccount
、funclatency
等等。 - 編寫自定義的特殊命令:使用 DTrace 的話,這就沒有必要了。允許定製消息快速傳遞和系統快速響應,DTrace 的高級分析很快。而 bcc 現在受限於它的多種工具以及它們的適用範圍。
簡單來說,如果你只使用 BPF 工具的話,就不必關注這些差異了。如果你經驗豐富,是個開發者(像我一樣),目前 bcc 的使用更難一些。
舉一個 bcc 的 Python 前端的例子,下面是追蹤硬碟 I/O 並列印出 I/O 大小的柱狀圖代碼:
from bcc import BPF
from time import sleep
# load BPF program
b = BPF(text="""
#include <uapi/linux/ptrace.h>
#include <linux/blkdev.h>
BPF_HISTOGRAM(dist);
int kprobe__blk_account_io_completion(struct pt_regs *ctx, struct request *req)
{
dist.increment(bpf_log2l(req->__data_len / 1024));
return 0;
}
""")
# header
print("Tracing... Hit Ctrl-C to end.")
# trace until Ctrl-C
try:
sleep(99999999)
except KeyboardInterrupt:
print
# output
b["dist"].print_log2_hist("kbytes")
注意 Python 代碼中嵌入的 C 語句(text=
)。
這就完成了任務,但仍有改進的空間。好在我們有時間去做:人們使用 Linux 4.9 並能用上 BPF 還得好幾個月呢,所以我們有時間來製造工具和前端。
高級語言
前端越簡單,比如高級語言,所改進的可能就越不如你所期望的。絕大多數人使用封裝好的工具(和圖形界面),僅有少部分人能寫出這些工具。但我不反對使用高級語言,比如 SystemTap,畢竟已經開發出來了。
#!/usr/bin/stap
/*
* opensnoop.stp Trace file open()s. Basic version of opensnoop.
*/
probe begin
{
printf("n%6s %6s %16s %sn", "UID", "PID", "COMM", "PATH");
}
probe syscall.open
{
printf("%6d %6d %16s %sn", uid(), pid(), execname(), filename);
}
如果擁有整合了語言和腳本的 SystemTap 前端與高性能的內置在內核中的 BPF 後端,會不會令人滿意呢?RedHat 公司的 Richard Henderson 已經在進行相關工作了,並且發布了 初代版本!
這是 ply,一個完全新穎的 BPF 高級語言:
#!/usr/bin/env ply
kprobe:SyS_*
{
$syscalls[func].count()
}
這也是一份承諾。
儘管如此,我認為工具開發者的實際難題不是使用什麼語言:而是要了解要用這些強大的工具做什麼?
如何幫助我們
- 推廣:BPF 追蹤器目前還沒有什麼市場方面的進展。儘管有公司了解並在使用它(Facebook、Netflix、Github 和其它公司),但要廣為人知尚需時日。你可以分享關於 BPF 的文章和資源給業內的其它公司來幫助我們。
- 教育:你可以撰寫文章,發表演講,甚至參與 bcc 文檔的編寫。分享 BPF 如何解決實際問題以及為公司帶來收益的實例。
- 解決 bcc 的問題:參考 bcc issue list,這包含了錯誤和需要的特性。
- 提交錯誤:使用 bcc/BPF,提交你發現的錯誤。
- 創造工具:有很多可視化的工具需要開發,但請不要太草率,因為大家會先花幾個小時學習使用你做的工具,所以請盡量把工具做的直觀好用(參考我的文檔)。就像 Mike Muuss 提及到他自己的 ping 程序:「要是我早知道這是我一生中最出名的成就,我就多開發一兩天,添加更多選項。」
- 高級語言:如果現有的 bcc 前端語言讓你很困擾,或許你能弄門更好的語言。要是你想將這門語言內建到 bcc 裡面,你需要使用 libbcc。或者你可以幫助 SystemTap BPF 或 ply 的工作。
- 整合圖形界面:除了 bcc 可以使用的 CLI 命令行工具,怎麼讓這些信息可視呢?延遲熱點圖,火焰圖等等。
其它追蹤器
那麼 SystemTap、ktap、sysdig、LTTng 等追蹤器怎麼樣呢?它們有個共同點,要麼使用了 BPF,要麼在自己的領域做得更好。會有單獨的文章介紹它們自己。
至於 DTrace ?我們公司目前還在基於 FreeBSD 系統的 CDN 中使用它。
更多 bcc/BPF 的信息
我已經寫了一篇《bcc/BPF 工具最終用戶教程》,一篇《bcc Python 開發者教程》,一篇《bcc/BPF 參考手冊》,並提供了一些有用的工具,每一個工具都有一個 example.txt 文件和 man page。我之前寫過的關於 bcc 和 BPF 的文章有:
- eBPF: One Small Step (後來就叫做 BPF)
- bcc: Taming Linux 4.3+ Tracing Superpowers
- Linux eBPF Stack Trace Hack (現在官方支持追蹤堆棧了)
- Linux eBPF Off-CPU Flame Graph
- Linux Wakeup and Off-Wake Profiling
- Linux Chain Graph Prototype
- Linux eBPF/bcc uprobes
- Linux BPF Superpowers
- Ubuntu Xenial bcc/BPF
- Linux bcc Tracing Security Capabilities
- Linux MySQL Slow Query Tracing with bcc/BPF
- Linux bcc ext4 Latency Tracing
- Linux bcc/BPF Run Queue (Scheduler) Latency
- Linux bcc/BPF Node.js USDT Tracing
- Linux bcc tcptop
- Linux 4.9's Efficient BPF-based Profiler
我在 Facebook 的 Performance@Scale Linux BPF Superpowers 大會上發表過一次演講。十二月份,我將在 Boston 發表關於 BPF/bcc 在 USENIX LISA 方面的演講和教程。
致謝
- Van Jacobson 和 Steve McCanne,他們創建了最初用作過濾器的 BPF 。
- Barton P. Miller,Jeffrey K. Hollingsworth,and Jon Cargille,發明了動態追蹤,並發表論文《Dynamic Program Instrumentation for Scalable Performance Tools》,可擴展高性能計算協議 (SHPCC),于田納西州諾克斯維爾市,1994 年 5 月發表。
- kerninst (ParaDyn, UW-Madison),展示了動態跟蹤的價值的早期動態跟蹤工具(上世紀 90 年代後期)
- Mathieu Desnoyers (在 LTTng),內核的主要開發者,主導 tracepoints 項目。
- IBM 開發的作為 DProbes 一部分的 kprobes,DProbes 在 2000 年時曾與 LTT 一起提供 Linux 動態追蹤,但沒有整合到一起。
- Bryan Cantrill, Mike Shapiro, and Adam Leventhal (Sun Microsystems),DTrace 的核心開發者,DTrace 是一款很棒的動態追蹤工具,安全而且簡單(2004 年)。對於動態追蹤技術,DTrace 是科技的重要轉折點:它很安全,默認安裝在 Solaris 以及其它以可靠性著稱的系統里。
- 來自 Sun Microsystems 的各部門的許多員工,促進了 DTrace,為我們帶來了高級系統追蹤的意識。
- Roland McGrath (在 Red Hat),utrace 項目的主要開發者,utrace 變成了後來的 uprobes。
- Alexei Starovoitov (PLUMgrid, 後來是 Facebook),加強版 BPF(可編程內核部件)的主要開發者。
- 那些幫助反饋、提交代碼、測試以及針對增強版 BPF 補丁(請在 lkml 搜索 BPF)的 Linux 內核工程師: Wang Nan、 Daniel Borkmann、 David S. Miller、 Peter Zijlstra 以及其它很多人。
- Brenden Blanco (PLUMgrid),bcc 的主要開發者。
- Sasha Goldshtein (Sela) 開發了 bcc 中的跟蹤點支持,和功能最強大的 bcc 工具 trace 及 argdist,幫助 USDT 項目的開發。
- Vicent Martí 和其它 Github 上的工程師,為 bcc 編寫了基於 lua 的前端,幫助 USDT 部分項目的開發。
- Allan McAleavy、 Mark Drayton,和其他的改進 bcc 的貢獻者。
感覺 Netflix 提供的環境和支持,讓我能夠編寫 BPF 和 bcc 跟蹤器並完成它們。我已經編寫了多年的追蹤工具(使用 TNF/prex、DTrace、SystemTap、ktap、ftrace、perf,現在是 bcc/BPF),並寫書、博客以及評論,
最後,感謝 Deirdré 編輯了另外一篇文章。
總結
Linux 沒有 DTrace(語言),但它現在有了,或者說擁有了 DTraceTookit(工具)。
通過增強內置的 BPF 引擎,Linux 4.9 內核擁有了用來支持現代化追蹤的最後一項能力。內核支持這一最難的部分已經做完了。今後的任務包括更多的命令行執行工具,以及高級語言和圖形用戶界面。
對於性能分析產品的客戶,這也是一件好事:你能查看延遲柱狀圖和熱點圖,CPU 處理和 CPU 之外的火焰圖,擁有更好的時延斷點和更低耗的工具。在用戶空間按包跟蹤和處理是沒有效率的方式。
那麼你什麼時候會升級到 Linux 4.9 呢?一旦官方發布,新的性能測試工具就來了:apt-get install bcc-tools
。
開始享受它吧!
Brendan
via: http://www.brendangregg.com/blog/2016-10-27/dtrace-for-linux-2016.html
作者:Brendan Gregg 譯者:GitFuture 校對:wxy
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive