LLVM-DEBUGINFO-ANALYZER(1)

💖 LLVM-DEBUGINFO-ANALYZER 💖

okay bestie, welcome to the llvm-debuginfo-analyzer manual page — it's a whole thing, and it's iconic. Let's get into it 💅

✨ NAME ✨

llvm-debuginfo-analyzer — print a logical representation of low-level debug information. fr fr, that's the whole vibe.


🎀 TABLE OF CONTENTS (yes she has a whole outline, main character energy) 🎀

  • SYNOPSIS
  • DESCRIPTION
  • OPTIONS
    • GENERAL
    • ATTRIBUTES
    • PRINT
    • OUTPUT
    • REPORT
    • SELECTION
      • ELEMENTS
      • LINES
      • SCOPES
      • SYMBOLS
      • TYPES
    • COMPARE
    • WARNING
    • INTERNAL
  • EXAMPLES
    • TEST CASE 1 - GENERAL OPTIONS
      • PRINTING MODE
        • BASIC DETAILS
        • SELECT LOGICAL ELEMENTS
      • COMPARISON MODE
        • LOGICAL VIEW
        • LOGICAL ELEMENTS
    • TEST CASE 2 - ASSEMBLER INSTRUCTIONS
      • CodeView - Clang (Windows)
      • CodeView - MSVC (Windows)
      • DWARF - Clang (Linux)
      • DWARF - GCC (Linux)
    • TEST CASE 3 - INCORRECT LEXICAL SCOPE FOR TYPEDEF
      • CodeView - Clang (Windows)
      • CodeView - MSVC (Windows)
      • DWARF - Clang (Linux)
      • DWARF - GCC (Linux)
    • TEST CASE 4 - MISSING NESTED ENUMERATIONS
      • CodeView - Clang (Windows)
      • CodeView - MSVC (Windows)
      • DWARF - Clang (Linux)
      • DWARF - GCC (Linux)
    • TEST CASE 5 - INCORRECT LEXICAL SCOPE FOR VARIABLE
      • CODEVIEW - Clang (Windows)
      • CODEVIEW - MSVC (Windows)
      • DWARF - Clang (Linux)
      • DWARF - GCC (Linux)
    • TEST CASE 6 - FULL LOGICAL VIEW
    • WEBASSEMBLY SUPPORT
      • PRINT BASIC DETAILS
      • SELECT LOGICAL ELEMENTS
      • COMPARISON MODE
        • LOGICAL VIEW
        • LOGICAL ELEMENTS
  • EXIT STATUS
  • LIMITATIONS AND KNOWN ISSUES
  • SEE ALSO

🌸 SYNOPSIS 🌸

llvm-debuginfo-analyzer [options] [filename ...]

🦋 DESCRIPTION 🦋

So basically llvm-debuginfo-analyzer parses debug and text sections inside binary object files, and then prints out their contents in a logical view — which is just a fancy way of saying a human-readable representation that maps super closely onto the structure of the original user source code. The object file formats it supports are ELF, Mach-O, WebAssembly, PDB and COFF. 💖

The logical view is the whole point: it abstracts away all the chaos and complexity that comes with the different low-level representations of the debugging information embedded in the object file. llvm-debuginfo-analyzer produces a canonical view of the debug information no matter how it's formatted. Same original source code = same logical view, regardless of the object file format (as long as the debug info is actually a correct representation of that source, iykyk).

The logical view is made up of these logical elements: type, scope, symbol and line — literally the basic software building blocks of the C/C++ programming language. Each logical element comes with a set of attributes, like types, classes, functions, variables, parameters, etc. You use --attribute to pick which attributes get included when a logical element is printed. And a logical element can also have a kind, which describes specific types of elements — for example, a scope could have a kind value of function, class, namespace. slay.

llvm-debuginfo-analyzer defaults to printing a pre-defined layout of logical elements and attributes. The command line options let you control the printed elements (--print), use a specific layout (--report), match a given pattern (--select, --select-offsets). Also, you can limit the output to specified logical elements using (--select-lines, --select-scopes, --select-symbols, --select-types).

