[llvm] Added --report=debugger option to llvm-debuginfo-analyzer (PR #159853)
Adam Yang via llvm-commits
llvm-commits at lists.llvm.org
Wed Oct 1 16:36:46 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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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();
More information about the llvm-commits
mailing list