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

Adam Yang via llvm-commits llvm-commits at lists.llvm.org
Thu Oct 23 14:51:53 PDT 2025


https://github.com/adam-yang updated https://github.com/llvm/llvm-project/pull/159853

>From a85243dffa1fd7bf30bed59c02725ae99e331858 Mon Sep 17 00:00:00 2001
From: Adam Yang <hanbyang at microsoft.com>
Date: Fri, 29 Aug 2025 19:55:34 -0700
Subject: [PATCH 01/15] Initial changes.

---
 .../CodeGen/AMDGPU/amdgpu-debuginfo-check.ll  | 115 ++++++++
 .../llvm-debuginfo-analyzer/CMakeLists.txt    |   1 +
 .../llvm-debuginfo-analyzer/DebuggerView.cpp  | 276 ++++++++++++++++++
 .../llvm-debuginfo-analyzer/DebuggerView.h    |  36 +++
 .../llvm-debuginfo-analyzer.cpp               |   6 +-
 5 files changed, 433 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll
 create mode 100644 llvm/tools/llvm-debuginfo-analyzer/DebuggerView.cpp
 create mode 100644 llvm/tools/llvm-debuginfo-analyzer/DebuggerView.h

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..e4fae7f1f8026
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll
@@ -0,0 +1,115 @@
+; RUN: llc %s -o %t.o -mcpu=gfx1030 -filetype=obj -O0
+; RUN: llvm-debuginfo-analyzer --check --check-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 datalayout = "e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-p7:160:256:256:32-p8:128:128-p9:192:256:256:32-p10:32:32-p11:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1-ni:7:8:9-p32:32:32-v8:8-v16:16-v32:32-v48:32-v64:32-v80:32-v96:32-v112:32-v128:32-v144:32-v160:32-v176:32-v192:32-v208:32-v224:32-v240:32-v256:32-i1:32-i8:8-i16:16-i32:32-i64:32-f16:16-f32:32-f64:32"
+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
+}
+
+; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
+declare noundef i64 @llvm.amdgcn.s.getpc() #1
+
+; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write)
+declare void @llvm.assume(i1 noundef) #2
+
+; Function Attrs: nocallback nofree nosync nounwind willreturn memory(write)
+declare void @llvm.amdgcn.struct.buffer.store.format.v4f32(<4 x float>, <4 x i32>, i32, i32, i32, i32 immarg) #3
+
+; Function Attrs: nocallback nofree nosync nounwind willreturn memory(read)
+declare float @llvm.amdgcn.struct.buffer.load.format.f32(<4 x i32>, i32, i32, i32, i32 immarg) #4
+
+attributes #0 = { memory(readwrite) "amdgpu-flat-work-group-size"="64,64" "amdgpu-memory-bound"="false" "amdgpu-num-sgpr"="4294967295" "amdgpu-num-vgpr"="4294967295" "amdgpu-prealloc-sgpr-spill-vgprs" "amdgpu-unroll-threshold"="1200" "amdgpu-wave-limiter"="false" "amdgpu-work-group-info-arg-no"="3" "denormal-fp-math"="ieee" "denormal-fp-math-f32"="preserve-sign" "target-features"=",+wavefrontsize64,+cumode,+enable-flat-scratch" }
+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)
\ No newline at end of file
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..0cfb7086d8a24
--- /dev/null
+++ b/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.cpp
@@ -0,0 +1,276 @@
+#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;
+
+cl::opt<bool> EnableDebuggerView("debugger-view");
+static cl::opt<bool> DebuggerViewVars("debugger-view-vars");
+static cl::opt<bool> DebuggerViewCode("debugger-view-code");
+static cl::opt<bool> DebuggerViewHelp("debugger-view-help");
+
+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;
+
+static cl::opt<std::string>
+    InputFilename(cl::Positional, "<input-file>",
+                  cl::desc("Input file, an object file with DWARF."),
+                  cl::Required);
+
+static cl::opt<bool> IncludeCode("code", cl::desc("Include asm"));
+static cl::opt<bool>
+    IncludeRanges("ranges", cl::desc("Include variable ranges"), cl::Hidden);
+static cl::opt<bool> IncludeVars("vars", cl::desc("Include live variables"));
+
+template <typename T> 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;
+}
+
+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) {
+    bool First = true;
+    const LVScope *PrevScope = nullptr;
+    while (Scope) {
+      if (Scope->getIsFunction() || Scope->getIsInlinedFunction()) {
+        OS << "[" << Scope->getName();
+        if (PrevScope && PrevScope->getIsInlinedFunction()) {
+          OS << ":"
+             << cast<LVScopeFunctionInlined>(PrevScope)->getCallLineNumber();
+        }
+        OS << "]";
+        First = false;
+        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";
+      }
+    }
+  }
+};
+#if 0
+int main(int argc, char *argv[]) {
+  InitLLVM X(argc, argv);
+
+  // Initialize targets and assembly printers/parsers.
+  llvm::InitializeAllTargetInfos();
+  llvm::InitializeAllTargetMCs();
+  InitializeAllDisassemblers();
+
+  cl::ParseCommandLineOptions(argc, argv, HelpText);
+
+  ScopedPrinter W(llvm::outs());
+  LVOptions Options;
+  Options.setAttributeAll();
+  Options.setAttributeAnyLocation();
+  Options.setPrintAll();
+  Options.setPrintAnyLine();
+  Options.resolveDependencies();
+  std::vector<std::string> Objects;
+  LVReaderHandler Handler(Objects, W, Options);
+  auto Readers = Take(Handler.createReader(InputFilename),
+                      Twine("Failed to create LV reader from '") +
+                          Twine(InputFilename) + Twine("'"));
+
+  auto *CU = Readers->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(outs(), Fn);
+      P.Print();
+    }
+  }
+
+  return EXIT_SUCCESS;
+}
+#endif
+
+int llvm::debuggerview::printDebuggerView(std::vector<std::string> &Objects, raw_ostream &OS) {
+
+  LVOptions Options;
+  Options.setAttributeAll();
+  Options.setAttributeAnyLocation();
+  Options.setPrintAll();
+  Options.setPrintAnyLine();
+  Options.resolveDependencies();
+
+  ScopedPrinter W(nulls());
+  LVReaderHandler Handler(Objects, W, Options);
+  auto Readers = Take(Handler.createReader(InputFilename),
+                      Twine("Failed to create LV reader from '") +
+                          Twine(InputFilename) + Twine("'"));
+
+  auto *CU = Readers->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..9b54122ce22dd
--- /dev/null
+++ b/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.h
@@ -0,0 +1,36 @@
+//===-- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// 
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef DEBUGGER_VIEW_H
+#define DEBUGGER_VIEW_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Support/CommandLine.h"
+#include <vector>
+
+namespace llvm {
+class ScopedPrinter;
+class raw_ostream;
+
+namespace logicalview {
+class LVReaderHandler;
+class LVOptions;
+} // namespace logicalview
+
+namespace debuggerview {
+
+extern cl::opt<bool> EnableDebuggerView;
+int printDebuggerView(std::vector<std::string> &Objects, raw_ostream &OS);
+
+} // namespace debuggerview
+} // namespace llvm
+#endif // DEBUGGER_VIEW_H
diff --git a/llvm/tools/llvm-debuginfo-analyzer/llvm-debuginfo-analyzer.cpp b/llvm/tools/llvm-debuginfo-analyzer/llvm-debuginfo-analyzer.cpp
index a5e2d39f67d7f..47157796eb910 100644
--- a/llvm/tools/llvm-debuginfo-analyzer/llvm-debuginfo-analyzer.cpp
+++ b/llvm/tools/llvm-debuginfo-analyzer/llvm-debuginfo-analyzer.cpp
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "Options.h"
+#include "DebuggerView.h"
 #include "llvm/DebugInfo/LogicalView/Core/LVOptions.h"
 #include "llvm/DebugInfo/LogicalView/LVReaderHandler.h"
 #include "llvm/Support/COM.h"
@@ -120,8 +121,11 @@ int main(int argc, char **argv) {
     llvm::append_range(Objects, Objs);
   }
 
-  propagateOptions();
+  if (debuggerview::EnableDebuggerView)
+    return debuggerview::printDebuggerView(Objects, OutputFile.os());
+
   ScopedPrinter W(OutputFile.os());
+  propagateOptions();
   LVReaderHandler ReaderHandler(Objects, W, ReaderOptions);
 
   // Print the command line.

>From 25793b49ff3dbb8dc111ab4acc80973048365e33 Mon Sep 17 00:00:00 2001
From: Adam Yang <hanbyang at microsoft.com>
Date: Fri, 19 Sep 2025 14:44:47 -0700
Subject: [PATCH 02/15] Cleaned up and hooked up the options

---
 .../DebugInfo/LogicalView/Core/LVLocation.h   |   2 +
 .../DebugInfo/LogicalView/LVReaderHandler.h   |   2 +
 .../DebugInfo/LogicalView/Core/LVLocation.cpp |  25 +--
 .../CodeGen/AMDGPU/amdgpu-debuginfo-check.ll  |   4 +-
 .../llvm-debuginfo-analyzer/DebuggerView.cpp  | 146 ++++++++----------
 .../llvm-debuginfo-analyzer/DebuggerView.h    |   3 +-
 .../llvm-debuginfo-analyzer.cpp               |   9 +-
 7 files changed, 95 insertions(+), 96 deletions(-)

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/include/llvm/DebugInfo/LogicalView/LVReaderHandler.h b/llvm/include/llvm/DebugInfo/LogicalView/LVReaderHandler.h
index f6dc65f39c7ac..aac1f21338eac 100644
--- a/llvm/include/llvm/DebugInfo/LogicalView/LVReaderHandler.h
+++ b/llvm/include/llvm/DebugInfo/LogicalView/LVReaderHandler.h
@@ -87,6 +87,8 @@ class LVReaderHandler {
     return std::move(Readers[0]);
   }
 
+  const LVReaders &getReaders() const { return TheReaders; }
+
   LLVM_ABI void print(raw_ostream &OS) const;
 
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
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
index e4fae7f1f8026..51d347fc5d7d2 100644
--- a/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll
+++ b/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll
@@ -1,5 +1,5 @@
 ; RUN: llc %s -o %t.o -mcpu=gfx1030 -filetype=obj -O0
-; RUN: llvm-debuginfo-analyzer --check --check-vars %t.o | FileCheck %s
+; 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.
@@ -112,4 +112,4 @@ attributes #4 = { nocallback nofree nosync nounwind willreturn memory(read) }
 !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)