And bestie, it doesn't stop there: llvm-debuginfo-analyzer can also compare a set of logical views (--compare), to find differences and identify possible debug information syntax issues (--warning) in any object file. 💅


✨ OPTIONS ✨

llvm-debuginfo-analyzer options are separated into several categories, each tailored to a different purpose:

  • GENERAL — standard LLVM options to display help, version, etc.
  • ATTRIBUTES — describe how to include different details when printing an element.
  • PRINT — specify which elements will be included when printing the view.
  • OUTPUT — describe the supported formats when printing the view.
  • REPORT — describe the format layouts for view printing.
  • SELECTION — allows to use specific criteria or conditions to select which elements to print.
  • COMPARE — compare logical views and print missing and/or added elements.
  • WARNING — print the warnings detected during the creation of the view.
  • INTERNAL — internal analysis of the logical view.

👑 GENERAL 👑

This section describes the standard help options, used to display the usage, version, response files, etc.

  • -h, --help — show help and usage for this command. (--help-hidden for more).
  • --help-list — show help and usage for this command without grouping the options into categories (--help-list-hidden for more).
  • --help-hidden — display all available options.
  • --print-all-options — print all option values after command line parsing.
  • --print-options — print non-default options after command line parsing
  • --version — display the version of the tool.
  • @<FILE> — read command-line options from <FILE>.

If no input file is specified, llvm-debuginfo-analyzer defaults to reading a.out and returns an error when no input file is found.

If - is used as the input file, llvm-debuginfo-analyzer reads the input from its standard input stream. 🫶


💜 ATTRIBUTES 💜

The following options enable attributes given for the printed elements. The attributes are divided into categories based on the type of data being added, such as: internal offsets in the binary file, location descriptors, register names, user source filenames, additional element transformations, toolchain name, binary file format, etc.

  • --attribute=<value[,value,...]> — with value being one of the options in the following lists.
    • =all: include all the below attributes.
    • =extended: add low-level attributes.
    • =standard: add standard high-level attributes.

The following attributes describe the most common information for a logical element. They help to identify the lexical scope level; the element visibility across modules (global, local); the toolchain name and source language that produced the binary file.

  • =global: element referenced across Compile Units.
  • =format: object file format name.
  • =language: source language name.
  • =level: lexical scope level (File=0, Compile Unit=1).
  • =local: element referenced only in the Compile Unit.
  • =producer: toolchain identification name.

The following attributes describe files and directory names from the user source code, where the elements are declared or defined; functions with public visibility across modules. These options allow to map the elements to their user code location, for cross references purposes.

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

The following attributes describe additional logical element source transformations, in order to display built-in types (int, bool, etc.); parameters and arguments used during template instantiation; parent name hierarchy; array dimensions information; compiler generated elements; type sizes and the underlying types associated with the types aliases.

  • =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.
  • =size: sizes for compound and base types.
  • =subrange: subrange encoding information for arrays.
  • =typename: template parameters.
  • =underlying: underlying type for type definitions.

The following attributes describe the debug location information for a symbol or scope. It includes the symbol percentage coverage and any gaps within the location layout; ranges determining the code sections attached to a function. When descriptors are used, the target processor registers are displayed.

  • =coverage: symbol location coverage.
  • =gaps: missing debug location (gaps).
  • =location: symbol debug location.
  • =range: debug location ranges.
  • =register: processor register names.

The following attributes are associated with low level details, such as: offsets in the binary file; discriminators added to the lines of inlined functions in order to distinguish specific instances; debug lines state machine registers; elements discarded by the compiler (inlining) or by the linker optimizations (dead-stripping); system compile units generated by the MS toolchain in PDBs.

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

The following attribute described specific information for the PE/COFF file format. It includes MS runtime types.

  • =system: display PDB's MS system elements.

The above attributes are grouped into standard and extended categories that can be enabled.

The standard group, contains those attributes that add sufficient information to describe a logical element and that can cover the normal situations while dealing with debug information.

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

The extended group — bestie, these are the attributes that need you to actually know debug info on a spiritual level. They're the ones you pull out when you want a lower level of detail. 💅

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

✨ PRINT ✨

