動態追蹤是深入研究複雜線上軟體系統細節的一個重要工具。它使我們能夠在不需要對軟體本身進行任何修改的情況下,準確定位這些系統中各種問題的根本原因。在 OpenResty XRay 中,我們的高階工具包涵蓋了與 C 相容的 Y 語言(Ylang),它可以有效地使用標準 C 程式碼來追蹤目標 C 程式。一旦透過我們的 ylang 最佳化編譯器編譯了 Ylang 工具套件,就可以生成與 eBPF+1、Stap+ 2GDB 相容的 動態追蹤 分析器。然而,在處理現實世界中非常普遍的 C++ 應用時,包括廣泛使用的開源軟體如 HotSpot JVM 和 NodeJS,卻存在一定的空白。

為了填補這一空白,我們開發了一個自定義的 C++ 編譯器,名為 Y++,目的是生成 Ylang 程式碼或 GNU C 的超集。值得注意的是,Y++ 已經支援了幾乎整個 C++23 語言標準。本文將使用很簡單的示例演示 Y++ 的一些功能。

設定目標 C++ 程式

我們將使用一個簡單但完整的 C++ 程式作為我們的追蹤目標。該程式在其全域性變數 int_vec 中利用了 STL 容器 std::vector

/* target.cxx:用於追蹤的示例 C++ 程式 */

#include <vector>
#include <cstdio>

std::vector<int> int_vec;

int main(void) {
    int sum = 0;
    std::vector<int>::iterator it;

    for (int i = 1; i <= 3; i++) {
        int_vec.push_back(i * 2);
    }
    for (it = int_vec.begin(); it != int_vec.end(); ++it) {
        sum += *it;
    }
    return sum - 12;
}

該程式可以使用標準的 C++ 編譯器命令進行編譯:

g++ -g -O2 target.cxx -o target

如果需要的話,也可以包含 -fPIC -pie 這樣的選項。

執行目標程式確認其預期功能:

$ ./target; echo $?
0

接下來,我們將繼續編寫一個外部的 C++ 程式碼片段來動態追蹤這個程序。

編寫 C++(或 Y++)分析器

我們將利用 eBPF+ 或 Stap+ 工具鏈開發一個 動態追蹤 分析器。該分析器將監視全域性 C++ 變數 int_vec 並實時輸出它的所有元素。下面是用於達成這一目的的 analyzer.yxx 工具。(.yxx 副檔名錶示 Y++ 原始檔。)

/* analyzer.yxx:用於 C++ 的動態追蹤工具 */

#include <vector>
#include <cstdio>

_target std::vector<int> int_vec;

static void probe_handler(void) {
    for (auto it = int_vec.begin(); it != int_vec.end(); ++it) {
        printf("%d\n", *it);
    }
}

_probe main() -> int {
    probe_handler();
}

此程式碼片段利用了一些標準 C++ 語言的擴充套件功能。 _target 關鍵字指定了目標程式中的符號,在這裡將 int_vec 變數宣告為在目標應用中找到的變數。 _probe 關鍵字宣告瞭一個動態探針位置,類似於我們的 Y 語言 中的語法結構,其定義了在目標程式的 main 函式返回時的一個探針位置。請注意,在我們的追蹤器程式碼中包含了像 vectorcstdio 這樣的標準標頭檔案,使追蹤器能夠輸出目標程式中 int_vec 的值。

使用我們的 y++ 編譯器將 analyzer.yxx 檔案編譯成 Ylang 程式碼:

y++ -o analyzer.y analyzer.yxx

隨後,我們可以使用 Ylang 工具鏈將此分析器編譯成能在各種平臺上執行的可執行工具,如 eBPF+、Stap+ 或 GDB。這是一個使用 eBPF+ 的示例:

ylang --gen-bpf --symtab debuginfo.jl analyzer.y

debuginfo.jl 檔案是自動從 target 二進位制檔案中的 DWARF 除錯符號生成的,這個過程需要使用 -g 編譯選項。我們將在本文後面討論如何放寬這一要求。

ylang 命令為 eBPF+ 工具鏈生成兩個檔案:analyzer.bpf.c 用於核心空間,analyzer.ubpf.c 用於使用者態。

這些生成的 C 語言原始檔可以使用 clanggcc 工具鏈分別編譯成 eBPF 位元組碼和可執行檔案 analyzer,用於載入 eBPF 位元組碼(我們還增強了 clang 編譯器的 eBPF 後端)。

將目標和分析器投入執行

按照上述描述生成的 analyzer 可執行檔案,我們能夠啟動目標程式並載入 eBPF 程式進行分析,示例如下:

$ ./analyzer ~/work/target
Start tracing...
2
4
6

此輸出驗證了分析器動態追蹤並輸出目標程式中全域性變數 int_vec 的值的能力。對於正在進行的目標程序,分析器支援使用 PID 以進行有針對性的追蹤:

./analyzer -p 12345 -t 3

