[llvm] Added --debugger-view option to llvm-debuginfo-analyzer (PR #159853)

via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 22 10:38:23 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-debuginfo

Author: Adam Yang (adam-yang)

<details>
<summary>Changes</summary>

--debugger-view is a new mode for `llvm-debuginfo-analyzer` that prints out the debug info logical view in a way that matches more closely to the way a debugger. By default, `--debugger-view` prints out every statement line and its source location and call stack. Take this source for example:

```example.hlsl
/*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 `--debugger-view` prints out the following:
```
LINE:  [0x0000000000] example.hlsl:15 [main]
LINE:  [0x0000000020] example.hlsl:15 [main]
LINE:  [0x0000000050] example.hlsl:16 [main]
LINE:  [0x0000000068] example.hlsl:18 [main]
LINE:  [0x0000000070] example.hlsl:16 [main]
LINE:  [0x0000000080] example.hlsl:7 [foo][main:35]
LINE:  [0x000000009c] example.hlsl:2 [bar][foo:22][main:35]
LINE:  [0x00000000b0] example.hlsl:18 [main]
LINE:  [0x00000000cc] example.hlsl:19 [main]
```
This allows for easy offline verification of specific inline call-stacks, line mapping order, etc.

Another option `--debugger-view-vars` prints live variables for each `LINE` statement:
```
LINE:  [0x0000000000] example.hlsl:15 [main]
LINE:  [0x0000000020] example.hlsl:15 [main]
LINE:  [0x0000000050] example.hlsl:16 [main]
  VAR: dtid: uint3 : regx VGPR0, piece 4 (line 15)
LINE:  [0x0000000068] example.hlsl:18 [main]
  VAR: dtid: uint3 : regx VGPR0, piece 4 (line 15)
LINE:  [0x0000000070] example.hlsl:16 [main]
  VAR: dtid: uint3 : regx VGPR0, piece 4 (line 15)
LINE:  [0x0000000080] example.hlsl:7 [foo][main:17]
  VAR: dtid: uint3 : regx VGPR0, piece 4 (line 15)
  VAR: a: float : regx VGPR2 (line 6)
  VAR: arg: float : regx VGPR2 (line 16)
LINE:  [0x000000009c] example.hlsl:2 [bar][foo:9][main:17]
  VAR: dtid: uint3 : regx VGPR0, piece 4 (line 15)
  VAR: my_var: float : regx VGPR2 (line 1)
LINE:  [0x00000000b0] example.hlsl:18 [main]
  VAR: dtid: uint3 : regx VGPR0, piece 4 (line 15)
  VAR: ret: float : regx VGPR6 (line 17)
LINE:  [0x00000000cc] example.hlsl:19 [main]
  VAR: dtid: uint3 : regx VGPR0, piece 4 (line 15)
  VAR: ret: float : regx VGPR6 (line 17)
```

Optionally, `--debugger-view-code` can be used to print out each instruction of the disasm to verify specific asm expressions for variable mappings.



---

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:

- (modified) llvm/include/llvm/DebugInfo/LogicalView/Core/LVLocation.h (+2) 
- (modified) llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp (+16-9) 
- (added) llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll (+110) 
- (modified) llvm/tools/llvm-debuginfo-analyzer/CMakeLists.txt (+1) 
- (added) llvm/tools/llvm-debuginfo-analyzer/DebuggerView.cpp (+272) 
- (added) llvm/tools/llvm-debuginfo-analyzer/DebuggerView.h (+37) 
- (modified) llvm/tools/llvm-debuginfo-analyzer/llvm-debuginfo-analyzer.cpp (+9-4) 


``````````diff
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 at 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 at 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]

``````````

</details>


https://github.com/llvm/llvm-project/pull/159853


More information about the llvm-commits mailing list