okay so these options are all about what actually gets printed. The layout you get is decided by --report. And heads up: in the tree layout, every element drags its enclosing lexical scopes along with it, even if you didn't ask for them explicitly. no thoughts, just scopes. 🦋

  • --print=<value[,value,...]> — With value being one of the options in the following lists.
    • all — Include all the below attributes.

The following options print the requested elements; in the case of any given select conditions (--select), only those elements that match them, will be printed. The elements value is a convenient way to specify instructions, lines, scopes, symbols and types all at once. (iconic, honestly — one word, five categories, we love efficiency 🫶)

  • 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).

The following options print information, collected during the creation of the elements, such as: scope contributions to the debug information; summary of elements created, printed or matched (--select); warnings produced during the view creation. 🌸

  • sizes — Debug Information scopes contributions.
  • summary — Summary of elements allocated, selected or printed.
  • warnings — Warnings detected.
Note: The --print=sizes option is ELF specific. 💀

🎀 OUTPUT 🎀

Now we're talking about how to control the output once you're printing those logical elements. slay.

  • --output-file=<path> — Redirect the output to a file specified by <path>, where - is the standard output stream.

llvm-debuginfo-analyzer has the concept of split view. When redirecting the output from a complex binary format, it is divided into individual files, each one containing the logical view output for a single compilation unit. it's giving one file per compile unit, main character energy per CU, fr fr. ✨

  • --output-folder=<name> — The folder to write a file per compilation unit when --output=split is specified.
  • --output-level=<level> — Only print elements up to the given lexical level value. The input file is at lexical level zero and a compilation unit is at lexical level one.
  • --output=<value[,value,...]> — With value being one of the options in the following lists.
    • 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> — Primary key when ordering the elements in the output (default: line). Sorting by logical element kind, requires be familiarity with the element kind selection options (--select-lines, --select-scopes, --select-symbols, --select-types), as those options describe the different logical element kinds.
    • none — Unsorted output (i.e. as read from input).
    • id — Sort by unique element ID.
    • kind — Sort by element kind.
    • line — Sort by element line number.
    • name — Sort by element name.
    • offset — Sort by element offset.

🌈 REPORT 🌈

Depending on the task being executed (print, compare, select), several layouts are supported to display the elements in a more suitable way, to make the output easier to understand. lowkey a glow-up for your terminal. 💖

  • --report=<value[,value,...]> — With value being one of the options in the following list.
    • 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.

The list layout presents the logical elements in a tabular form without any parent-child relationship. This may be the preferred way to display elements that match specific conditions when comparing logical views, making it easier to find differences.

The children, parents and view layout displays the elements in a tree format, with the scopes representing their nodes, and types, symbols, lines and other scopes representing the children. The layout shows the lexical scoping relationship between elements, with the binary file being the tree root (level 0) and each compilation unit being a child (level 1).

The children layout includes the elements that match any given criteria (--select) or (--compare) and its children.

The parents layout includes the elements that match any given criteria (--select) or (--compare) and its parents.

The combined view layout includes the elements that match any given criteria (--select) or (--compare), its parents and children.

Notes:

  1. When a selection criteria (--select) is specified with no report option, the list layout is selected.
  2. The comparison mode always uses the view layout.

🦋 SELECTION 🦋

When printing an element, different data can be included and it varies (--attribute) from data directly associated with the binary file (offset) to high level details such as coverage, lexical scope level, location. As the printed output can reach a considerable size, several selection options, enable printing of specific elements. (she's big, okay, we contain her. 😭)

The pattern matching can ignore the case (--select-nocase) and be extended to use regular expressions (--select-regex).


⭐ ELEMENTS ⭐

The following options allow printing of elements that match the given <pattern>, offset <value> or an element <condition>.

  • --select=<pattern> — Print all elements whose name or line number matches the given <pattern>.
  • --select-offsets=<value[,value,...]> — Print all elements whose offset matches the given values. See --attribute option.
  • --select-elements=<condition[,condition,...]> — Print all elements that satisfy the given <condition>. With condition being one of the options in the following list.
    • discarded — Discarded elements by the linker.
    • global — Element referenced across Compile Units.
    • optimized — Optimized inlined abstract references.
  • --select-nocase — Pattern matching is case-insensitive when using --select.
  • --select-regex — Treat any <pattern> strings as regular expressions when selecting with --select option. If --select-nocase is specified, the regular expression becomes case-insensitive.

If the <pattern> criteria is too general, a more selective option can be specified to target a particular category of elements: lines (--select-lines), scopes (--select-scopes), symbols (--select-symbols) and types (--select-types). 💅

These options require knowledge of the debug information format (DWARF, CodeView), as the given kind describes a very specific type of element. iykyk. 👑


💜 LINES 💜

The following options allow printing of lines that match the given <kind>. The given criteria describes the debug line state machine registers. (registers?? in my line table?? it's more likely than you think 🫶)

  • --select-lines=<kind[,kind,...]> — With kind being one of the options in the following list.
    • 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.
    • LineAssembler — Lines that correspond to disassembly text.
    • LineDebug — Lines that correspond to debug lines.
    • NeverStepInto — marks a never step into.
    • NewStatement — Marks a new statement.
    • PrologueEnd — Marks the end of a function prologue.

🧚 SCOPES 🧚

The following options allow printing of scopes that match the given <kind>.

  • --select-scopes=<kind[,kind,...]> — With kind being one of the options in the following list.
    • 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.
    • Module — A module.
    • 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.

🍓 SYMBOLS 🍓

The following options allow printing of symbols that match the given <kind>.

  • --select-symbols=<kind[,kind,...]> — With kind being one of the options in the following list.
    • 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.

🌸 TYPES 🌸

The following options allow printing of types that match the given <kind>.

  • --select-types=<kind[,kind,...]> — With kind being one of the options in the following list.
  • =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.

💅 COMPARE

okay bestie, here's the tea ☕ — sometimes just printing the elements is not the move. Like when you're tryna see what changed between different versions of the same toolchain, or what some specific compiler optimization did to your vibes. Printing? Not it. 🙅‍♀️

In those situations you're actually hunting for which elements got added or removed. And because debug info formats are so complicated, using a regular diff tool to find those elements is genuinely so hard — lowkey impossible when you're dealing with different debug formats. 😭

llvm-debuginfo-analyzer supports a logical element comparison, which lets you find semantic differences between logical views — whether those came from different toolchain versions or even completely different debug information formats. slay ✨

When you compare logical views created from different debug formats, the accuracy depends on how close the debug information represents the user code. For instance, a logical view created from a binary file with DWARF debug information may include more detailed data than a logical view created from a binary file with CodeView debug information. (dw, that's just how it is 🫶)

The following options describe the elements to compare.

  • --compare=<value[,value,...]> — with value being one of the options in the following list.
    • =all — include all the below elements.
    • =lines — include lines.
    • =scopes — include scopes.
    • =symbols — include symbols.
    • =types — include types.

llvm-debuginfo-analyzer takes the first binary file on the command line as the reference and the second one as the target. And because we want the report to actually be descriptive, the comparison is done twice — the reference and target views get swapped, so you get both the missing elements from the target view and the added elements to the reference view. two-for-one deal, we love to see it 💖

See --report options on how to describe the comparison reports.


⚠️ WARNING ⚠️

When reading the input object files, llvm-debuginfo-analyzer can detect issues in the raw debug information. These may not be considered fatal to the purpose of printing a logical view, but they can give you an indication about the quality — and potentially expose issues with the generated debug information. not to be dramatic but this is basically a vibe check for your debug info 🫣

The following options describe the warnings to be recorded for later printing, if they are requested by --print.

  • --warning=<value[,value,...]> — with value being one of the options in the following list.
    • =all — include all the below warnings.

The following options collect additional information during the creation of the logical view, to include invalid coverage values and locations for symbols; invalid code ranges; lines that are zero.

  • =coverages — invalid symbol coverages values.
  • =lines — debug lines that are zero.
  • =locations — invalid symbol locations.
  • =ranges — invalid code ranges.

🔍 INTERNAL

For a better understanding of the logical view, access to more detailed internal information could be needed. That data would help identify debug information processed, or incorrect logical element management. Typically these kind of options are only available in debug builds. 💀

But bestie — llvm-debuginfo-analyzer supports these advanced options in both release and debug builds. iconic behavior, honestly 👑

  • --internal=<value[,value,...]> — with value being one of the options in the following list.
    • =all — include all the below options.

The following options allow to check the integrity of the logical view; collect the debug tags that are processed or not implemented; ignore the logical element line number, to facilitate the logical view comparison when using external comparison tools; print the command line options used to invoke 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.
Note: For ELF format, the collected tags represent the debug tags that are not processed. For PE/COFF format, they represent the tags that are processed. 🌈

🌸 EXAMPLES 🌸

This section includes some real binary files to show how to use llvm-debuginfo-analyzer to print a logical view and to diagnose possible debug information issues. real ones. no cap 🧚

🎀 TEST CASE 1 - GENERAL OPTIONS 🎀

The below example is used to show different output generated by llvm-debuginfo-analyzer. We compiled the example for an X86 ELF target with Clang (-O0 -g):

   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  }

✨ PRINTING MODE ✨

In this mode llvm-debuginfo-analyzer prints the logical view — or portions of it — based on criteria patterns (including regular expressions) to select the kind of logical elements to be included in the output. picky, but in a cute way 💅

🦋 BASIC DETAILS 🦋

The following command prints basic details for all the logical elements sorted by the debug information internal offset; it includes its lexical level and debug info format.

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

or

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

Each row represents an element that is present within the debug information. The first column represents the scope level, followed by the associated line number (if any), and finally the description of the element. so basically: level, line, lore. 📖

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'

On closer inspection, we can see what could be a potential debug issue: 👀

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

The 'INTEGER' definition is at level [003], the same lexical scope as the anonymous {Block} ('true' branch for the 'if' statement) — whereas in the original source code the typedef statement is clearly inside that block. So the 'INTEGER' definition should also be at level [004] inside the block. like girl it's in the wrong place, not the vibe 😭

👑 SELECT LOGICAL ELEMENTS 👑

The following prints all instructions, symbols and types that contain 'inte' or 'movl' in their names or types, using a tab layout and given the number of matches.

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

ate and left no crumbs, the summary is right there 🍓

💖 COMPARISON MODE 💖

Okay bestie, this is where it gets actually magical. In this mode llvm-debuginfo-analyzer compares logical views and hands you a report of the logical elements that are missing or added. This is a very powerful aid in finding semantic differences in the debug information produced by different toolchain versions — or even completely different toolchains altogether. Like, for example, a compiler producing DWARF can be directly compared against a completely different compiler that produces CodeView. 🦋

Given the previous example, we found the above debug information issue (related to the previous invalid scope location for the 'typedef int INTEGER') by comparing against another compiler.

Using GCC to generate test-dwarf-gcc.o, we can apply a selection pattern with the printing mode to obtain the following logical view output. 👑

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'

The output shows that both objects contain the same elements. But the 'typedef INTEGER' is located at different scope level. The GCC generated object, shows '4', which is the correct value. 😭 (not me getting emotional over a scope level)

Note that there is no requirement that GCC must produce identical or similar DWARF to Clang to allow the comparison. We're only comparing the semantics. Same energy when comparing CodeView debug information generated by MSVC and Clang. ✨

There are 2 comparison methods: logical view and logical elements.


🌈 LOGICAL VIEW 🌈

It compares the logical view as a whole unit; for a match, each compared logical element must have the same parents and children.

Using the llvm-debuginfo-analyzer comparison functionality, that issue can be seen in a more global context, that can include the logical view.

The output shows in view form the missing (-), added (+) elements, giving more context by swapping the reference and target object files.

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'

The output shows the merging view path (reference and target) with the missing and added elements. 🫶


🦋 LOGICAL ELEMENTS 🦋

It compares individual logical elements without considering if their parents are the same. For both comparison methods, the equal criteria includes the name, source code location, type, lexical scope level.

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

Changing the Reference and Target order:

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

As the Reference and Target are switched, the Added Types from the first case now are listed as Missing Types. 💅


🔥 TEST CASE 2 - ASSEMBLER INSTRUCTIONS 🔥

The below example is used to show different output generated by llvm-debuginfo-analyzer. We compiled the example for an X86 Codeview and ELF targets with recent versions of Clang, GCC and MSVC (-O0 -g) for Windows and Linux.

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

These are the logical views that llvm-debuginfo-analyzer generates for 3 different compilers (MSVC, Clang and GCC), emitting different debug information formats (CodeView, DWARF) on Windows and Linux. 🌈

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}

The logical views shows the intermixed lines and assembler instructions, allowing to compare the code generated by the different toolchains. 🧚


🌺 TEST CASE 3 - INCORRECT LEXICAL SCOPE FOR TYPEDEF 🌺

The below example is used to show different output generated by llvm-debuginfo-analyzer. We compiled the example for an X86 Codeview and ELF targets with recent versions of Clang, GCC and MSVC (-O0 -g).

 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  }

