llvm-debuginfo-analyzer - 打印底层调试信息的逻辑表示。

概要

llvm-debuginfo-analyzer [选项] [文件名 …]

描述

llvm-debuginfo-analyzer 解析二进制目标文件中的调试和文本 sections,并以逻辑视图打印其内容,逻辑视图是一种人类可读的表示形式,与原始用户源代码的结构紧密匹配。 支持的目标文件格式包括 ELF、Mach-O、WebAssembly、PDB 和 COFF。

逻辑视图 抽象了与嵌入在目标文件中的不同底层调试信息表示形式相关的复杂性。 llvm-debuginfo-analyzer 生成调试信息的规范视图,无论其格式如何。 无论目标文件格式如何,都将看到相同的逻辑视图,前提是调试信息正确表示相同的原始源代码。

逻辑视图包括以下逻辑元素类型作用域符号,它们是 C/C++ 编程语言中使用的基本软件元素。 每个逻辑元素都有一组属性,例如类型函数变量参数等。 可以使用 --attribute 来指定打印逻辑元素时要包含的属性。 逻辑元素可能具有种类,用于描述元素的特定类型。 例如,作用域 可以具有 functionclassnamespace 的种类值。

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.

以下属性描述了符号或作用域的调试位置信息。 它包括符号百分比覆盖率以及位置布局中的任何间隙; 确定附加到函数的代码 sections 的范围。 当使用描述符时,将显示目标处理器寄存器。

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

列表 布局以表格形式呈现逻辑元素,没有任何父子关系。 这可能是显示比较逻辑视图时匹配特定条件的元素的首选方式,使其更容易找到差异。

子项父项视图 布局以树状格式显示元素,其中作用域表示其节点,类型、符号、行和其他作用域表示子项。 该布局显示了元素之间的词法作用域关系,其中二进制文件是树根(级别 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.

比较

在处理调试信息时,在某些情况下,打印元素不是正确的方法。 例如,当我们对同一工具链的不同版本引起的效果或特定编译器优化的影响感兴趣时。

对于这些情况,我们希望查看添加或删除了哪些元素。 由于调试信息格式复杂,因此很难使用常规的 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-analyzerrelease(发布)和 debug(调试)构建版本中都支持这些高级选项,但唯一 ID 除外,该 ID 仅在 debug(调试)构建版本中生成。

--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’,使用制表符布局并给出匹配数量。

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 生成的不同输出。我们使用最新版本的 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 - 变量的不正确词法作用域

以下示例用于展示 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’,使用制表符布局并给出匹配数量。

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。

局限性和已知问题

请参阅 Limitations

另请参阅

llvm-dwarfdump