Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions llvm/docs/CommandGuide/llvm-debuginfo-analyzer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,8 @@ to make the output easier to understand.
.. code-block:: text

=children: Elements and children are displayed in a tree format.
=debugger: Lines, and optionally variables and instructions are
displayed in a way to simulate stepping through a debugger.
=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.
Expand All @@ -410,6 +412,10 @@ child (level 1).
The **children** layout includes the elements that match any given
criteria (:option:`--select`) or (:option:`--compare`) and its children.

The **debugger** layout prints each statement line in order and variables
live at each line (if `--print=symbols` given), as well as instructions
(if `--print=instructions` given).

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

Expand Down Expand Up @@ -855,6 +861,87 @@ layout and given the number of matches.
-----------------------------
Total 26 8

DEBUGGER VIEW
"""""""""""""
In debugger view, :program:`llvm-debuginfo-analyzer` prints out
debug-info in a manner that emulates a debugger. For each function, each
statement line is printed out in order, complete with the inlined
callstack. This is useful to verify the specific orders of lines, as
well as verifying inline callstacks.

Copy link
Member

@CarlosAlbertoEnciso CarlosAlbertoEnciso Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency with other View layouts, the Debugger View should support additional attributes such as --attribute=offset,level.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added support for them. In fact the offsets and levels are controlled by the same mechanism for printing them as the other options.

.. code-block:: none

