Skip to content
Open
Show file tree
Hide file tree
Changes from 11 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
6 changes: 6 additions & 0 deletions llvm/docs/CommandGuide/llvm-debuginfo-analyzer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,8 @@ to make the output easier to understand.
=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.
=debugger: Lines, and optionally variables and instructions are
displayed in a way to simulate stepping through a debugger.

The **list** layout presents the logical elements in a tabular form
without any parent-child relationship. This may be the preferred way to
Expand All @@ -417,6 +419,10 @@ The combined **view** layout includes the elements that match any given
criteria (:option:`--select`) or (:option:`--compare`), its parents
and children.

The combined **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).

Copy link
Member

Choose a reason for hiding this comment

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

Can we add a section between SELECT LOGICAL ELEMENTS and COMPARISON MODE that shows the debugger view, using one of the input files located in llvm/test/tools/llvm-debuginfo-analyzer. First using the standard logical view and then the debugger view.
Something like:

DEBUGGER VIEW
"""""""""""""
The following prints ... logical view ...

.. code-block:: none

  llvm-debuginfo-analyzer --print=symbols test-dwarf-clang.o

  the generated logical view ...  

The following prints ... debugger view ...

.. code-block:: none

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

  the generated debugger view ...  

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 this section.

**Notes**:

1. When a selection criteria (:option:`--select`) is specified with no
Expand Down
2 changes: 2 additions & 0 deletions llvm/include/llvm/DebugInfo/LogicalView/Core/LVLocation.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ 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;
};
Expand All @@ -177,6 +178,7 @@ 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;
};
Expand Down
4 changes: 3 additions & 1 deletion llvm/include/llvm/DebugInfo/LogicalView/Core/LVOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,8 @@ enum class LVReportKind {
Children, // --report=children
List, // --report=list
Parents, // --report=parents
View // --report=view
View, // --report=view
Debugger // --report=debugger
};
using LVReportKindSet = std::set<LVReportKind>;

Expand Down Expand Up @@ -408,6 +409,7 @@ class LVOptions {
REPORT_OPTION(Children);
REPORT_OPTION(List);
REPORT_OPTION(Parents);
REPORT_OPTION(Debugger);
Copy link
Member

Choose a reason for hiding this comment

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

Can we preserve the alphabetical order?

REPORT_OPTION(View);
BOOL_FUNCTION(Report, AnyView);
BOOL_FUNCTION(Report, Execute);
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
25 changes: 16 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,15 +670,9 @@ 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,
Expand Down
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 @@ -189,6 +197,7 @@ void LVOptions::resolveDependencies() {
setReportList();
setReportParents();
setReportView();
setReportDebugger();
}

// '--report=view' is a shortcut for '--report=parents,children'.
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
171 changes: 170 additions & 1 deletion llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//

#include "llvm/DebugInfo/LogicalView/Core/LVReader.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/DebugInfo/LogicalView/Core/LVScope.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormatAdapters.h"
Expand Down Expand Up @@ -516,13 +517,181 @@ 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();
}