okay so bestie, the above test is literally here to illustrate a scope issue found in the Clang compiler: PR44884 (Bugs LLVM) https://bugs.llvm.org/show_bug.cgi?id=44884 / PR44229 (GitHub LLVM) https://github.com/llvm/llvm-project/issues/44229

the lines 4 and 7 contain 2 typedefs, defined at different lexical scopes:

 4    typedef int INT;
 7      typedef float FLOAT;

and these are the logical views that llvm-debuginfo-analyzer generates for 3 different compilers (MSVC, Clang and GCC), emitting different debug information formats (CodeView, DWARF) on different platforms 👑:

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}

so from the previous logical views, we can see that the Clang compiler emits both typedefs at the same lexical scope (3), which is wrong 😭. GCC and MSVC emit correct lexical scope for both typedefs, bestie. they ate. 💅

using the llvm-debuginfo-analyzer selection facilities, we can produce a simple tabular output showing just the logical types that are 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'

it also shows, that the CodeView debug information does not generate source code line numbers for those logical types 🫠. and the logical view is sorted by the types name. periodt.


💀 TEST CASE 4 - MISSING NESTED ENUMERATIONS 💀

the below example is used to show different output generated by llvm-debuginfo-analyzer. we compiled the example for an X86 Codeview and ELF targets with recent versions of Clang, GCC and MSVC (-O0 -g):

 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  }

