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=<值[,值,...]>

其中「值」是以下清單中的一個選項。

=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=<值[,值,...]>

其中「值」是以下清單中的一個選項。

=all: Include all the below attributes.

以下選項會列印要求的元素;在任何給定選擇條件 (--select) 的情況下,只會列印符合條件的元素。「**元素**」值是一種同時指定指令、行、範圍、符號和類型的便捷方式。

=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=<路徑>

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

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

--output-folder=<名稱>

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

--output-level=<級別>

僅列印達到給定「**詞彙級別**」值的元素。輸入檔案位於詞彙級別零,而編譯單元位於詞彙級別一。

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

其中「值」是以下清單中的一個選項。

=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=<鍵>

用於在輸出中對元素進行排序的主鍵(默認值:行)。 按邏輯元素種類排序,需要熟悉元素種類選擇選項(--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=<值[,值,...]>

其中**值**是以下列表中的一個選項。

=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.

**列表**佈局以表格形式呈現邏輯元素,没有任何父子關係。 這可能是顯示在比較邏輯視圖時符合特定條件的元素的首選方式,可以更容易地發現差異。

**子項**、**父項**和**視圖**佈局以樹狀格式顯示元素,範圍表示其節點,類型、符號、行和其他範圍表示子項。 佈局顯示元素之間的詞彙範圍關係,二進制文件是樹根(級別 0),每個編譯單元都是子項(級別 1)。

**子項**佈局包含符合任何給定條件的元素(--select)或(--compare)及其子項。

**父項**佈局包含符合任何給定條件的元素(--select)或(--compare)及其父項。

組合的**視圖**佈局包含符合任何給定條件的元素(--select)或(--compare)、其父項和子項。

備註:

  1. 當指定了選擇條件(--select)而沒有報告選項時,將選擇**列表**佈局。

  2. 比較模式始終使用**視圖**佈局。

選擇

列印元素時,可以包含不同的數據,且數據會因 (--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.

比較

在處理偵錯資訊時,有些情況下列印元素並不是正確的方法。例如,當我們感興趣的是由不同版本的相同工具鏈所造成的影響,或者特定編譯器優化的影響時。

對於這些情況,我們希望看到哪些元素被添加或刪除。由於偵錯資訊格式複雜,使用常規的差異工具很難找到這些元素;甚至在處理不同的偵錯格式時是不可能的。

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

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

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

--compare=<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,...]>

其中**值**是以下列表中的一個選項。

=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.

內部

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

llvm-debuginfo-analyzer 在*發布*和*偵錯*建置中都支援這些進階選項,但僅在*偵錯*建置中生成的唯一 ID 除外。

--internal=<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],與匿名 {區塊}(‘if’ 陳述式的 ‘true’ 分支)位於相同的詞彙範圍,然而在原始程式碼中,typedef 陳述式明顯位於該區塊內,因此 ‘INTEGER’ 的定義也應該位於區塊內的層級 [004]

選擇邏輯元素

以下使用表格佈局列印所有名稱或類型中包含 ‘inte’‘movl’指令符號類型,並給出相符的數量。

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 除錯資訊時,情況相同。

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

邏輯視圖

它會將邏輯視圖作為一個整體進行比較;若要相符,每個比較的邏輯元素必須具有相同的父系和子系。

使用 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

由於參考目標已交換,因此第一種情況中的新增類型現在列為遺漏類型

測試案例 2 - 組合語言指令

以下範例用於顯示 llvm-debuginfo-analyzer 產生的不同輸出。我們使用 Windows 和 Linux 上最新版本的 Clang、GCC 和 MSVC(-O0 -g)針對 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 除錯資訊中沒有找到型別

測試案例 5 - 變數的詞彙範圍不正確

以下範例用於顯示 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 (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]    14           {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%)

範圍大小表顯示每個範圍對除錯資訊的位元組貢獻,可用於確定同一個工具鏈的不同版本之間 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”指令符號型別

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’,這是正確的值。

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

邏輯視圖

它會將邏輯視圖作為一個整體進行比較;若要相符,每個比較的邏輯元素必須具有相同的父系和子系。

輸出以視圖形式顯示遺漏 (-)、新增 (+) 的元素,並透過交換參考和目標物件檔案來提供更多環境。

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

由於參考目標已交換,因此第一種情況中的新增類型現在列為遺漏類型

退出狀態

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

限制和已知問題

請參閱 限制

另請參閱

llvm-dwarfdump