使用伯克利包過濾器 Berkeley Packet Filter(BPF)編譯器集合Compiler Collection(BCC)工具深度探查你的 Linux 程式碼。
在 Linux 中出現的一種新技術能夠為系統管理員和開發者提供大量用於效能分析和故障排除的新工具和儀表盤。它被稱為增強的伯克利資料包過濾器 enhanced Berkeley Packet Filter(eBPF,或 BPF),雖然這些改進並不是由伯克利開發的,而且它們不僅僅是處理資料包,更多的是過濾。我將討論在 Fedora 和 Red Hat Linux 發行版中使用 BPF 的一種方法,並在 Fedora 26 上演示。
BPF 可以在核心中執行由使用者定義的沙盒程式,可以立即新增新的自定義功能。這就像按需給 Linux 系統新增超能力一般。 你可以使用它的例子包括如下:
- 高階效能跟蹤工具:對檔案系統操作、TCP 事件、使用者級事件等的可程式設計的低開銷檢測。
- 網路效能: 儘早丟棄資料包以提高對 DDoS 的恢復能力,或者在核心中重定向資料包以提高效能。
- 安全監控: 7×24 小時的自定義檢測和記錄核心空間與使用者空間內的可疑事件。
在可能的情況下,BPF 程式必須通過一個核心驗證機制來保證它們的安全執行,這比寫自定義的核心模組更安全。我在此假設大多數人並不編寫自己的 BPF 程式,而是使用別人寫好的。在 GitHub 上的 BPF Compiler Collection (bcc) 專案中,我已釋出許多開原始碼。bcc 為 BPF 開發提供了不同的前端支援,包括 Python 和 Lua,並且是目前最活躍的 BPF 工具專案。
7 個有用的 bcc/BPF 新工具
為了瞭解 bcc/BPF 工具和它們的檢測內容,我建立了下面的圖表並新增到 bcc 專案中。
這些是命令列介面工具,你可以通過 SSH 使用它們。目前大多數分析,包括我的老闆,都是用 GUI 和儀表盤進行的。SSH 是最後的手段。但這些命令列工具仍然是預覽 BPF 能力的好方法,即使你最終打算通過一個可用的 GUI 使用它。我已著手向一個開源 GUI 新增 BPF 功能,但那是另一篇文章的主題。現在我想向你分享今天就可以使用的 CLI 工具。
1、 execsnoop
從哪兒開始呢?如何檢視新的程式。那些會消耗系統資源,但很短暫的程式,它們甚至不會出現在 top(1)
命令或其它工具中的顯示之中。這些新程式可以使用 execsnoop 進行檢測(或使用行業術語說,可以被追蹤traced)。 在追蹤時,我將在另一個視窗中通過 SSH 登入:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# /usr/share/bcc/tools/execsnoop PCOMM PID PPID RET ARGS sshd 12234 727 0 /usr/sbin/sshd -D -R unix_chkpwd 12236 12234 0 /usr/sbin/unix_chkpwd root nonull unix_chkpwd 12237 12234 0 /usr/sbin/unix_chkpwd root chkexpiry bash 12239 12238 0 /bin/bash id 12241 12240 0 /usr/bin/id -un hostname 12243 12242 0 /usr/bin/hostname pkg-config 12245 12244 0 /usr/bin/pkg-config --variable=completionsdir bash-completion grepconf.sh 12246 12239 0 /usr/libexec/grepconf.sh -c grep 12247 12246 0 /usr/bin/grep -qsi ^COLOR.*none /etc/GREP_COLORS tty 12249 12248 0 /usr/bin/tty -s tput 12250 12248 0 /usr/bin/tput colors dircolors 12252 12251 0 /usr/bin/dircolors --sh /etc/DIR_COLORS grep 12253 12239 0 /usr/bin/grep -qi ^COLOR.*none /etc/DIR_COLORS grepconf.sh 12254 12239 0 /usr/libexec/grepconf.sh -c grep 12255 12254 0 /usr/bin/grep -qsi ^COLOR.*none /etc/GREP_COLORS grepconf.sh 12256 12239 0 /usr/libexec/grepconf.sh -c grep 12257 12256 0 /usr/bin/grep -qsi ^COLOR.*none /etc/GREP_COLORS |
哇哦。 那是什麼? 什麼是 grepconf.sh
? 什麼是 /etc/GREP_COLORS
? 是 grep
在讀取它自己的配置檔案……由 grep
執行的? 這究竟是怎麼工作的?
歡迎來到有趣的系統追蹤世界。 你可以學到很多關於系統是如何工作的(或者根本不工作,在有些情況下),並且發現一些簡單的優化方法。 execsnoop
通過跟蹤 exec()
系統呼叫來工作,exec()
通常用於在新程式中載入不同的程式程式碼。
2、 opensnoop
接著上面繼續,所以,grepconf.sh
可能是一個 shell 指令碼,對吧? 我將執行 file(1)
來檢查它,並使用opensnoop bcc 工具來檢視開啟的檔案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# /usr/share/bcc/tools/opensnoop PID COMM FD ERR PATH 12420 file 3 0 /etc/ld.so.cache 12420 file 3 0 /lib64/libmagic.so.1 12420 file 3 0 /lib64/libz.so.1 12420 file 3 0 /lib64/libc.so.6 12420 file 3 0 /usr/lib/locale/locale-archive 12420 file -1 2 /etc/magic.mgc 12420 file 3 0 /etc/magic 12420 file 3 0 /usr/share/misc/magic.mgc 12420 file 3 0 /usr/lib64/gconv/gconv-modules.cache 12420 file 3 0 /usr/libexec/grepconf.sh 1 systemd 16 0 /proc/565/cgroup 1 systemd 16 0 /proc/536/cgroup |
像 execsnoop
和 opensnoop
這樣的工具會將每個事件列印一行。上圖顯示 file(1)
命令當前開啟(或嘗試開啟)的檔案:返回的檔案描述符(“FD” 列)對於 /etc/magic.mgc
是 -1,而 “ERR” 列指示它是“檔案未找到”。我不知道該檔案,也不知道 file(1)
正在讀取的 /usr/share/misc/magic.mgc
檔案是什麼。我不應該感到驚訝,但是 file(1)
在識別檔案型別時沒有問題:
1 2 3 4 |
# file /usr/share/misc/magic.mgc /etc/magic /usr/share/misc/magic.mgc: magic binary file for file(1) cmd (version 14) (little endian) /etc/magic: magic text file for file(1) cmd, ASCII text |
opensnoop
通過跟蹤 open()
系統呼叫來工作。為什麼不使用 strace -feopen file
命令呢? 在這種情況下是可以的。然而,opensnoop
的一些優點在於它能在系統範圍內工作,並且跟蹤所有程式的 open()
系統呼叫。注意上例的輸出中包括了從 systemd 開啟的檔案。opensnoop
應該系統開銷更低:BPF 跟蹤已經被優化過,而當前版本的 strace(1)
仍然使用較老和較慢的 ptrace(2)
介面。
3、 xfsslower
bcc/BPF 不僅僅可以分析系統呼叫。xfsslower 工具可以跟蹤大於 1 毫秒(引數)延遲的常見 XFS 檔案系統操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# /usr/share/bcc/tools/xfsslower 1 Tracing XFS operations slower than 1 ms TIME COMM PID T BYTES OFF_KB LAT(ms) FILENAME 14:17:34 systemd-journa 530 S 0 0 1.69 system.journal 14:17:35 auditd 651 S 0 0 2.43 audit.log 14:17:42 cksum 4167 R 52976 0 1.04 at 14:17:45 cksum 4168 R 53264 0 1.62 [ 14:17:45 cksum 4168 R 65536 0 1.01 certutil 14:17:45 cksum 4168 R 65536 0 1.01 dir 14:17:45 cksum 4168 R 65536 0 1.17 dirmngr-client 14:17:46 cksum 4168 R 65536 0 1.06 grub2-file 14:17:46 cksum 4168 R 65536 128 1.01 grub2-fstest [...] |
在上圖輸出中,我捕獲到了多個延遲超過 1 毫秒 的 cksum(1)
讀取操作(欄位 “T” 等於 “R”)。這是在 xfsslower
工具執行的時候,通過在 XFS 中動態地檢測核心函式實現的,並當它結束的時候解除該檢測。這個 bcc 工具也有其它檔案系統的版本:ext4slower
、btrfsslower
、zfsslower
和 nfsslower
。
這是個有用的工具,也是 BPF 追蹤的重要例子。對檔案系統效能的傳統分析主要集中在塊 I/O 統計資訊 —— 通常你看到的是由 iostat(1)
工具輸出,並由許多效能監視 GUI 繪製的圖表。這些統計資料顯示的是磁碟如何執行,而不是真正的檔案系統如何執行。通常比起磁碟來說,你更關心的是檔案系統的效能,因為應用程式是在檔案系統中發起請求和等待。並且,檔案系統的效能可能與磁碟的效能大為不同!檔案系統可以完全從記憶體快取中讀取資料,也可以通過預讀演算法和回寫快取來填充快取。xfsslower
顯示了檔案系統的效能 —— 這是應用程式直接體驗到的效能。通常這對於排除整個儲存子系統的問題是有用的;如果確實沒有檔案系統延遲,那麼效能問題很可能是在別處。
4、 biolatency
雖然檔案系統效能對於理解應用程式效能非常重要,但研究磁碟效能也是有好處的。當各種快取技巧都無法挽救其延遲時,磁碟的低效能終會影響應用程式。 磁碟效能也是容量規劃研究的目標。
iostat(1)
工具顯示了平均磁碟 I/O 延遲,但平均值可能會引起誤解。 以直方圖的形式研究 I/O 延遲的分佈是有用的,這可以通過使用 [biolatency] 來實現18:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# /usr/share/bcc/tools/biolatency Tracing block device I/O... Hit Ctrl-C to end. ^C usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 1 | | 64 -> 127 : 63 |**** | 128 -> 255 : 121 |********* | 256 -> 511 : 483 |************************************ | 512 -> 1023 : 532 |****************************************| 1024 -> 2047 : 117 |******** | 2048 -> 4095 : 8 | | |
這是另一個有用的工具和例子;它使用一個名為 maps 的 BPF 特性,它可以用來實現高效的核心摘要統計。從核心層到使用者層的資料傳輸僅僅是“計數”列。 使用者級程式生成其餘的。
值得注意的是,這種工具大多支援 CLI 選項和引數,如其使用資訊所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# /usr/share/bcc/tools/biolatency -h usage: biolatency [-h] [-T] [-Q] [-m] [-D] [interval] [count] Summarize block device I/O latency as a histogram positional arguments: interval output interval, in seconds count number of outputs optional arguments: -h, --help show this help message and exit -T, --timestamp include timestamp on output -Q, --queued include OS queued time in I/O time -m, --milliseconds millisecond histogram -D, --disks print a histogram per disk device examples: ./biolatency # summarize block I/O latency as a histogram ./biolatency 1 10 # print 1 second summaries, 10 times ./biolatency -mT 1 # 1s summaries, milliseconds, and timestamps ./biolatency -Q # include OS queued time in I/O time ./biolatency -D # show each disk device separately |
它們的行為就像其它 Unix 工具一樣,以利於採用而設計。
5、 tcplife
另一個有用的工具是 tcplife ,該例顯示 TCP 會話的生命週期和吞吐量統計。
1 2 3 4 5 6 7 |
# /usr/share/bcc/tools/tcplife PID COMM LADDR LPORT RADDR RPORT TX_KB RX_KB MS 12759 sshd 192.168.56.101 22 192.168.56.1 60639 2 3 1863.82 12783 sshd 192.168.56.101 22 192.168.56.1 60640 3 3 9174.53 12844 wget 10.0.2.15 34250 54.204.39.132 443 11 1870 5712.26 12851 curl 10.0.2.15 34252 54.204.39.132 443 0 74 505.90 |
在你說 “我不是可以只通過 tcpdump(8)
就能輸出這個?” 之前請注意,執行 tcpdump(8)
或任何資料包嗅探器,在高資料包速率的系統上的開銷會很大,即使 tcpdump(8)
的使用者層和核心層機制已經過多年優化(要不可能更差)。tcplife
不會測試每個資料包;它只會有效地監視 TCP 會話狀態的變化,並由此得到該會話的持續時間。它還使用已經跟蹤了吞吐量的核心計數器,以及程式和命令資訊(“PID” 和 “COMM” 列),這些對於 tcpdump(8)
等線上嗅探工具是做不到的。
6、 gethostlatency
之前的每個例子都涉及到核心跟蹤,所以我至少需要一個使用者級跟蹤的例子。 這就是 gethostlatency,它檢測用於名稱解析的 gethostbyname(3)
和相關的庫呼叫:
1 2 3 4 5 6 7 8 9 10 11 |
# /usr/share/bcc/tools/gethostlatency TIME PID COMM LATms HOST 06:43:33 12903 curl 188.98 opensource.com 06:43:36 12905 curl 8.45 opensource.com 06:43:40 12907 curl 6.55 opensource.com 06:43:44 12911 curl 9.67 opensource.com 06:45:02 12948 curl 19.66 opensource.cats 06:45:06 12950 curl 18.37 opensource.cats 06:45:07 12952 curl 13.64 opensource.cats 06:45:19 13139 curl 13.10 opensource.cats |
是的,總是有 DNS 請求,所以有一個工具來監視系統範圍內的 DNS 請求會很方便(這隻有在應用程式使用標準系統庫時才有效)。看看我如何跟蹤多個對 “opensource.com” 的查詢? 第一個是 188.98 毫秒,然後更快,不到 10 毫秒,毫無疑問,這是快取的作用。它還追蹤多個對 “opensource.cats” 的查詢,一個不存在的可憐主機名,但我們仍然可以檢查第一個和後續查詢的延遲。(第二次查詢後是否有一些否定快取的影響?)
7、 trace
好的,再舉一個例子。 trace 工具由 Sasha Goldshtein 提供,並提供了一些基本的 printf(1)
功能和自定義探針。 例如:
1 2 3 4 |
# /usr/share/bcc/tools/trace 'pam:pam_start "%s: %s", arg1, arg2' PID TID COMM FUNC - 13266 13266 sshd pam_start sshd: root |
在這裡,我正在跟蹤 libpam
及其 pam_start(3)
函式,並將其兩個引數都列印為字串。 libpam
用於插入式身份驗證模組系統,該輸出顯示 sshd 為 “root” 使用者呼叫了 pam_start()
(我登入了)。 其使用資訊中有更多的例子(trace -h
),而且所有這些工具在 bcc 版本庫中都有手冊頁和示例檔案。 例如 trace_example.txt
和 trace.8
。
通過包安裝 bcc
安裝 bcc 最佳的方法是從 iovisor 倉儲庫中安裝,按照 bcc 的 INSTALL.md 進行即可。IO Visor 是包括了 bcc 的 Linux 基金會專案。4.x 系列 Linux 核心中增加了這些工具所使用的 BPF 增強功能,直到 4.9 新增了全部支援。這意味著擁有 4.8 核心的 Fedora 25 可以執行這些工具中的大部分。 使用 4.11 核心的 Fedora 26 可以全部執行它們(至少在目前是這樣)。
如果你使用的是 Fedora 25(或者 Fedora 26,而且這個帖子已經在很多個月前釋出了 —— 你好,來自遙遠的過去!),那麼這個通過包安裝的方式是可以工作的。 如果您使用的是 Fedora 26,那麼請跳至“通過原始碼安裝”部分,它避免了一個已修復的的已知錯誤。 這個錯誤修復目前還沒有進入 Fedora 26 軟體包的依賴關係。 我使用的系統是:
1 2 3 4 5 |
# uname -a Linux localhost.localdomain 4.11.8-300.fc26.x86_64 #1 SMP Thu Jun 29 20:09:48 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux # cat /etc/fedora-release Fedora release 26 (Twenty Six) |
以下是我所遵循的安裝步驟,但請參閱 INSTALL.md 獲取更新的版本:
1 2 3 4 5 6 7 |
# echo -e '[iovisor]\nbaseurl=https://repo.iovisor.org/yum/nightly/f25/$basearch\nenabled=1\ngpgcheck=0' | sudo tee /etc/yum.repos.d/iovisor.repo # dnf install bcc-tools [...] Total download size: 37 M Installed size: 143 M Is this ok [y/N]: y |
安裝完成後,您可以在 /usr/share
中看到新的工具:
1 2 3 4 5 |
# ls /usr/share/bcc/tools/ argdist dcsnoop killsnoop softirqs trace bashreadline dcstat llcstat solisten ttysnoop [...] |
試著執行其中一個:
1 2 3 4 5 6 7 8 9 |
# /usr/share/bcc/tools/opensnoop chdir(/lib/modules/4.11.8-300.fc26.x86_64/build): No such file or directory Traceback (most recent call last): File "/usr/share/bcc/tools/opensnoop", line 126, in b = BPF(text=bpf_text) File "/usr/lib/python3.6/site-packages/bcc/__init__.py", line 284, in __init__ raise Exception("Failed to compile BPF module %s" % src_file) Exception: Failed to compile BPF module |
執行失敗,提示 /lib/modules/4.11.8-300.fc26.x86_64/build
丟失。 如果你也遇到這個問題,那只是因為系統缺少核心標頭檔案。 如果你看看這個檔案指向什麼(這是一個符號連結),然後使用 dnf whatprovides
來搜尋它,它會告訴你接下來需要安裝的包。 對於這個系統,它是:
1 2 3 4 5 6 7 |
# dnf install kernel-devel-4.11.8-300.fc26.x86_64 [...] Total download size: 20 M Installed size: 63 M Is this ok [y/N]: y [...] |
現在:
1 2 3 4 5 6 7 8 |
# /usr/share/bcc/tools/opensnoop PID COMM FD ERR PATH 11792 ls 3 0 /etc/ld.so.cache 11792 ls 3 0 /lib64/libselinux.so.1 11792 ls 3 0 /lib64/libcap.so.2 11792 ls 3 0 /lib64/libc.so.6 [...] |
執行起來了。 這是捕獲自另一個視窗中的 ls 命令活動。 請參閱前面的部分以使用其它有用的命令。
通過原始碼安裝
如果您需要從原始碼安裝,您還可以在 INSTALL.md 中找到文件和更新說明。 我在 Fedora 26 上做了如下的事情:
1 2 3 4 5 6 7 8 9 |
sudo dnf install -y bison cmake ethtool flex git iperf libstdc++-static \ python-netaddr python-pip gcc gcc-c++ make zlib-devel \ elfutils-libelf-devel sudo dnf install -y luajit luajit-devel # for Lua support sudo dnf install -y \ http://pkgs.repoforge.org/netperf/netperf-2.6.0-1.el6.rf.x86_64.rpm sudo pip install pyroute2 sudo dnf install -y clang clang-devel llvm llvm-devel llvm-static ncurses-devel |
除 netperf
外一切妥當,其中有以下錯誤:
1 2 |
Curl error (28): Timeout was reached for http://pkgs.repoforge.org/netperf/netperf-2.6.0-1.el6.rf.x86_64.rpm [Connection timed out after 120002 milliseconds] |
不必理會,netperf
是可選的,它只是用於測試,而 bcc 沒有它也會編譯成功。
以下是餘下的 bcc 編譯和安裝步驟:
1 2 3 4 5 6 |
git clone https://github.com/iovisor/bcc.git mkdir bcc/build; cd bcc/build cmake .. -DCMAKE_INSTALL_PREFIX=/usr make sudo make install |
現在,命令應該可以工作了:
1 2 3 4 5 6 7 8 |
# /usr/share/bcc/tools/opensnoop PID COMM FD ERR PATH 4131 date 3 0 /etc/ld.so.cache 4131 date 3 0 /lib64/libc.so.6 4131 date 3 0 /usr/lib/locale/locale-archive 4131 date 3 0 /etc/localtime [...] |
寫在最後和其他的前端
這是一個可以在 Fedora 和 Red Hat 系列作業系統上使用的新 BPF 效能分析強大功能的快速瀏覽。我演示了 BPF 的流行前端 bcc ,幷包括了其在 Fedora 上的安裝說明。bcc 附帶了 60 多個用於效能分析的新工具,這將幫助您充分利用 Linux 系統。也許你會直接通過 SSH 使用這些工具,或者一旦 GUI 監控程式支援 BPF 的話,你也可以通過它們來使用相同的功能。
此外,bcc 並不是正在開發的唯一前端。ply 和 bpftrace,旨在為快速編寫自定義工具提供更高階的語言支援。此外,SystemTap 剛剛釋出版本 3.2,包括一個早期的實驗性 eBPF 後端。 如果這個繼續開發,它將為執行多年來開發的許多 SystemTap 指令碼和 tapset(庫)提供一個安全和高效的生產級引擎。(隨同 eBPF 使用 SystemTap 將是另一篇文章的主題。)
如果您需要開發自定義工具,那麼也可以使用 bcc 來實現,儘管語言比 SystemTap、ply 或 bpftrace 要冗長得多。我的 bcc 工具可以作為程式碼示例,另外我還貢獻了用 Python 開發 bcc 工具的教程。 我建議先學習 bcc 的 multi-tools,因為在需要編寫新工具之前,你可能會從裡面獲得很多經驗。 您可以從它們的 bcc 儲存庫funccount,funclatency,funcslower,stackcount,trace ,argdist 的示例檔案中研究 bcc。
感謝 Opensource.com 進行編輯。
關於作者
Brendan Gregg 是 Netflix 的一名高階效能架構師,在那裡他進行大規模的計算機效能設計、分析和調優。