LLVM-DEBUGINFO-ANALYZER(1)
π llvm-debuginfo-analyzer β the LLVM manual, but make it make sense π
π NAME π
llvm-debuginfo-analyzer β print a logical representation of low-level debug information. periodt π
okay so bestie, before we get into it, hereβs the whole map of this page β yes, all of it, because structure is self-care:
- SYNOPSIS
- DESCRIPTION
- OPTIONS
- GENERAL
- ATTRIBUTES
- 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
- PRINTING MODE
- 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
- TEST CASE 1 - GENERAL OPTIONS
- EXIT STATUS
- SEE ALSO
β¨ SYNOPSIS β¨
the whole synopsis is literally just:
llvm-debuginfo-analyzer [options] [filename ...]
πΈ DESCRIPTION πΈ
llvm-debuginfo-analyzer parses debug and text sections in binary object files and prints their contents in a logical view, which is a human readable representation that closely matches the structure of the original user source code. Supported object file formats include ELF, Mach-O, PDB and COFF. slay.
The logical view abstracts the complexity associated with the different low-level representations of the debugging information that is embedded in the object file. llvm-debuginfo-analyzer produces a canonical view of the debug information regardless of how it is formatted. The same logical view will be seen regardless of object file format, assuming the debug information correctly represents the same original source code. canonical view is the main character energy here β¨
The logical view includes the following logical elements: type, scope, symbol and line, which are the basic software elements used in the C/C++ programming language. Each logical element has a set of attributes, such as types, classes, functions, variables, parameters, etc. The --attribute can be used to specify which attributes to include when printing a logical element. A logical element may have a kind that describes specific types of elements. For instance, a scope could have a kind value of function, class, namespace.
llvm-debuginfo-analyzer defaults to print a pre-defined layout of logical elements and attributes. The command line options can be used to control the printed elements (--print), using a specific layout (--report), matching a given pattern (--select, --select-offsets). Also, the output can be limited to specified logical elements using (--select-lines, --select-scopes, --select-symbols, --select-types).
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. comparison mode ate, no cap π₯
π 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. basic but essential, like water π«Ά
-h,--helpβ Show help and usage for this command. (--help-hiddenfor more).--help-listβ Show help and usage for this command without grouping the options into categories (--help-list-hiddenfor 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 read a.out and return an error when no input file is found. not it silently doing nothing, bestie β it wants a.out π
If - is used as the input file, llvm-debuginfo-analyzer reads the input from its standard input stream. stdin behavior unlocked π
π¦ ATTRIBUTES π¦
The following options enable attributes given for the printed elements. The attributes are divided in 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,...]>β Withvaluebeing 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 that produced the binary file.
=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.
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 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.=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. the βjust enough detailβ bundle π
=base=coverage=directories=discriminator=filename=files=format=level=producer=publics=range=reference=zero
The extended group, contains those attributes that require a more extended knowledge about debug information. They are intended when a lower level of detail is required.
β¦and the rest of the --attribute values, no cap:
=argument=discarded=encoded=gaps=generated=global=inserted=linkage=local=location=offset=operation=pathname=qualified=qualifier=register=subrange=system=typename
β¨ PRINT β¨
Okay so the following options control which elements get printed. The layout? Thatβs decided by --report. In tree layout, every element gets its enclosing lexical scopes printed too, even if you didnβt explicitly ask for them. iconic.
--print=<value[,value,...]>β pick your values, comma-separated. Each value is one of the options in the lists below.=allβ include all the below attributes.
The next batch prints the requested elements. If youβve got select conditions (--select), only the elements that match them get printed. The =elements value is the convenient βall of these at onceβ option for instructions, lines, scopes, symbols and types.
=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).
These next ones print info collected while the elements were being created, like scope contributions to the debug information, a summary of elements created / printed / matched (--select), and warnings produced during 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. iykyk.
πΎ OUTPUT πΎ
These options describe how to control the output generated when printing the logical elements.
--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 gets divided into individual files, each one containing the logical view output for a single compilation unit. slay.
--output-folder=<name>β the folder to write a file per compilation unit when--output=splitis 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,...]>β pick your output values, where value is one of these:=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 means you gotta be familiar with the element-kind selection options (--select-lines,--select-scopes,--select-symbols,--select-types), because those options describe the different logical element kinds.=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, so the output is easier to understand. bestie, we love a readable output.
--report=<value[,value,...]>β where value is one of these:=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. lowkey perfect for spotting diffs.
The children, parents and view layouts display 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
- When a selection criteria (
--select) is specified with no report option, the list layout is selected. - 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. The printed output can reach a considerable size, so several selection options let you print only specific elements. no cap, filter your faves.
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 the--attributeoption.--select-elements=<condition[,condition,...]>β print all elements that satisfy the given<condition>. Each condition is one of these:=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 the--selectoption. If--select-nocaseis specified, the regular expression becomes case-insensitive.
If the <pattern> criteria is too general, you can specify a more selective option 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, COFF), because the given kind describes a very specific type of element. nerd mode activated.
π LINES π
The following options allow printing of lines that match the given <kind>. The given criteria describe the debug line state machine registers.
--select-lines=<kind[,kind,...]>β where kind is one of these:=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.
πͺΊ SCOPES πͺΊ
The following options allow printing of scopes that match the given <kind>.
--select-scopes=<kind[,kind,...]>β where kind is one of these:=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.
π·οΈ SYMBOLS π·οΈ
The following options allow printing of symbols that match the given <kind>.
--select-symbols=<kind[,kind,...]>β where kind is one of these:=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 so bestie, sometimes with debug info you donβt just wanna print the elements β thatβs not the vibe. The actual situation is when youβre curious about what changed between different versions of the same toolchain, or what a specific compiler optimization did. π
In those cases, youβre basically looking for which elements got added or removed. And because debug info formats are complicated (like, why are they like this π), using a regular diff tool to find those elements is super hard β honestly 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 β even if those views came from different toolchain versions or different debug information formats. Slay. β¨
When comparing 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/COFF debug information. So like, DWARF is giving main character energy in that scenario.
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. To get a more descriptive report, the comparison is done twice. The reference and target views are swapped, in order to produce those missing elements from the target view and those added elements to the reference view. Itβs giving double-checking, but make it debug info. π«Ά
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 an indication about the quality and potentially expose issues with the generated debug information. So like, not a crisis, but alsoβ¦ bestie, keep an eye on it. π
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. Such data would help to identify debug information processed or incorrect logical element management. Typically these kind of options are available only in debug builds.
llvm-debuginfo-analyzer supports these advanced options in both release and debug builds, with the exception of the unique ID that is generated only in debug builds. So like, you can still peek under the hood in release mode β just not at the unique ID. π
--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 files, real vibes. π
π 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.
β¨ 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.
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. So likeβ¦ thatβs a little sus. π§
π¦ 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
COMPARISON MODE π
In this mode llvm-debuginfo-analyzer compares logical views to produce a report with the logical elements that are missing or added. This a very powerful aid in finding semantic differences in the debug information produced by different toolchain versions or even completely different toolchains altogether (For example a compiler producing DWARF can be directly compared against a completely different compiler that produces CodeView). iconic
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. the way that '4' just casually exposes the whole bug β no cap
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. The same case 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. strict queen behavior, fr fr
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. look at those little + and - doing all the work π
π¦ 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. so like, name + location + type + scope level, all four or it doesn't count
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. the plot twist nobody asked for but everybody needed β¨
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. three compilers, two formats, two OSes, one little hello-world β and they're all here in one view π«Ά
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 this little cutie is the whole reason we're here β it's the test that shows off 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 contains 2 typedefs, defined at different lexical scopes.
4 typedef int INT;
7 typedef float FLOAT;
Right?? Two typedefs, two different scopes. 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. iconic behavior honestly π
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 the way Clang fumbled this π
But wait β using the llvm-debuginfo-analyzer selection facilities, we can produce a simple tabular output showing just the logical types that are Typedef. slay β¨
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 the those logical types. The logical view is sorted by the types name. iykyk π
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>. another one, we stay winning π
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) π
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'
Okay bestie, look at those logical views up there π β the DWARF debug info from Clang is literally missing any references to the enumerators RED and BLUE. No cap. But GCC's DWARF? And CodeView from Clang and MSVC? They totally include those references. Slay for them, I guess. π
So using the selection facilities in llvm-debuginfo-analyzer, we can make a logical view that shows only the logical types that are Enumerator and their parents. And it's sorted by the types name. Cute, right? β¨
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 if you use those same selection facilities, you can also get a simple tabular output with a summary for the logical types that are Enumerator. Again, sorted by the types name. Obsessed. π₯
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
So if you look at the Found column, you can see that zero Types were found in the DWARF debug info generated by Clang. Literally none. π
TEST CASE 5 - INCORRECT LEXICAL SCOPE FOR VARIABLE π«Ά
Okay so this next example is here to show you the different output that llvm-debuginfo-analyzer generates. We compiled it for X86 Codeview and ELF targets, using recent versions of Clang, GCC, and MSVC with -O0 -g. No cap. π§
// 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
Since this test depends on inline compiler options, the header file above defines forceinline. Periodt. π
#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 }
The test above is literally there to illustrate a variable issue that was found in the Clang compiler: PR43860 (Bugs LLVM) https://bugs.llvm.org/show_bug.cgi?id=43860 / PR43205 (GitHub) https://github.com/llvm/llvm-project/issues/43205. Ngl, kind of iconic that we can see it. π
Here 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. It's giving variety show. π
llvm-debuginfo-analyzer --attribute=level,format,producer
--output-sort=name
--print=symbols
pr-43860-codeview-clang.o
pr-43860-codeview-msvc.o
pr-43860-dwarf-clang.o
pr-43860-dwarf-gcc.o
CODEVIEW - Clang (Windows) π»
Logical View:
[000] {File} 'pr-43860-codeview-clang.o' -> COFF-x86-64
[001] {CompileUnit} 'pr-43860.cpp'
[002] {Producer} 'clang version 14.0.0'
[002] 2 {Function} inlined 'InlineFunction' -> 'int'
[003] {Parameter} '' -> 'int'
[002] {Function} extern not_inlined 'test' -> 'int'
[003] {Variable} 'A' -> 'int'
[003] {InlinedFunction} inlined 'InlineFunction' -> 'int'
[004] {Parameter} 'Param' -> 'int'
[004] {Variable} 'Var_1' -> 'int'
[004] {Variable} 'Var_2' -> 'int'
[003] {Parameter} 'Param_1' -> 'int'
[003] {Parameter} 'Param_2' -> 'int'
CODEVIEW - MSVC (Windows) πͺ
Logical View:
[000] {File} 'pr-43860-codeview-msvc.o' -> COFF-i386
[001] {CompileUnit} 'pr-43860.cpp'
[002] {Producer} 'Microsoft (R) Optimizing Compiler'
[002] {Function} extern not_inlined 'InlineFunction' -> 'int'
[003] {Block}
[004] {Variable} 'Var_2' -> 'int'
[003] {Variable} 'Param' -> 'int'
[003] {Variable} 'Var_1' -> 'int'
[002] {Function} extern not_inlined 'test' -> 'int'
[003] {Variable} 'A' -> 'int'
[003] {Variable} 'Param_1' -> 'int'
[003] {Variable} 'Param_2' -> 'int'
DWARF - Clang (Linux) π§
Logical View:
[000] {File} 'pr-43860-dwarf-clang.o' -> elf64-x86-64
[001] {CompileUnit} 'pr-43860.cpp'
[002] {Producer} 'clang version 14.0.0'
[002] 2 {Function} extern inlined 'InlineFunction' -> 'int'
[003] {Block}
[004] 5 {Variable} 'Var_2' -> 'int'
[003] 2 {Parameter} 'Param' -> 'int'
[003] 3 {Variable} 'Var_1' -> 'int'
[002] 11 {Function} extern not_inlined 'test' -> 'int'
[003] 12 {Variable} 'A' -> 'int'
[003] 14 {InlinedFunction} inlined 'InlineFunction' -> 'int'
[004] {Block}
[005] {Variable} 'Var_2' -> 'int'
[004] {Parameter} 'Param' -> 'int'
[004] {Variable} 'Var_1' -> 'int'
[003] 11 {Parameter} 'Param_1' -> 'int'
[003] 11 {Parameter} 'Param_2' -> 'int'
DWARF - GCC (Linux) π»
Logical View:
[000] {File} 'pr-43860-dwarf-gcc.o' -> elf64-x86-64
[001] {CompileUnit} 'pr-43860.cpp'
[002] {Producer} 'GNU C++14 9.3.0'
[002] 2 {Function} extern declared_inlined 'InlineFunction' -> 'int'
[003] {Block}
[004] 5 {Variable} 'Var_2' -> 'int'
[003] 2 {Parameter} 'Param' -> 'int'
[003] 3 {Variable} 'Var_1' -> 'int'
[002] 11 {Function} extern not_inlined 'test' -> 'int'
[003] 12 {Variable} 'A' -> 'int'
[003] 13 {InlinedFunction} declared_inlined 'InlineFunction' -> 'int'
[004] {Block}
[005] {Variable} 'Var_2' -> 'int'
[004] {Parameter} 'Param' -> 'int'
[004] {Variable} 'Var_1' -> 'int'
[003] 11 {Parameter} 'Param_1' -> 'int'
[003] 11 {Parameter} 'Param_2' -> 'int'
So looking at those logical views up there? Yeah π
the CodeView debug info that the Clang compiler generates shows Var_1 and Var_2 chillin' at the same lexical scope (4) inside the function InlineFuction. Meanwhile the DWARF from GCC/Clang and the CodeView from MSVC put those same variables at the correct lexical scope: 3 and 4 respectively. Iconic behavior gap, honestly β¨
And bestie, this is where the selection stuff in llvm-debuginfo-analyzer comes in clutch β we can spit out a lil' tabular output showing ONLY the logical elements that have the var pattern in their name. The logical view gets 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'
And it also shows that the CodeView debug information does not generate source code line numbers for those logical symbols π The logical view is sorted by the types name.
π¬ TEST CASE 6 β FULL LOGICAL VIEW π¬
Okay so 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. We're going goblin mode π§
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, that's the good stuff β
[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. So likeβ¦ optimized builds get fuzzier to debug, no cap π
πͺ EXIT STATUS πͺ
llvm-debuginfo-analyzer returns 0 if the input files were parsed and printed successfully π«Ά Otherwise, it returns 1. That's it, that's the whole vibe check β¨
π SEE ALSO π
llvm-dwarfdump <llvm-dwarfdump.html>
π Author π
Maintained by the LLVM Team (https://llvm.org/) π
π Copyright π
2003-2026, LLVM Project
And that's a wrap bestie!! π¦ This was version 15, dated 2026-09-09 β go off, LLVM-DEBUGINFO-ANALYZER(1), you absolute icon π β¨π
π¦ where this page comes from
- ubuntu stonking: llvm-18 1:18.1.8-20ubuntu11 Β· APACHE-2-LLVM-EXCEPTIONS
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms