llvm-debuginfo-analyzer - 列印低階除錯資訊的邏輯表示法。

概要

llvm-debuginfo-analyzer [選項] [檔案名稱 …]

描述

llvm-debuginfo-analyzer 解析二進位物件檔案中的除錯和文字區段,並以邏輯視圖列印其內容,這是一種人類可讀的表示法,與原始使用者原始碼的結構非常吻合。支援的物件檔案格式包括 ELF、Mach-O、WebAssembly、PDB 和 COFF。

邏輯視圖 抽象化了與嵌入在物件檔案中的除錯資訊的不同低階表示法相關聯的複雜性。llvm-debuginfo-analyzer 產生除錯資訊的標準視圖,無論其格式為何。假設除錯資訊正確地表示相同的原始碼,則無論物件檔案格式為何,都將看到相同的邏輯視圖。

邏輯視圖包括以下 邏輯元素類型作用域符號,它們是 C/C++ 程式語言中使用的基本軟體元素。每個邏輯元素都有一組 屬性,例如 類型類別函數變數參數 等。--attribute 可用於指定列印邏輯元素時要包含哪些屬性。邏輯元素可能具有 種類,用於描述元素的特定類型。例如,作用域 的種類值可以是 函數類別命名空間

llvm-debuginfo-analyzer 預設為列印預定義的邏輯元素和屬性佈局。命令列選項可用於控制要列印的元素 (--print)、使用特定佈局 (--report)、符合給定模式 (--select--select-offsets)。此外,可以使用 (--select-lines--select-scopes--select-symbols--select-types) 將輸出限制為指定的邏輯元素。

llvm-debuginfo-analyzer 也可以比較一組邏輯視圖 (--compare),以找出差異並識別任何物件檔案中可能的除錯資訊語法問題 (--warning)。

選項

llvm-debuginfo-analyzer 選項分為幾個類別,每個類別都針對不同的目的而設計

  • 一般 - 標準 LLVM 選項,用於顯示說明、版本等。

  • 屬性 - 描述如何在列印元素時包含不同的細節。

  • 列印 - 指定列印視圖時將包含哪些元素。

  • 輸出 - 描述列印視圖時支援的格式。

  • 報告 - 描述視圖列印的格式佈局。

  • 選取 - 允許使用特定條件或準則來選取要列印的元素。

  • 比較 - 比較邏輯視圖並列印遺失和/或新增的元素。

  • 警告 - 列印在建立視圖期間偵測到的警告。

  • 內部 - 邏輯視圖的內部分析。

一般

本節描述標準說明選項,用於顯示用法、版本、回應檔案等。

-h, --help

顯示此命令的說明和用法。(–help-hidden 以取得更多資訊)。

--help-list

顯示此命令的說明和用法,而不將選項分組為類別(–help-list-hidden 以取得更多資訊)。

--help-hidden

顯示所有可用的選項。

--print-all-options

在命令列解析後列印所有選項值。

--print-options

在命令列解析後列印非預設選項

--version

顯示工具的版本。

@<FILE>

<FILE> 讀取命令列選項。

如果未指定輸入檔案,llvm-debuginfo-analyzer 預設讀取 a.out,且在找不到輸入檔案時傳回錯誤。

如果使用 - 作為輸入檔案,llvm-debuginfo-analyzer 從其標準輸入串流讀取輸入。

屬性

以下選項啟用為列印元素給定的屬性。屬性根據要新增的資料類型分為幾種類別,例如:二進位檔案中的內部偏移、位置描述符、暫存器名稱、使用者原始檔名、額外元素轉換、工具鏈名稱、二進位檔案格式等。

--attribute=<value[,value,...]>

其中 value 是以下列表中的選項之一。

=all: Include all the below attributes.
=extended: Add low-level attributes.
=standard: Add standard high-level attributes.

以下屬性描述邏輯元素最常見的資訊。它們有助於識別詞法作用域層級;元素在模組之間的能見度(全域、區域);產生二進位檔案的工具鏈名稱。

=global: Element referenced across Compile Units.
=format: Object file format name.
=level: Lexical scope level (File=0, Compile Unit=1).
=local: Element referenced only in the Compile Unit.
=producer: Toolchain identification name.

以下屬性描述使用者原始碼中的檔案和目錄名稱,其中宣告或定義了元素;在模組之間具有公用能見度的函數。這些選項允許將元素對應到其使用者程式碼位置,以用於交叉參考目的。

=directories: Directories referenced in the debug information.
=filename: Filename where the element is defined.
=files: Files referenced in the debug information.
=pathname: Pathname where the object is defined.
=publics: Function names that are public.

以下屬性描述額外的邏輯元素原始碼轉換,以便顯示內建類型(int、bool 等);在範本實例化期間使用的參數和引數;父系名稱階層;陣列維度資訊;編譯器產生的元素以及與類型別名相關聯的基礎類型。

=argument: Template parameters replaced by its arguments.
=base: Base types (int, bool, etc.).
=generated: Compiler generated elements.
=encoded: Template arguments encoded in the template name.
=qualified: The element type include parents in its name.
=reference: Element declaration and definition references.
=subrange: Subrange encoding information for arrays.
=typename: Template parameters.
=underlying: Underlying type for type definitions.

以下屬性描述符號或作用域的除錯位置資訊。它包括符號百分比覆蓋率和位置佈局中的任何間隙;決定附加到函數的程式碼區段的範圍。當使用描述符時,會顯示目標處理器暫存器。

=coverage: Symbol location coverage.
=gaps: Missing debug location (gaps).
=location: Symbol debug location.
=range: Debug location ranges.
=register: Processor register names.

以下屬性與低階細節相關聯,例如:二進位檔案中的偏移;新增到內嵌函數行的鑑別器,以區分特定實例;除錯行狀態機暫存器;編譯器丟棄的元素(內嵌)或連結器最佳化(無用程式碼刪除);MS 工具鏈在 PDB 中產生的系統編譯單元。

=discarded: Discarded elements by the linker.
=discriminator: Discriminators for inlined function instances.
=inserted: Generated inlined abstract references.
=linkage: Object file linkage name.
=offset: Debug information offset.
=qualifier: Line qualifiers (Newstatement, BasicBlock, etc).
=zero: Zero line numbers.

以下屬性描述 PE/COFF 檔案格式的特定資訊。它包括 MS 執行階段類型。

=system: Display PDB's MS system elements.

以上屬性分為 標準擴充 類別,可以啟用它們。

標準 群組包含那些屬性,它們新增足夠的資訊來描述邏輯元素,並且可以涵蓋處理除錯資訊時的正常情況。

=base
=coverage
=directories
=discriminator
=filename
=files
=format
=level
=producer
=publics
=range
=reference
=zero

擴充 群組包含那些需要更多除錯資訊知識的屬性。它們適用於需要更低層級細節的情況。

=argument
=discarded
=encoded
=gaps
=generated
=global
=inserted
=linkage
=local
=location
=offset
=operation
=pathname
=qualified
=qualifier
=register
=subrange
=system
=typename

列印

以下選項描述要列印的元素。使用的佈局由 --report 決定。在樹狀佈局中,所有元素都會列印其封閉的詞法作用域,即使未明確指定也是如此。

--print=<value[,value,...]>

其中 value 是以下列表中的選項之一。

=all: Include all the below attributes.

以下選項列印請求的元素;在任何給定的選取條件 (--select) 的情況下,只會列印那些符合條件的元素。elements 值是一種方便的方式,可以一次指定指令、行、作用域、符號和類型。

=elements: Instructions, lines, scopes, symbols and types.
=instructions: Assembler instructions for code sections.
=lines: Source lines referenced in the debug information.
=scopes: Lexical blocks (function, class, namespace, etc).
=symbols: Symbols (variable, member, parameter, etc).
=types: Types (pointer, reference, type alias, etc).

以下選項列印在元素建立期間收集的資訊,例如:作用域對除錯資訊的貢獻;已建立、已列印或已符合 (--select) 的元素摘要;在視圖建立期間產生的警告。

=sizes: Debug Information scopes contributions.
=summary: Summary of elements allocated, selected or printed.
=warnings: Warnings detected.

注意:–print=sizes 選項是 ELF 特有的。

輸出

以下選項描述如何控制列印邏輯元素時產生的輸出。

--output-file=<path>

將輸出重新導向到由 <path> 指定的檔案,其中 - 是標準輸出串流。

llvm-debuginfo-analyzer 具有 分割視圖 的概念。當重新導向來自複雜二進位格式的輸出時,它會 分割 成個別檔案,每個檔案都包含單個編譯單元的邏輯視圖輸出。

--output-folder=<name>

當指定 –output=split 時,將檔案寫入每個編譯單元的資料夾。

--output-level=<level>

僅列印到給定的 詞法層級 值的元素。輸入檔案位於詞法層級零,而編譯單元位於詞法層級一。

--output=<value[,value,...]>

