-
Notifications
You must be signed in to change notification settings - Fork 15.1k
Added --report=debugger option to llvm-debuginfo-analyzer #159853
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
|
@llvm/pr-subscribers-backend-amdgpu @llvm/pr-subscribers-debuginfo Author: Adam Yang (adam-yang) Changes--debugger-view is a new mode for /*01*/ float bar(float b) {
/*02*/ float ret = sin(b);
/*03*/ return ret;
/*04*/ }
/*05*/
/*06*/ float foo(float a) {
/*07*/ float my_var = sin(a);
/*08*/ float my_var2 = my_var * 3;
/*09*/ float my_var3 = bar(my_var2);
/*10*/ return my_var3;
/*11*/ }
/*12*/
/*13*/ [RootSignature("DescriptorTable(UAV(u0,numDescriptors=2))")]
/*14*/ [numthreads(64,1,1)]
/*15*/ void main(uint3 dtid : SV_DispatchThreadID) {
/*16*/ float arg = u0[dtid.x];
/*17*/ float ret = foo(arg);
/*18*/ u1[dtid.x] = ret;
/*19*/ }The default This allows for easy offline verification of specific inline call-stacks, line mapping order, etc. Another option Optionally, Patch is 22.21 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/159853.diff 7 Files Affected:
diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVLocation.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVLocation.h
index 0718e33f5645b..c55b8259b4ee8 100644
--- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVLocation.h
+++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVLocation.h
@@ -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;
};
@@ -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;
};
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp
index 3c078d8ee74b8..4724f4f007163 100644
--- a/llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp
@@ -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 = ", ";
+ }
+ }
+}
+
void LVLocationSymbol::printExtra(raw_ostream &OS, bool Full) const {
OS << "{Location}";
if (getIsCallSite())
@@ -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,
diff --git a/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll b/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll
new file mode 100644
index 0000000000000..56d6d3b9c9dbd
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll
@@ -0,0 +1,110 @@
+; RUN: llc %s -o %t.o -mcpu=gfx1030 -filetype=obj -O0
+; RUN: llvm-debuginfo-analyzer --debugger-view --debugger-view-vars %t.o | FileCheck %s
+
+; This test compiles this module with AMDGPU backend under -O0,
+; and makes sure llvm-debuginfo-check works for it.
+
+; CHECK: FUNCTION: main
+; CHECK: LINE: {{.+}}basic_var.hlsl:7
+; CHECK: LINE: {{.+}}basic_var.hlsl:11
+; CHECK: VAR: dtid: uint3 : reg{{.+}}, piece 4
+; CHECK: LINE: {{.+}}basic_var.hlsl:17
+; CHECK: VAR: dtid: uint3 : reg{{.+}}, piece 4
+; CHECK: LINE: {{.+}}basic_var.hlsl:11
+; CHECK: VAR: dtid: uint3 : reg{{.+}}, piece 4
+; CHECK: LINE: {{.+}}basic_var.hlsl:14
+; CHECK-DAG: VAR: dtid: uint3 : reg{{.+}}, piece 4
+; CHECK: LINE: {{.+}}basic_var.hlsl:17
+; CHECK-DAG: VAR: dtid: uint3 : reg{{.+}}, piece 4
+; CHECK-DAG: VAR: my_var2: float : reg{{.+}}
+; CHECK: LINE: {{.+}}basic_var.hlsl:19
+; CHECK-DAG: VAR: dtid: uint3 : reg{{.+}}, piece 4
+; CHECK-DAG: VAR: my_var2: float : reg{{.+}}
+
+source_filename = "module"
+target triple = "amdgcn-amd-amdpal"
+
+%dx.types.ResRet.f32 = type { float, float, float, float, i32 }
+
+; Function Attrs: memory(readwrite)
+define dllexport amdgpu_cs void @_amdgpu_cs_main(i32 inreg noundef %globalTable, i32 inreg noundef %userdata4, <3 x i32> inreg noundef %WorkgroupId, i32 inreg noundef %MultiDispatchInfo, <3 x i32> noundef %LocalInvocationId) #0 !dbg !14 {
+ %LocalInvocationId.i0 = extractelement <3 x i32> %LocalInvocationId, i64 0, !dbg !28
+ %WorkgroupId.i0 = extractelement <3 x i32> %WorkgroupId, i64 0, !dbg !28
+ %1 = call i64 @llvm.amdgcn.s.getpc(), !dbg !28
+ %2 = shl i32 %WorkgroupId.i0, 6, !dbg !28
+ %3 = add i32 %LocalInvocationId.i0, %2, !dbg !28
+ #dbg_value(i32 %3, !29, !DIExpression(DW_OP_LLVM_fragment, 0, 32), !28)
+ %4 = and i64 %1, -4294967296, !dbg !30
+ %5 = zext i32 %userdata4 to i64, !dbg !30
+ %6 = or disjoint i64 %4, %5, !dbg !30
+ %7 = inttoptr i64 %6 to ptr addrspace(4), !dbg !30
+ call void @llvm.assume(i1 true) [ "align"(ptr addrspace(4) %7, i32 4), "dereferenceable"(ptr addrspace(4) %7, i32 -1) ], !dbg !30
+ %8 = load <4 x i32>, ptr addrspace(4) %7, align 4, !dbg !30, !invariant.load !2
+ %9 = call float @llvm.amdgcn.struct.buffer.load.format.f32(<4 x i32> %8, i32 %3, i32 0, i32 0, i32 0), !dbg !30
+ #dbg_value(%dx.types.ResRet.f32 poison, !31, !DIExpression(), !32)
+ %10 = fmul reassoc arcp contract afn float %9, 2.000000e+00, !dbg !33
+ #dbg_value(float %10, !34, !DIExpression(), !35)
+ call void @llvm.assume(i1 true) [ "align"(ptr addrspace(4) %7, i32 4), "dereferenceable"(ptr addrspace(4) %7, i32 -1) ], !dbg !36
+ %11 = getelementptr i8, ptr addrspace(4) %7, i64 32, !dbg !36
+ %.upto01 = insertelement <4 x float> poison, float %10, i64 0, !dbg !36
+ %12 = shufflevector <4 x float> %.upto01, <4 x float> poison, <4 x i32> zeroinitializer, !dbg !36
+ %13 = load <4 x i32>, ptr addrspace(4) %11, align 4, !dbg !36, !invariant.load !2
+ call void @llvm.amdgcn.struct.buffer.store.format.v4f32(<4 x float> %12, <4 x i32> %13, i32 %3, i32 0, i32 0, i32 0), !dbg !36
+ ret void, !dbg !37
+}
+
+declare noundef i64 @llvm.amdgcn.s.getpc() #1
+
+declare void @llvm.assume(i1 noundef) #2
+
+declare void @llvm.amdgcn.struct.buffer.store.format.v4f32(<4 x float>, <4 x i32>, i32, i32, i32, i32 immarg) #3
+
+declare float @llvm.amdgcn.struct.buffer.load.format.f32(<4 x i32>, i32, i32, i32, i32 immarg) #4
+
+attributes #0 = { memory(readwrite) }
+attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+attributes #2 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }
+attributes #3 = { nocallback nofree nosync nounwind willreturn memory(write) }
+attributes #4 = { nocallback nofree nosync nounwind willreturn memory(read) }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!12, !13}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "dxcoob 1.7.2308.16 (52da17e29)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, globals: !3)
+!1 = !DIFile(filename: "tests\\basic_var.hlsl", directory: "")
+!2 = !{}
+!3 = !{!4, !10}
+!4 = distinct !DIGlobalVariableExpression(var: !5, expr: !DIExpression())
+!5 = !DIGlobalVariable(name: "u0", linkageName: "\01?u0@@3V?$RWBuffer@M@@A", scope: !0, file: !1, line: 2, type: !6, isLocal: false, isDefinition: true)
+!6 = !DICompositeType(tag: DW_TAG_class_type, name: "RWBuffer<float>", file: !1, line: 2, size: 32, align: 32, elements: !2, templateParams: !7)
+!7 = !{!8}
+!8 = !DITemplateTypeParameter(name: "element", type: !9)
+!9 = !DIBasicType(name: "float", size: 32, align: 32, encoding: DW_ATE_float)
+!10 = distinct !DIGlobalVariableExpression(var: !11, expr: !DIExpression())
+!11 = !DIGlobalVariable(name: "u1", linkageName: "\01?u1@@3V?$RWBuffer@M@@A", scope: !0, file: !1, line: 3, type: !6, isLocal: false, isDefinition: true)
+!12 = !{i32 2, !"Dwarf Version", i32 5}
+!13 = !{i32 2, !"Debug Info Version", i32 3}
+!14 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 7, type: !15, scopeLine: 7, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!15 = !DISubroutineType(types: !16)
+!16 = !{null, !17}
+!17 = !DIDerivedType(tag: DW_TAG_typedef, name: "uint3", file: !1, baseType: !18)
+!18 = !DICompositeType(tag: DW_TAG_class_type, name: "vector<unsigned int, 3>", file: !1, size: 96, align: 32, elements: !19, templateParams: !24)
+!19 = !{!20, !22, !23}
+!20 = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: !18, file: !1, baseType: !21, size: 32, align: 32, flags: DIFlagPublic)
+!21 = !DIBasicType(name: "unsigned int", size: 32, align: 32, encoding: DW_ATE_unsigned)
+!22 = !DIDerivedType(tag: DW_TAG_member, name: "y", scope: !18, file: !1, baseType: !21, size: 32, align: 32, offset: 32, flags: DIFlagPublic)
+!23 = !DIDerivedType(tag: DW_TAG_member, name: "z", scope: !18, file: !1, baseType: !21, size: 32, align: 32, offset: 64, flags: DIFlagPublic)
+!24 = !{!25, !26}
+!25 = !DITemplateTypeParameter(name: "element", type: !21)
+!26 = !DITemplateValueParameter(name: "element_count", type: !27, value: i32 3)
+!27 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed)
+!28 = !DILocation(line: 7, column: 17, scope: !14)
+!29 = !DILocalVariable(name: "dtid", arg: 1, scope: !14, file: !1, line: 7, type: !17)
+!30 = !DILocation(line: 11, column: 18, scope: !14)
+!31 = !DILocalVariable(name: "my_var", scope: !14, file: !1, line: 11, type: !9)
+!32 = !DILocation(line: 11, column: 9, scope: !14)
+!33 = !DILocation(line: 14, column: 26, scope: !14)
+!34 = !DILocalVariable(name: "my_var2", scope: !14, file: !1, line: 14, type: !9)
+!35 = !DILocation(line: 14, column: 9, scope: !14)
+!36 = !DILocation(line: 17, column: 14, scope: !14)
+!37 = !DILocation(line: 19, column: 1, scope: !14)
diff --git a/llvm/tools/llvm-debuginfo-analyzer/CMakeLists.txt b/llvm/tools/llvm-debuginfo-analyzer/CMakeLists.txt
index 3e16d81abe35c..9de3078ffee55 100644
--- a/llvm/tools/llvm-debuginfo-analyzer/CMakeLists.txt
+++ b/llvm/tools/llvm-debuginfo-analyzer/CMakeLists.txt
@@ -15,4 +15,5 @@ set(LLVM_LINK_COMPONENTS
add_llvm_tool(llvm-debuginfo-analyzer
llvm-debuginfo-analyzer.cpp
Options.cpp
+ DebuggerView.cpp
)
diff --git a/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.cpp b/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.cpp
new file mode 100644
index 0000000000000..015096d49bd95
--- /dev/null
+++ b/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.cpp
@@ -0,0 +1,272 @@
+//===-- DebuggerView.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Options and functions related to --debugger-view for llvm-debuginfo-analyzer
+//
+//===----------------------------------------------------------------------===//
+
+#include "DebuggerView.h"
+#include "Options.h"
+#include "llvm/ADT/SetVector.h"
+#include "llvm/DebugInfo/LogicalView/LVReaderHandler.h"
+
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+
+using namespace llvm;
+using namespace debuggerview;
+
+cl::OptionCategory llvm::debuggerview::Category(
+ "Debugger View",
+ "Special printing mode that emulate how debugger uses debug info.");
+
+cl::opt<bool> llvm::debuggerview::Enable(
+ "debugger-view",
+ cl::desc("Enables debugger view. Normal debug-info printing is disabled "
+ "and options are ignored."),
+ cl::init(false), cl::cat(Category));
+static cl::opt<bool>
+ IncludeVars("debugger-view-vars",
+ cl::desc("Include live variables at each statement line."),
+ cl::init(false), cl::cat(Category));
+static cl::opt<bool>
+ IncludeCode("debugger-view-code",
+ cl::desc("Include disassembly at each statement line"),
+ cl::init(false), cl::cat(Category));
+static cl::opt<bool> IncludeRanges("debugger-view-ranges",
+ cl::desc("Include variable ranges"),
+ cl::init(false), cl::cat(Category));
+static cl::opt<bool> Help(
+ "debugger-view-help",
+ cl::desc(
+ "Print a detailed help screen about what kind of output to expect"),
+ cl::init(false), cl::cat(Category));
+
+constexpr const char *HelpText =
+ R"(Prints debug info in a way that is easy to verify correctness of debug info.
+FUNCTION: main
+ LINE: my_source_file.c:1 [main] <---- New statement lines, inlined callstack
+ VAR: argc : int : {expression} <---- Variables live at this point
+ VAR: argv : char ** : {expression}
+ LINE: my_source_file.c:2 [main]
+ VAR: argc : int
+ VAR: argv : char **
+ LINE: my_source_file.c:3 [main]
+ VAR: argc : int
+ VAR: argv : char **
+ LINE: my_source_file.c:4 [main]
+)";
+
+using namespace llvm;
+using namespace logicalview;
+
+template <typename T>
+static T Take(Expected<T> ExpectedResult, const Twine &Msg) {
+ if (!ExpectedResult) {
+ auto Err = ExpectedResult.takeError();
+ errs() << Msg << " " << toStringWithoutConsuming(Err) << '\n';
+ exit(2);
+ }
+ T ret = std::move(*ExpectedResult);
+ return ret;
+}
+
+namespace {
+
+struct ScopePrinter {
+ std::vector<const LVLine *> Lines;
+ std::unordered_map<LVAddress, std::vector<const LVLocation *>> LivetimeBegins;
+ std::unordered_map<LVAddress, std::vector<const LVLocation *>>
+ LivetimeEndsExclusive;
+ raw_ostream &OS;
+
+ 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 << "RANGES: " << Symbol->getName() << " (line "
+ << Symbol->getLineNumber() << ")" << ": ";
+ }
+
+ for (const LVLocation *Loc : SymbolLocations) {
+ if (Loc->getIsGapEntry())
+ continue;
+
+ LVAddress Begin = Loc->getLowerAddress();
+ LVAddress End = Loc->getUpperAddress();
+ LivetimeBegins[Begin].push_back(Loc);
+ LivetimeEndsExclusive[End].push_back(Loc);
+ if (IncludeRanges) {
+ OS << "[" << hexValue(Begin) << ":" << hexValue(End) << "] ";
+ }
+ }
+
+ if (IncludeRanges)
+ OS << "\n";
+ }
+ }
+ }
+
+ ScopePrinter(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() {
+ 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 : LivetimeBegins[Line->getAddress()])
+ LiveSymbols.insert(Loc);
+ // Update live list: remove dead
+ for (auto Loc : LivetimeEndsExclusive[Line->getAddress()])
+ LiveSymbols.remove(Loc);
+
+ if (Line->getIsNewStatement() && Line->getIsLineDebug() &&
+ Line->getLineNumber() != 0) {
+ auto LineDebug = cast<LVLineDebug>(Line);
+
+ 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, 1);
+ OS << "VAR: " << 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
+
+int llvm::debuggerview::printDebuggerView(std::vector<std::string> &Objects,
+ raw_ostream &OS) {
+ if (Help) {
+ OS << HelpText;
+ return EXIT_SUCCESS;
+ }
+
+ LVOptions Options;
+ Options.setAttributeAll();
+ Options.setAttributeAnyLocation();
+ Options.setPrintAll();
+ Options.setPrintAnyLine();
+ Options.resolveDependencies();
+
+ ScopedPrinter W(nulls());
+ LVReaderHandler Handler(Objects, W, Options);
+ std::vector<std::unique_ptr<LVReader>> Readers;
+ for (auto &Object : Objects) {
+ auto ExpectedReader = Handler.createReader(Object);
+ if (!ExpectedReader) {
+ auto Err = ExpectedReader.takeError();
+ errs() << "Failed to create reader: " << toStringWithoutConsuming(Err)
+ << '\n';
+ return 2;
+ }
+ Readers.emplace_back(std::move(*ExpectedReader));
+ }
+
+ for (auto &Reader : Readers) {
+ auto *CU = Reader->getCompileUnit();
+ if (!CU) {
+ errs() << "No compute unit found.\n";
+ return 2;
+ }
+
+ for (LVElement *Child : *CU->getChildren()) {
+ auto *Fn = dyn_cast<LVScopeFunction>(Child);
+ if (Fn) {
+ const LVLines *Lines = Fn->getLines();
+ // If there's no lines, this function has no body.
+ if (!Lines)
+ continue;
+ outs() << "FUNCTION: " << Child->getName() << "\n";
+
+ ScopePrinter P(OS, Fn);
+ P.Print();
+ }
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.h b/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.h
new file mode 100644
index 0000000000000..d775fa0c80d2d
--- /dev/null
+++ b/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.h
@@ -0,0 +1,37 @@
+//===-- DebuggerView.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Options and functions related to --debugger-view for llvm-debuginfo-analyzer
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef DEBUGGER_VIEW_H
+#define DEBUGGER_VIEW_H
+
+#include "l...
[truncated]
|
|
@adam-yang, The concept of the
we should keep the existing ones: {Line} and {Variable}
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! In addition to @CarlosAlbertoEnciso's comments, here are a couple more suggestions / additional comments.
Made all the requested changes. |
… core library. Made the output look more like the other outputs of the tool.
895983b to
431ec62
Compare
| REPORT_OPTION(Children); | ||
| REPORT_OPTION(List); | ||
| REPORT_OPTION(Parents); | ||
| REPORT_OPTION(Debugger); |
There was a problem hiding this comment.
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?
| auto *CU = getCompileUnit(); | ||
| if (!CU) | ||
| return createStringError(std::make_error_code(std::errc::invalid_argument), | ||
| "Error: No compute unit found."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May be: compile unit?
| "Selected elements are displayed in a tree view " | ||
| "(Include parents and children."))); | ||
| "(Include parents and children."), | ||
| clEnumValN( |
There was a problem hiding this comment.
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?
| } // namespace | ||
|
|
||
| Error LVReader::printDebugger() { | ||
| auto *CU = getCompileUnit(); |
There was a problem hiding this comment.
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.
| 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). | ||
|
|
There was a problem hiding this comment.
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 ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added this section.
| .. code-block:: none | ||
| llvm-debuginfo-analyzer --report=debugger | ||
| test-dwarf-clang.o test-dwarf-gcc.o |
There was a problem hiding this comment.
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'
| 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. | ||
|
|
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
| return Error::success(); | ||
|
|
||
| for (auto *Scope : *Root->getScopes()) { | ||
| auto *CU = dyn_cast<LVScopeCompileUnit>(Scope); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Root has only CompileUnit children. Use the inheritance relationships to invoke the needed printing code. Have a look at LVScopeRoot::doPrintMatches to see how to handle the output split feature.
Error LVScopeRoot::doPrintDebugger(bool Split, raw_ostream &OS, ....) const {
if (Scopes) {
...
for (LVScope *Scope : *Scopes) {
// Virtual function in LVScope and defined for:
// LVScopeCompileUnit, LVScopeFunction and LVScopeFunctionInlined
Scope->printDebugger(.....);
}
...
}
void LVScopeCompileUnit::printDebugger(raw_ostream &OS, ...) {
...
}
LVScopeFunction::printDebugger(raw_ostream &OS, ...) {
...
}
LVScopeFunctionInlined::printDebugger(raw_ostream &OS, ...) {
...
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. The bulk of the logic is still in one place though, under LVFunction::printDebugger, but much of the specifics have been moved to be handled by virtual functions.
|
FYI: There is a PR #69175 to move all the printing support to a common function. |
|
@adam-yang Thanks for updating your patch. I will have a look at it in the next couple of days. |
|
@adam-yang, @CarlosAlbertoEnciso I could try to take another look next week, if needed 🙂. |
|
Can you include a test for the new option. |
| << Symbol->getLineNumber() << ")" << ": "; | ||
|
|
||
| for (const LVLocation *Loc : SymbolLocations) { | ||
| if (Loc->getIsGapEntry()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Loc --> Location
| if (getPrintSymbols()) | ||
| setAttributeRange(); | ||
| } | ||
|
|
There was a problem hiding this comment.
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.
| } | ||
|
|
||
| void LVLocationSymbol::printDebugger(raw_ostream &OS, LVLevel Indent) const { | ||
| LVSymbol *Sym = getParentSymbol(); |
There was a problem hiding this comment.
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
| LifetimeBegins[Begin].push_back(Loc); | ||
| LifetimeEndsExclusive[End].push_back(Loc); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency: Loc --> Location
| if (Scope->scopeCount()) { | ||
| for (const LVScope *ChildScope : *Scope->getScopes()) | ||
| Worklist.push_back(ChildScope); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (Scope->scopeCount()) {
for (const LVScope *ChildScope : *Scope->getScopes())
Worklist.push_back(ChildScope);
}
Can that be replaced with something like:
Worklist.insert(Worklist.end(), *Scope->getScopes()->begin(), *Scope->getScopes()->end());
| for (const LVLine *Line : *Scope->getLines()) { | ||
| AllLines.push_back(Line); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same change here?
| {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) |
There was a problem hiding this comment.
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
@jalopezg-git That would be great. |
jalopezg-git
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot for the contribution, @adam-yang! I managed to squeeze the review in the 'lunch' slot in WG21 meeting 🙂.
This looks very nice! I have attached some minor comments that add to those of @CarlosAlbertoEnciso.
| std::string Leading; | ||
| for (LVOperation *Operation : *Entries) { | ||
| OS << Leading | ||
| << (CodeViewLocation ? Operation->getOperandsCodeViewInfo() | ||
| : Operation->getOperandsDWARFInfo()); | ||
| Leading = ", "; | ||
| } |
There was a problem hiding this comment.
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::printDebugger(raw_ostream &OS, LVLevel Indent) const { | ||
| LVSymbol *Sym = getParentSymbol(); | ||
| OS << indentAsString(Indent) << formattedKind(Sym->kind()); | ||
| OS << " " << Sym->getName() << ": " << Sym->getType()->getName() << " : "; |
There was a problem hiding this comment.
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.
| clEnumValN( | ||
| LVReportKind::Debugger, "debugger", | ||
| "Selected elements are displayed in a simulated debugger view " | ||
| "(Include parents and children."), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| "(Include parents and children."), | |
| "(Include parents and children)"), |
| {Line} test.cpp:8 [foo] | ||
| {Line} test.cpp:9 [foo] | ||
| Optionally, by adding `--print=symbols`, live variables for each line is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| Optionally, by adding `--print=symbols`, live variables for each line is | |
| Optionally, by adding `--print=symbols`, live variables for each line are |
| if (a->getAddress() != b->getAddress()) | ||
| return a->getAddress() < b->getAddress(); | ||
| if (a->getIsLineDebug() != b->getIsLineDebug()) | ||
| return a->getIsLineDebug(); | ||
| return a->getID() < b->getID(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possibly, this could use the suggestion below (unsure if it preserves the intended ordering for a->getIsLineDebug() though). WDYT, @adam-yang?
| if (a->getAddress() != b->getAddress()) | |
| return a->getAddress() < b->getAddress(); | |
| if (a->getIsLineDebug() != b->getIsLineDebug()) | |
| return a->getIsLineDebug(); | |
| return a->getID() < b->getID(); | |
| return std::tie(a->getAddress(), a->getIsLineDebug(), a->getID()) | |
| < std::tie(b->getAddress(), b->getIsLineDebug(), b->getID()); |
| // 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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIUC, LiveSymbols content is only consumed if IncludeVars evaluates to true, i.e. we could potentially skip updating it when not required.
--report=debuggeris a new report mode forllvm-debuginfo-analyzerthat prints out the debug info logical view in a way that matches more closely to a debugger. By default,--report=debuggerprints out every statement line and its source location and call stack. Take this source for example:The default
--report=debuggerprints out the following:This allows for easy offline verification of specific inline call-stacks, line mapping order, etc.
Adding
--print=symbolsprints live variables for each{Line}statement:Optionally,
--print=instructionscan be used to print out each instruction of the disasm to verify specific asm expressions for variable mappings.Additionally,
--attributecan be used to include things such asoffsets and scope levels for {Line} and {Instruction}.