XRay 檢測機制

版本:

1,截至 2016-11-08

簡介

XRay 是一個函式呼叫追蹤系統,它結合了編譯器插入的檢測點和一個執行時期程式庫,可以動態地啟用和停用檢測機制。

關於 XRay 的更多高階資訊,請參閱 XRay 白皮書

本文檔描述如何在 LLVM 中使用實作的 XRay。

LLVM 中的 XRay

XRay 由三個主要部分組成

  • 編譯器插入的檢測點。

  • 一個用於在執行時期啟用/停用追蹤的執行時期程式庫。

  • 一套用於分析追蹤紀錄的工具。

    注意: 截至 2018 年 7 月 25 日,XRay 僅適用於運行 Linux 的以下架構:x86_64、arm7 (無 thumb)、aarch64、powerpc64le、mips、mipsel、mips64、mips64el、NetBSD:x86_64、FreeBSD:x86_64 和 OpenBSD:x86_64。

編譯器插入的檢測點以 nop-sleds 的形式存在於最終生成的二進制檔案中,以及一個名為 xray_instr_map 的 ELF 區段,其中包含指向這些檢測點的條目。執行時期程式庫依賴於能夠存取 xray_instr_map 的條目,並在執行時期覆寫檢測點。

使用 XRay

您可以使用幾種方式來使用 XRay

  • 檢測您的 C/C++/Objective-C/Objective-C++ 應用程式。

  • 使用正確的函式屬性生成 LLVM IR。

本節的其餘部分涵蓋了這些主要方法,以及稍後如何自訂 XRay 在經 XRay 檢測的二進制檔案中的行為。

檢測您的 C/C++/Objective-C 應用程式

為您的應用程式獲取 XRay 檢測機制的最簡單方法是在您的 clang 調用中啟用 -fxray-instrument 旗標。

例如

clang -fxray-instrument ...

預設情況下,至少有 200 個指令(或包含迴圈)的函式將獲得 XRay 檢測點。您可以通過 -fxray-instruction-threshold= 旗標來調整該數字

clang -fxray-instrument -fxray-instruction-threshold=1 ...

可以使用 -fxray-ignore-loops 停用迴圈檢測,僅使用指令閾值。您還可以通過源代碼級別的屬性來專門檢測二進制檔案中的函式,以使其始終或永不被檢測。您可以使用 GCC 樣式的屬性或 C++11 樣式的屬性來執行此操作。

[[clang::xray_always_instrument]] void always_instrumented();

[[clang::xray_never_instrument]] void never_instrumented();

void alt_always_instrumented() __attribute__((xray_always_instrument));

void alt_never_instrumented() __attribute__((xray_never_instrument));

在連結二進制檔案時,您可以手動連結 XRay 執行時期程式庫,或者使用 clang-fxray-instrument 旗標自動連結它。或者,您可以從 compiler-rt 靜態連結 XRay 執行時期程式庫 – 這些封存檔將採用 libclang_rt.xray-{arch} 的名稱,其中 {arch} 是 clang 支援的助記符(x86_64、arm7 等)。

LLVM 函式屬性

如果您直接使用 LLVM IR,您可以將 function-instrument 字串屬性添加到您的函式中,以獲得與 C/C++/Objective-C 源代碼級別屬性類似的效果

define i32 @always_instrument() uwtable "function-instrument"="xray-always" {
  ; ...
}

define i32 @never_instrument() uwtable "function-instrument"="xray-never" {
  ; ...
}

您還可以設定 xray-instruction-threshold 屬性,並為在函式被檢測之前函式中應包含多少指令提供數字字串值。

define i32 @maybe_instrument() uwtable "xray-instruction-threshold"="2" {
  ; ...
}

特殊案例檔案

屬性可以通過使用特殊案例檔案來賦予,而不是將它們添加到原始源代碼檔案中。您可以使用它來標記某些函式和類別永遠不、總是或使用來自檔案的第一個參數日誌進行檢測。檔案的格式如下所述

# Comments are supported
[always]
fun:always_instrument
fun:log_arg1=arg1 # Log the first argument for the function

[never]
fun:never_instrument

這些檔案可以通過 -fxray-attr-list= 旗標提供給 clang。您可能通過旗標的多個實例載入多個檔案。

XRay 執行時期程式庫

XRay 執行時期程式庫是 compiler-rt 專案的一部分,它實作了執行修補和取消修補插入的檢測點的執行時期組件。當您使用 clang 連結您的二進制檔案和 -fxray-instrument 旗標時,它將自動連結 XRay 執行時期。

XRay 執行時期的預設實作將在 main 啟動之前啟用 XRay 檢測機制,這適用於生命週期短的應用程式。此實作還記錄所有函式進入和退出事件,這可能會在生成的追蹤紀錄中產生大量記錄。