其中 value 是以下列表中的選項之一。

=all: Include all the below outputs.
=json: Use JSON as the output format (Not implemented).
=split: Split the output by Compile Units.
=text: Use a free form text output.
--output-sort=<key>

在對輸出中的元素進行排序時的主要鍵(預設值:line)。按邏輯元素種類排序需要熟悉元素種類選取選項 (--select-lines--select-scopes--select-symbols--select-types),因為這些選項描述了不同的邏輯元素種類。

=kind: Sort by element kind.
=line: Sort by element line number.
=name: Sort by element name.
=offset: Sort by element offset.

報告

根據正在執行的任務(列印、比較、選取),支援幾種佈局以更合適的方式顯示元素,使輸出更易於理解。

--report=<value[,value,...]>

其中 value 是以下列表中的選項之一。

=all: Include all the below reports.
=children: Elements and children are displayed in a tree format.
=list: Elements are displayed in a tabular format.
=parents: Elements and parents are displayed in a tree format.
=view: Elements, parents and children are displayed in a tree format.

list 佈局以表格形式呈現邏輯元素,而沒有任何父子關係。這可能是顯示符合特定條件的元素的較佳方式,在比較邏輯視圖時,使其更容易找到差異。

childrenparentsview 佈局以樹狀格式顯示元素,其中作用域表示其節點,而類型、符號、行和其他作用域表示子節點。該佈局顯示元素之間的詞法作用域關係,其中二進位檔案是樹狀根節點(層級 0),而每個編譯單元都是子節點(層級 1)。

children 佈局包括符合任何給定準則 (--select) 或 (--compare) 的元素及其子節點。

parents 佈局包括符合任何給定準則 (--select) 或 (--compare) 的元素及其父節點。

組合的 view 佈局包括符合任何給定準則 (--select) 或 (--compare) 的元素、其父節點和子節點。

注意事項:

  1. 當指定選取準則 (--select) 而沒有報告選項時,將選取 list 佈局。

  2. 比較模式始終使用 view 佈局。

選取

列印元素時,可以包含不同的資料,並且資料會有所不同 (--attribute),從與二進位檔案直接關聯的資料(偏移)到高階細節,例如覆蓋率、詞法作用域層級、位置。由於列印輸出可能會達到相當大的大小,因此可以使用幾個選取選項來啟用特定元素的列印。

模式比對可以忽略大小寫 (--select-nocase),並且可以擴充為使用正規表示式 (--select-regex)。

元素

以下選項允許列印符合給定 <pattern>、偏移 <value> 或元素 <condition> 的元素。

--select=<pattern>

列印名稱或行號符合給定 <pattern> 的所有元素。

--select-offsets=<value[,value,...]>

列印偏移符合給定值的所有元素。請參閱 --attribute 選項。

--select-elements=<condition[,condition,...]>

列印滿足給定 <condition> 的所有元素。其中 condition 是以下列表中的選項之一。

=discarded: Discarded elements by the linker.
=global: Element referenced across Compile Units.
=optimized: Optimized inlined abstract references.
--select-nocase

使用 --select 時,模式比對不區分大小寫。

--select-regex

在使用 --select 選項進行選取時,將任何 <pattern> 字串視為正規表示式。如果指定了 --select-nocase,則正規表示式將不區分大小寫。

如果 <pattern> 準則太過於廣泛,則可以指定更具選取性的選項來針對特定類別的元素:行 (--select-lines)、作用域 (--select-scopes)、符號 (--select-symbols) 和類型 (--select-types)。

這些選項需要了解除錯資訊格式 (DWARF、CodeView),因為給定的 kind 描述了非常特定類型的元素。

以下選項允許列印符合給定 <kind> 的行。給定的準則描述了除錯行狀態機暫存器。

--select-lines=<kind[,kind,...]>

其中 kind 是以下列表中的選項之一。

=AlwaysStepInto: marks an always step into.
=BasicBlock: Marks a new basic block.
=Discriminator: Line that has a discriminator.
=EndSequence: Marks the end in the sequence of lines.
=EpilogueBegin: Marks the start of a function epilogue.
=LineDebug: Lines that correspond to debug lines.
=LineAssembler: Lines that correspond to disassembly text.
=NeverStepInto: marks a never step into.
=NewStatement: Marks a new statement.
=PrologueEnd: Marks the end of a function prologue.

作用域

以下選項允許列印符合給定 <kind> 的作用域。

--select-scopes=<kind[,kind,...]>

其中 kind 是以下列表中的選項之一。

=Aggregate: A class, structure or union.
=Array: An array.
=Block: A generic block (lexical block or exception block).
=CallSite: A call site.
=CatchBlock: An exception block.
=Class: A class.
=CompileUnit: A compile unit.
=EntryPoint: A subroutine entry point.
=Enumeration: An enumeration.
=Function: A function.
=FunctionType: A function pointer.
=InlinedFunction: An inlined function.
=Label: A label.
=LexicalBlock: A lexical block.
=Namespace: A namespace.
=Root: The element representing the main scope.
=Structure: A structure.
=Subprogram: A subprogram.
=Template: A template definition.
=TemplateAlias: A template alias.
=TemplatePack: A template pack.
=TryBlock: An exception try block.
=Union: A union.

符號

以下選項允許列印符合給定 <kind> 的符號。

--select-symbols=<kind[,kind,...]>

其中 kind 是以下列表中的選項之一。

=CallSiteParameter: A call site parameter.
=Constant: A constant symbol.
=Inheritance: A base class.
=Member: A member class.
=Parameter: A parameter to function.
=Unspecified: Unspecified parameters to function.
=Variable: A variable.

類型

以下選項允許列印符合給定 <kind> 的類型。

--select-types=<kind[,kind,...]>

其中 kind 是以下列表中的選項之一。

=Base: Base type (integer, boolean, etc).
=Const: Constant specifier.
=Enumerator: Enumerator.
=Import: Import declaration.
=ImportDeclaration: Import declaration.
=ImportModule: Import module.
=Pointer: Pointer type.
=PointerMember: Pointer to member function.
=Reference: Reference type.
=Restrict: Restrict specifier.
=RvalueReference: R-value reference.
=Subrange: Array subrange.
=TemplateParam: Template parameter.
=TemplateTemplateParam: Template template parameter.
=TemplateTypeParam: Template type parameter.
=TemplateValueParam: Template value parameter.
=Typedef: Type definition.
=Unspecified: Unspecified type.
=Volatile: Volatile specifier.

比較

在處理除錯資訊時,在某些情況下,列印元素並不是正確的方法。在我們對相同工具鏈的不同版本所造成的影響,或特定編譯器最佳化的影響感興趣時,就是這種情況。

對於這些情況,我們希望查看哪些元素已新增或移除。由於複雜的除錯資訊格式,因此很難使用常規的 diff 工具來找到這些元素;即使在處理不同的除錯格式時也不可能。

llvm-debuginfo-analyzer 支援邏輯元素比較,允許找到不同工具鏈版本或甚至除錯資訊格式產生的邏輯視圖之間的語義差異。

當比較從不同除錯格式建立的邏輯視圖時,其準確性取決於除錯資訊表示使用者程式碼的程度。例如,從具有 DWARF 除錯資訊的二進位檔案建立的邏輯視圖可能包含比從具有 CodeView 除錯資訊的二進位檔案建立的邏輯視圖更詳細的資料。

以下選項描述要比較的元素。

--compare=<value[,value,...]>

其中 value 是以下列表中的選項之一。

=all: Include all the below elements.
=lines: Include lines.
=scopes: Include scopes.
=symbols: Include symbols.
=types: Include types.

llvm-debuginfo-analyzer 將命令列上的第一個二進位檔案作為 參考,第二個作為 目標。為了獲得更具描述性的報告,比較會執行兩次。參考視圖和目標視圖會交換,以便產生來自目標視圖的 遺失 元素,以及新增到參考視圖的 已新增 元素。

請參閱 --report 選項,了解如何描述比較報告。

警告

在讀取輸入物件檔案時,llvm-debuginfo-analyzer 可以偵測原始除錯資訊中的問題。這些問題可能不會被認為對列印邏輯視圖的目的至關重要,但它們可以指示品質,並可能揭露產生的除錯資訊的問題。

以下選項描述要記錄以供稍後列印的警告,如果 --print 請求它們。

--warning=<value[,value,...]>

其中 value 是以下列表中的選項之一。

=all: Include all the below warnings.

以下選項在建立邏輯視圖期間收集額外資訊,以包含符號的無效覆蓋率值和位置;無效的程式碼範圍;值為零的行。

=coverages: Invalid symbol coverages values.
=lines: Debug lines that are zero.
=locations: Invalid symbol locations.
=ranges: Invalid code ranges.

內部

為了更好地理解邏輯視圖,可能需要存取更詳細的內部資訊。此類資料將有助於識別已處理的除錯資訊或不正確的邏輯元素管理。通常,這類選項僅在 debug 建置中可用。

