檢測剖析格式

概覽

Clang 支援兩種透過檢測進行剖析的類型 [1]:基於前端和基於 IR,兩者都可以支援各種用例 [2]。本文檔描述了兩種二進制序列化格式(原始和索引),用於儲存檢測剖析,特別強調 IRPGO 用例,因為當特定標頭欄位和酬載區段在不同用例中具有不同的解釋方式時,本文檔將基於 IRPGO。

注意

前端生成的剖析與覆蓋率映射一起用於 基於原始碼的程式碼覆蓋率覆蓋率映射格式 與剖析格式不同。

原始剖析格式

原始剖析是透過執行已檢測的二進制檔生成的。來自可執行檔或共用程式庫的原始剖析資料 [3] 由一個標頭和多個區段組成,每個區段都是記憶體傾印。原始剖析資料需要相當精簡且快速生成。

原始剖析格式沒有向後或向前版本相容性保證。也就是說,編譯器和工具 需要 特定的原始剖析版本才能解析剖析。

若要將剖析回饋給編譯器以進行最佳化建置(例如,透過 IR 檢測的 -fprofile-use),則必須將原始剖析轉換為索引格式。

一般儲存佈局

原始剖析資料格式的儲存佈局如下圖所示。基本上,當原始剖析讀入記憶體緩衝區時,區段的實際位元組偏移量是根據佈局中的區段順序和其前面所有區段的大小資訊推斷出來的。

+----+-----------------------+
|    |        Magic          |
|    +-----------------------+
|    |        Version        |
|    +-----------------------+
H    |   Size Info for       |
E    |      Section 1        |
A    +-----------------------+
D    |   Size Info for       |
E    |      Section 2        |
R    +-----------------------+
|    |          ...          |
|    +-----------------------+
|    |   Size Info for       |
|    |      Section N        |
+----+-----------------------+
P    |       Section 1       |
A    +-----------------------+
Y    |       Section 2       |
L    +-----------------------+
O    |          ...          |
A    +-----------------------+
D    |       Section N       |
+----+-----------------------+

注意

可能會填充區段以符合特定的對齊需求。為簡單起見,上方資料配置圖和本文其餘部分省略了僅用於填充目的的標頭欄位和資料區段。

Payload 區塊

二進制 ID

儲存已檢測二進制檔的二進制 ID,以便將二進制檔與用於原始碼覆蓋率的配置文件相關聯。請參閱 二進制 ID RFC 以了解設計。

配置文件元數據

此區塊儲存元數據,以便將計數器和值配置文件映射回已檢測的程式碼區域(例如,用於 IRPGO 的 LLVM IR)。

內存中元數據的表示形式是 __llvm_profile_data。某些欄位用於從配置文件中的其他區塊引用數據。這些欄位的說明如下:

NameRef

函數 PGO 名稱的 MD5。PGO 名稱的格式為 [<檔案路徑><分隔符號>]<已修改名稱>,其中 <檔案路徑><分隔符號> 是為本地鏈接函數提供的,用於區分可能相同的函數。

FuncHash

函數 IR 的校驗和,考慮了控制流程圖和已檢測的值站點。有關詳細信息,請參閱 computeCFGHash

CounterPtr

配置文件數據與對應計數器起始地址之間的內存地址差異。計數器位置以這種方式儲存(作為鏈接時常量),以便與直接快照符號地址相比,減少已檢測二進制文件的大小。有關更多信息,請參閱 提交 a1532ed

注意

對於非 IRPGO 使用情況,CounterPtr 可能代表不同的值。例如,對於 二進制配置文件關聯,它表示計數器的絕對地址。如有疑問,請查看原始碼。

BitmapPtr

配置文件數據與對應位圖起始地址之間的內存地址差異。

注意

CounterPtr 類似,對於非 IRPGO 使用情況,此欄位可能代表不同的值。

FunctionPointer

記錄已檢測二進制文件運行時的函數地址。這用於在從原始配置文件轉換為索引配置文件期間,將已分析的被調用者地址映射到 NameRef

Values

在二維數組中表示值配置文件。第一維中的元素數量是所有種類中已檢測值站點的數量。第一維中的每個元素都是鏈表的頭部,第二維中的每個元素都是鏈表元素,攜帶 <已分析值,計數> 作為有效負載。編譯器運行時在寫入值配置文件時使用此選項。

注意

前端和 IR PGO 檢測都支持值分析,但在所有情況下都不支持(例如,輕量級檢測)。

NumCounters

已檢測函數的計數器數量。

NumValueSites

這是一個計數器陣列,每個計數器代表函數中某種值所對應的已檢測位置的數量。

NumBitmapBytes

函數的位元圖位元組數。

效能分析計數器

對於 PGO [4],特定 FuncHash 的已檢測函數中的計數器會連續儲存,並且順序與檢測點選擇一致。

如上所述,記錄的計數器偏移量是相對於效能分析中繼資料的。那麼如何在原始效能分析資料中找到函數計數器呢?

基本上,效能分析讀取器會迭代效能分析中繼資料(從 效能分析中繼資料 區段),並利用記錄的相對距離,如下圖所示。

       + --> start(__llvm_prf_data) --> +---------------------+ ------------+
       |                                |       Data 1        |             |
       |                                +---------------------+  =====||    |
       |                                |       Data 2        |       ||    |
       |                                +---------------------+       ||    |
       |                                |        ...          |       ||    |
Counter|                                +---------------------+       ||    |
 Delta |                                |       Data N        |       ||    |
       |                                +---------------------+       ||    |   CounterPtr1
       |                                                              ||    |
       |                                              CounterPtr2     ||    |
       |                                                              ||    |
       |                                                              ||    |
       + --> start(__llvm_prf_cnts) --> +---------------------+       ||    |
                                        |        ...          |       ||    |
                                        +---------------------+  -----||----+
                                        |    Counter for      |       ||
                                        |       Data 1        |       ||
                                        +---------------------+       ||
                                        |        ...          |       ||
                                        +---------------------+  =====||
                                        |    Counter for      |
                                        |       Data 2        |
                                        +---------------------+
                                        |        ...          |
                                        +---------------------+
                                        |    Counter for      |
                                        |       Data N        |
                                        +---------------------+

在圖表中:

  • 效能分析標頭會記錄 CounterDelta,其值為 start(__llvm_prf_cnts) - start(__llvm_prf_data)。為了方便起見,我們將其稱為 CounterDeltaInitVal

  • 對於每個效能分析資料記錄 ProfileDataNCounterPtr 會記錄為 start(CounterN) - start(ProfileDataN),其中 ProfileDataN__llvm_prf_data 中的第 N 個項目,而 CounterN 則代表對應的效能分析計數器。

每次讀取器前進到下一個資料記錄時,它會 更新 CounterDelta,減去一個 ProfileData 的大小。

對於與第一個資料記錄相對應的計數器,相對於計數器區段開頭的位元組偏移量計算為 CounterPtr1 - CounterDeltaInitVal。當效能分析讀取器前進到第二個資料記錄時,請注意 CounterDelta 已更新為 CounterDeltaInitVal - sizeof(ProfileData)。因此,相對於計數器區段開頭的位元組偏移量計算為 CounterPtr2 - (CounterDeltaInitVal - sizeof(ProfileData))

位元圖

此區段用於基於原始碼的 修改條件/決策覆蓋率 程式碼覆蓋率。請查看 位元圖 RFC 以了解設計。

名稱

此區段包含可能已壓縮的函數 PGO 名稱字串串聯。如果已壓縮,則使用 zlib 程式庫。

當原始效能分析轉換為索引效能分析時,函數名稱在 PGO 資料雜湊表中充當鍵值。它們對於 llvm-profdata 以人類可讀的方式顯示效能分析也至關重要。

虛擬表格效能分析資料

此區段用於 類型效能分析。每個項目對應一個虛擬表格,並由以下 C++ 結構定義

struct VTableProfData {
  // The start address of the vtable, collected at runtime.
  uint64_t StartAddress;
  // The byte size of the vtable. `StartAddress` and `ByteSize` specifies an address range to look up.
  uint32_t ByteSize;
  // The hash of vtable's (PGO) name
  uint64_t MD5HashOfName;
};

在使用配置文件時,編譯器會在已排序的虛擬函式表地址範圍內查找配置文件地址,並透過雜湊名稱將地址映射到特定的虛擬函式表。

虛擬函式表名稱

本節與上方的函式名稱章節類似,不同之處在於它包含已分析虛擬函式表的 PGO 名稱。 它是一個獨立的章節,以便原始配置文件讀取器可以直接透過訪問相應的配置文件資料章節來找到每個名稱集。