the above test is used to illustrate a scope issue found in the Clang compiler: PR46466 (Bugs LLVM) https://bugs.llvm.org/show_bug.cgi?id=46466 / PR45811 (GitHub LLVM) https://github.com/llvm/llvm-project/issues/45811

these are the logical views that llvm-debuginfo-analyzer generates for 3 different compilers (MSVC, Clang and GCC), emitting different debug information formats (CodeView, DWARF) on different platforms ✨:

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)

   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'

So looking at all the logical views we just pulled up, bestie, the tea is this: the DWARF debug info that Clang makes? It literally does not reference the enumerators RED and BLUE at all. Meanwhile the DWARF from GCC, plus the CodeView from Clang and from MSVC — they do reference them. Iconic behaviour, honestly. 💅

Now, using the selection facilities of llvm-debuginfo-analyzer, we can cook up a logical view that shows only the logical types that are an Enumerator plus their parents. And this logical view is sorted by the type name. Obsessed.

          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'

And then, still using those selection facilities, we can also get a simple tabular output that includes a summary for the logical types that are an Enumerator. This one's also sorted by the type name, bestie. 🎀

          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
Peep the numbers under the Found column and you'll see it: zero Types were found in the DWARF debug information that Clang generated. Not one. The audacity. 😭

🌸 TEST CASE 5 - INCORRECT LEXICAL SCOPE FOR VARIABLE 🌸

Okay so this next example is here to show the different output that llvm-debuginfo-analyzer generates. We compiled this one for an X86 Codeview target and an ELF target, with recent versions of Clang, GCC and MSVC (-O0 -g). ✨

          // 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

Because this whole test depends on the inline compiler options, that header file up there is what defines forceinline. Cute, right? 💅

          #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  }

This test right here is illustrating a variable issue that was found in the Clang compiler: PR43860 (Bugs LLVM) at https://bugs.llvm.org/show_bug.cgi?id=43860 and PR43205 (GitHub) at https://github.com/llvm/llvm-project/issues/43205. Not me reporting compiler bugs, couldn't be me. 🙈