\ No newline at end of file
+!37 = !DILocation(line: 19, column: 1, scope: !14)
diff --git a/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.cpp b/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.cpp
index 0cfb7086d8a24..69d50c37033d6 100644
--- a/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.cpp
+++ b/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.cpp
@@ -1,3 +1,4 @@
+#include "DebuggerView.h"
 #include "Options.h"
 #include "llvm/ADT/SetVector.h"
 #include "llvm/DebugInfo/LogicalView/LVReaderHandler.h"
@@ -7,11 +8,33 @@
 #include <unordered_set>
 
 using namespace llvm;
+using namespace debuggerview;
 
-cl::opt<bool> EnableDebuggerView("debugger-view");
-static cl::opt<bool> DebuggerViewVars("debugger-view-vars");
-static cl::opt<bool> DebuggerViewCode("debugger-view-code");
-static cl::opt<bool> DebuggerViewHelp("debugger-view-help");
+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.
@@ -31,17 +54,8 @@ FUNCTION: main
 using namespace llvm;
 using namespace logicalview;
 
-static cl::opt<std::string>
-    InputFilename(cl::Positional, "<input-file>",
-                  cl::desc("Input file, an object file with DWARF."),
-                  cl::Required);
-
-static cl::opt<bool> IncludeCode("code", cl::desc("Include asm"));
-static cl::opt<bool>
-    IncludeRanges("ranges", cl::desc("Include variable ranges"), cl::Hidden);
-static cl::opt<bool> IncludeVars("vars", cl::desc("Include live variables"));
-
-template <typename T> T Take(Expected<T> ExpectedResult, const Twine &Msg) {
+template <typename T>
+static T Take(Expected<T> ExpectedResult, const Twine &Msg) {
   if (!ExpectedResult) {
     auto Err = ExpectedResult.takeError();
     errs() << Msg << " " << toStringWithoutConsuming(Err) << '\n';
@@ -51,6 +65,8 @@ template <typename T> T Take(Expected<T> ExpectedResult, const Twine &Msg) {
   return ret;
 }
 
+namespace {
+
 struct ScopePrinter {
   std::vector<const LVLine *> Lines;
   std::unordered_map<LVAddress, std::vector<const LVLocation *>> LivetimeBegins;
@@ -189,56 +205,16 @@ struct ScopePrinter {
     }
   }
 };
-#if 0
-int main(int argc, char *argv[]) {
-  InitLLVM X(argc, argv);
 
-  // Initialize targets and assembly printers/parsers.
-  llvm::InitializeAllTargetInfos();
-  llvm::InitializeAllTargetMCs();
-  InitializeAllDisassemblers();
-
-  cl::ParseCommandLineOptions(argc, argv, HelpText);
-
-  ScopedPrinter W(llvm::outs());
-  LVOptions Options;
-  Options.setAttributeAll();
-  Options.setAttributeAnyLocation();
-  Options.setPrintAll();
-  Options.setPrintAnyLine();
-  Options.resolveDependencies();
-  std::vector<std::string> Objects;
-  LVReaderHandler Handler(Objects, W, Options);
-  auto Readers = Take(Handler.createReader(InputFilename),
-                      Twine("Failed to create LV reader from '") +
-                          Twine(InputFilename) + Twine("'"));
-
-  auto *CU = Readers->getCompileUnit();
-  if (!CU) {
-    errs() << "No compute unit found.\n";
-    return 2;
-  }
+} // namespace
 
-  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(outs(), Fn);
-      P.Print();
-    }
+int llvm::debuggerview::printDebuggerView(std::vector<std::string> &Objects,
+                                          raw_ostream &OS) {
+  if (Help) {
+    OS << HelpText;
+    return EXIT_SUCCESS;
   }
 
-  return EXIT_SUCCESS;
-}
-#endif
-
-int llvm::debuggerview::printDebuggerView(std::vector<std::string> &Objects, raw_ostream &OS) {
-
   LVOptions Options;
   Options.setAttributeAll();
   Options.setAttributeAnyLocation();
@@ -248,27 +224,37 @@ int llvm::debuggerview::printDebuggerView(std::vector<std::string> &Objects, raw
 
   ScopedPrinter W(nulls());
   LVReaderHandler Handler(Objects, W, Options);
-  auto Readers = Take(Handler.createReader(InputFilename),
-                      Twine("Failed to create LV reader from '") +
-                          Twine(InputFilename) + Twine("'"));
-
-  auto *CU = Readers->getCompileUnit();
-  if (!CU) {
-    errs() << "No compute unit found.\n";
-    return 2;
+  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 (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();
+  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();
+      }
     }
   }
 
diff --git a/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.h b/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.h
index 9b54122ce22dd..bf358c314ed2a 100644
--- a/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.h
+++ b/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.h
@@ -28,7 +28,8 @@ class LVOptions;
 
 namespace debuggerview {
 
-extern cl::opt<bool> EnableDebuggerView;
+extern cl::OptionCategory Category;
+extern cl::opt<bool> Enable;
 int printDebuggerView(std::vector<std::string> &Objects, raw_ostream &OS);
 
 } // namespace debuggerview
diff --git a/llvm/tools/llvm-debuginfo-analyzer/llvm-debuginfo-analyzer.cpp b/llvm/tools/llvm-debuginfo-analyzer/llvm-debuginfo-analyzer.cpp
index 47157796eb910..31d8774b46702 100644
--- a/llvm/tools/llvm-debuginfo-analyzer/llvm-debuginfo-analyzer.cpp
+++ b/llvm/tools/llvm-debuginfo-analyzer/llvm-debuginfo-analyzer.cpp
@@ -96,9 +96,10 @@ int main(int argc, char **argv) {
   cl::extrahelp HelpResponse(
       "\nPass @FILE as argument to read options from FILE.\n");
 
-  cl::HideUnrelatedOptions(
-      {&AttributeCategory, &CompareCategory, &InternalCategory, &OutputCategory,
-       &PrintCategory, &ReportCategory, &SelectCategory, &WarningCategory});
+  cl::HideUnrelatedOptions({&AttributeCategory, &CompareCategory,
+                            &InternalCategory, &OutputCategory, &PrintCategory,
+                            &ReportCategory, &SelectCategory, &WarningCategory,
+                            &debuggerview::Category});
   cl::ParseCommandLineOptions(argc, argv,
                               "Printing a logical representation of low-level "
                               "debug information.\n");
@@ -121,7 +122,7 @@ int main(int argc, char **argv) {
     llvm::append_range(Objects, Objs);
   }
 
-  if (debuggerview::EnableDebuggerView)
+  if (debuggerview::Enable)
     return debuggerview::printDebuggerView(Objects, OutputFile.os());
 
   ScopedPrinter W(OutputFile.os());

>From 76efd6539bb70db380ee04bdd31975646f2e1ef7 Mon Sep 17 00:00:00 2001
From: Adam Yang <hanbyang at microsoft.com>
Date: Mon, 22 Sep 2025 00:00:38 -0700
Subject: [PATCH 03/15] formatting

---
 llvm/tools/llvm-debuginfo-analyzer/DebuggerView.cpp  | 12 ++++++++++++
 llvm/tools/llvm-debuginfo-analyzer/DebuggerView.h    |  2 +-
 .../llvm-debuginfo-analyzer.cpp                      |  2 +-
 3 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.cpp b/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.cpp
index 69d50c37033d6..3408751496e44 100644
--- a/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.cpp
+++ b/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.cpp
@@ -1,3 +1,15 @@
+//===-- 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"
diff --git a/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.h b/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.h
index bf358c314ed2a..d775fa0c80d2d 100644
--- a/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.h
+++ b/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.h
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// 
+// Options and functions related to --debugger-view for llvm-debuginfo-analyzer
 //
 //===----------------------------------------------------------------------===//
 
diff --git a/llvm/tools/llvm-debuginfo-analyzer/llvm-debuginfo-analyzer.cpp b/llvm/tools/llvm-debuginfo-analyzer/llvm-debuginfo-analyzer.cpp
index 31d8774b46702..36b289411c044 100644
--- a/llvm/tools/llvm-debuginfo-analyzer/llvm-debuginfo-analyzer.cpp
+++ b/llvm/tools/llvm-debuginfo-analyzer/llvm-debuginfo-analyzer.cpp
@@ -11,8 +11,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "Options.h"
 #include "DebuggerView.h"
+#include "Options.h"
 #include "llvm/DebugInfo/LogicalView/Core/LVOptions.h"
 #include "llvm/DebugInfo/LogicalView/LVReaderHandler.h"
 #include "llvm/Support/COM.h"

>From 8b0d1727ee249095849f924049f6ab561d4ce476 Mon Sep 17 00:00:00 2001
From: Adam Yang <hanbyang at microsoft.com>
Date: Mon, 22 Sep 2025 10:25:11 -0700
Subject: [PATCH 04/15] Fixed linux build failure by deleting a variable that's
 not actually used

---
 llvm/tools/llvm-debuginfo-analyzer/DebuggerView.cpp | 2 --
 1 file changed, 2 deletions(-)

diff --git a/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.cpp b/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.cpp
index 3408751496e44..015096d49bd95 100644
--- a/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.cpp
+++ b/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.cpp
@@ -145,7 +145,6 @@ struct ScopePrinter {
   }
 
   static void PrintCallstack(raw_ostream &OS, const LVScope *Scope) {
-    bool First = true;
     const LVScope *PrevScope = nullptr;
     while (Scope) {
       if (Scope->getIsFunction() || Scope->getIsInlinedFunction()) {
@@ -155,7 +154,6 @@ struct ScopePrinter {
              << cast<LVScopeFunctionInlined>(PrevScope)->getCallLineNumber();
         }
         OS << "]";
-        First = false;
         PrevScope = Scope;
       }
       Scope = Scope->getParentScope();

>From 0752ef852c490f06708b085eb33a68b5e7e57b29 Mon Sep 17 00:00:00 2001
From: Adam Yang <hanbyang at microsoft.com>
Date: Mon, 22 Sep 2025 10:36:57 -0700
Subject: [PATCH 05/15] Test cleanup. Deleted a function

---
 llvm/include/llvm/DebugInfo/LogicalView/LVReaderHandler.h | 2 --
 llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll        | 7 +------
 2 files changed, 1 insertion(+), 8 deletions(-)

diff --git a/llvm/include/llvm/DebugInfo/LogicalView/LVReaderHandler.h b/llvm/include/llvm/DebugInfo/LogicalView/LVReaderHandler.h
index aac1f21338eac..f6dc65f39c7ac 100644
--- a/llvm/include/llvm/DebugInfo/LogicalView/LVReaderHandler.h
+++ b/llvm/include/llvm/DebugInfo/LogicalView/LVReaderHandler.h
@@ -87,8 +87,6 @@ class LVReaderHandler {
     return std::move(Readers[0]);
   }
 
-  const LVReaders &getReaders() const { return TheReaders; }
-
   LLVM_ABI void print(raw_ostream &OS) const;
 
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
diff --git a/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll b/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll
index 51d347fc5d7d2..56d6d3b9c9dbd 100644
--- a/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll
+++ b/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll
@@ -22,7 +22,6 @@
 ; CHECK-DAG: VAR: my_var2: float : reg{{.+}}
 
 source_filename = "module"
-target datalayout = "e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-p7:160:256:256:32-p8:128:128-p9:192:256:256:32-p10:32:32-p11:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1-ni:7:8:9-p32:32:32-v8:8-v16:16-v32:32-v48:32-v64:32-v80:32-v96:32-v112:32-v128:32-v144:32-v160:32-v176:32-v192:32-v208:32-v224:32-v240:32-v256:32-i1:32-i8:8-i16:16-i32:32-i64:32-f16:16-f32:32-f64:32"
 target triple = "amdgcn-amd-amdpal"
 
 %dx.types.ResRet.f32 = type { float, float, float, float, i32 }
@@ -54,19 +53,15 @@ define dllexport amdgpu_cs void @_amdgpu_cs_main(i32 inreg noundef %globalTable,
   ret void, !dbg !37
 }
 
-; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
 declare noundef i64 @llvm.amdgcn.s.getpc() #1
 
-; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write)
 declare void @llvm.assume(i1 noundef) #2
 
-; Function Attrs: nocallback nofree nosync nounwind willreturn memory(write)
 declare void @llvm.amdgcn.struct.buffer.store.format.v4f32(<4 x float>, <4 x i32>, i32, i32, i32, i32 immarg) #3
 
-; Function Attrs: nocallback nofree nosync nounwind willreturn memory(read)
 declare float @llvm.amdgcn.struct.buffer.load.format.f32(<4 x i32>, i32, i32, i32, i32 immarg) #4
 
-attributes #0 = { memory(readwrite) "amdgpu-flat-work-group-size"="64,64" "amdgpu-memory-bound"="false" "amdgpu-num-sgpr"="4294967295" "amdgpu-num-vgpr"="4294967295" "amdgpu-prealloc-sgpr-spill-vgprs" "amdgpu-unroll-threshold"="1200" "amdgpu-wave-limiter"="false" "amdgpu-work-group-info-arg-no"="3" "denormal-fp-math"="ieee" "denormal-fp-math-f32"="preserve-sign" "target-features"=",+wavefrontsize64,+cumode,+enable-flat-scratch" }
+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) }

>From 6a0edef7216f552c3dafaca77dc6be26520b7f39 Mon Sep 17 00:00:00 2001
From: Adam Yang <hanbyang at microsoft.com>
Date: Mon, 22 Sep 2025 10:39:33 -0700
Subject: [PATCH 06/15] Updated test comment

---
 llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll b/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll
index 56d6d3b9c9dbd..fb0ab834b3551 100644
--- a/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll
+++ b/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll
@@ -2,7 +2,7 @@
 ; 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.
+; and makes sure llvm-debuginfo-analyzer --debugger-view works for it.
 
 ; CHECK: FUNCTION: main
 ; CHECK: LINE: {{.+}}basic_var.hlsl:7

>From bf7848e845febe12f6160b443045f527865e385b Mon Sep 17 00:00:00 2001
From: Adam Yang <hanbyang at microsoft.com>
Date: Mon, 29 Sep 2025 15:54:23 -0700
Subject: [PATCH 07/15] Changed --debugger-view into --report=debugger, and
 moved it into the core library. Made the output look more like the other
 outputs of the tool.

---
 .../DebugInfo/LogicalView/Core/LVOptions.h    |   4 +-
 .../DebugInfo/LogicalView/Core/LVReader.h     |   1 +
 .../DebugInfo/LogicalView/Core/LVOptions.cpp  |  11 +-
 .../DebugInfo/LogicalView/Core/LVReader.cpp   | 172 ++++++++++-
 .../CodeGen/AMDGPU/amdgpu-debuginfo-check.ll  |  36 +--
 .../llvm-debuginfo-analyzer/CMakeLists.txt    |   1 -
 .../llvm-debuginfo-analyzer/DebuggerView.cpp  | 272 ------------------
 .../llvm-debuginfo-analyzer/DebuggerView.h    |  37 ---
 .../tools/llvm-debuginfo-analyzer/Options.cpp |   6 +-
 .../llvm-debuginfo-analyzer.cpp               |  13 +-
 10 files changed, 212 insertions(+), 341 deletions(-)
 delete mode 100644 llvm/tools/llvm-debuginfo-analyzer/DebuggerView.cpp
 delete mode 100644 llvm/tools/llvm-debuginfo-analyzer/DebuggerView.h

diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVOptions.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVOptions.h
index 2afbb013fc62f..a7d4c1030ad53 100644
--- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVOptions.h
+++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVOptions.h
@@ -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>;
 
@@ -408,6 +409,7 @@ class LVOptions {
   REPORT_OPTION(Children);
   REPORT_OPTION(List);
   REPORT_OPTION(Parents);
+  REPORT_OPTION(Debugger);
   REPORT_OPTION(View);
   BOOL_FUNCTION(Report, AnyView);
   BOOL_FUNCTION(Report, Execute);
diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h
index 371bffb2ed163..4dbb43c1099a0 100644
--- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h
+++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h
@@ -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() {}
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVOptions.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVOptions.cpp
index af35e58ac0dd6..81dad0d6bed04 100644
--- a/llvm/lib/DebugInfo/LogicalView/Core/LVOptions.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVOptions.cpp
@@ -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();
+  }
+
   // '--warning=all' settings.
   if (getWarningAll()) {
     setWarningCoverages();
@@ -189,6 +197,7 @@ void LVOptions::resolveDependencies() {
     setReportList();
     setReportParents();
     setReportView();
+    setReportDebugger();
   }
 
   // '--report=view' is a shortcut for '--report=parents,children'.
@@ -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
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
index d973a47f68732..c637cda70c69c 100644
--- a/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
@@ -10,6 +10,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "llvm/ADT/SetVector.h"
 #include "llvm/DebugInfo/LogicalView/Core/LVReader.h"
 #include "llvm/DebugInfo/LogicalView/Core/LVScope.h"
 #include "llvm/Support/FileSystem.h"
@@ -516,13 +517,182 @@ 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 *>> LivetimeBegins;
+  std::unordered_map<LVAddress, std::vector<const LVLocation *>>
+      LivetimeEndsExclusive;
+  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())
+            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";
+      }
+    }
+  }
+
+  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 : 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);
+
+        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();
+  if (!CU) {
+    return createStringError(std::make_error_code(std::errc::invalid_argument), "Error: No compute unit found.");
+  }
+
+  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";
+
+      DebuggerViewPrinter P(OS, Fn);
+      P.Print();
+    }
+  }
+  return Error::success();
+}
+
 Error LVReader::printScopes() {
   if (bool DoPrint =
           (options().getPrintExecute() || options().getComparePrint())) {
diff --git a/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll b/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll
index fb0ab834b3551..437f11d3f0533 100644
--- a/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll
+++ b/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll
@@ -1,25 +1,25 @@
 ; RUN: llc %s -o %t.o -mcpu=gfx1030 -filetype=obj -O0
-; RUN: llvm-debuginfo-analyzer --debugger-view --debugger-view-vars %t.o | FileCheck %s
+; RUN: llvm-debuginfo-analyzer --report=debugger --print=symbols %t.o | FileCheck %s
 
 ; This test compiles this module with AMDGPU backend under -O0,
-; and makes sure llvm-debuginfo-analyzer --debugger-view works for it.
+; and makes sure llvm-debuginfo-analyzer --report=debugger 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{{.+}}
+; CHECK: {Function}: main
+; CHECK: {Line}: {{.+}}basic_var.hlsl:7
+; CHECK: {Line}: {{.+}}basic_var.hlsl:11
+; CHECK: {Variable}: dtid: uint3 : reg{{.+}}, piece 4
+; CHECK: {Line}: {{.+}}basic_var.hlsl:17
+; CHECK: {Variable}: dtid: uint3 : reg{{.+}}, piece 4
+; CHECK: {Line}: {{.+}}basic_var.hlsl:11
+; CHECK: {Variable}: dtid: uint3 : reg{{.+}}, piece 4
+; CHECK: {Line}: {{.+}}basic_var.hlsl:14
+; CHECK-DAG: {Variable}: dtid: uint3 : reg{{.+}}, piece 4
+; CHECK: {Line}: {{.+}}basic_var.hlsl:17
+; CHECK-DAG: {Variable}: dtid: uint3 : reg{{.+}}, piece 4
+; CHECK-DAG: {Variable}: my_var2: float : reg{{.+}}
+; CHECK: {Line}: {{.+}}basic_var.hlsl:19
+; CHECK-DAG: {Variable}: dtid: uint3 : reg{{.+}}, piece 4
+; CHECK-DAG: {Variable}: my_var2: float : reg{{.+}}
 
 source_filename = "module"
 target triple = "amdgcn-amd-amdpal"
diff --git a/llvm/tools/llvm-debuginfo-analyzer/CMakeLists.txt b/llvm/tools/llvm-debuginfo-analyzer/CMakeLists.txt
index 9de3078ffee55..3e16d81abe35c 100644
--- a/llvm/tools/llvm-debuginfo-analyzer/CMakeLists.txt
+++ b/llvm/tools/llvm-debuginfo-analyzer/CMakeLists.txt
@@ -15,5 +15,4 @@ 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
deleted file mode 100644
index 015096d49bd95..0000000000000
--- a/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.cpp
+++ /dev/null
@@ -1,272 +0,0 @@
-//===-- 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
deleted file mode 100644
index d775fa0c80d2d..0000000000000
--- a/llvm/tools/llvm-debuginfo-analyzer/DebuggerView.h
+++ /dev/null
@@ -1,37 +0,0 @@
-//===-- 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 "llvm/ADT/ArrayRef.h"
-#include "llvm/Support/CommandLine.h"
-#include <vector>
-
-namespace llvm {
-class ScopedPrinter;
-class raw_ostream;
-
-namespace logicalview {
-class LVReaderHandler;
-class LVOptions;
-} // namespace logicalview
-
-namespace debuggerview {
-
-extern cl::OptionCategory Category;
-extern cl::opt<bool> Enable;
-int printDebuggerView(std::vector<std::string> &Objects, raw_ostream &OS);
-
-} // namespace debuggerview
-} // namespace llvm
-#endif // DEBUGGER_VIEW_H
diff --git a/llvm/tools/llvm-debuginfo-analyzer/Options.cpp b/llvm/tools/llvm-debuginfo-analyzer/Options.cpp
index 3d1373896f234..1629b86807d7c 100644
--- a/llvm/tools/llvm-debuginfo-analyzer/Options.cpp
+++ b/llvm/tools/llvm-debuginfo-analyzer/Options.cpp
@@ -261,7 +261,11 @@ cl::list<LVReportKind> cmdline::ReportOptions(
                       "(Include parents)"),
            clEnumValN(LVReportKind::View, "view",
                       "Selected elements are displayed in a tree view "
-                      "(Include parents and children.")));
+                      "(Include parents and children."),
+           clEnumValN(
+               LVReportKind::Debugger, "debugger",
+               "Selected elements are displayed in a simulated debugger view "
+               "(Include parents and children.")));
 
 //===----------------------------------------------------------------------===//
 // '--select' options
