XRay 飛航資料記錄器追蹤格式¶
- 版本:
截至 2017-07-20 為止的版本 1
簡介¶
在飛航資料記錄器模式下收集 XRay 追蹤時,應用程式的每個執行緒都會聲明緩衝區以填充追蹤資料,這些資料在某個時間點會被完成並清空。
分析器的目標是最小化開銷,清空的資料直接對應到緩衝區。
本文件描述追蹤檔案的格式。
通用¶
每個追蹤檔案都對應於特定執行緒中的一系列事件。
檔案包含標頭,後面跟著一系列區分記錄類型。
位元組欄位的位元組順序與產生追蹤檔案的平台的位元組順序相符。
標頭區段¶
追蹤檔案以 32 位元組的標頭開頭。
欄位 |
大小 (位元組) |
描述 |
---|---|---|
版本 |
|
預期讀取器版本。本文件描述版本 == 1 時的格式 |
類型 |
|
編碼追蹤類型的列舉。飛航資料記錄器模式追蹤的類型 == 1 |
位元欄位 |
|
保存未與位元組對齊的參數。下面會進一步說明。 |
cycle_frequency |
|
用於以刻度為單位測量事件持續時間的 CPU 振盪器的頻率(以赫茲為單位)。 |
buffer_size |
|
標頭後面的追蹤資料部分的大小(以位元組為單位)。 |
保留 |
|
保留供未來使用。 |
檔案標頭的位元欄位參數由以下欄位組成。
欄位 |
大小 (位元) |
描述 |
---|---|---|
constant_tsc |
|
平台的時間戳記計數器(用於記錄事件之間的刻度)是否以恆定頻率計數,即使 CPU 頻率發生變化。0 == 非恆定。1 == 恆定。 |
nonstop_tsc |
|
tsc 是否會持續計數,無論 CPU 是否處於低功耗狀態。0 == 停止。1 == 不停止。 |
保留 |
|
無意義。 |
資料區段¶
追蹤中的標頭後面跟著資料區段,其大小與標頭中的 buffer_size 欄位相符。
資料區段是不同類型元素的串流。
序列中有一些類別的資料。
函式 記錄
:函式記錄包含進入和退出函式執行的時間。函式記錄每個有 8 個位元組。中繼資料記錄
:中繼資料記錄有多種用途。大多數情況下,它們會擷取對於每個函式記錄成本可能過高的資訊,但這些資訊是將精細時間點關聯化所需。它們也用作使用者定義事件資料承載的標記。中繼資料記錄每個有 16 位元組。事件資料
:自由格式資料可能會與二進位檔追蹤的事件相關聯,並編碼由處理常式函式定義的資料。事件資料前面始終會帶有一個標記記錄,指示其大小。函式引數
:某些函式的引數會包含在追蹤中。這些是指標位址或基本型別,會獨立於高階語言中的型別進行讀取和記錄。對於追蹤器來說,它們都是數字。具有附加引數的函式記錄會在函式進入記錄上指示它們的存在。我們僅支援從引數零開始記錄連續的函式引數序列,這將是成員函式呼叫的「this」指標。例如,我們不支援記錄第一個和第三個引數。
記憶體格式的讀取器必須維持狀態機。此格式不會嘗試填充以進行對齊,並且不可搜尋。
函式記錄¶
函式記錄具有 8 位元組的佈局。此佈局會編碼資訊,以重建已檢測函式的呼叫堆疊及其持續時間。
欄位 |
大小 (位元) |
描述 |
---|---|---|
判別式 |
|
指示讀取器應讀取函式記錄還是中繼資料記錄。對於函式記錄,設定為 |
動作 |
|
指定是正在進入函式、退出函式,還是由最佳化產生的非標準進入或退出。 |
function_id |
|
函式的數值 ID。透過 xray 檢測對應解析為名稱。檢測對應由 xray 在編譯時建置到物件檔案中,並將函式 ID 與位址配對。它用於修補,並作為二進位檔符號的查詢,以取得名稱。 |
tsc_delta |
|
自前一個記錄記錄 delta 或其他 TSC 重置事件以來,時間戳記計數器的計時次數。 |
在小端序機器上,位元欄位按從最低有效位元到最高有效位的順序排列。讀取器可以讀取 8 位元值,並對判別式應用遮罩 0x01
。同樣地,它們可以讀取 32 位元並向右無符號移位 0x04
以取得 function_id 欄位。
在大端序機器上,位元欄位按從最高有效位元到最低有效位的順序寫入。讀取器將讀取 8 位元值,並向右無符號移位 7 位元以取得判別式。可以透過讀取 32 位元值並應用遮罩 0x0FFFFFFF
來取得 function_id 欄位。
函式動作類型如下。
類型 |
編號 |
描述 |
---|---|---|
進入 |
|
典型的函式進入。 |
退出 |
|
典型的函式退出。 |
Tail_Exit |
|
由於尾端呼叫最佳化而從函式退出。 |
Entry_Args |
|
記錄引數的函式進入。 |
Entry_Args 記錄不包含引數本身。相反地,每個已記錄引數的中繼資料記錄會跟在串流中的函式記錄之後。
中繼資料記錄¶
在整個緩衝區中散佈著 16 位元組的 Metadata 記錄。對於典型的已檢測二進制文件,它們將比 Function 記錄更稀疏,並且它們提供了更完整的二進制執行狀態。
Metadata 記錄佈局部分取決於記錄,但它們共享一個通用結構。
針對 Function 記錄描述的相同位元欄規則也適用於 MetadataRecords 的第一個位元組。在這個位元組內,小端序機器使用 lsb 到 msb 的順序,而大端序機器使用 msb 到 lsb 的順序。
欄位 |
大小 |
描述 |
---|---|---|
判別式 |
|
指示讀取器應該讀取 Function 還是 Metadata 記錄。對於 Metadata 記錄,設定為 |
record_kind |
|
Metadata 記錄的類型。 |
資料 |
|
一個資料欄位,針對每種記錄類型以不同的方式使用。 |
以下是列舉記錄類型的表格。
編號 |
類型 |
---|---|
0 |
NewBuffer |
1 |
EndOfBuffer |
2 |
NewCPUId |
3 |
TSCWrap |
4 |
WallTimeMarker |
5 |
CustomEventMarker |
6 |
CallArgument |
NewBuffer 記錄¶
每個緩衝區在標頭之後都以 NewBuffer 記錄開始。它記錄追蹤所屬執行緒的執行緒 ID。
其資料區段如下。
欄位 |
大小 (位元組) |
描述 |
---|---|---|
thread_Id |
|
緩衝區的執行緒 ID。 |
保留 |
|
未使用。 |
WallClockTime 記錄¶
在 NewBuffer 記錄之後,每個緩衝區都會記錄一個絕對時間,作為時間戳記計數器增量所記錄持續時間的參考。
其資料區段如下。
欄位 |
大小 (位元組) |
描述 |
---|---|---|
秒 |
|
絕對時間刻度上的秒數。起點未指定,取決於追蹤器配置的實作和平台。 |
微秒 |
|
時間的微秒分量。 |
保留 |
|
未使用。 |
NewCpuId 記錄¶
每個函數條目都會調用一個例程來確定正在執行哪個 CPU。通常,這是通過 readtscp 完成的,它同時讀取時間戳記計數器。
如果追蹤檢測到執行已切換 CPU,或者這是第一個檢測到的條目點,則追蹤器將輸出 NewCpuId 記錄。
其資料區段如下。
欄位 |
大小 (位元組) |
描述 |
---|---|---|
cpu_id |
|
CPU ID。 |
absolute_tsc |
|
時間戳記計數器的絕對值。 |
保留 |
|
未使用。 |
TSCWrap 記錄¶
由於每個函數記錄都使用 32 位元值來表示自上次參考以來時間戳記計數器的滴答數,因此該值可能會溢出,尤其是對於稀疏檢測的二進制文件。
當此增量不適合 32 位元表示時,將以 TSCWrap 記錄的形式寫入參考絕對時間戳記計數器記錄。
其資料區段如下。
欄位 |
大小 (位元組) |
描述 |
---|---|---|
absolute_tsc |
|
時間戳記計數器值。 |
保留 |
|
未使用。 |
CallArgument 記錄¶
在 Entry_Args 類型函數記錄之後,可能有一個或多個 CallArgument 記錄,其中包含被追蹤函數的參數值。
CallArgument 記錄序列的順序與函數參數的順序一一對應。
CallArgument 資料區段
欄位 |
大小 (位元組) |
描述 |
---|---|---|
argument |
|
數值參數(可能是指標地址)。 |
保留 |
|
未使用。 |
CustomEventMarker 記錄¶
XRay 提供了記錄自定義事件的功能。這可以用於記錄 RPC 的追蹤資訊或類似的應用程式特定追蹤資料。
自定義事件本身是緩衝區內具有任意大小的非結構化(應用程式定義)記憶體區段。它們前面是 CustomEventMarkers,用於指示它們的存在和大小。
CustomEventMarker 資料區段
欄位 |
大小 (位元組) |
描述 |
---|---|---|
event_size |
|
前面事件的大小。 |
absolute_tsc |
|
事件的時間戳記計數器。 |
保留 |
|
未使用。 |
EndOfBuffer 記錄¶
EndOfBuffer 記錄類型表示此緩衝區中沒有更多追蹤資料。讀取器應跳過在緩衝區開始之前表示的剩餘 buffer_size,並尋找另一個標頭或 EOF。
格式語法和不變量¶
並非所有 Metadata 記錄和 Function 記錄的序列都是有效的資料。序列應解析為狀態機。有效格式的期望值可以用上下文無關語法表示。
這嘗試使用 EBNF 格式的語句來解釋格式。
Format := Header ThreadBuffer* EOF
ThreadBuffer := NewBuffer WallClockTime NewCPUId BodySequence* End
BodySequence := NewCPUId | TSCWrap | Function | CustomEvent
Function := (Function_Entry_Args CallArgument*) | Function_Other_Type
CustomEvent := CustomEventMarker CustomEventUnstructuredMemory
End := EndOfBuffer RemainingBufferSizeToSkip
函數記錄順序¶
以下說明可能有助於理解對函數記錄的預期。
具有 Exit 的函數預期在追蹤中先有一個對應的 Entry 或 Entry_Args 函數記錄。
Tail_Exit 函數記錄記錄程式計數器將採用的返回地址的函數 ID。換句話說,如果沒有使用尾端呼叫最佳化,則將從呼叫堆疊中彈出的最終函數。
並非所有標記為檢測的函數都一定在追蹤中。追蹤器使用啟發式方法來保留非平凡函數的追蹤。
並非每個 entry 都必須有一個追蹤的 Exit 或 Tail Exit。緩衝區可能會用完空間,或者程式可能會在檢測的函數退出之前請求追蹤器完成以返回緩衝區。