llvm-debuginfo-analyzerreleasedebug 建置中都支援這些進階選項,但只有在 debug 建置中才會產生唯一 ID。

--internal=<value[,value,...]>

其中 value 是以下列表中的選項之一。

=all: Include all the below options.

以下選項允許檢查邏輯視圖的完整性;收集已處理或未實作的除錯標籤;忽略邏輯元素行號,以便在使用外部比較工具時方便邏輯視圖比較;列印用於調用 llvm-debuginfo-analyzer 的命令列選項。

=id: Print unique element ID.
=cmdline: Print command line.
=integrity: Check elements integrity.
=none: Ignore element line number.
=tag: Debug information tags.

注意: 對於 ELF 格式,收集的標籤表示未處理的除錯標籤。對於 PE/COFF 格式,它們表示已處理的標籤。

範例

本節包含一些真實的二進位檔案,以展示如何使用 llvm-debuginfo-analyzer 列印邏輯視圖並診斷可能的除錯資訊問題。

測試案例 1 - 一般選項

以下範例用於展示 llvm-debuginfo-analyzer 產生的不同輸出。我們使用 Clang (-O0 -g) 為 X86 ELF 目標編譯了該範例

1  using INTPTR = const int *;
2  int foo(INTPTR ParamPtr, unsigned ParamUnsigned, bool ParamBool) {
3    if (ParamBool) {
4      typedef int INTEGER;
5      const INTEGER CONSTANT = 7;
6      return CONSTANT;
7    }
8    return ParamUnsigned;
9  }

列印模式

在此模式下,llvm-debuginfo-analyzer 列印 邏輯視圖 或其部分,基於準則模式(包括正規表示式)來選取要包含在輸出中的 邏輯元素 種類。

基本細節

以下命令列印所有邏輯元素的基本細節,並按除錯資訊內部偏移量排序;它包括其詞法層級和除錯資訊格式。

llvm-debuginfo-analyzer --attribute=level,format
                        --output-sort=offset
                        --print=scopes,symbols,types,lines,instructions
                        test-dwarf-clang.o

llvm-debuginfo-analyzer --attribute=level,format
                        --output-sort=offset
                        --print=elements
                        test-dwarf-clang.o

每行代表除錯資訊中存在的元素。第一列代表作用域層級,後跟關聯的行號(如果有的話),最後是元素的描述。

Logical View:
[000]           {File} 'test-dwarf-clang.o' -> elf64-x86-64

[001]             {CompileUnit} 'test.cpp'
[002]     2         {Function} extern not_inlined 'foo' -> 'int'
[003]     2           {Parameter} 'ParamPtr' -> 'INTPTR'
[003]     2           {Parameter} 'ParamUnsigned' -> 'unsigned int'
[003]     2           {Parameter} 'ParamBool' -> 'bool'
[003]                 {Block}
[004]     5             {Variable} 'CONSTANT' -> 'const INTEGER'
[004]     5             {Line}
[004]                   {Code} 'movl  $0x7, -0x1c(%rbp)'
[004]     6             {Line}
[004]                   {Code} 'movl  $0x7, -0x4(%rbp)'
[004]                   {Code} 'jmp   0x6'
[004]     8             {Line}
[004]                   {Code} 'movl  -0x14(%rbp), %eax'
[003]     4           {TypeAlias} 'INTEGER' -> 'int'
[003]     2           {Line}
[003]                 {Code} 'pushq   %rbp'
[003]                 {Code} 'movq    %rsp, %rbp'
[003]                 {Code} 'movb    %dl, %al'
[003]                 {Code} 'movq    %rdi, -0x10(%rbp)'
[003]                 {Code} 'movl    %esi, -0x14(%rbp)'
[003]                 {Code} 'andb    $0x1, %al'
[003]                 {Code} 'movb    %al, -0x15(%rbp)'
[003]     3           {Line}
[003]                 {Code} 'testb   $0x1, -0x15(%rbp)'
[003]                 {Code} 'je      0x13'
[003]     8           {Line}
[003]                 {Code} 'movl    %eax, -0x4(%rbp)'
[003]     9           {Line}
[003]                 {Code} 'movl    -0x4(%rbp), %eax'
[003]                 {Code} 'popq    %rbp'
[003]                 {Code} 'retq'
[003]     9           {Line}
[002]     1         {TypeAlias} 'INTPTR' -> '* const int'

仔細檢查後,我們可以發現可能存在的除錯問題

[003]                 {Block}
[003]     4           {TypeAlias} 'INTEGER' -> 'int'

'INTEGER' 定義位於層級 [003],與匿名 {Block}('if' 語句的 'true' 分支)相同的詞法作用域,而在原始碼中,typedef 語句顯然位於該區塊內,因此 'INTEGER' 定義也應該位於區塊內的層級 [004]

選取邏輯元素

以下命令列印名稱或類型中包含 'inte''movl' 的所有 指令符號類型,使用 tab 佈局並給定符合的數量。

llvm-debuginfo-analyzer --attribute=level
                        --select-nocase --select-regex
                        --select=INTe --select=movl
                        --report=list
                        --print=symbols,types,instructions,summary
                        test-dwarf-clang.o

Logical View:
[000]           {File} 'test-dwarf-clang.o'

[001]           {CompileUnit} 'test.cpp'
[003]           {Code} 'movl  $0x7, -0x1c(%rbp)'
[003]           {Code} 'movl  $0x7, -0x4(%rbp)'
[003]           {Code} 'movl  %eax, -0x4(%rbp)'
[003]           {Code} 'movl  %esi, -0x14(%rbp)'
[003]           {Code} 'movl  -0x14(%rbp), %eax'
[003]           {Code} 'movl  -0x4(%rbp), %eax'
[003]     4     {TypeAlias} 'INTEGER' -> 'int'
[004]     5     {Variable} 'CONSTANT' -> 'const INTEGER'

-----------------------------
Element      Total      Found
-----------------------------
Scopes           3          0
Symbols          4          1
Types            2          1
Lines           17          6
-----------------------------
Total           26          8

比較模式

在此模式下,llvm-debuginfo-analyzer 比較邏輯視圖以產生一份報告,其中包含遺失或新增的邏輯元素。這對於尋找不同工具鏈版本或甚至完全不同的工具鏈(例如,產生 DWARF 的編譯器可以直接與產生 CodeView 的完全不同的編譯器進行比較)產生的除錯資訊中的語義差異非常有幫助。

根據先前的範例,我們透過與另一個編譯器進行比較,找到了上述除錯資訊問題(與先前 'typedef int INTEGER' 的無效作用域位置相關)。

使用 GCC 產生 test-dwarf-gcc.o,我們可以應用選取模式和列印模式來獲得以下邏輯視圖輸出。

llvm-debuginfo-analyzer --attribute=level
                        --select-regex --select-nocase --select=INTe
                        --report=list
                        --print=symbols,types
                        test-dwarf-clang.o test-dwarf-gcc.o

Logical View:
[000]           {File} 'test-dwarf-clang.o'

[001]           {CompileUnit} 'test.cpp'
[003]     4     {TypeAlias} 'INTEGER' -> 'int'
[004]     5     {Variable} 'CONSTANT' -> 'const INTEGER'

Logical View:
[000]           {File} 'test-dwarf-gcc.o'

[001]           {CompileUnit} 'test.cpp'
[004]     4     {TypeAlias} 'INTEGER' -> 'int'
[004]     5     {Variable} 'CONSTANT' -> 'const INTEGER'

輸出顯示兩個物件都包含相同的元素。但是 'typedef INTEGER' 位於不同的作用域層級。GCC 產生的物件顯示 '4',這是正確的值。

請注意,GCC 不需要產生與 Clang 相同或相似的 DWARF 即可進行比較。我們僅比較語義。比較 MSVC 和 Clang 產生的 CodeView 除錯資訊時也是如此。

有 2 種比較方法:邏輯視圖和邏輯元素。

邏輯視圖

它將邏輯視圖作為一個整體單位進行比較;為了匹配,每個比較的邏輯元素都必須具有相同的父節點和子節點。

使用 llvm-debuginfo-analyzer 比較功能,可以在更全域的上下文中看到該問題,其中可以包括邏輯視圖。

輸出以視圖形式顯示 遺失 (-)、已新增 (+) 元素,透過交換參考物件檔案和目標物件檔案來提供更多上下文。

llvm-debuginfo-analyzer --attribute=level
                        --compare=types
                        --report=view
                        --print=symbols,types
                        test-dwarf-clang.o test-dwarf-gcc.o

Reference: 'test-dwarf-clang.o'
Target:    'test-dwarf-gcc.o'

Logical View:
 [000]           {File} 'test-dwarf-clang.o'

 [001]             {CompileUnit} 'test.cpp'
 [002]     1         {TypeAlias} 'INTPTR' -> '* const int'
 [002]     2         {Function} extern not_inlined 'foo' -> 'int'
 [003]                 {Block}
 [004]     5             {Variable} 'CONSTANT' -> 'const INTEGER'