These are the logical views that llvm-debuginfo-analyzer generates for 3 different compilers (MSVC, Clang and GCC), emitting different debug information formats (CodeView, DWARF) on different platforms. Vibe check passed. 🦋

          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'
          [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'

okay so looking at those logical views from before, we can literally see that the CodeView debug info that the Clang compiler makes shows the variables Var_1 and Var_2 sitting at the same lexical scope (4) inside the function InlineFuction. Meanwhile the DWARF from GCC/Clang, and the CodeView from MSVC, put those variables at the correct lexical scope: 3 and 4 respectively. slay for them, honestly. 💅

Using the llvm-debuginfo-analyzer selection facilities, we can cook up a simple tabular output showing just the logical elements that have the var pattern in their name. And the logical view is sorted by the variables name. 🎀

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'

It also shows that the CodeView debug information does not generate source code line numbers for the those logical symbols. (and the logical view here is sorted by the types name, fyi 🦋)

🧚 TEST CASE 6 - FULL LOGICAL VIEW

For the advanced users out there — llvm-debuginfo-analyzer can display low level information that includes offsets within the debug information section, debug location operands, linkage names, etc. 👑

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%)

The Scope Sizes table shows the contribution in bytes to the debug information by each scope — which you can use to determine unexpected size changes in the DWARF sections between different versions of the same toolchain. fr fr, super useful for catching sneaky bloat. ✨

[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]

The {Range} attribute describes the line ranges for a logical scope. In this case, the function foo is within the lines 2 and 9. 📏

The {Coverage} and {Location} attributes describe the debug location and coverage for logical symbols. For optimized code, the coverage value decreases and it affects the program debuggability. that's the trade-off bestie, no cap. 💀


🌈 WEBASSEMBLY SUPPORT

The below example is used to show the WebAssembly output generated by llvm-debuginfo-analyzer. We compiled the example for a WebAssembly 32-bit target with Clang (-O0 -g --target=wasm32): 🦋

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  }

The following command prints basic details for all the logical elements sorted by the debug information internal offset; it includes its lexical level and debug info format. 🎀

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

or

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

Every single row is one element chillin' inside the debug info 💖 — first column = the scope level, then the line number if there even is one, and last but not least the description of the element itself. That's the whole layout, bestie, no cap.

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

[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} 'i32.const     7'
[004]                   {Code} 'local.set     10'
[004]                   {Code} 'local.get     5'
[004]                   {Code} 'local.get     10'
[004]                   {Code} 'i32.store     12'
[004]     6             {Line}
[004]                   {Code} 'i32.const     7'
[004]                   {Code} 'local.set     11'
[004]                   {Code} 'local.get     5'
[004]                   {Code} 'local.get     11'
[004]                   {Code} 'i32.store     28'
[004]                   {Code} 'br            1'
[004]     -             {Line}
[004]                   {Code} 'end'
[003]     4           {TypeAlias} 'INTEGER' -> 'int'
[003]     2           {Line}
[003]                 {Code} 'nop'
[003]                 {Code} 'end'
[003]                 {Code} 'i64.div_s'
[003]                 {Code} 'global.get      0'
[003]                 {Code} 'local.set       3'
[003]                 {Code} 'i32.const       32'
[003]                 {Code} 'local.set       4'
[003]                 {Code} 'local.get       3'
[003]                 {Code} 'local.get       4'
[003]                 {Code} 'i32.sub'
[003]                 {Code} 'local.set       5'
[003]                 {Code} 'local.get       5'
[003]                 {Code} 'local.get       0'
[003]                 {Code} 'i32.store       24'
[003]                 {Code} 'local.get       5'
[003]                 {Code} 'local.get       1'
[003]                 {Code} 'i32.store       20'
[003]                 {Code} 'local.get       2'
[003]                 {Code} 'local.set       6'
[003]                 {Code} 'local.get       5'
[003]                 {Code} 'local.get       6'
[003]                 {Code} 'i32.store8      19'
[003]     3           {Line}
[003]                 {Code} 'local.get       5'
[003]                 {Code} 'i32.load8_u     19'
[003]                 {Code} 'local.set       7'
[003]     3           {Line}
[003]                 {Code} 'i32.const       1'
[003]                 {Code} 'local.set       8'
[003]                 {Code} 'local.get       7'
[003]                 {Code} 'local.get       8'
[003]                 {Code} 'i32.and'
[003]                 {Code} 'local.set       9'
[003]                 {Code} 'block'
[003]                 {Code} 'block'
[003]                 {Code} 'local.get       9'
[003]                 {Code} 'i32.eqz'
[003]                 {Code} 'br_if           0'
[003]     8           {Line}
[003]                 {Code} 'local.get       5'
[003]                 {Code} 'i32.load        20'
[003]                 {Code} 'local.set       12'
[003]     8           {Line}
[003]                 {Code} 'local.get       5'
[003]                 {Code} 'local.get       12'
[003]                 {Code} 'i32.store       28'
[003]     -           {Line}
[003]                 {Code} 'end'
[003]     9           {Line}
[003]                 {Code} 'local.get       5'
[003]                 {Code} 'i32.load        28'
[003]                 {Code} 'local.set       13'
[003]                 {Code} 'local.get       13'
[003]                 {Code} 'return'
[003]                 {Code} 'end'
[003]     9           {Line}
[003]                 {Code} 'unreachable'
[002]     1         {TypeAlias} 'INTPTR' -> '* const int'