diff --git a/llvm/tools/llvm-debuginfo-analyzer/llvm-debuginfo-analyzer.cpp b/llvm/tools/llvm-debuginfo-analyzer/llvm-debuginfo-analyzer.cpp
index 36b289411c044..a5e2d39f67d7f 100644
--- a/llvm/tools/llvm-debuginfo-analyzer/llvm-debuginfo-analyzer.cpp
+++ b/llvm/tools/llvm-debuginfo-analyzer/llvm-debuginfo-analyzer.cpp
@@ -11,7 +11,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "DebuggerView.h"
 #include "Options.h"
 #include "llvm/DebugInfo/LogicalView/Core/LVOptions.h"
 #include "llvm/DebugInfo/LogicalView/LVReaderHandler.h"
@@ -96,10 +95,9 @@ int main(int argc, char **argv) {
   cl::extrahelp HelpResponse(
       "\nPass @FILE as argument to read options from FILE.\n");
 
-  cl::HideUnrelatedOptions({&AttributeCategory, &CompareCategory,
-                            &InternalCategory, &OutputCategory, &PrintCategory,
-                            &ReportCategory, &SelectCategory, &WarningCategory,
-                            &debuggerview::Category});
+  cl::HideUnrelatedOptions(
+      {&AttributeCategory, &CompareCategory, &InternalCategory, &OutputCategory,
+       &PrintCategory, &ReportCategory, &SelectCategory, &WarningCategory});
   cl::ParseCommandLineOptions(argc, argv,
                               "Printing a logical representation of low-level "
                               "debug information.\n");
@@ -122,11 +120,8 @@ int main(int argc, char **argv) {
     llvm::append_range(Objects, Objs);
   }
 
-  if (debuggerview::Enable)
-    return debuggerview::printDebuggerView(Objects, OutputFile.os());
-
-  ScopedPrinter W(OutputFile.os());
   propagateOptions();
+  ScopedPrinter W(OutputFile.os());
   LVReaderHandler ReaderHandler(Objects, W, ReaderOptions);
 
   // Print the command line.

>From 5d0f679181d4170a3a94aee3c780e76aa2879f98 Mon Sep 17 00:00:00 2001
From: Adam Yang <hanbyang at microsoft.com>
Date: Mon, 29 Sep 2025 16:13:20 -0700
Subject: [PATCH 08/15] Addressed some more feedback

---
 .../DebugInfo/LogicalView/Core/LVReader.cpp   | 22 ++++++++++---------
 .../CodeGen/AMDGPU/amdgpu-debuginfo-check.ll  |  4 ++--
 2 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
index c637cda70c69c..abd1516bbc561 100644
--- a/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
@@ -10,8 +10,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "llvm/ADT/SetVector.h"
 #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"
@@ -531,9 +531,9 @@ namespace {
 
 struct DebuggerViewPrinter {
   std::vector<const LVLine *> Lines;
-  std::unordered_map<LVAddress, std::vector<const LVLocation *>> LivetimeBegins;
+  std::unordered_map<LVAddress, std::vector<const LVLocation *>> LifetimeBegins;
   std::unordered_map<LVAddress, std::vector<const LVLocation *>>
-      LivetimeEndsExclusive;
+      LifetimeEndsExclusive;
   raw_ostream &OS;
 
   const bool IncludeRanges = false;
@@ -566,8 +566,9 @@ struct DebuggerViewPrinter {
 
           LVAddress Begin = Loc->getLowerAddress();
           LVAddress End = Loc->getUpperAddress();
-          LivetimeBegins[Begin].push_back(Loc);
-          LivetimeEndsExclusive[End].push_back(Loc);
+          LifetimeBegins[Begin].push_back(Loc);
+          LifetimeEndsExclusive[End].push_back(Loc);
+
           if (IncludeRanges) {
             OS << "[" << hexValue(Begin) << ":" << hexValue(End) << "] ";
           }
@@ -629,10 +630,10 @@ struct DebuggerViewPrinter {
     for (const LVLine *Line : Lines) {
       const LVScope *Scope = Line->getParentScope();
       // Update live list: Add lives
-      for (auto Loc : LivetimeBegins[Line->getAddress()])
+      for (auto Loc : LifetimeBegins[Line->getAddress()])
         LiveSymbols.insert(Loc);
       // Update live list: remove dead
-      for (auto Loc : LivetimeEndsExclusive[Line->getAddress()])
+      for (auto Loc : LifetimeEndsExclusive[Line->getAddress()])
         LiveSymbols.remove(Loc);
 
       if (Line->getIsNewStatement() && Line->getIsLineDebug() &&
@@ -653,8 +654,8 @@ struct DebuggerViewPrinter {
             if (SymScope != LineScope && !IsChildScopeOf(LineScope, SymScope))
               continue;
             PrintIndent(OS, 2);
-            OS << "{Variable}: " << Sym->getName() << ": " << Sym->getType()->getName()
-               << " : ";
+            OS << "{Variable}: " << Sym->getName() << ": "
+               << Sym->getType()->getName() << " : ";
             SymLoc->printLocations(OS);
             OS << " (line " << Sym->getLineNumber() << ")";
             OS << "\n";
@@ -674,7 +675,8 @@ struct DebuggerViewPrinter {
 Error LVReader::printDebugger() {
   auto *CU = getCompileUnit();
   if (!CU) {
-    return createStringError(std::make_error_code(std::errc::invalid_argument), "Error: No compute unit found.");
+    return createStringError(std::make_error_code(std::errc::invalid_argument),
+                             "Error: No compute unit found.");
   }
 
   for (LVElement *Child : *CU->getChildren()) {
diff --git a/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll b/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll
index 437f11d3f0533..3da21b157f1cf 100644
--- a/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll
+++ b/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll
@@ -1,8 +1,8 @@
 ; RUN: llc %s -o %t.o -mcpu=gfx1030 -filetype=obj -O0
 ; RUN: llvm-debuginfo-analyzer --report=debugger --print=symbols %t.o | FileCheck %s
 
-; This test compiles this module with AMDGPU backend under -O0,
-; and makes sure llvm-debuginfo-analyzer --report=debugger works for it.
+; The test compiles this module using the AMDGPU backend under `-O0`,
+; and makes sure `llvm-debuginfo-analyzer --report=debugger` works for it.
 
 ; CHECK: {Function}: main
 ; CHECK: {Line}: {{.+}}basic_var.hlsl:7

>From ceee148d46940e4088590828d25f3d2a70316658 Mon Sep 17 00:00:00 2001
From: Adam Yang <hanbyang at microsoft.com>
Date: Mon, 29 Sep 2025 17:06:25 -0700
Subject: [PATCH 09/15] More LLVM idiomatic changes. Attempted to fix linux
 build

---
 llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
index abd1516bbc561..1280bf21854db 100644
--- a/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
@@ -555,10 +555,9 @@ struct DebuggerViewPrinter {
         if (SymbolLocations.empty())
           continue;
 
-        if (IncludeRanges) {
+        if (IncludeRanges)
           OS << "{Range}: " << Symbol->getName() << " (line "
              << Symbol->getLineNumber() << ")" << ": ";
-        }
 
         for (const LVLocation *Loc : SymbolLocations) {
           if (Loc->getIsGapEntry())
@@ -569,9 +568,8 @@ struct DebuggerViewPrinter {
           LifetimeBegins[Begin].push_back(Loc);
           LifetimeEndsExclusive[End].push_back(Loc);
 
-          if (IncludeRanges) {
+          if (IncludeRanges)
             OS << "[" << hexValue(Begin) << ":" << hexValue(End) << "] ";
-          }
         }
 
         if (IncludeRanges)
@@ -674,12 +672,11 @@ struct DebuggerViewPrinter {
 
 Error LVReader::printDebugger() {
   auto *CU = getCompileUnit();
-  if (!CU) {
+  if (!CU)
     return createStringError(std::make_error_code(std::errc::invalid_argument),
                              "Error: No compute unit found.");
-  }
 
-  for (LVElement *Child : *CU->getChildren()) {
+  for (const LVElement *Child : *CU->getChildren()) {
     auto *Fn = dyn_cast<LVScopeFunction>(Child);
     if (Fn) {
       const LVLines *Lines = Fn->getLines();

>From dbc8d36a746da03fc71eb6015ca6f7824386cdf6 Mon Sep 17 00:00:00 2001
From: Adam Yang <hanbyang at microsoft.com>
Date: Mon, 29 Sep 2025 17:12:49 -0700
Subject: [PATCH 10/15] Added entry in the document

---
 llvm/docs/CommandGuide/llvm-debuginfo-analyzer.rst | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/llvm/docs/CommandGuide/llvm-debuginfo-analyzer.rst b/llvm/docs/CommandGuide/llvm-debuginfo-analyzer.rst
index 6fcf8423e8125..9512cc20a50cb 100644
--- a/llvm/docs/CommandGuide/llvm-debuginfo-analyzer.rst
+++ b/llvm/docs/CommandGuide/llvm-debuginfo-analyzer.rst
@@ -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
@@ -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).
+
 **Notes**:
 
 1. When a selection criteria (:option:`--select`) is specified with no

>From 431ec628ca9c9ac29a4eb7b8ea1942a64501e4ac Mon Sep 17 00:00:00 2001
From: Adam Yang <hanbyang at microsoft.com>
Date: Wed, 1 Oct 2025 16:35:13 -0700
Subject: [PATCH 11/15] Fixed build failure with latest main

---
 llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
index 1280bf21854db..24c05924f098b 100644
--- a/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
@@ -676,14 +676,14 @@ Error LVReader::printDebugger() {
     return createStringError(std::make_error_code(std::errc::invalid_argument),
                              "Error: No compute unit found.");
 
-  for (const LVElement *Child : *CU->getChildren()) {
-    auto *Fn = dyn_cast<LVScopeFunction>(Child);
+  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}: " << Child->getName() << "\n";
+      outs() << "{Function}: " << ChildScope->getName() << "\n";
 
       DebuggerViewPrinter P(OS, Fn);
       P.Print();

>From 936e9f661ace94e8943c9393df041a3f4e101ced Mon Sep 17 00:00:00 2001
From: Adam Yang <hanbyang at microsoft.com>
Date: Fri, 10 Oct 2025 16:42:01 -0700
Subject: [PATCH 12/15] Addressed feedback

---
 .../CommandGuide/llvm-debuginfo-analyzer.rst  | 67 +++++++++++++++++++
 .../DebugInfo/LogicalView/Core/LVOptions.h    |  6 +-
 .../DebugInfo/LogicalView/Core/LVOptions.cpp  |  2 +-
 .../DebugInfo/LogicalView/Core/LVReader.cpp   | 60 +++++++++--------
 .../tools/llvm-debuginfo-analyzer/Options.cpp | 10 +--
 5 files changed, 108 insertions(+), 37 deletions(-)

diff --git a/llvm/docs/CommandGuide/llvm-debuginfo-analyzer.rst b/llvm/docs/CommandGuide/llvm-debuginfo-analyzer.rst
index 9512cc20a50cb..61b32dcca9a4d 100644
--- a/llvm/docs/CommandGuide/llvm-debuginfo-analyzer.rst
+++ b/llvm/docs/CommandGuide/llvm-debuginfo-analyzer.rst
@@ -861,6 +861,73 @@ 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.
+
+.. code-block:: none
+
+  llvm-debuginfo-analyzer --report=debugger
+                          test-dwarf-clang.o test-dwarf-gcc.o
+  {Function}: foo
+    {Line}:  [0x0000000000] test.cpp:2 [foo]
+    {Line}:  [0x0000000012] test.cpp:3 [foo]
+    {Line}:  [0x000000001c] test.cpp:5 [foo]
+    {Line}:  [0x0000000023] test.cpp:6 [foo]
+    {Line}:  [0x000000002f] test.cpp:8 [foo]
+    {Line}:  [0x0000000035] test.cpp:9 [foo]
+  {Function}: foo
+    {Line}:  [0x0000000000] test.cpp:2 [foo]
+    {Line}:  [0x0000000014] test.cpp:3 [foo]
+    {Line}:  [0x000000001a] test.cpp:5 [foo]
+    {Line}:  [0x0000000021] test.cpp:6 [foo]
+    {Line}:  [0x0000000028] test.cpp:8 [foo]
+    {Line}:  [0x000000002b] test.cpp:9 [foo]
+
+Optionally, by adding `--print=symbols`, live variables for each line is
+printed out.
+
+.. code-block:: none
+
+  llvm-debuginfo-analyzer --report=debugger
+                          test-dwarf-clang.o
+
+  {Function}: foo
+    {Line}:  [0x0000000000] test.cpp:2 [foo]
+      {Variable}: ParamBool: bool : fbreg -21 (line 2)
+      {Variable}: ParamPtr: INTPTR : fbreg -16 (line 2)
+      {Variable}: ParamUnsigned: unsigned int : fbreg -20 (line 2)
+    {Line}:  [0x0000000012] test.cpp:3 [foo]
+      {Variable}: ParamBool: bool : fbreg -21 (line 2)
+      {Variable}: ParamPtr: INTPTR : fbreg -16 (line 2)
+      {Variable}: ParamUnsigned: unsigned int : fbreg -20 (line 2)
+    {Line}:  [0x000000001c] test.cpp:5 [foo]
+      {Variable}: CONSTANT: const INTEGER : fbreg -28 (line 5)
+      {Variable}: ParamBool: bool : fbreg -21 (line 2)
+      {Variable}: ParamPtr: INTPTR : fbreg -16 (line 2)
+      {Variable}: ParamUnsigned: unsigned int : fbreg -20 (line 2)
+    {Line}:  [0x0000000023] test.cpp:6 [foo]
+      {Variable}: CONSTANT: const INTEGER : fbreg -28 (line 5)
+      {Variable}: ParamBool: bool : fbreg -21 (line 2)
+      {Variable}: ParamPtr: INTPTR : fbreg -16 (line 2)
+      {Variable}: ParamUnsigned: unsigned int : fbreg -20 (line 2)
+    {Line}:  [0x000000002f] test.cpp:8 [foo]
+      {Variable}: ParamBool: bool : fbreg -21 (line 2)
+      {Variable}: ParamPtr: INTPTR : fbreg -16 (line 2)
+      {Variable}: ParamUnsigned: unsigned int : fbreg -20 (line 2)
+    {Line}:  [0x0000000035] test.cpp:9 [foo]
+      {Variable}: ParamBool: bool : fbreg -21 (line 2)
+      {Variable}: ParamPtr: INTPTR : fbreg -16 (line 2)
+      {Variable}: 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.
+
 COMPARISON MODE
 ^^^^^^^^^^^^^^^
 In this mode :program:`llvm-debuginfo-analyzer` compares logical views
diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVOptions.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVOptions.h
index a7d4c1030ad53..595bb29775d42 100644
--- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVOptions.h
+++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVOptions.h
@@ -165,10 +165,10 @@ 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
-  Debugger  // --report=debugger
+  View      // --report=view
 };
 using LVReportKindSet = std::set<LVReportKind>;
 
@@ -407,9 +407,9 @@ class LVOptions {
   // --report.
   REPORT_OPTION(All);
   REPORT_OPTION(Children);
+  REPORT_OPTION(Debugger);
   REPORT_OPTION(List);
   REPORT_OPTION(Parents);
-  REPORT_OPTION(Debugger);
   REPORT_OPTION(View);
   BOOL_FUNCTION(Report, AnyView);
   BOOL_FUNCTION(Report, Execute);
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVOptions.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVOptions.cpp
index 81dad0d6bed04..05d136c7c00b5 100644
--- a/llvm/lib/DebugInfo/LogicalView/Core/LVOptions.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVOptions.cpp
@@ -194,10 +194,10 @@ void LVOptions::resolveDependencies() {
   // '--reports=all' settings.
   if (getReportAll()) {
     setReportChildren();
+    setReportDebugger();
     setReportList();
     setReportParents();
     setReportView();
-    setReportDebugger();
   }
 
   // '--report=view' is a shortcut for '--report=parents,children'.
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
index 24c05924f098b..dd12ce2ead4f2 100644
--- a/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
@@ -517,7 +517,7 @@ Error LVReader::doPrint() {
     if (options().getReportParents() || options().getReportView())
       if (Error Err = printScopes())
         return Err;
-    // Requested debugger report
+    // Requested debugger report.
     if (options().getReportDebugger())
       if (Error Err = printDebugger())
         return Err;
@@ -538,10 +538,10 @@ struct DebuggerViewPrinter {
 
   const bool IncludeRanges = false;
 
-  void Walk(raw_ostream &OS, const LVScope *Scope) {
+  void walk(raw_ostream &OS, const LVScope *Scope) {
     if (Scope->scopeCount()) {
       for (const LVScope *ChildScope : *Scope->getScopes())
-        Walk(OS, ChildScope);
+        walk(OS, ChildScope);
     }
     if (Scope->lineCount()) {
       for (const LVLine *Line : *Scope->getLines()) {
@@ -579,7 +579,7 @@ struct DebuggerViewPrinter {
   }
 
   DebuggerViewPrinter(raw_ostream &OS, const LVScopeFunction *Fn) : OS(OS) {
-    Walk(OS, Fn);
+    walk(OS, Fn);
     std::sort(Lines.begin(), Lines.end(),
               [](const LVLine *a, const LVLine *b) -> bool {
                 if (a->getAddress() != b->getAddress())
@@ -590,12 +590,12 @@ struct DebuggerViewPrinter {
               });
   }
 
-  static void PrintIndent(raw_ostream &OS, int Indent) {
+  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) {
+  static void printCallstack(raw_ostream &OS, const LVScope *Scope) {
     const LVScope *PrevScope = nullptr;
     while (Scope) {
       if (Scope->getIsFunction() || Scope->getIsInlinedFunction()) {
@@ -611,7 +611,7 @@ struct DebuggerViewPrinter {
     }
   }
 
-  static bool IsChildScopeOf(const LVScope *A, const LVScope *B) {
+  static bool isChildScopeOf(const LVScope *A, const LVScope *B) {
     while (A) {
       A = A->getParentScope();
       if (A == B)
@@ -620,7 +620,7 @@ struct DebuggerViewPrinter {
     return false;
   }
 
-  void Print() {
+  void print() {
     const bool IncludeVars = options().getPrintSymbols();
     const bool IncludeCode = options().getPrintInstructions();
     SetVector<const LVLocation *>
@@ -638,20 +638,20 @@ struct DebuggerViewPrinter {
           Line->getLineNumber() != 0) {
         auto LineDebug = cast<LVLineDebug>(Line);
 
-        PrintIndent(OS, 1);
+        printIndent(OS, 1);
         OS << "{Line}: " << " [" << hexValue(LineDebug->getAddress()) << "] "
            << LineDebug->getPathname() << ":" << LineDebug->getLineNumber()
            << " ";
-        PrintCallstack(OS, Scope);
+        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))
+            if (SymScope != LineScope && !isChildScopeOf(LineScope, SymScope))
               continue;
-            PrintIndent(OS, 2);
+            printIndent(OS, 2);
             OS << "{Variable}: " << Sym->getName() << ": "
                << Sym->getType()->getName() << " : ";
             SymLoc->printLocations(OS);
@@ -671,24 +671,28 @@ struct DebuggerViewPrinter {
 } // namespace
 
 Error LVReader::printDebugger() {
-  auto *CU = getCompileUnit();
-  if (!CU)
-    return createStringError(std::make_error_code(std::errc::invalid_argument),
-                             "Error: No compute unit found.");
-
-  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();
+  if (!Root || !Root->scopeCount())
+    return Error::success();
+
+  for (auto *Scope : *Root->getScopes()) {
+    auto *CU = dyn_cast<LVScopeCompileUnit>(Scope);
+    if (!CU)
+      continue;
+    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();
 }
 
diff --git a/llvm/tools/llvm-debuginfo-analyzer/Options.cpp b/llvm/tools/llvm-debuginfo-analyzer/Options.cpp
index 1629b86807d7c..417a5c8537992 100644
--- a/llvm/tools/llvm-debuginfo-analyzer/Options.cpp
+++ b/llvm/tools/llvm-debuginfo-analyzer/Options.cpp
@@ -254,6 +254,10 @@ cl::list<LVReportKind> cmdline::ReportOptions(
            clEnumValN(LVReportKind::Children, "children",
                       "Selected elements are displayed in a tree view "
                       "(Include children)"),
+           clEnumValN(
+               LVReportKind::Debugger, "debugger",
+               "Selected elements are displayed in a simulated debugger view "
+               "(Include parents and children."),
            clEnumValN(LVReportKind::List, "list",
                       "Selected elements are displayed in a tabular format."),
            clEnumValN(LVReportKind::Parents, "parents",
@@ -261,11 +265,7 @@ cl::list<LVReportKind> cmdline::ReportOptions(
                       "(Include parents)"),
            clEnumValN(LVReportKind::View, "view",
                       "Selected elements are displayed in a tree view "
-                      "(Include parents and children."),
-           clEnumValN(
-               LVReportKind::Debugger, "debugger",
-               "Selected elements are displayed in a simulated debugger view "
-               "(Include parents and children.")));
+                      "(Include parents and children.")));
 
 //===----------------------------------------------------------------------===//
 // '--select' options

>From 6ad0daabab5a1e37ff48855c6e2a73574f2afdf1 Mon Sep 17 00:00:00 2001
From: Adam Yang <hanbyang at microsoft.com>
Date: Wed, 22 Oct 2025 14:36:57 -0700
Subject: [PATCH 13/15] Address feedback: pt1

---
 .../CommandGuide/llvm-debuginfo-analyzer.rst  |  4 +--
 .../DebugInfo/LogicalView/Core/LVReader.cpp   | 12 ++++---
 .../CodeGen/AMDGPU/amdgpu-debuginfo-check.ll  | 34 ++++++++++---------
 3 files changed, 27 insertions(+), 23 deletions(-)

diff --git a/llvm/docs/CommandGuide/llvm-debuginfo-analyzer.rst b/llvm/docs/CommandGuide/llvm-debuginfo-analyzer.rst
index 61b32dcca9a4d..da2d8343bd2c4 100644
--- a/llvm/docs/CommandGuide/llvm-debuginfo-analyzer.rst
+++ b/llvm/docs/CommandGuide/llvm-debuginfo-analyzer.rst
@@ -391,11 +391,11 @@ 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.
-   =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
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
index dd12ce2ead4f2..54d5978216684 100644
--- a/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
@@ -556,7 +556,7 @@ struct DebuggerViewPrinter {
           continue;
 
         if (IncludeRanges)
-          OS << "{Range}: " << Symbol->getName() << " (line "
+          OS << "{Range} " << Symbol->getName() << " (line "
              << Symbol->getLineNumber() << ")" << ": ";
 
         for (const LVLocation *Loc : SymbolLocations) {
@@ -639,7 +639,7 @@ struct DebuggerViewPrinter {
         auto LineDebug = cast<LVLineDebug>(Line);
 
         printIndent(OS, 1);
-        OS << "{Line}: " << " [" << hexValue(LineDebug->getAddress()) << "] "
+        OS << "{Line} " << " [" << hexValue(LineDebug->getAddress()) << "] "
            << LineDebug->getPathname() << ":" << LineDebug->getLineNumber()
            << " ";
         printCallstack(OS, Scope);
@@ -652,7 +652,7 @@ struct DebuggerViewPrinter {
             if (SymScope != LineScope && !isChildScopeOf(LineScope, SymScope))
               continue;
             printIndent(OS, 2);
-            OS << "{Variable}: " << Sym->getName() << ": "
+            OS << "{Variable} " << Sym->getName() << ": "
                << Sym->getType()->getName() << " : ";
             SymLoc->printLocations(OS);
             OS << " (line " << Sym->getLineNumber() << ")";
@@ -661,7 +661,7 @@ struct DebuggerViewPrinter {
         }
 
       } else if (IncludeCode && Line->getIsLineAssembler()) {
-        OS << "  {Code}: " << " [" << hexValue(Line->getAddress()) << "]  "
+        OS << "  {Code} " << " [" << hexValue(Line->getAddress()) << "]  "
            << Line->getName() << "\n";
       }
     }
@@ -673,11 +673,13 @@ struct DebuggerViewPrinter {
 Error LVReader::printDebugger() {
   if (!Root || !Root->scopeCount())
     return Error::success();
+  outs() << "{File} " << getFilename() << "\n";
 
   for (auto *Scope : *Root->getScopes()) {
     auto *CU = dyn_cast<LVScopeCompileUnit>(Scope);
     if (!CU)
       continue;
+    outs() << "{CompileUnit} " << CU->getName() << "\n";
     for (const LVScope *ChildScope : *CU->getScopes()) {
       auto *Fn = dyn_cast<LVScopeFunction>(ChildScope);
       if (Fn) {
@@ -685,7 +687,7 @@ Error LVReader::printDebugger() {
         // If there's no lines, this function has no body.
         if (!Lines)
           continue;
-        outs() << "{Function}: " << ChildScope->getName() << "\n";
+        outs() << "{Function} " << ChildScope->getName() << "\n";
 
         DebuggerViewPrinter P(OS, Fn);
         P.print();
diff --git a/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll b/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll
index 3da21b157f1cf..e440958568bd0 100644
--- a/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll
+++ b/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll
@@ -4,22 +4,24 @@
 ; The test compiles this module using the AMDGPU backend under `-O0`,
 ; and makes sure `llvm-debuginfo-analyzer --report=debugger` works for it.
 
-; CHECK: {Function}: main
-; CHECK: {Line}: {{.+}}basic_var.hlsl:7
-; CHECK: {Line}: {{.+}}basic_var.hlsl:11
-; CHECK: {Variable}: dtid: uint3 : reg{{.+}}, piece 4
-; CHECK: {Line}: {{.+}}basic_var.hlsl:17
-; CHECK: {Variable}: dtid: uint3 : reg{{.+}}, piece 4
-; CHECK: {Line}: {{.+}}basic_var.hlsl:11
-; CHECK: {Variable}: dtid: uint3 : reg{{.+}}, piece 4
-; CHECK: {Line}: {{.+}}basic_var.hlsl:14
-; CHECK-DAG: {Variable}: dtid: uint3 : reg{{.+}}, piece 4
-; CHECK: {Line}: {{.+}}basic_var.hlsl:17
-; CHECK-DAG: {Variable}: dtid: uint3 : reg{{.+}}, piece 4
-; CHECK-DAG: {Variable}: my_var2: float : reg{{.+}}
-; CHECK: {Line}: {{.+}}basic_var.hlsl:19
-; CHECK-DAG: {Variable}: dtid: uint3 : reg{{.+}}, piece 4
-; CHECK-DAG: {Variable}: my_var2: float : reg{{.+}}
+; CHECK: {File}
+; CHECK: {CompileUnit} basic_var.hlsl
+; CHECK: {Function} main
+; CHECK: {Line} {{.+}}basic_var.hlsl:7
+; CHECK: {Line} {{.+}}basic_var.hlsl:11
+; CHECK: {Variable} dtid: uint3 : reg{{.+}}, piece 4
+; CHECK: {Line} {{.+}}basic_var.hlsl:17
+; CHECK: {Variable} dtid: uint3 : reg{{.+}}, piece 4
+; CHECK: {Line} {{.+}}basic_var.hlsl:11
+; CHECK: {Variable} dtid: uint3 : reg{{.+}}, piece 4
+; CHECK: {Line} {{.+}}basic_var.hlsl:14
+; CHECK-DAG: {Variable} dtid: uint3 : reg{{.+}}, piece 4
+; CHECK: {Line} {{.+}}basic_var.hlsl:17
+; CHECK-DAG: {Variable} dtid: uint3 : reg{{.+}}, piece 4
+; CHECK-DAG: {Variable} my_var2: float : reg{{.+}}
+; CHECK: {Line} {{.+}}basic_var.hlsl:19
+; CHECK-DAG: {Variable} dtid: uint3 : reg{{.+}}, piece 4
+; CHECK-DAG: {Variable} my_var2: float : reg{{.+}}
 
 source_filename = "module"
 target triple = "amdgcn-amd-amdpal"

>From dbc62cd073ae4990e8131962606912315f0ceb69 Mon Sep 17 00:00:00 2001
From: Adam Yang <hanbyang at microsoft.com>
Date: Wed, 22 Oct 2025 17:39:28 -0700
Subject: [PATCH 14/15] Refactored most code

---
 .../llvm/DebugInfo/LogicalView/Core/LVLine.h  |   4 +
 .../DebugInfo/LogicalView/Core/LVLocation.h   |   2 +
 .../llvm/DebugInfo/LogicalView/Core/LVScope.h |   8 +
 .../lib/DebugInfo/LogicalView/Core/LVLine.cpp |  31 ++++
 .../DebugInfo/LogicalView/Core/LVLocation.cpp |  10 ++
 .../DebugInfo/LogicalView/Core/LVReader.cpp   | 170 +-----------------
 .../DebugInfo/LogicalView/Core/LVScope.cpp    | 124 +++++++++++++
 7 files changed, 182 insertions(+), 167 deletions(-)

diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVLine.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVLine.h
index 3618ce7b0ecda..d15cc50f54c58 100644
--- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVLine.h
+++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVLine.h
@@ -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.
@@ -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.
@@ -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
diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVLocation.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVLocation.h
index c55b8259b4ee8..fc5f11817f2ac 100644
--- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVLocation.h
+++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVLocation.h
@@ -160,6 +160,7 @@ class LLVM_ABI LVLocation : public LVObject {
   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 {
@@ -181,6 +182,7 @@ class LLVM_ABI LVLocationSymbol final : public LVLocation {
   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
diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h
index f4f3516769938..f17afec54658e 100644
--- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h
+++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h
@@ -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;
@@ -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.
@@ -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).
@@ -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.
@@ -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
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp
index c3810d282abc0..1dad78c98b58d 100644
--- a/llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp
@@ -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.
 //===----------------------------------------------------------------------===//
@@ -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";
+}
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp
index 4724f4f007163..cf57ee79a875c 100644
--- a/llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp
@@ -679,3 +679,13 @@ void LVLocationSymbol::printExtra(raw_ostream &OS, bool Full) const {
                     /*PrintRef=*/false);
   }
 }
+
+void LVLocationSymbol::printDebugger(raw_ostream &OS, LVLevel Indent) const {
+  LVSymbol *Sym = getParentSymbol();
+  OS << indentAsString(Indent) << formattedKind(Sym->kind());
+  OS << " " << Sym->getName() << ": "
+    << Sym->getType()->getName() << " : ";
+  printLocations(OS);
+  OS << " (line " << Sym->getLineNumber() << ")";
+  OS << "\n";
+}
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
index 54d5978216684..745d20fc0943d 100644
--- a/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
@@ -527,175 +527,11 @@ Error LVReader::doPrint() {
   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())
-            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() {
-  if (!Root || !Root->scopeCount())
-    return Error::success();
-  outs() << "{File} " << getFilename() << "\n";
-
-  for (auto *Scope : *Root->getScopes()) {
-    auto *CU = dyn_cast<LVScopeCompileUnit>(Scope);
-    if (!CU)
-      continue;
-    outs() << "{CompileUnit} " << CU->getName() << "\n";
-    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();
-      }
-    }
-  }
+  if (Error Err = createSplitFolder())
+    return Err;
 
-  return Error::success();
+  return Root->doPrintDebugger(OutputSplit, outs());
 }
 
 Error LVReader::printScopes() {
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp
index e03932622b259..af95f453f3f08 100644
--- a/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp
@@ -10,6 +10,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "llvm/ADT/SetVector.h"
 #include "llvm/DebugInfo/LogicalView/Core/LVScope.h"
 #include "llvm/DebugInfo/LogicalView/Core/LVCompare.h"
 #include "llvm/DebugInfo/LogicalView/Core/LVLine.h"
@@ -971,6 +972,16 @@ bool LVScope::equals(const LVScopes *References, const LVScopes *Targets) {
   return false;
 }
 
+bool LVScope::isChildScopeOf(const LVScope *Scope) const {
+  const LVScope *Self = this;
+  while (Self) {
+    Self = Self->getParentScope();
+    if (Self == Scope)
+      return true;
+  }
+  return false;
+}
+
 void LVScope::report(LVComparePass Pass) {
   getComparator().printItem(this, Pass);
   getComparator().push(this);
@@ -1698,6 +1709,13 @@ void LVScopeCompileUnit::printMatchedElements(raw_ostream &OS,
   }
 }
 
+void LVScopeCompileUnit::printDebugger(raw_ostream &OS) const {
+  outs() << "{CompileUnit} " << getName() << "\n";
+  for (const LVScope *ChildScope : *getScopes()) {
+    ChildScope->printDebugger(OS);
+  }
+}
+
 void LVScopeCompileUnit::print(raw_ostream &OS, bool Full) const {
   // Reset counters for printed and found elements.
   const_cast<LVScopeCompileUnit *>(this)->Found.reset();
@@ -1903,6 +1921,95 @@ void LVScopeFunction::printExtra(raw_ostream &OS, bool Full) const {
   }
 }
 
+void LVScopeFunction::printDebugger(raw_ostream &OS) const {
+  const LVLines *Lines = getLines();
+  // If there's no lines, this function has no body.
+  if (!Lines)
+    return;
+
+  OS << formattedKind(kind()) << " " << getName() << "\n";
+
+  std::vector<const LVLine *> AllLines;
+  std::unordered_map<LVAddress, std::vector<const LVLocation *>> LifetimeBegins;
+  std::unordered_map<LVAddress, std::vector<const LVLocation *>>
+    LifetimeEndsExclusive;
+
+  // Collect all child scope lines and symbols
+  std::vector<const LVScope *> Worklist = { this };
+  for (unsigned i = 0; i < Worklist.size(); i++) {
+    if (scopeCount()) {
+      for (const LVScope *ChildScope : *getScopes())
+        Worklist.push_back(ChildScope);
+    }
+    if (lineCount()) {
+      for (const LVLine *Line : *getLines()) {
+        AllLines.push_back(Line);
+      }
+    }
+    if (symbolCount()) {
+      for (const LVSymbol *Symbol : *getSymbols()) {
+        LVLocations SymbolLocations;
+        Symbol->getLocations(SymbolLocations);
+        if (SymbolLocations.empty())
+          continue;
+
+        for (const LVLocation *Loc : SymbolLocations) {
+          if (Loc->getIsGapEntry())
+            continue;
+
+          LVAddress Begin = Loc->getLowerAddress();
+          LVAddress End = Loc->getUpperAddress();
+          LifetimeBegins[Begin].push_back(Loc);
+          LifetimeEndsExclusive[End].push_back(Loc);
+        }
+      }
+    }
+  }
+
+  // Sort all lines by their address.
+  std::sort(AllLines.begin(), AllLines.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();
+    });
+
+  // Print everything out
+  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 : AllLines) {
+    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) {
+      Line->printDebugger(OS, 1);
+      if (IncludeVars) {
+        for (auto SymLoc : LiveSymbols) {
+          const LVSymbol *Sym = SymLoc->getParentSymbol();
+          auto SymScope = Sym->getParentScope();
+          auto LineScope = Line->getParentScope();
+          if (SymScope != LineScope && !LineScope->isChildScopeOf(SymScope))
+            continue;
+          SymLoc->printDebugger(OS, 2);
+        }
+      }
+    }
+    else if (IncludeCode && Line->getIsLineAssembler()) {
+      Line->printDebugger(OS, 1);
+    }
+  }
+}
+
 //===----------------------------------------------------------------------===//
 // DWARF inlined function (DW_TAG_inlined_function).
 //===----------------------------------------------------------------------===//
@@ -2119,6 +2226,23 @@ Error LVScopeRoot::doPrintMatches(bool Split, raw_ostream &OS,
   return Error::success();
 }
 
+Error LVScopeRoot::doPrintDebugger(bool Split, raw_ostream &OS) const {
+  static raw_ostream *StreamSplit = &OS;
+  for (LVScope *Scope : *Scopes) {
+    if (Split) {
+      std::string ScopeName(Scope->getName());
+      if (std::error_code EC =
+        getReaderSplitContext().open(ScopeName, ".txt", OS))
+        return createStringError(EC, "Unable to create split output file %s",
+          ScopeName.c_str());
+      StreamSplit = static_cast<raw_ostream *>(&getReaderSplitContext().os());
+    }
+    *StreamSplit << "{File} " << getName() << "\n";
+    Scope->printDebugger(*StreamSplit);
+  }
+  return Error::success();
+}
+
 //===----------------------------------------------------------------------===//
 // DWARF template parameter pack (DW_TAG_GNU_template_parameter_pack).
 //===----------------------------------------------------------------------===//

>From 0f5a6a99454c5341164da8d43fd79b74bf1ca481 Mon Sep 17 00:00:00 2001
From: Adam Yang <hanbyang at microsoft.com>
Date: Thu, 23 Oct 2025 14:51:29 -0700
Subject: [PATCH 15/15] Formatting. Replaced a few more explit {kind} with
 `formattedKind`, and doc update

---
 .../CommandGuide/llvm-debuginfo-analyzer.rst  | 96 +++++++++++--------
 .../DebugInfo/LogicalView/Core/LVScope.cpp    | 51 +++++-----
 .../CodeGen/AMDGPU/amdgpu-debuginfo-check.ll  | 28 +++---
 3 files changed, 97 insertions(+), 78 deletions(-)

diff --git a/llvm/docs/CommandGuide/llvm-debuginfo-analyzer.rst b/llvm/docs/CommandGuide/llvm-debuginfo-analyzer.rst
index da2d8343bd2c4..d30540a0dff7c 100644
--- a/llvm/docs/CommandGuide/llvm-debuginfo-analyzer.rst
+++ b/llvm/docs/CommandGuide/llvm-debuginfo-analyzer.rst
@@ -873,20 +873,28 @@ well as verifying inline callstacks.
 
   llvm-debuginfo-analyzer --report=debugger
                           test-dwarf-clang.o test-dwarf-gcc.o
-  {Function}: foo
-    {Line}:  [0x0000000000] test.cpp:2 [foo]
-    {Line}:  [0x0000000012] test.cpp:3 [foo]
-    {Line}:  [0x000000001c] test.cpp:5 [foo]
-    {Line}:  [0x0000000023] test.cpp:6 [foo]
-    {Line}:  [0x000000002f] test.cpp:8 [foo]
-    {Line}:  [0x0000000035] test.cpp:9 [foo]
-  {Function}: foo
-    {Line}:  [0x0000000000] test.cpp:2 [foo]
-    {Line}:  [0x0000000014] test.cpp:3 [foo]
-    {Line}:  [0x000000001a] test.cpp:5 [foo]
-    {Line}:  [0x0000000021] test.cpp:6 [foo]
-    {Line}:  [0x0000000028] test.cpp:8 [foo]
-    {Line}:  [0x000000002b] test.cpp:9 [foo]
+
+  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
 printed out.
@@ -896,38 +904,44 @@ printed out.
   llvm-debuginfo-analyzer --report=debugger
                           test-dwarf-clang.o
 
-  {Function}: foo
-    {Line}:  [0x0000000000] test.cpp:2 [foo]
-      {Variable}: ParamBool: bool : fbreg -21 (line 2)
-      {Variable}: ParamPtr: INTPTR : fbreg -16 (line 2)
-      {Variable}: ParamUnsigned: unsigned int : fbreg -20 (line 2)
-    {Line}:  [0x0000000012] test.cpp:3 [foo]
-      {Variable}: ParamBool: bool : fbreg -21 (line 2)
-      {Variable}: ParamPtr: INTPTR : fbreg -16 (line 2)
-      {Variable}: ParamUnsigned: unsigned int : fbreg -20 (line 2)
-    {Line}:  [0x000000001c] test.cpp:5 [foo]
-      {Variable}: CONSTANT: const INTEGER : fbreg -28 (line 5)
-      {Variable}: ParamBool: bool : fbreg -21 (line 2)
-      {Variable}: ParamPtr: INTPTR : fbreg -16 (line 2)
-      {Variable}: ParamUnsigned: unsigned int : fbreg -20 (line 2)
-    {Line}:  [0x0000000023] test.cpp:6 [foo]
-      {Variable}: CONSTANT: const INTEGER : fbreg -28 (line 5)
-      {Variable}: ParamBool: bool : fbreg -21 (line 2)
-      {Variable}: ParamPtr: INTPTR : fbreg -16 (line 2)
-      {Variable}: ParamUnsigned: unsigned int : fbreg -20 (line 2)
-    {Line}:  [0x000000002f] test.cpp:8 [foo]
-      {Variable}: ParamBool: bool : fbreg -21 (line 2)
-      {Variable}: ParamPtr: INTPTR : fbreg -16 (line 2)
-      {Variable}: ParamUnsigned: unsigned int : fbreg -20 (line 2)
-    {Line}:  [0x0000000035] test.cpp:9 [foo]
-      {Variable}: ParamBool: bool : fbreg -21 (line 2)
-      {Variable}: ParamPtr: INTPTR : fbreg -16 (line 2)
-      {Variable}: ParamUnsigned: unsigned int : fbreg -20 (line 2)
+  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)
+    {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
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp
index af95f453f3f08..43a060fc7637d 100644
--- a/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp
@@ -1710,7 +1710,7 @@ void LVScopeCompileUnit::printMatchedElements(raw_ostream &OS,
 }
 
 void LVScopeCompileUnit::printDebugger(raw_ostream &OS) const {
-  outs() << "{CompileUnit} " << getName() << "\n";
+  outs() << formattedKind(kind()) << " " << getName() << "\n";
   for (const LVScope *ChildScope : *getScopes()) {
     ChildScope->printDebugger(OS);
   }
@@ -1932,22 +1932,23 @@ void LVScopeFunction::printDebugger(raw_ostream &OS) const {
   std::vector<const LVLine *> AllLines;
   std::unordered_map<LVAddress, std::vector<const LVLocation *>> LifetimeBegins;
   std::unordered_map<LVAddress, std::vector<const LVLocation *>>
-    LifetimeEndsExclusive;
+      LifetimeEndsExclusive;
 
   // Collect all child scope lines and symbols
-  std::vector<const LVScope *> Worklist = { this };
+  std::vector<const LVScope *> Worklist = {this};
   for (unsigned i = 0; i < Worklist.size(); i++) {
-    if (scopeCount()) {
-      for (const LVScope *ChildScope : *getScopes())
+    const LVScope *Scope = Worklist[i];
+    if (Scope->scopeCount()) {
+      for (const LVScope *ChildScope : *Scope->getScopes())
         Worklist.push_back(ChildScope);
     }
-    if (lineCount()) {
-      for (const LVLine *Line : *getLines()) {
+    if (Scope->lineCount()) {
+      for (const LVLine *Line : *Scope->getLines()) {
         AllLines.push_back(Line);
       }
     }
-    if (symbolCount()) {
-      for (const LVSymbol *Symbol : *getSymbols()) {
+    if (Scope->symbolCount()) {
+      for (const LVSymbol *Symbol : *Scope->getSymbols()) {
         LVLocations SymbolLocations;
         Symbol->getLocations(SymbolLocations);
         if (SymbolLocations.empty())
@@ -1968,19 +1969,19 @@ void LVScopeFunction::printDebugger(raw_ostream &OS) const {
 
   // Sort all lines by their address.
   std::sort(AllLines.begin(), AllLines.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();
-    });
+            [](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();
+            });
 
   // Print everything out
   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.
+      LiveSymbols; // This needs to be ordered since we're iterating over it.
   for (const LVLine *Line : AllLines) {
     const LVScope *Scope = Line->getParentScope();
     // Update live list: Add lives
@@ -1991,7 +1992,7 @@ void LVScopeFunction::printDebugger(raw_ostream &OS) const {
       LiveSymbols.remove(Loc);
 
     if (Line->getIsNewStatement() && Line->getIsLineDebug() &&
-      Line->getLineNumber() != 0) {
+        Line->getLineNumber() != 0) {
       Line->printDebugger(OS, 1);
       if (IncludeVars) {
         for (auto SymLoc : LiveSymbols) {
@@ -2003,8 +2004,7 @@ void LVScopeFunction::printDebugger(raw_ostream &OS) const {
           SymLoc->printDebugger(OS, 2);
         }
       }
-    }
-    else if (IncludeCode && Line->getIsLineAssembler()) {
+    } else if (IncludeCode && Line->getIsLineAssembler()) {
       Line->printDebugger(OS, 1);
     }
   }
@@ -2227,18 +2227,23 @@ Error LVScopeRoot::doPrintMatches(bool Split, raw_ostream &OS,
 }
 
 Error LVScopeRoot::doPrintDebugger(bool Split, raw_ostream &OS) const {
+  OS << "\nLogical View:\n";
   static raw_ostream *StreamSplit = &OS;
   for (LVScope *Scope : *Scopes) {
     if (Split) {
       std::string ScopeName(Scope->getName());
       if (std::error_code EC =
-        getReaderSplitContext().open(ScopeName, ".txt", OS))
+              getReaderSplitContext().open(ScopeName, ".txt", OS))
         return createStringError(EC, "Unable to create split output file %s",
-          ScopeName.c_str());
+                                 ScopeName.c_str());
       StreamSplit = static_cast<raw_ostream *>(&getReaderSplitContext().os());
     }
-    *StreamSplit << "{File} " << getName() << "\n";
+    *StreamSplit << formattedKind(kind()) << " " << getName() << "\n";
     Scope->printDebugger(*StreamSplit);
+    if (Split) {
+      getReaderSplitContext().close();
+      StreamSplit = &getReader().outputStream();
+    }
   }
   return Error::success();
 }
diff --git a/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll b/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll
index e440958568bd0..24df0bc76853b 100644
--- a/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll
+++ b/llvm/test/CodeGen/AMDGPU/amdgpu-debuginfo-check.ll
@@ -1,5 +1,5 @@
 ; RUN: llc %s -o %t.o -mcpu=gfx1030 -filetype=obj -O0
-; RUN: llvm-debuginfo-analyzer --report=debugger --print=symbols %t.o | FileCheck %s
+; RUN: llvm-debuginfo-analyzer --report=debugger --print=symbols --attribute=level,offset %t.o | FileCheck %s
 
 ; The test compiles this module using the AMDGPU backend under `-O0`,
 ; and makes sure `llvm-debuginfo-analyzer --report=debugger` works for it.
@@ -7,20 +7,20 @@
 ; CHECK: {File}
 ; CHECK: {CompileUnit} basic_var.hlsl
 ; CHECK: {Function} main
-; CHECK: {Line} {{.+}}basic_var.hlsl:7
-; CHECK: {Line} {{.+}}basic_var.hlsl:11
-; CHECK: {Variable} dtid: uint3 : reg{{.+}}, piece 4
-; CHECK: {Line} {{.+}}basic_var.hlsl:17
-; CHECK: {Variable} dtid: uint3 : reg{{.+}}, piece 4
-; CHECK: {Line} {{.+}}basic_var.hlsl:11
-; CHECK: {Variable} dtid: uint3 : reg{{.+}}, piece 4
-; CHECK: {Line} {{.+}}basic_var.hlsl:14
-; CHECK-DAG: {Variable} dtid: uint3 : reg{{.+}}, piece 4
-; CHECK: {Line} {{.+}}basic_var.hlsl:17
-; CHECK-DAG: {Variable} dtid: uint3 : reg{{.+}}, piece 4
+; CHECK: {Line} [0x{{[0-9a-f]+}}][003] basic_var.hlsl:7 [main]
+; CHECK: {Line} [0x{{[0-9a-f]+}}][003] basic_var.hlsl:11 [main]
+; CHECK: {Parameter} dtid: uint3 : reg{{.+}}, piece 4
+; CHECK: {Line} [0x{{[0-9a-f]+}}][003] basic_var.hlsl:17 [main]
+; CHECK: {Parameter} dtid: uint3 : reg{{.+}}, piece 4
+; CHECK: {Line} [0x{{[0-9a-f]+}}][003] basic_var.hlsl:11 [main]
+; CHECK: {Parameter} dtid: uint3 : reg{{.+}}, piece 4
+; CHECK: {Line} [0x{{[0-9a-f]+}}][003] basic_var.hlsl:14 [main]
+; CHECK-DAG: {Parameter} dtid: uint3 : reg{{.+}}, piece 4
+; CHECK: {Line} [0x{{[0-9a-f]+}}][003] basic_var.hlsl:17 [main]
+; CHECK-DAG: {Parameter} dtid: uint3 : reg{{.+}}, piece 4
 ; CHECK-DAG: {Variable} my_var2: float : reg{{.+}}
-; CHECK: {Line} {{.+}}basic_var.hlsl:19
-; CHECK-DAG: {Variable} dtid: uint3 : reg{{.+}}, piece 4
+; CHECK: {Line} [0x{{[0-9a-f]+}}][003] basic_var.hlsl:19 [main]
+; CHECK-DAG: {Parameter} dtid: uint3 : reg{{.+}}, piece 4
 ; CHECK-DAG: {Variable} my_var2: float : reg{{.+}}
 
 source_filename = "module"



More information about the llvm-commits mailing list