本節儲存在原始配置文件中,以便 llvm-profdata 可以以人類可讀的方式顯示配置文件。

值配置文件資料

本節包含值分析的配置文件資料。

與配置文件中繼資料相對應的值配置文件會作為一個記錄連續序列化,並且值配置文件記錄的儲存順序與相應的配置文件資料相同,以便原始配置文件讀取器可以同時推進指向配置文件資料的指標和指向值配置文件記錄的指標[5],以便為每個函式、每個函式雜湊值配置文件資料查找值配置文件。

索引式配置文件格式

索引式配置文件是由 llvm-profdata 生成的。 在索引式配置文件中,函式資料被組織為磁碟上的雜湊表,以便編譯器可以在 IR 模組中查找函式的配置文件資料。

編譯器和工具必須保持與索引式配置文件的向後相容性。 也就是說,使用較新版本程式碼建置的工具或編譯器必須能夠理解由較舊工具或編譯器生成的配置文件。

一般儲存佈局

ASCII 圖示描繪了索引式配置文件的一般儲存佈局。具體來說,索引式配置文件標頭描述了各個有效載荷章節的位元組偏移量。

                +-----------------------+---+
                |        Magic          |   |
                +-----------------------+   |
                |        Version        |   |
                +-----------------------+   |
                |        HashType       |   H
                +-----------------------+   E
                |       Byte Offset     |   A
        +------ |      of section A     |   D
        |       +-----------------------+   E
        |       |       Byte Of fset    |   R
    +-----------|      of section B     |   |
    |   |       +-----------------------+   |
    |   |       |         ...           |   |
    |   |       +-----------------------+   |
    |   |       |      Byte Offset      |   |
+---------------|     of section Z      |   |
|   |   |       +-----------------------+---+
|   |   |       |    Profile Summary    |   |
|   |   |       +-----------------------+   P
|   |   +------>|      Section A        |   A
|   |           +-----------------------+   Y
|   +---------->|      Section B        |   L
|               +-----------------------+   O
|               |         ...           |   A
|               +-----------------------+   D
+-------------->|      Section Z        |   |
                +-----------------------+---+

注意

配置文件摘要章節位於有效載荷的開頭。它緊隨標頭之後,因此在讀取標頭後,其位置是隱式知道的。

標頭

標頭結構是真實來源,結構欄位應說明標頭中的內容。在較高的層級上,*Offset 欄位記錄章節位元組偏移量,讀取器使用這些偏移量來定位感興趣的章節並跳過不感興趣的章節。

注意

為了保持索引式配置文件的向後相容性,不應從結構定義中刪除現有欄位;也不應修改欄位順序。 新欄位應附加在後面。

有效載荷章節

(CS) 配置文件摘要

本節位於配置文件標頭之後。 它儲存序列化的配置文件摘要。 對於上下文相關的基於 IR 的檢測 PGO,本節儲存與上下文相關配置文件相對應的額外配置文件摘要。

函數數據

本節將函式及其效能分析資料儲存在磁碟雜湊表中。名稱相同的函式的效能分析資料會被歸類在一起,並共用一個雜湊表項目(例如,這些函式可能來自不同的共用程式庫)。它們的效能分析資料會被組織成一系列的鍵值對,其中鍵值是 函式雜湊 (FuncHash),而值則是函式的效能分析資訊(由 InstrProfRecord 表示)。

記憶體效能分析資料 (MemProf Profile data)

本節儲存函式的記憶體效能分析資料。請參閱 MemProf 二進位序列化格式 RFC 以了解其設計。

二進位 ID (Binary Ids)

本節用於承載原始效能分析資料中的 二進位 ID 資訊。

時間序列效能分析追蹤 (Temporal Profile Traces)

本節用於承載原始效能分析資料中的時間序列效能分析資訊。請參閱 時間序列效能分析 以了解其設計。

虛擬表格名稱 (Virtual Table Names)

本節用於在索引式效能分析資料中儲存原始效能分析資料中的虛擬表格名稱。

與儲存為 函式資料 雜湊表鍵值的函式名稱不同,虛擬表格名稱需要儲存在索引式效能分析資料的獨立區段中。這樣一來,llvm-profdata 就可以用人類可讀的方式顯示已分析的虛擬表格資訊。

效能分析資料的使用 (Profile Data Usage)

llvm-profdata 是一個用於顯示和處理基於插裝的效能分析資料的命令列工具。如需支援的用法,請查看 llvm-profdata 文件