llvm-debuginfo-analyzer --report=debugger
test-dwarf-clang.o test-dwarf-gcc.o

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency with the another general printing layouts, include:
Logical View title follow by {File and CompileUnit entries

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


Logical View:
{File} test-dwarf-clang.o
{CompileUnit} test.cpp
{Function} foo
{Line} test.cpp:2 [foo]
{Line} test.cpp:3 [foo]
{Line} test.cpp:5 [foo]
{Line} test.cpp:6 [foo]
{Line} test.cpp:8 [foo]
{Line} test.cpp:9 [foo]

Logical View:
{File} test-dwarf-gcc.o
{CompileUnit} test.cpp
{Function} foo
{Line} test.cpp:2 [foo]
{Line} test.cpp:3 [foo]
{Line} test.cpp:5 [foo]
{Line} test.cpp:6 [foo]
{Line} test.cpp:8 [foo]
{Line} test.cpp:9 [foo]

Optionally, by adding `--print=symbols`, live variables for each line is
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Optionally, by adding `--print=symbols`, live variables for each line is
Optionally, by adding `--print=symbols`, live variables for each line are

printed out.

.. code-block:: none

llvm-debuginfo-analyzer --report=debugger
test-dwarf-clang.o

Logical View:
{File} test-dwarf-clang.o
{CompileUnit} test.cpp
{Function} foo
{Line} test.cpp:2 [foo]
{Parameter} ParamBool: bool : fbreg -21 (line 2)
{Parameter} ParamPtr: INTPTR : fbreg -16 (line 2)
{Parameter} ParamUnsigned: unsigned int : fbreg -20 (line 2)
{Line} test.cpp:3 [foo]
{Parameter} ParamBool: bool : fbreg -21 (line 2)
{Parameter} ParamPtr: INTPTR : fbreg -16 (line 2)
{Parameter} ParamUnsigned: unsigned int : fbreg -20 (line 2)
Copy link
Member

@CarlosAlbertoEnciso CarlosAlbertoEnciso Oct 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The format looks good, but I have some questions/suggestions.

Logical View:
  {File} test-dwarf-clang.o
  {CompileUnit} test.cpp
  {Function} foo
    {Line}  test.cpp:2 [foo]
      {Parameter} ParamBool: bool : fbreg -21 (line 2)
      {Parameter} ParamPtr: INTPTR : fbreg -16 (line 2)
      {Parameter} ParamUnsigned: unsigned int : fbreg -20 (line 2)
    {Line}  test.cpp:3 [foo]
      {Parameter} ParamBool: bool : fbreg -21 (line 2)
      {Parameter} ParamPtr: INTPTR : fbreg -16 (line 2)
      {Parameter} ParamUnsigned: unsigned int : fbreg -20 (line 2)

As you are using indentation to describe the lexical scope, to keep consistency, the {CompileUnit} and {Function} should have different indentation as the {File}. Also, any string (type name, symbol name) are enclosed by single quotes.

Logical View:
{File} 'test-dwarf-clang.o'
                                <-- blank line
  {CompileUnit} 'test.cpp'
    {Function} 'foo' -> 'int'   <-- type

Any specific reasons for the symbol location to be on the same line as the symbol information. For optimized code, you can have multiple location entries. That is why we print the locations in a different lexical scope:

{Parameter} 'ParamBool' -> 'bool' : fbreg -21 (line 2)
-->
{Parameter} 'ParamBool' -> 'bool' (line 2)
  {Location}
    {Entry} fbreg -21    

{Line} test.cpp:5 [foo]
{Parameter} ParamBool: bool : fbreg -21 (line 2)
{Parameter} ParamPtr: INTPTR : fbreg -16 (line 2)
{Parameter} ParamUnsigned: unsigned int : fbreg -20 (line 2)
{Variable} CONSTANT: const INTEGER : fbreg -28 (line 5)
{Line} test.cpp:6 [foo]
{Parameter} ParamBool: bool : fbreg -21 (line 2)
{Parameter} ParamPtr: INTPTR : fbreg -16 (line 2)
{Parameter} ParamUnsigned: unsigned int : fbreg -20 (line 2)
{Variable} CONSTANT: const INTEGER : fbreg -28 (line 5)
{Line} test.cpp:8 [foo]
{Parameter} ParamBool: bool : fbreg -21 (line 2)
{Parameter} ParamPtr: INTPTR : fbreg -16 (line 2)
{Parameter} ParamUnsigned: unsigned int : fbreg -20 (line 2)
{Line} test.cpp:9 [foo]
{Parameter} ParamBool: bool : fbreg -21 (line 2)
{Parameter} ParamPtr: INTPTR : fbreg -16 (line 2)
{Parameter} ParamUnsigned: unsigned int : fbreg -20 (line 2)

Optionally, `--print=instructions`, the lines are interleaved with the
instructions. Combined with the output of `--print=symbols`, tests can
verify specific expressions for live variables.

Additionally, `--attribute` can be used to include things such as
offsets and scope levels for {Line} and {Instruction}.

COMPARISON MODE
^^^^^^^^^^^^^^^
In this mode :program:`llvm-debuginfo-analyzer` compares logical views
Expand Down
4 changes: 4 additions & 0 deletions llvm/include/llvm/DebugInfo/LogicalView/Core/LVLine.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ class LLVM_ABI LVLine : public LVElement {

void print(raw_ostream &OS, bool Full = true) const override;
void printExtra(raw_ostream &OS, bool Full = true) const override {}
virtual void printDebugger(raw_ostream &OS, LVLevel Indent) const {}
};

// Class to represent a DWARF line record object.
Expand Down Expand Up @@ -134,6 +135,8 @@ class LLVM_ABI LVLineDebug final : public LVLine {
bool equals(const LVLine *Line) const override;

void printExtra(raw_ostream &OS, bool Full = true) const override;
void printDebugger(raw_ostream &OS, LVLevel Indent) const override;
void printInlineCallstack(raw_ostream &OS) const;
};

// Class to represent an assembler line extracted from the text section.
Expand All @@ -153,6 +156,7 @@ class LLVM_ABI LVLineAssembler final : public LVLine {
bool equals(const LVLine *Line) const override;

void printExtra(raw_ostream &OS, bool Full = true) const override;
void printDebugger(raw_ostream &OS, LVLevel Indent) const override;
};

} // end namespace logicalview
Expand Down
4 changes: 4 additions & 0 deletions llvm/include/llvm/DebugInfo/LogicalView/Core/LVLocation.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,10 @@ class LLVM_ABI LVLocation : public LVObject {
void printRaw(raw_ostream &OS, bool Full = true) const;
virtual void printRawExtra(raw_ostream &OS, bool Full = true) const {}

virtual void printLocations(raw_ostream &OS) const {}
void print(raw_ostream &OS, bool Full = true) const override;
void printExtra(raw_ostream &OS, bool Full = true) const override;
virtual void printDebugger(raw_ostream &OS, LVLevel Indent) const {}
};