+[004]     4             {TypeAlias} 'INTEGER' -> 'int'
 [003]     2           {Parameter} 'ParamBool' -> 'bool'
 [003]     2           {Parameter} 'ParamPtr' -> 'INTPTR'
 [003]     2           {Parameter} 'ParamUnsigned' -> 'unsigned int'
-[003]     4           {TypeAlias} 'INTEGER' -> 'int'

輸出顯示合併的視圖路徑(參考和目標),以及遺失和新增的元素。

邏輯元素

它比較單個邏輯元素,而不考慮其父節點是否相同。對於兩種比較方法,相等準則包括名稱、原始碼位置、類型、詞法作用域層級。

llvm-debuginfo-analyzer --attribute=level
                        --compare=types
                        --report=list
                        --print=symbols,types,summary
                        test-dwarf-clang.o test-dwarf-gcc.o

Reference: 'test-dwarf-clang.o'
Target:    'test-dwarf-gcc.o'

(1) Missing Types:
-[003]     4     {TypeAlias} 'INTEGER' -> 'int'

(1) Added Types:
+[004]     4     {TypeAlias} 'INTEGER' -> 'int'

----------------------------------------
Element   Expected    Missing      Added
----------------------------------------
Scopes           4          0          0
Symbols          0          0          0
Types            2          1          1
Lines            0          0          0
----------------------------------------
Total            6          1          1

變更 參考目標 順序

llvm-debuginfo-analyzer --attribute=level
                        --compare=types
                        --report=list
                        --print=symbols,types,summary
                        test-dwarf-gcc.o test-dwarf-clang.o

Reference: 'test-dwarf-gcc.o'
Target:    'test-dwarf-clang.o'

(1) Missing Types:
-[004]     4     {TypeAlias} 'INTEGER' -> 'int'

(1) Added Types:
+[003]     4     {TypeAlias} 'INTEGER' -> 'int'

----------------------------------------
Element   Expected    Missing      Added
----------------------------------------
Scopes           4          0          0
Symbols          0          0          0
Types            2          1          1
Lines            0          0          0
----------------------------------------
Total            6          1          1

Reference(參考)和 Target(目標)對調時,第一個案例中的Added Types(新增類型)現在會列為Missing Types(遺失類型)。

測試案例 2 - 組譯器指令

以下範例用於展示 llvm-debuginfo-analyzer 產生的不同輸出。我們使用最新版本的 Clang、GCC 和 MSVC (-O0 -g) 為 Windows 和 Linux,針對 X86 Codeview 和 ELF 目標編譯了此範例。

1  extern int printf(const char * format, ... );
2
3  int main()
4  {
5    printf("Hello, World\n");
6    return 0;
7  }

以下是 llvm-debuginfo-analyzer 為 3 種不同的編譯器(MSVC、Clang 和 GCC)產生的邏輯視圖,這些編譯器在 Windows 和 Linux 上發出不同的除錯資訊格式(CodeView、DWARF)。

llvm-debuginfo-analyzer --attribute=level,format,producer
                        --print=lines,instructions
                        hello-world-codeview-clang.o
                        hello-world-codeview-msvc.o
                        hello-world-dwarf-clang.o
                        hello-world-dwarf-gcc.o

CodeView - Clang (Windows)

Logical View:
[000]           {File} 'hello-world-codeview-clang.o' -> COFF-x86-64

[001]             {CompileUnit} 'hello-world.cpp'
[002]               {Producer} 'clang version 14.0.0'
[002]               {Function} extern not_inlined 'main' -> 'int'
[003]     4           {Line}
[003]                 {Code} 'subq    $0x28, %rsp'
[003]                 {Code} 'movl    $0x0, 0x24(%rsp)'
[003]     5           {Line}
[003]                 {Code} 'leaq    (%rip), %rcx'
[003]                 {Code} 'callq   0x0'
[003]     6           {Line}
[003]                 {Code} 'xorl    %eax, %eax'
[003]                 {Code} 'addq    $0x28, %rsp'
[003]                 {Code} 'retq'

CodeView - MSVC (Windows)

Logical View:
[000]           {File} 'hello-world-codeview-msvc.o' -> COFF-i386

[001]             {CompileUnit} 'hello-world.cpp'
[002]               {Producer} 'Microsoft (R) Optimizing Compiler'
[002]               {Function} extern not_inlined 'main' -> 'int'
[003]     4           {Line}
[003]                 {Code} 'pushl   %ebp'
[003]                 {Code} 'movl    %esp, %ebp'
[003]     5           {Line}
[003]                 {Code} 'pushl   $0x0'
[003]                 {Code} 'calll   0x0'
[003]                 {Code} 'addl    $0x4, %esp'
[003]     6           {Line}
[003]                 {Code} 'xorl    %eax, %eax'
[003]     7           {Line}
[003]                 {Code} 'popl    %ebp'
[003]                 {Code} 'retl'

DWARF - Clang (Linux)

Logical View:
[000]           {File} 'hello-world-dwarf-clang.o' -> elf64-x86-64

[001]             {CompileUnit} 'hello-world.cpp'
[002]               {Producer} 'clang version 14.0.0'
[002]     3         {Function} extern not_inlined 'main' -> 'int'
[003]     4           {Line}
[003]                 {Code} 'pushq   %rbp'
[003]                 {Code} 'movq    %rsp, %rbp'
[003]                 {Code} 'subq    $0x10, %rsp'
[003]                 {Code} 'movl    $0x0, -0x4(%rbp)'
[003]     5           {Line}
[003]                 {Code} 'movabsq $0x0, %rdi'
[003]                 {Code} 'movb    $0x0, %al'
[003]                 {Code} 'callq   0x0'
[003]     6           {Line}
[003]                 {Code} 'xorl    %eax, %eax'
[003]                 {Code} 'addq    $0x10, %rsp'
[003]                 {Code} 'popq    %rbp'
[003]                 {Code} 'retq'
[003]     6           {Line}

DWARF - GCC (Linux)

Logical View:
[000]           {File} 'hello-world-dwarf-gcc.o' -> elf64-x86-64

[001]             {CompileUnit} 'hello-world.cpp'
[002]               {Producer} 'GNU C++14 9.3.0'
[002]     3         {Function} extern not_inlined 'main' -> 'int'
[003]     4           {Line}
[003]                 {Code} 'endbr64'
[003]                 {Code} 'pushq   %rbp'
[003]                 {Code} 'movq    %rsp, %rbp'
[003]     5           {Line}
[003]                 {Code} 'leaq    (%rip), %rdi'
[003]                 {Code} 'movl    $0x0, %eax'
[003]                 {Code} 'callq   0x0'
[003]     6           {Line}
[003]                 {Code} 'movl    $0x0, %eax'
[003]     7           {Line}
[003]                 {Code} 'popq    %rbp'
[003]                 {Code} 'retq'
[003]     7           {Line}

邏輯視圖顯示了交錯的程式碼行和組譯器指令,允許比較不同工具鏈產生的程式碼。

測試案例 3 - TYPEDEF 的詞法作用域不正確

以下範例用於展示 llvm-debuginfo-analyzer 產生的不同輸出。我們使用最新版本的 Clang、GCC 和 MSVC (-O0 -g) 針對 X86 Codeview 和 ELF 目標編譯了此範例。

 1  int bar(float Input) { return (int)Input; }
 2
 3  unsigned foo(char Param) {
 4    typedef int INT;                // ** Definition for INT **
 5    INT Value = Param;
 6    {
 7      typedef float FLOAT;          // ** Definition for FLOAT **
 8      {
 9        FLOAT Added = Value + Param;
10        Value = bar(Added);
11      }
12    }
13    return Value + Param;
14  }

上述測試用於說明在 Clang 編譯器中發現的作用域問題:PR44884 (Bugs LLVM) / PR44229 (GitHub LLVM)

第 4 行和第 7 行包含 2 個 typedef,定義在不同的詞法作用域中。

4    typedef int INT;
7      typedef float FLOAT;

以下是 llvm-debuginfo-analyzer 為 3 種不同的編譯器(MSVC、Clang 和 GCC)產生的邏輯視圖,這些編譯器在不同平台上發出不同的除錯資訊格式(CodeView、DWARF)。

llvm-debuginfo-analyzer --attribute=level,format,producer
                        --print=symbols,types,lines
                        --output-sort=kind
                        pr-44884-codeview-clang.o
                        pr-44884-codeview-msvc.o
                        pr-44884-dwarf-clang.o
                        pr-44884-dwarf-gcc.o

CodeView - Clang (Windows)

Logical View:
[000]           {File} 'pr-44884-codeview-clang.o' -> COFF-x86-64