這個命令會追蹤指定的 PID 的程序,最多分析 3 秒。

雖然描述的過程可能看起來複雜,但我們的 OpenResty XRay 產品可以自動化整個工作流程。

提升對複雜 C++ 應用的支援

我們目前已經支援許多 C++ 功能,比如類繼承、虛擬函式表、使用 newdelete 運算子進行動態記憶體分配。

我們也正在努力擴充套件對更復雜的追蹤器 C++ 程式的支援範圍,比如 HotSpot JVM、NodeJS 或 MySQL 中的 C++ 程式碼片段。這將有助於我們能夠建立更多非侵入式的 動態追蹤 分析器,以應用於 JVM、Java 程式以及更廣泛的 C++ 應用。我們在這方面正在迅速取得進展,敬請期待我們的後續更新。

關於除錯符號

除錯符號對於追蹤(或除錯)二進位制可執行程式至關重要,這需要在使用 g++ 命令編譯時使用 -g 選項。這一要求與除錯構建不同,除錯構建引入的除錯程式碼可能會減慢應用的執行速度。重要的是,除錯符號不會產生執行時開銷,並且在程序執行期間不會載入到記憶體中。

在現實世界的場景中,除錯符號可能會丟失,或者目標程式可能沒有使用 -g 選項進行編譯。幸運的是,OpenResty XRay 私有的 AI 和機器學習演算法可以從被 strip 過的 ELF 二進位制檔案中重新生成除錯符號(透過我們內部的 symgen 工具),這適用於像 Nginx、LuaJIT 和 Python 等常見的開源軟體和許多其他軟體。我們還為客戶特定的程式提供定製模型訓練,有效地將 動態追蹤 的能力擴充套件到幾乎所有的程序。

歡迎觀看 我們的 bilibili 影片 來親眼見證這裡的魔法。

結論

我們自主研發的 C++ 編譯器 Y++,標誌著在 動態追蹤 方面的重大進展。透過將 動態追蹤 的能力擴充套件到 C++ 應用,我們不僅填補了一個重要的空白,還為更復雜的分析和可觀測技術鋪平了道路。本文提供的示例僅僅是展示了使用 Y++ 和我們的 動態追蹤(包括 eBPF+ 和 Stap+)所有可能性的冰山一角。

隨著我們不斷完善工具並擴充套件其功能,我們期待著一個美好未來:開發人員和系統管理員將能夠更加深入瞭解其應用,從而創造出更可靠和效能更優的軟體。無論您是在大規模分散式系統中解決複雜問題,還是試圖最佳化軟體效能,本文討論的工具和方法都為實現您的目標提供了一個堅實的框架,而無需進行侵入式的程式碼修改。

此外,我們正不斷努力支援更復雜的 C++ 應用,包括 HotSpot JVM 中的應用,這也彰顯了我們對創新和更廣泛的軟體開發社群的承諾。透過充分利用動態追蹤的力量,結合我們的私有的技術,如 OpenResty XRay,我們正處於制定解決當今開發人員面臨的真實挑戰的解決方案的前沿。

關於作者

章亦春(Github ID: agentzh)是開源 OpenResty® 專案創始人兼 OpenResty Inc. 公司 CEO 和創始人。

章亦春是中國早期開源技術和文化的倡導者和領軍人物,曾供職於多家國際知名的高科技企業,如 Cloudflare、雅虎、阿里巴巴,是 “邊緣計算”、“動態追蹤” 和 “機器程式設計” 的先驅,擁有超過 22 年的程式設計及 16 年的開源經驗。在開源領域中以 OpenResty® 專案負責人的身份而知名,該專案已被全球超過 4000 萬個網站領域採用。

章亦春於 2017 年創立的企業軟體初創公司 OpenResty Inc.,擁有來自全球一些最大公司的客戶。其主打產品,OpenResty XRay,是一款非侵入式的效能分析和故障排除工具,極大地增強並利用了 動態追蹤 技術。並且其 OpenResty Edge 產品是一款強大的分散式流量管理和私有 CDN 軟體產品。

作為一名熱衷於開源貢獻的人士,章亦春為多個開源專案貢獻了累計超過百萬行程式碼,其中包括,Linux 核心、Nginx、LuaJITGDBSystemTapLLVM、Perl 等,並編寫過 60 多個開源軟體庫。

關注我們

如果您喜歡本文,歡迎關注我們 OpenResty Inc. 公司的 部落格網站。也歡迎掃碼關注我們的微信公眾號:

我們的微信公眾號

翻譯

我們提供了 英文版 原文和中譯版(本文)。我們也歡迎讀者提供其他語言的翻譯版本,只要是全文翻譯不帶省略,我們都將會考慮採用,非常感謝!


  1. eBPF+ 是我們增強版的 eBPF,無需對現有 Linux 核心進行補丁即可執行。 ↩︎

  2. Stap+ 是我們的高階版本 SystemTap,提供了擴充套件功能。 ↩︎