class LLVM_ABI LVLocationSymbol final : public LVLocation {
Expand All @@ -177,8 +179,10 @@ class LLVM_ABI LVLocationSymbol final : public LVLocation {
uint64_t LocDescOffset) override;
void addObject(LVSmall Opcode, ArrayRef<LVUnsigned> Operands) override;

void printLocations(raw_ostream &OS) const override;
void printRawExtra(raw_ostream &OS, bool Full = true) const override;
void printExtra(raw_ostream &OS, bool Full = true) const override;
void printDebugger(raw_ostream &OS, LVLevel Indent) const override;
};

} // end namespace logicalview
Expand Down
2 changes: 2 additions & 0 deletions llvm/include/llvm/DebugInfo/LogicalView/Core/LVOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ using LVPrintKindSet = std::set<LVPrintKind>;
enum class LVReportKind {
All, // --report=all
Children, // --report=children
Debugger, // --report=debugger
List, // --report=list
Parents, // --report=parents
View // --report=view
Expand Down Expand Up @@ -406,6 +407,7 @@ class LVOptions {
// --report.
REPORT_OPTION(All);
REPORT_OPTION(Children);
REPORT_OPTION(Debugger);
REPORT_OPTION(List);
REPORT_OPTION(Parents);
REPORT_OPTION(View);
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ class LLVM_ABI LVReader {
return std::string(Path);
}

Error printDebugger();
virtual Error printScopes();
virtual Error printMatchedElements(bool UseMatchedElements);
virtual void sortScopes() {}
Expand Down
8 changes: 8 additions & 0 deletions llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,9 @@ class LLVM_ABI LVScope : public LVElement {
// given 'Targets'.
static bool equals(const LVScopes *References, const LVScopes *Targets);

// Returns true if 'Scope' is in the parent stack, false if not.
bool isChildScopeOf(const LVScope *Scope) const;

// For the given 'Scopes' returns a scope that is logically equal
// to the current scope; otherwise 'nullptr'.
virtual LVScope *findEqualScope(const LVScopes *Scopes) const;
Expand All @@ -338,6 +341,8 @@ class LLVM_ABI LVScope : public LVElement {
void printExtra(raw_ostream &OS, bool Full = true) const override;
virtual void printWarnings(raw_ostream &OS, bool Full = true) const {}
virtual void printMatchedElements(raw_ostream &OS, bool UseMatchedElements) {}
virtual void printDebugger(raw_ostream &OS) const {}
virtual void printInlineCallstack(raw_ostream &OS) const {}
};

// Class to represent a DWARF Union/Structure/Class.
Expand Down Expand Up @@ -635,6 +640,7 @@ class LLVM_ABI LVScopeCompileUnit final : public LVScope {
void printExtra(raw_ostream &OS, bool Full = true) const override;
void printWarnings(raw_ostream &OS, bool Full = true) const override;
void printMatchedElements(raw_ostream &OS, bool UseMatchedElements) override;
void printDebugger(raw_ostream &OS) const override;
};

// Class to represent a DWARF enumerator (DW_TAG_enumeration_type).
Expand Down Expand Up @@ -716,6 +722,7 @@ class LLVM_ABI LVScopeFunction : public LVScope {
LVScope *findEqualScope(const LVScopes *Scopes) const override;

void printExtra(raw_ostream &OS, bool Full = true) const override;
void printDebugger(raw_ostream &OS) const override;
};

// Class to represent a DWARF inlined function.
Expand Down Expand Up @@ -850,6 +857,7 @@ class LLVM_ABI LVScopeRoot final : public LVScope {
void printExtra(raw_ostream &OS, bool Full = true) const override;
Error doPrintMatches(bool Split, raw_ostream &OS,
bool UseMatchedElements) const;
Error doPrintDebugger(bool Split, raw_ostream &OS) const;
};

// Class to represent a DWARF template parameter pack
Expand Down
31 changes: 31 additions & 0 deletions llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,31 @@ void LVLineDebug::printExtra(raw_ostream &OS, bool Full) const {
OS << "\n";
}

void LVLineDebug::printInlineCallstack(raw_ostream &OS) const {
const LVScope *Scope = getParentScope();
const LVScope *PrevScope = nullptr;
while (Scope) {
if (Scope->getIsFunction() || Scope->getIsInlinedFunction()) {
OS << "[" << Scope->getName();
if (PrevScope && PrevScope->getIsInlinedFunction()) {
OS << ":"
<< cast<LVScopeFunctionInlined>(PrevScope)->getCallLineNumber();
}
OS << "]";
PrevScope = Scope;
}
Scope = Scope->getParentScope();
}
}

void LVLineDebug::printDebugger(raw_ostream &OS, LVLevel Indent) const {
OS << indentAsString(Indent) << formattedKind(kind()) << " ";
printAttributes(OS);
OS << " " << getPathname() << ":" << getLineNumber() << " ";
printInlineCallstack(OS);
OS << "\n";
}

//===----------------------------------------------------------------------===//
// Assembler line extracted from the ELF .text section.
//===----------------------------------------------------------------------===//
Expand All @@ -220,3 +245,9 @@ void LVLineAssembler::printExtra(raw_ostream &OS, bool Full) const {
OS << " " << formattedName(getName());
OS << "\n";
}

void LVLineAssembler::printDebugger(raw_ostream &OS, LVLevel Indent) const {
OS << indentAsString(Indent) << formattedKind(kind()) << " ";
printAttributes(OS);
OS << " " << getName() << "\n";
}
34 changes: 25 additions & 9 deletions llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,19 @@ void LVLocation::print(LVLocations *Locations, raw_ostream &OS, bool Full) {
Location->print(OS, Full);
}

void LVLocationSymbol::printLocations(raw_ostream &OS) const {
if (Entries) {
bool CodeViewLocation = getParentSymbol()->getHasCodeViewLocation();
std::string Leading;
for (LVOperation *Operation : *Entries) {
OS << Leading
<< (CodeViewLocation ? Operation->getOperandsCodeViewInfo()
: Operation->getOperandsDWARFInfo());
Leading = ", ";
}
Comment on lines +654 to +660
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I see that this code was moved from this original location, but perhaps we could take the chance to adopt llvm::interleave() to print to OS w/ interleaved ,s.

}
}

void LVLocationSymbol::printExtra(raw_ostream &OS, bool Full) const {
OS << "{Location}";
if (getIsCallSite())
Expand All @@ -657,18 +670,21 @@ void LVLocationSymbol::printExtra(raw_ostream &OS, bool Full) const {

// Print location entries.
if (Full && Entries) {
bool CodeViewLocation = getParentSymbol()->getHasCodeViewLocation();
std::stringstream Stream;
std::string Leading;
for (LVOperation *Operation : *Entries) {
Stream << Leading
<< (CodeViewLocation ? Operation->getOperandsCodeViewInfo()
: Operation->getOperandsDWARFInfo());
Leading = ", ";
}
std::string Str;
raw_string_ostream Stream(Str);
printLocations(Stream);
printAttributes(OS, Full, "{Entry} ", const_cast<LVLocationSymbol *>(this),
StringRef(Stream.str()),
/*UseQuotes=*/false,
/*PrintRef=*/false);
}
}

void LVLocationSymbol::printDebugger(raw_ostream &OS, LVLevel Indent) const {
LVSymbol *Sym = getParentSymbol();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency: Instead of Sym use Symbol

OS << indentAsString(Indent) << formattedKind(Sym->kind());
OS << " " << Sym->getName() << ": " << Sym->getType()->getName() << " : ";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC, getType() may return nullptr under certain circumstances (e.g., for manually-forged or otherwise incomplete DWARF input). I think we should check for non-null here before dereferencing.

printLocations(OS);
OS << " (line " << Sym->getLineNumber() << ")";
OS << "\n";
}
11 changes: 10 additions & 1 deletion llvm/lib/DebugInfo/LogicalView/Core/LVOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,14 @@ void LVOptions::resolveDependencies() {
setPrintWarnings();
}

if (getReportDebugger()) {
// Must include at least the lines, otherwise there's nothing to print
setPrintLines();
// Printing symbols in debugger report requires the symbol ranges
if (getPrintSymbols())
setAttributeRange();
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a very minor points. Missing full stops in the comments.

// '--warning=all' settings.
if (getWarningAll()) {
setWarningCoverages();
Expand Down Expand Up @@ -186,6 +194,7 @@ void LVOptions::resolveDependencies() {
// '--reports=all' settings.
if (getReportAll()) {
setReportChildren();
setReportDebugger();
setReportList();
setReportParents();
setReportView();
Expand All @@ -202,7 +211,7 @@ void LVOptions::resolveDependencies() {
setReportAnyView();

// The report will include: List or Parents or Children.
if (getReportList() || getReportAnyView())
if (getReportList() || getReportAnyView() || getReportDebugger())
setReportExecute();

// If a view or element comparison has been requested, the following options
Expand Down
12 changes: 11 additions & 1 deletion llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -516,13 +516,23 @@ Error LVReader::doPrint() {
if (options().getReportParents() || options().getReportView())
if (Error Err = printScopes())
return Err;

// Requested debugger report.
if (options().getReportDebugger())
if (Error Err = printDebugger())
return Err;
return Error::success();
}

return printScopes();
}

Error LVReader::printDebugger() {
if (Error Err = createSplitFolder())
return Err;

return Root->doPrintDebugger(OutputSplit, outs());
}

Error LVReader::printScopes() {
if (bool DoPrint =
(options().getPrintExecute() || options().getComparePrint())) {
Expand Down
Loading