[001]             {CompileUnit} 'pr-44884.cpp'
[002]               {Producer} 'clang version 14.0.0'
[002]               {Function} extern not_inlined 'bar' -> 'int'
[003]                 {Parameter} 'Input' -> 'float'
[003]     1           {Line}
[002]               {Function} extern not_inlined 'foo' -> 'unsigned'
[003]                 {Block}
[004]                   {Variable} 'Added' -> 'float'
[004]     9             {Line}
[004]    10             {Line}
[003]                 {Parameter} 'Param' -> 'char'
[003]                 {TypeAlias} 'FLOAT' -> 'float'
[003]                 {TypeAlias} 'INT' -> 'int'
[003]                 {Variable} 'Value' -> 'int'
[003]     3           {Line}
[003]     5           {Line}
[003]    13           {Line}

CodeView - MSVC (Windows)

Logical View:
[000]           {File} 'pr-44884-codeview-msvc.o' -> COFF-i386

[001]             {CompileUnit} 'pr-44884.cpp'
[002]               {Producer} 'Microsoft (R) Optimizing Compiler'
[002]               {Function} extern not_inlined 'bar' -> 'int'
[003]                 {Variable} 'Input' -> 'float'
[003]     1           {Line}
[002]               {Function} extern not_inlined 'foo' -> 'unsigned'
[003]                 {Block}
[004]                   {Block}
[005]                     {Variable} 'Added' -> 'float'
[004]                   {TypeAlias} 'FLOAT' -> 'float'
[004]     9             {Line}
[004]    10             {Line}
[003]                 {TypeAlias} 'INT' -> 'int'
[003]                 {Variable} 'Param' -> 'char'
[003]                 {Variable} 'Value' -> 'int'
[003]     3           {Line}
[003]     5           {Line}
[003]    13           {Line}
[003]    14           {Line}

DWARF - Clang (Linux)

Logical View:
[000]           {File} 'pr-44884-dwarf-clang.o' -> elf64-x86-64

[001]             {CompileUnit} 'pr-44884.cpp'
[002]               {Producer} 'clang version 14.0.0'
[002]     1         {Function} extern not_inlined 'bar' -> 'int'
[003]     1           {Parameter} 'Input' -> 'float'
[003]     1           {Line}
[003]     1           {Line}
[003]     1           {Line}
[002]     3         {Function} extern not_inlined 'foo' -> 'unsigned int'
[003]                 {Block}
[004]     9             {Variable} 'Added' -> 'FLOAT'
[004]     9             {Line}
[004]     9             {Line}
[004]     9             {Line}
[004]     9             {Line}
[004]     9             {Line}
[004]    10             {Line}
[004]    10             {Line}
[004]    10             {Line}
[004]    13             {Line}
[003]     3           {Parameter} 'Param' -> 'char'
[003]     7           {TypeAlias} 'FLOAT' -> 'float'
[003]     4           {TypeAlias} 'INT' -> 'int'
[003]     5           {Variable} 'Value' -> 'INT'
[003]     3           {Line}
[003]     5           {Line}
[003]     5           {Line}
[003]    13           {Line}
[003]    13           {Line}
[003]    13           {Line}
[003]    13           {Line}

DWARF - GCC (Linux)

Logical View:
[000]           {File} 'pr-44884-dwarf-gcc.o' -> elf32-littlearm

[001]             {CompileUnit} 'pr-44884.cpp'
[002]               {Producer} 'GNU C++14 10.2.1 20201103'
[002]     1         {Function} extern not_inlined 'bar' -> 'int'
[003]     1           {Parameter} 'Input' -> 'float'
[003]     1           {Line}
[003]     1           {Line}
[003]     1           {Line}
[002]     3         {Function} extern not_inlined 'foo' -> 'unsigned int'
[003]                 {Block}
[004]                   {Block}
[005]     9               {Variable} 'Added' -> 'FLOAT'
[005]     9               {Line}
[005]     9               {Line}
[005]     9               {Line}
[005]    10               {Line}
[005]    13               {Line}
[004]     7             {TypeAlias} 'FLOAT' -> 'float'
[003]     3           {Parameter} 'Param' -> 'char'
[003]     4           {TypeAlias} 'INT' -> 'int'
[003]     5           {Variable} 'Value' -> 'INT'
[003]     3           {Line}
[003]     5           {Line}
[003]    13           {Line}
[003]    14           {Line}
[003]    14           {Line}

從先前的邏輯視圖中,我們可以看見 Clang 編譯器在相同的詞法作用域 (3) 中發出兩個 typedef,這是錯誤的。GCC 和 MSVC 為兩個 typedef 發出正確的詞法作用域。

使用 llvm-debuginfo-analyzer 的選擇功能,我們可以產生一個簡單的表格輸出,僅顯示屬於 Typedef 的邏輯類型。

llvm-debuginfo-analyzer --attribute=level,format
                        --output-sort=name
                        --select-types=Typedef
                        --report=list
                        --print=types
                        pr-44884-*.o

Logical View:
[000]           {File} 'pr-44884-codeview-clang.o' -> COFF-x86-64

[001]           {CompileUnit} 'pr_44884.cpp'
[003]           {TypeAlias} 'FLOAT' -> 'float'
[003]           {TypeAlias} 'INT' -> 'int'

Logical View:
[000]           {File} 'pr-44884-codeview-msvc.o' -> COFF-i386

[001]           {CompileUnit} 'pr_44884.cpp'
[004]           {TypeAlias} 'FLOAT' -> 'float'
[003]           {TypeAlias} 'INT' -> 'int'

Logical View:
[000]           {File} 'pr-44884-dwarf-clang.o' -> elf64-x86-64

[001]           {CompileUnit} 'pr_44884.cpp'
[003]     7     {TypeAlias} 'FLOAT' -> 'float'
[003]     4     {TypeAlias} 'INT' -> 'int'

Logical View:
[000]           {File} 'pr-44884-dwarf-gcc.o' -> elf32-littlearm

[001]           {CompileUnit} 'pr_44884.cpp'
[004]     7     {TypeAlias} 'FLOAT' -> 'float'
[003]     4     {TypeAlias} 'INT' -> 'int'

它也顯示,CodeView 除錯資訊不會為這些邏輯類型產生原始碼行號。邏輯視圖依類型名稱排序。

測試案例 4 - 遺失巢狀列舉

以下範例用於展示 llvm-debuginfo-analyzer 產生的不同輸出。我們使用最新版本的 Clang、GCC 和 MSVC (-O0 -g) 針對 X86 Codeview 和 ELF 目標編譯了此範例。

 1  struct Struct {
 2    union Union {
 3      enum NestedEnum { RED, BLUE };
 4    };
 5    Union U;
 6  };
 7
 8  Struct S;
 9  int test() {
10    return S.U.BLUE;
11  }

上述測試用於說明在 Clang 編譯器中發現的作用域問題:PR46466 (Bugs LLVM) / PR45811 (GitHub LLVM)

以下是 llvm-debuginfo-analyzer 為 3 種不同的編譯器(MSVC、Clang 和 GCC)產生的邏輯視圖,這些編譯器在不同平台上發出不同的除錯資訊格式(CodeView、DWARF)。

llvm-debuginfo-analyzer --attribute=level,format,producer
                        --output-sort=name
                        --print=symbols,types
                        pr-46466-codeview-clang.o
                        pr-46466-codeview-msvc.o
                        pr-46466-dwarf-clang.o
                        pr-46466-dwarf-gcc.o

CodeView - Clang (Windows)

Logical View:
[000]           {File} 'pr-46466-codeview-clang.o' -> COFF-x86-64

[001]             {CompileUnit} 'pr-46466.cpp'
[002]               {Producer} 'clang version 14.0.0'
[002]               {Variable} extern 'S' -> 'Struct'
[002]     1         {Struct} 'Struct'
[003]                 {Member} public 'U' -> 'Union'
[003]     2           {Union} 'Union'
[004]     3             {Enumeration} 'NestedEnum' -> 'int'
[005]                     {Enumerator} 'BLUE' = '0x1'
[005]                     {Enumerator} 'RED' = '0x0'

CodeView - MSVC (Windows)

Logical View:
[000]           {File} 'pr-46466-codeview-msvc.o' -> COFF-i386

[001]             {CompileUnit} 'pr-46466.cpp'
[002]               {Producer} 'Microsoft (R) Optimizing Compiler'
[002]               {Variable} extern 'S' -> 'Struct'
[002]     1         {Struct} 'Struct'
[003]                 {Member} public 'U' -> 'Union'
[003]     2           {Union} 'Union'
[004]     3             {Enumeration} 'NestedEnum' -> 'int'
[005]                     {Enumerator} 'BLUE' = '0x1'
[005]                     {Enumerator} 'RED' = '0x0'

DWARF - Clang (Linux)

Logical View:
[000]           {File} 'pr-46466-dwarf-clang.o' -> elf64-x86-64

[001]             {CompileUnit} 'pr-46466.cpp'
[002]               {Producer} 'clang version 14.0.0'
[002]     8         {Variable} extern 'S' -> 'Struct'
[002]     1         {Struct} 'Struct'
[003]     5           {Member} public 'U' -> 'Union'

DWARF - GCC (Linux)

Logical View:
[000]           {File} 'pr-46466-dwarf-gcc.o' -> elf64-x86-64

[001]             {CompileUnit} 'pr-46466.cpp'
[002]               {Producer} 'GNU C++14 9.3.0'
[002]     8         {Variable} extern 'S' -> 'Struct'
[002]     1         {Struct} 'Struct'
[003]     5           {Member} public 'U' -> 'Union'
[003]     2           {Union} 'Union'
[004]     3             {Enumeration} 'NestedEnum' -> 'unsigned int'
[005]                     {Enumerator} 'BLUE' = '0x1'
[005]                     {Enumerator} 'RED' = '0x0'

從先前的邏輯視圖中,我們可以看見 Clang 編譯器產生的 DWARF 除錯資訊不包含任何對列舉元 REDBLUE 的參考。GCC 產生的 DWARF,Clang 和 MSVC 產生的 CodeView,它們都包含此類參考。

使用 llvm-debuginfo-analyzer 的選擇功能,我們可以產生一個邏輯視圖,僅顯示屬於 Enumerator 及其父項的邏輯類型。邏輯視圖依類型名稱排序。

llvm-debuginfo-analyzer --attribute=format,level
                        --output-sort=name
                        --select-types=Enumerator
                        --report=parents
                        --print=types
                        pr-46466-*.o
Logical View:
[000]           {File} 'pr-46466-codeview-clang.o' -> COFF-x86-64

[001]             {CompileUnit} 'pr-46466.cpp'
[002]     1         {Struct} 'Struct'
[003]     2           {Union} 'Union'
[004]     3             {Enumeration} 'NestedEnum' -> 'int'
[005]                     {Enumerator} 'BLUE' = '0x1'
[005]                     {Enumerator} 'RED' = '0x0'

Logical View:
[000]           {File} 'pr-46466-codeview-msvc.o' -> COFF-i386

[001]             {CompileUnit} 'pr-46466.cpp'
[002]     1         {Struct} 'Struct'
[003]     2           {Union} 'Union'
[004]     3             {Enumeration} 'NestedEnum' -> 'int'
[005]                     {Enumerator} 'BLUE' = '0x1'
[005]                     {Enumerator} 'RED' = '0x0'

Logical View:
[000]           {File} 'pr-46466-dwarf-clang.o' -> elf64-x86-64

[001]             {CompileUnit} 'pr-46466.cpp'

Logical View:
[000]           {File} 'pr-46466-dwarf-gcc.o' -> elf64-x86-64

[001]             {CompileUnit} 'pr-46466.cpp'
[002]     1         {Struct} 'Struct'
[003]     2           {Union} 'Union'
[004]     3             {Enumeration} 'NestedEnum' -> 'unsigned int'
[005]                     {Enumerator} 'BLUE' = '0x1'
[005]                     {Enumerator} 'RED' = '0x0'

使用 llvm-debuginfo-analyzer 的選擇功能,我們可以產生一個簡單的表格輸出,包含屬於 Enumerator 的邏輯類型的摘要。邏輯視圖依類型名稱排序。

llvm-debuginfo-analyzer --attribute=format,level
                        --output-sort=name
                        --select-types=Enumerator
                        --print=types,summary
                        pr-46466-*.o
Logical View:
[000]           {File} 'pr-46466-codeview-clang.o' -> COFF-x86-64

[001]           {CompileUnit} 'pr-46466.cpp'
[005]           {Enumerator} 'BLUE' = '0x1'
[005]           {Enumerator} 'RED' = '0x0'

-----------------------------
Element      Total      Found
-----------------------------
Scopes           5          0
Symbols          2          0
Types            6          2
Lines            0          0
-----------------------------
Total           13          2

Logical View:
[000]           {File} 'pr-46466-codeview-msvc.o' -> COFF-i386

[001]           {CompileUnit} 'pr-46466.cpp'
[005]           {Enumerator} 'BLUE' = '0x1'
[005]           {Enumerator} 'RED' = '0x0'

-----------------------------
Element      Total      Found
-----------------------------
Scopes           5          0
Symbols          2          0
Types            7          2
Lines            0          0
-----------------------------
Total           14          2

Logical View:
[000]           {File} 'pr-46466-dwarf-clang.o' -> elf64-x86-64

[001]           {CompileUnit} 'pr-46466.cpp'

-----------------------------
Element      Total      Found
-----------------------------
Scopes           4          0
Symbols          0          0
Types            0          0
Lines            0          0
-----------------------------
Total            4          0

Logical View:
[000]           {File} 'pr-46466-dwarf-gcc.o' -> elf64-x86-64

[001]           {CompileUnit} 'pr-46466.cpp'
[005]           {Enumerator} 'BLUE' = '0x1'
[005]           {Enumerator} 'RED' = '0x0'

-----------------------------
Element      Total      Found
-----------------------------
Scopes           5          0
Symbols          0          0
Types            2          2
Lines            0          0
-----------------------------
Total            7          2

Found 欄位下印出的值,我們可以看見 Clang 產生的 DWARF 除錯資訊中找不到任何 Types(類型)。

測試案例 5 - VARIABLE 的詞法作用域不正確

以下範例用於展示 llvm-debuginfo-analyzer 產生的不同輸出。我們使用最新版本的 Clang、GCC 和 MSVC (-O0 -g) 針對 X86 Codeview 和 ELF 目標編譯了此範例。

// definitions.h
#ifdef _MSC_VER
  #define forceinline __forceinline
#elif defined(__clang__)
  #if __has_attribute(__always_inline__)
    #define forceinline inline __attribute__((__always_inline__))
  #else
    #define forceinline inline
  #endif
#elif defined(__GNUC__)
  #define forceinline inline __attribute__((__always_inline__))
#else
  #define forceinline inline
  #error
#endif

由於此測試依賴於內聯編譯器選項,因此上述標頭檔定義了 forceinline

#include "definitions.h"
 1  #include "definitions.h"
 2  forceinline int InlineFunction(int Param) {
 3    int Var_1 = Param;
 4    {
 5      int Var_2 = Param + Var_1;
 6      Var_1 = Var_2;
 7    }
 8    return Var_1;
 9  }
10
11  int test(int Param_1, int Param_2) {
12    int A = Param_1;
13    A += InlineFunction(Param_2);
14    return A;
15  }

上述測試用於說明在 Clang 編譯器中發現的變數問題:PR43860 (Bugs LLVM) / PR43205 (GitHub)

以下是 llvm-debuginfo-analyzer 為 3 種不同的編譯器(MSVC、Clang 和 GCC)產生的邏輯視圖,這些編譯器在不同平台上發出不同的除錯資訊格式(CodeView、DWARF)。

llvm-debuginfo-analyzer --attribute=level,format,producer
                        --output-sort=name
                        --print=symbols
                        pr-43860-codeview-clang.o
                        pr-43860-codeview-msvc.o
                        pr-43860-dwarf-clang.o
                        pr-43860-dwarf-gcc.o

CODEVIEW - Clang (Windows)

Logical View:
[000]           {File} 'pr-43860-codeview-clang.o' -> COFF-x86-64

[001]             {CompileUnit} 'pr-43860.cpp'
[002]               {Producer} 'clang version 14.0.0'
[002]     2         {Function} inlined 'InlineFunction' -> 'int'
[003]                 {Parameter} '' -> 'int'
[002]               {Function} extern not_inlined 'test' -> 'int'
[003]                 {Variable} 'A' -> 'int'
[003]                 {InlinedFunction} inlined 'InlineFunction' -> 'int'
[004]                   {Parameter} 'Param' -> 'int'
[004]                   {Variable} 'Var_1' -> 'int'
[004]                   {Variable} 'Var_2' -> 'int'
[003]                 {Parameter} 'Param_1' -> 'int'
[003]                 {Parameter} 'Param_2' -> 'int'

CODEVIEW - MSVC (Windows)

Logical View:
[000]           {File} 'pr-43860-codeview-msvc.o' -> COFF-i386

[001]             {CompileUnit} 'pr-43860.cpp'
[002]               {Producer} 'Microsoft (R) Optimizing Compiler'
[002]               {Function} extern not_inlined 'InlineFunction' -> 'int'
[003]                 {Block}
[004]                   {Variable} 'Var_2' -> 'int'
[003]                 {Variable} 'Param' -> 'int'
[003]                 {Variable} 'Var_1' -> 'int'
[002]               {Function} extern not_inlined 'test' -> 'int'
[003]                 {Variable} 'A' -> 'int'
[003]                 {Variable} 'Param_1' -> 'int'
[003]                 {Variable} 'Param_2' -> 'int'