同樣,預設情況下,XRay 追蹤紀錄的檔案名稱是 xray-log.XXXXXX,其中 XXXXXX 部分是隨機生成的。

這些選項可以通過程式運行時的 XRAY_OPTIONS 環境變數來控制,我們在下面列出了選項及其預設值。

選項

類型

預設值

描述

patch_premain

布林值

false

是否在 main 之前修補檢測點。

xray_mode

const char*

""

預設模式,用於在 main 之前安裝和初始化。

xray_logfile_base

const char*

xray-log.

XRay 日誌檔的檔案名稱基礎。

verbosity

整數

0

執行時期詳細程度級別。

除了環境變數,您還可以提供您自己的 const char *__xray_default_options(void) 函式定義,該函式返回選項字串。此方法有效地在程式構建時提供預設選項。例如,您可以創建一個額外的源代碼檔案(例如 xray-opt.c),其中包含以下 __xray_default_options 定義

__attribute__((xray_never_instrument))
const char *__xray_default_options() {
  return "patch_premain=true,xray_mode=xray-basic";
}

並將其與您要檢測的程式連結

clang -fxray-instrument prog.c xray-opt.c ...

即使不設定 XRAY_OPTIONS,經檢測的二進制檔案也將預設使用 ‘patch_premain=true,xray_mode=xray-basic’。

請注意,您仍然可以使用運行時的 XRAY_OPTIONS 覆寫由 __xray_default_options 指定的選項。

如果您選擇不使用 XRay 執行時期附帶的預設日誌記錄實作,和/或控制 XRay 檢測機制的運行時間/方式,您可以直接使用 XRay API 來執行此操作。為此,您需要從 compiler-rt xray 目錄中包含 xray_log_interface.h。我們在下面列出了重要的 API 函式

  • __xray_log_register_mode(...):針對字串模式識別碼註冊日誌記錄實作。該實作是 xray/xray_log_interface.h 中定義的 XRayLogImpl 的實例。

  • __xray_log_select_mode(...):選擇要安裝的模式,與字串模式識別碼相關聯。只有使用 __xray_log_register_mode(...) 註冊的實作才能使用此函式選擇。

  • __xray_log_init_mode(...):此函式允許初始化和重新初始化已安裝的日誌記錄實作。有關詳細資訊,請參閱 XRay compiler-rt 安裝的 xray/xray_log_interface.h

一旦初始化了日誌記錄實作,就可以通過 __xray_log_finalize() 函式最終確定實作來「停止」它。最終確定例程與初始化相反。最終確定後,可以通過 __xray_log_flushLog() 函式清除實作的資料。對於支援內存處理的實作,這些實作應註冊一個迭代器函式,以通過 __xray_log_set_buffer_iterator(...) 提供對資料的訪問,這允許呼叫 __xray_log_process_buffers(...) 函式的程式碼處理內存中的資料。

所有這些都在 xray/xray_log_interface.h 標頭中得到了更好的解釋。

基本模式

XRay 支援基本日誌記錄模式,該模式將追蹤應用程式的執行,並定期附加到單個日誌。可以通過在 XRAY_OPTIONS 環境變數中設定 xray_mode=xray-basic 來安裝/啟用此模式。與 patch_premain=true 結合使用,這可以允許從開始到結束追蹤應用程式。

與通過 __xray_log_select_mode(...) 安裝的所有其他模式一樣,可以通過 __xray_log_init_mode(...) 函式配置實作,提供模式字串和旗標選項。基本模式特定的預設值可以在 XRAY_BASIC_OPTIONS 環境變數中提供。

飛行資料記錄器模式

XRay 支援一種日誌記錄模式,該模式允許應用程式僅捕獲固定量的內存價值的事件。「飛行資料記錄器」(FDR) 模式的工作方式非常像飛機的「黑盒子」,它不斷將資料記錄到固定大小的循環緩衝區隊列中的內存中,並以程式方式提供資料,直到緩衝區最終確定並刷新。要在您的應用程式上使用 FDR 模式,您可以將 xray_mode 變數設定為 XRAY_OPTIONS 環境變數中的 xray-fdr。FDR 模式實作的其他選項可以在 XRAY_FDR_OPTIONS 環境變數中提供。程式化配置可以通過在選擇/安裝後呼叫 __xray_log_init_mode("xray-fdr", <configuration string>) 來完成。

當緩衝區刷新到磁碟時,結果是 XRay FDR 格式 描述的二進制追蹤紀錄格式

當 FDR 模式開啟時,它將持續寫入和回收內存緩衝區,直到日誌記錄實作最終確定為止 – 在此時可以刷新並稍後重新初始化。要以程式方式執行此操作,我們遵循下面提供的工作流程

// Patch the sleds, if we haven't yet.
auto patch_status = __xray_patch();

// Maybe handle the patch_status errors.

// When we want to flush the log, we need to finalize it first, to give
// threads a chance to return buffers to the queue.
auto finalize_status = __xray_log_finalize();
if (finalize_status != XRAY_LOG_FINALIZED) {
  // maybe retry, or bail out.
}

// At this point, we are sure that the log is finalized, so we may try
// flushing the log.
auto flush_status = __xray_log_flushLog();
if (flush_status != XRAY_LOG_FLUSHED) {
  // maybe retry, or bail out.
}

FDR 模式實作的預設設定將創建與基本日誌實作類似命名的日誌,但將具有不同的日誌格式。所有追蹤分析工具(和追蹤紀錄讀取程式庫)都將支援所有版本的 FDR 模式格式,因為我們將在未來添加更多功能和記錄類型。

注意: 我們不保證永久支援我們更新我們將來支援的日誌版本。格式的棄用將在開發人員郵件列表中宣布和討論。

追蹤分析工具

我們目前在 LLVM 中有一個追蹤分析工具的開端,可以在 tools/llvm-xray 目錄中找到。llvm-xray 工具目前支援以下子命令

  • extract:從二進制檔案中提取檢測機制映射,並將其作為 YAML 返回。

  • account:使用各種排序選項和輸出格式(支援 CSV、YAML 和控制台友好的 TEXT)執行基本函式呼叫會計統計。

  • convert:將 XRay 日誌檔案從一種格式轉換為另一種格式。我們可以將二進制 XRay 追蹤紀錄(基本模式和 FDR 模式)轉換為 YAML、火焰圖 友好的文本格式,以及 Chrome Trace Viewer (catapult) <https://github.com/catapult-project/catapult> 格式。

  • graph:生成 XRay 追蹤紀錄中找到的函式之間函式呼叫關係的 DOT 圖。

  • stack:從 XRay 追蹤紀錄中的函式呼叫時間軸重建函式呼叫堆疊。

這些子命令使用作為 XRay 程式庫一部分的各種程式庫組件,與 LLVM 發行版一起發布。這些是

  • llvm/XRay/Trace.h:一個追蹤紀錄讀取程式庫,用於方便地將支援形式的 XRay 追蹤紀錄載入到方便的內存表示中。所有處理追蹤紀錄的分析工具都使用此實作。

  • llvm/XRay/Graph.h:圖形子命令使用的半通用圖形類型,用於方便地表示具有與邊和頂點相關聯的統計資訊的函式呼叫圖。

  • llvm/XRay/InstrumentationMap.h:一個方便的工具,用於分析經 XRay 檢測的物件檔和二進制檔案中的檢測機制映射。extractstack 子命令使用此特定程式庫。

最小化二進制檔案大小

XRay 支援多種不同的檢測點,包括 function-entryfunction-exitcustomtyped 點。可以使用 -fxray-instrumentation-bundle= 旗標單獨啟用這些點。例如,如果您只想檢測函式進入和自訂點,您可以指定

clang -fxray-instrument -fxray-instrumentation-bundle=function-entry,custom ...

這將完全省略其他 sled 類型,從而減小二進制檔案大小。您還可以使用檢測機制組僅檢測函式的採樣子集。例如,要僅檢測四分之一的可用函式,請調用

clang -fxray-instrument -fxray-function-groups=4

將根據函式名稱的哈希值任意選擇一個子集。要採樣不同的子集,您可以指定 -fxray-selected-function-group=,其中組號在 0 到 xray-function-groups - 1 的範圍內。這些選項可以一起用於生成具有不同檢測子集的多個二進制檔案。如果您只需要在運行時控制在任何給定時間追蹤哪些函式,則最好使用 XRay 執行時期程式庫的 __xray_patch_function() 方法有選擇地修補和取消修補您需要的個別函式。

未來工作

目前正在進行許多工作,以擴展圍繞 XRay 檢測機制系統構建的工具集。

追蹤分析工具

  • 正在進行與工具集成或開發工具的工作,以可視化來自 XRay 追蹤紀錄的發現。特別是,stack 工具正在擴展到允許繪製圖表和探索每個呼叫堆疊中時間長度的輸出格式。

  • 對於大型檢測二進制檔案,生成的 XRay 追蹤紀錄的大小可能會很快變得難以處理。我們正在努力整合修剪技術和啟發式方法,供分析工具篩選追蹤紀錄並僅顯示相關資訊。

更多平台

我們期待收到將 XRay 移植到更多架構和作業系統的貢獻。