namespace {

struct DebuggerViewPrinter {
std::vector<const LVLine *> Lines;
std::unordered_map<LVAddress, std::vector<const LVLocation *>> LifetimeBegins;
std::unordered_map<LVAddress, std::vector<const LVLocation *>>
LifetimeEndsExclusive;
raw_ostream &OS;

const bool IncludeRanges = false;

void Walk(raw_ostream &OS, const LVScope *Scope) {
if (Scope->scopeCount()) {
for (const LVScope *ChildScope : *Scope->getScopes())
Walk(OS, ChildScope);
}
if (Scope->lineCount()) {
for (const LVLine *Line : *Scope->getLines()) {
Lines.push_back(Line);
}
}
if (Scope->symbolCount()) {
for (const LVSymbol *Symbol : *Scope->getSymbols()) {
LVLocations SymbolLocations;
Symbol->getLocations(SymbolLocations);
if (SymbolLocations.empty())
continue;

if (IncludeRanges)
OS << "{Range}: " << Symbol->getName() << " (line "
<< Symbol->getLineNumber() << ")" << ": ";

for (const LVLocation *Loc : SymbolLocations) {
if (Loc->getIsGapEntry())

Choose a reason for hiding this comment

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

Loc --> Location

continue;

LVAddress Begin = Loc->getLowerAddress();
LVAddress End = Loc->getUpperAddress();
LifetimeBegins[Begin].push_back(Loc);
LifetimeEndsExclusive[End].push_back(Loc);

if (IncludeRanges)
OS << "[" << hexValue(Begin) << ":" << hexValue(End) << "] ";
}

if (IncludeRanges)
OS << "\n";
}
}
}

DebuggerViewPrinter(raw_ostream &OS, const LVScopeFunction *Fn) : OS(OS) {
Walk(OS, Fn);
std::sort(Lines.begin(), Lines.end(),
[](const LVLine *a, const LVLine *b) -> bool {
if (a->getAddress() != b->getAddress())
return a->getAddress() < b->getAddress();
if (a->getIsLineDebug() != b->getIsLineDebug())
return a->getIsLineDebug();
return a->getID() < b->getID();
});
}

static void PrintIndent(raw_ostream &OS, int Indent) {
for (int i = 0; i < Indent; i++)
OS << " ";
}

static void PrintCallstack(raw_ostream &OS, const LVScope *Scope) {
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();
}
}

static bool IsChildScopeOf(const LVScope *A, const LVScope *B) {
while (A) {
A = A->getParentScope();
if (A == B)
return true;
}
return false;
}

void Print() {
const bool IncludeVars = options().getPrintSymbols();
const bool IncludeCode = options().getPrintInstructions();
SetVector<const LVLocation *>
LiveSymbols; // This needs to be ordered since we're iterating over it.
for (const LVLine *Line : Lines) {
const LVScope *Scope = Line->getParentScope();
// Update live list: Add lives
for (auto Loc : LifetimeBegins[Line->getAddress()])
LiveSymbols.insert(Loc);
// Update live list: remove dead
for (auto Loc : LifetimeEndsExclusive[Line->getAddress()])
LiveSymbols.remove(Loc);

if (Line->getIsNewStatement() && Line->getIsLineDebug() &&
Line->getLineNumber() != 0) {
auto LineDebug = cast<LVLineDebug>(Line);

PrintIndent(OS, 1);
OS << "{Line}: " << " [" << hexValue(LineDebug->getAddress()) << "] "
<< LineDebug->getPathname() << ":" << LineDebug->getLineNumber()
<< " ";
PrintCallstack(OS, Scope);
OS << "\n";
if (IncludeVars) {
for (auto SymLoc : LiveSymbols) {
const LVSymbol *Sym = SymLoc->getParentSymbol();
auto SymScope = Sym->getParentScope();
auto LineScope = LineDebug->getParentScope();
if (SymScope != LineScope && !IsChildScopeOf(LineScope, SymScope))
continue;
PrintIndent(OS, 2);
OS << "{Variable}: " << Sym->getName() << ": "
<< Sym->getType()->getName() << " : ";
SymLoc->printLocations(OS);
OS << " (line " << Sym->getLineNumber() << ")";
OS << "\n";
}
}

} else if (IncludeCode && Line->getIsLineAssembler()) {
OS << " {Code}: " << " [" << hexValue(Line->getAddress()) << "] "
<< Line->getName() << "\n";
}
}
}
};

} // namespace

Error LVReader::printDebugger() {
auto *CU = getCompileUnit();
Copy link
Member

@CarlosAlbertoEnciso CarlosAlbertoEnciso Oct 9, 2025

Choose a reason for hiding this comment

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

Have you considered the case of multiple CUs in the same input file?
The CU returned by getCompileUnit() points to the last CU processed.
I would suggest to start with the Scope Root and traverse its children that are Compile Units.

for (const LVScopeCompileUnit *CU : Root->getScopes()) {
  for (const LVScope *ChildScope : *CU->getScopes()) {
    ..
  }
}

In that way, the debugger view is generated for all CUs in the input file.

if (!CU)
return createStringError(std::make_error_code(std::errc::invalid_argument),
"Error: No compute unit found.");
Copy link
Member

Choose a reason for hiding this comment

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

May be: compile unit?


for (const LVScope *ChildScope : *CU->getScopes()) {
auto *Fn = dyn_cast<LVScopeFunction>(ChildScope);
if (Fn) {
const LVLines *Lines = Fn->getLines();
// If there's no lines, this function has no body.
if (!Lines)
continue;
outs() << "{Function}: " << ChildScope->getName() << "\n";

DebuggerViewPrinter P(OS, Fn);
P.Print();
}
}
return Error::success();
}

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