🌈 SELECT LOGICAL ELEMENTS 🌈

Okay so here's where it gets fun 💅 — this one prints literally every instruction, symbol and type whose names or types contain 'block' or '.store'. Tab layout, and it tells you how many matches you got. Obsessed. ✨

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

💀 COMPARISON MODE 💀

So real talk — using that example from before, we caught the debug info issue above (yeah, the one about that invalid scope location for the 'typedef int INTEGER') by comparing it against a whole other compiler. Queen behavior honestly. 👑

Generate test-dwarf-gcc.o with GCC, slap a selection pattern on it with the printing mode, and you get this logical view output:

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'

So what are we seeing here? Both objects have the same elements, periodt. But that 'typedef INTEGER' is sitting at a different scope level. The GCC generated object shows '4', which is the correct value. GCC ate, ngl. ✨

There are 2 comparison methods: logical view and logical elements.


🦋 LOGICAL VIEW 🦋

It compares the logical view as one whole unit — and for it to match, every compared logical element has to have the same parents and children. Strict queen. 💅

The output shows the missing (-) and added (+) elements in view form, and it gives you extra context by swapping the reference and target object files.

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'

The output shows the merging view path (reference and target) with the missing and added elements. Both sides, side by side, living their truth. 🫶


🌸 LOGICAL ELEMENTS 🌸

This one compares individual logical elements without caring whether their parents are the same. Free-spirited icon behavior. For both comparison methods, the equal criteria includes the name, source code location, type, and lexical scope level.

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

Now watch what happens when we flip the Reference and Target order 🔄

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

And the plot twist? Since the Reference and Target got switched, the Added Types from the first case are now listed as Missing Types. It's giving main character swapped with the understudy. 💀✨

💖 EXIT STATUS 💖

okay so this is the part where the tool tells you how it's feeling, and it only has two moods:

  • 0 — everything slayed. The input files were parsed and printed successfully. You ate. ✨
  • 1 — nope. Something didn't parse or didn't print. Not the vibe. 😭

That's it, that's the whole range. Binary queen. 💅


🚧 LIMITATIONS AND KNOWN ISSUES 🚧

See Limitations. (Yes bestie, it really just points you there — no cap, that's the whole section. Go read it. 🫶)


👀 SEE ALSO 👀

  • llvm-dwarfdump <llvm-dwarfdump.html> — your other DWARF bestie, go say hi 💜

👑 Author 👑

Maintained by the LLVM Team (https://llvm.org/). They're the main characters fr fr. 🫡


2003-2026, LLVM Project


okay that's the whole page bestie, you made it to the end and I'm so proud of you 🎀 This lil manual page came from the LLVM source tree (version 22, dated 2026-07-16) and it's literally called LLVM-DEBUGINFO-ANALYZER(1) — iconic name, iconic energy. Now go run it on something and tell me what the debug info said. 💖✨🦋

📦 where this page comes from