DWARF - Clang (Linux)

Logical View:
[000]           {File} 'pr-43860-dwarf-clang.o' -> elf64-x86-64

[001]             {CompileUnit} 'pr-43860.cpp'
[002]               {Producer} 'clang version 14.0.0'
[002]     2         {Function} extern inlined 'InlineFunction' -> 'int'
[003]                 {Block}
[004]     5             {Variable} 'Var_2' -> 'int'
[003]     2           {Parameter} 'Param' -> 'int'
[003]     3           {Variable} 'Var_1' -> 'int'
[002]    11         {Function} extern not_inlined 'test' -> 'int'
[003]    12           {Variable} 'A' -> 'int'
[003]    13           {InlinedFunction} inlined 'InlineFunction' -> 'int'
[004]                   {Block}
[005]                     {Variable} 'Var_2' -> 'int'
[004]                   {Parameter} 'Param' -> 'int'
[004]                   {Variable} 'Var_1' -> 'int'
[003]    11           {Parameter} 'Param_1' -> 'int'
[003]    11           {Parameter} 'Param_2' -> 'int'

DWARF - GCC (Linux)

Logical View:
[000]           {File} 'pr-43860-dwarf-gcc.o' -> elf64-x86-64

[001]             {CompileUnit} 'pr-43860.cpp'
[002]               {Producer} 'GNU C++14 9.3.0'
[002]     2         {Function} extern declared_inlined 'InlineFunction' -> 'int'
[003]                 {Block}
[004]     5             {Variable} 'Var_2' -> 'int'
[003]     2           {Parameter} 'Param' -> 'int'
[003]     3           {Variable} 'Var_1' -> 'int'
[002]    11         {Function} extern not_inlined 'test' -> 'int'
[003]    12           {Variable} 'A' -> 'int'
[003]    13           {InlinedFunction} declared_inlined 'InlineFunction' -> 'int'
[004]                   {Block}
[005]                     {Variable} 'Var_2' -> 'int'
[004]                   {Parameter} 'Param' -> 'int'
[004]                   {Variable} 'Var_1' -> 'int'
[003]    11           {Parameter} 'Param_1' -> 'int'
[003]    11           {Parameter} 'Param_2' -> 'int'

從先前的邏輯視圖中,我們可以看見 Clang 編譯器產生的 CodeView 除錯資訊顯示,變數 Var_1Var_2 在函式 InlineFuction 中位於相同的詞法作用域 (4)。GCC/Clang 產生的 DWARF 以及 MSVC 產生的 CodeView,則顯示這些變數位於正確的詞法作用域:分別為 34

使用 llvm-debuginfo-analyzer 的選擇功能,我們可以產生一個簡單的表格輸出,僅顯示名稱中包含 var 模式的邏輯元素。邏輯視圖依變數名稱排序。

llvm-debuginfo-analyzer --attribute=level,format
                        --output-sort=name
                        --select-regex --select-nocase --select=Var
                        --report=list
                        --print=symbols
                        pr-43860-*.o
Logical View:
[000]           {File} 'pr-43860-codeview-clang.o' -> COFF-x86-64

[001]           {CompileUnit} 'pr-43860.cpp'
[004]           {Variable} 'Var_1' -> 'int'
[004]           {Variable} 'Var_2' -> 'int'

Logical View:
[000]           {File} 'pr-43860-codeview-msvc.o' -> COFF-i386

[001]           {CompileUnit} 'pr-43860.cpp'
[003]           {Variable} 'Var_1' -> 'int'
[004]           {Variable} 'Var_2' -> 'int'

Logical View:
[000]           {File} 'pr-43860-dwarf-clang.o' -> elf64-x86-64

[001]           {CompileUnit} 'pr-43860.cpp'
[004]           {Variable} 'Var_1' -> 'int'
[003]     3     {Variable} 'Var_1' -> 'int'
[005]           {Variable} 'Var_2' -> 'int'
[004]     5     {Variable} 'Var_2' -> 'int'

Logical View:
[000]           {File} 'pr-43860-dwarf-gcc.o' -> elf64-x86-64

[001]           {CompileUnit} 'pr-43860.cpp'
[004]           {Variable} 'Var_1' -> 'int'
[003]     3     {Variable} 'Var_1' -> 'int'
[005]           {Variable} 'Var_2' -> 'int'
[004]     5     {Variable} 'Var_2' -> 'int'

它也顯示,CodeView 除錯資訊不會為這些邏輯符號產生原始碼行號。邏輯視圖依類型名稱排序。

測試案例 6 - 完整邏輯視圖

對於進階使用者,llvm-debuginfo-analyzer 可以顯示低階資訊,包括除錯資訊區段內的偏移量、除錯位置運算元、連結名稱等。

llvm-debuginfo-analyzer --attribute=all
                        --print=all
                        test-dwarf-clang.o

Logical View:
[0x0000000000][000]            {File} 'test-dwarf-clang.o' -> elf64-x86-64

[0x000000000b][001]              {CompileUnit} 'test.cpp'
[0x000000000b][002]                {Producer} 'clang version 12.0.0'
                                   {Directory} ''
                                   {File} 'test.cpp'
                                   {Public} 'foo' [0x0000000000:0x000000003a]
[0x000000000b][002]                {Range} Lines 2:9 [0x0000000000:0x000000003a]
[0x00000000bc][002]                {BaseType} 'bool'
[0x0000000099][002]                {BaseType} 'int'
[0x00000000b5][002]                {BaseType} 'unsigned int'

[0x00000000a0][002]   {Source} '/test.cpp'
[0x00000000a0][002]      1         {TypeAlias} 'INTPTR' -> [0x00000000ab]'* const int'
[0x000000002a][002]      2         {Function} extern not_inlined 'foo' -> [0x0000000099]'int'
[0x000000002a][003]                  {Range} Lines 2:9 [0x0000000000:0x000000003a]
[0x000000002a][003]                  {Linkage}  0x2 '_Z3fooPKijb'
[0x0000000071][003]                  {Block}
[0x0000000071][004]                    {Range} Lines 5:8 [0x000000001c:0x000000002f]
[0x000000007e][004]      5             {Variable} 'CONSTANT' -> [0x00000000c3]'const INTEGER'
[0x000000007e][005]                      {Coverage} 100.00%
[0x000000007f][005]                      {Location}
[0x000000007f][006]                        {Entry} Stack Offset: -28 (0xffffffffffffffe4) [DW_OP_fbreg]
[0x000000001c][004]      5             {Line} {NewStatement} '/test.cpp'
[0x000000001c][004]                    {Code} 'movl   $0x7, -0x1c(%rbp)'
[0x0000000023][004]      6             {Line} {NewStatement} '/test.cpp'
[0x0000000023][004]                    {Code} 'movl   $0x7, -0x4(%rbp)'
[0x000000002a][004]                    {Code} 'jmp    0x6'
[0x000000002f][004]      8             {Line} {NewStatement} '/test.cpp'
[0x000000002f][004]                    {Code} 'movl   -0x14(%rbp), %eax'
[0x0000000063][003]      2           {Parameter} 'ParamBool' -> [0x00000000bc]'bool'
[0x0000000063][004]                    {Coverage} 100.00%
[0x0000000064][004]                    {Location}
[0x0000000064][005]                      {Entry} Stack Offset: -21 (0xffffffffffffffeb) [DW_OP_fbreg]
[0x0000000047][003]      2           {Parameter} 'ParamPtr' -> [0x00000000a0]'INTPTR'
[0x0000000047][004]                    {Coverage} 100.00%
[0x0000000048][004]                    {Location}
[0x0000000048][005]                      {Entry} Stack Offset: -16 (0xfffffffffffffff0) [DW_OP_fbreg]
[0x0000000055][003]      2           {Parameter} 'ParamUnsigned' -> [0x00000000b5]'unsigned int'
[0x0000000055][004]                    {Coverage} 100.00%
[0x0000000056][004]                    {Location}
[0x0000000056][005]                      {Entry} Stack Offset: -20 (0xffffffffffffffec) [DW_OP_fbreg]
[0x000000008d][003]      4           {TypeAlias} 'INTEGER' -> [0x0000000099]'int'
[0x0000000000][003]      2           {Line} {NewStatement} '/test.cpp'
[0x0000000000][003]                  {Code} 'pushq    %rbp'
[0x0000000001][003]                  {Code} 'movq     %rsp, %rbp'
[0x0000000004][003]                  {Code} 'movb     %dl, %al'
[0x0000000006][003]                  {Code} 'movq     %rdi, -0x10(%rbp)'
[0x000000000a][003]                  {Code} 'movl     %esi, -0x14(%rbp)'
[0x000000000d][003]                  {Code} 'andb     $0x1, %al'
[0x000000000f][003]                  {Code} 'movb     %al, -0x15(%rbp)'
[0x0000000012][003]      3           {Line} {NewStatement} {PrologueEnd} '/test.cpp'
[0x0000000012][003]                  {Code} 'testb    $0x1, -0x15(%rbp)'
[0x0000000016][003]                  {Code} 'je       0x13'
[0x0000000032][003]      8           {Line} '/test.cpp'
[0x0000000032][003]                  {Code} 'movl     %eax, -0x4(%rbp)'
[0x0000000035][003]      9           {Line} {NewStatement} '/test.cpp'
[0x0000000035][003]                  {Code} 'movl     -0x4(%rbp), %eax'
[0x0000000038][003]                  {Code} 'popq     %rbp'
[0x0000000039][003]                  {Code} 'retq'
[0x000000003a][003]      9           {Line} {NewStatement} {EndSequence} '/test.cpp'

-----------------------------
Element      Total    Printed
-----------------------------
Scopes           3          3
Symbols          4          4
Types            5          5
Lines           25         25
-----------------------------
Total           37         37

Scope Sizes:
       189 (100.00%) : [0x000000000b][001]              {CompileUnit} 'test.cpp'
       110 ( 58.20%) : [0x000000002a][002]      2         {Function} extern not_inlined 'foo' -> [0x0000000099]'int'
        27 ( 14.29%) : [0x0000000071][003]                  {Block}

Totals by lexical level:
[001]:        189 (100.00%)
[002]:        110 ( 58.20%)
[003]:         27 ( 14.29%)

Scope Sizes(作用域大小)表格顯示每個作用域對除錯資訊的位元組貢獻,可用於判斷相同工具鏈不同版本之間 DWARF 區段中意外的大小變更。

[0x000000002a][002]      2         {Function} extern not_inlined 'foo' -> [0x0000000099]'int'
[0x000000002a][003]                  {Range} Lines 2:9 [0x0000000000:0x000000003a]
[0x000000002a][003]                  {Linkage}  0x2 '_Z3fooPKijb'
[0x0000000071][003]                  {Block}
[0x0000000071][004]                    {Range} Lines 5:8 [0x000000001c:0x000000002f]
[0x000000007e][004]      5             {Variable} 'CONSTANT' -> [0x00000000c3]'const INTEGER'
[0x000000007e][005]                      {Coverage} 100.00%
[0x000000007f][005]                      {Location}
[0x000000007f][006]                        {Entry} Stack Offset: -28 (0xffffffffffffffe4) [DW_OP_fbreg]

{Range}(範圍)屬性描述邏輯作用域的行範圍。在此案例中,函式 foo 位於第 2 行和第 9 行之間。

{Coverage}(覆蓋率)和 {Location}(位置)屬性描述邏輯符號的除錯位置和覆蓋率。對於最佳化程式碼,覆蓋率值會降低,並影響程式的可除錯性。

WEBASSEMBLY 支援

以下範例用於展示 llvm-debuginfo-analyzer 產生的 WebAssembly 輸出。我們使用 Clang (-O0 -g –target=wasm32) 針對 WebAssembly 32 位元目標編譯了此範例。

1  using INTPTR = const int *;
2  int foo(INTPTR ParamPtr, unsigned ParamUnsigned, bool ParamBool) {
3    if (ParamBool) {
4      typedef int INTEGER;
5      const INTEGER CONSTANT = 7;
6      return CONSTANT;
7    }
8    return ParamUnsigned;
9  }

選擇邏輯元素

以下程式碼會印出所有名稱或類型中包含 'block''.store'指令符號類型,使用 Tab 佈局並提供符合的數量。

llvm-debuginfo-analyzer --attribute=level
                        --select-nocase --select-regex
                        --select=BLOCK --select=.store
                        --report=list
                        --print=symbols,types,instructions,summary
                        test-clang.o

Logical View:
[000]           {File} 'test-clang.o'

[001]           {CompileUnit} 'test.cpp'
[003]           {Code} 'block'
[003]           {Code} 'block'
[004]           {Code} 'i32.store     12'
[003]           {Code} 'i32.store     20'
[003]           {Code} 'i32.store     24'
[004]           {Code} 'i32.store     28'
[003]           {Code} 'i32.store     28'
[003]           {Code} 'i32.store8    19'

-----------------------------
Element      Total    Printed
-----------------------------
Scopes           3          0
Symbols          4          0
Types            2          0
Lines           62          8
-----------------------------
Total           71          8

比較模式

根據先前的範例,我們透過與另一個編譯器進行比較,找到了上述除錯資訊問題(與先前 'typedef int INTEGER' 的無效作用域位置相關)。

使用 GCC 產生 test-dwarf-gcc.o,我們可以應用選取模式和列印模式來獲得以下邏輯視圖輸出。

llvm-debuginfo-analyzer --attribute=level
                        --select-regex --select-nocase --select=INTe
                        --report=list
                        --print=symbols,types
                        test-clang.o test-dwarf-gcc.o

Logical View:
[000]           {File} 'test-clang.o'

[001]           {CompileUnit} 'test.cpp'
[003]     4     {TypeAlias} 'INTEGER' -> 'int'
[004]     5     {Variable} 'CONSTANT' -> 'const INTEGER'

Logical View:
[000]           {File} 'test-dwarf-gcc.o'

[001]           {CompileUnit} 'test.cpp'
[004]     4     {TypeAlias} 'INTEGER' -> 'int'
[004]     5     {Variable} 'CONSTANT' -> 'const INTEGER'

輸出顯示兩個物件都包含相同的元素。但是 'typedef INTEGER' 位於不同的作用域層級。GCC 產生的物件顯示 '4',這是正確的值。

有 2 種比較方法:邏輯視圖和邏輯元素。

邏輯視圖

它將邏輯視圖作為一個整體單位進行比較;為了匹配,每個比較的邏輯元素都必須具有相同的父節點和子節點。

輸出以視圖形式顯示 遺失 (-)、已新增 (+) 元素,透過交換參考物件檔案和目標物件檔案來提供更多上下文。

llvm-debuginfo-analyzer --attribute=level
                        --compare=types
                        --report=view
                        --print=symbols,types
                        test-clang.o test-dwarf-gcc.o

Reference: 'test-clang.o'
Target:    'test-dwarf-gcc.o'

Logical View:
 [000]           {File} 'test-clang.o'

 [001]             {CompileUnit} 'test.cpp'
 [002]     1         {TypeAlias} 'INTPTR' -> '* const int'
 [002]     2         {Function} extern not_inlined 'foo' -> 'int'
 [003]                 {Block}
 [004]     5             {Variable} 'CONSTANT' -> 'const INTEGER'
+[004]     4             {TypeAlias} 'INTEGER' -> 'int'
 [003]     2           {Parameter} 'ParamBool' -> 'bool'
 [003]     2           {Parameter} 'ParamPtr' -> 'INTPTR'
 [003]     2           {Parameter} 'ParamUnsigned' -> 'unsigned int'
-[003]     4           {TypeAlias} 'INTEGER' -> 'int'

輸出顯示合併的視圖路徑(參考和目標),以及遺失和新增的元素。

邏輯元素

它比較單個邏輯元素,而不考慮其父節點是否相同。對於兩種比較方法,相等準則包括名稱、原始碼位置、類型、詞法作用域層級。

llvm-debuginfo-analyzer --attribute=level
                        --compare=types
                        --report=list
                        --print=symbols,types,summary
                        test-clang.o test-dwarf-gcc.o

Reference: 'test-clang.o'
Target:    'test-dwarf-gcc.o'

(1) Missing Types:
-[003]     4     {TypeAlias} 'INTEGER' -> 'int'

(1) Added Types:
+[004]     4     {TypeAlias} 'INTEGER' -> 'int'

----------------------------------------
Element   Expected    Missing      Added
----------------------------------------
Scopes           4          0          0
Symbols          0          0          0
Types            2          1          1
Lines            0          0          0
----------------------------------------
Total            6          1          1

變更 參考目標 順序

llvm-debuginfo-analyzer --attribute=level
                        --compare=types
                        --report=list
                        --print=symbols,types,summary
                        test-dwarf-gcc.o test-clang.o

Reference: 'test-dwarf-gcc.o'
Target:    'test-clang.o'

(1) Missing Types:
-[004]     4     {TypeAlias} 'INTEGER' -> 'int'

(1) Added Types:
+[003]     4     {TypeAlias} 'INTEGER' -> 'int'

----------------------------------------
Element   Expected    Missing      Added
----------------------------------------
Scopes           4          0          0
Symbols          0          0          0
Types            2          1          1
Lines            0          0          0
----------------------------------------
Total            6          1          1

Reference(參考)和 Target(目標)對調時,第一個案例中的Added Types(新增類型)現在會列為Missing Types(遺失類型)。

結束狀態

llvm-debuginfo-analyzer 如果輸入檔案已成功解析和印出,則傳回 0。否則,傳回 1。

限制和已知問題

請參閱 限制

另請參閱

llvm-dwarfdump