Skip to content

Commit 290ba28

Browse files
authored
[lldb-dap] Show assembly depending on stop-disassembly-display settings (#136494)
Show assembly code when the source code for a frame is not available in the debugger machine Edit: this functionality will work only when using `stop-disassembly-display = no-source` in the settings Fix #136492 After the fix: [Screencast From 2025-04-20 18-00-30.webm](https://github.com/user-attachments/assets/1ce41715-cf4f-42a1-8f5c-6196b9d685dc)
1 parent a1decfe commit 290ba28

File tree

13 files changed

+288
-37
lines changed

13 files changed

+288
-37
lines changed

lldb/include/lldb/Core/Debugger.h

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -232,14 +232,6 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
232232

233233
void SetLoggingCallback(lldb::LogOutputCallback log_callback, void *baton);
234234

235-
// Properties Functions
236-
enum StopDisassemblyType {
237-
eStopDisassemblyTypeNever = 0,
238-
eStopDisassemblyTypeNoDebugInfo,
239-
eStopDisassemblyTypeNoSource,
240-
eStopDisassemblyTypeAlways
241-
};
242-
243235
Status SetPropertyValue(const ExecutionContext *exe_ctx,
244236
VarSetOperationType op, llvm::StringRef property_path,
245237
llvm::StringRef value) override;
@@ -336,7 +328,7 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
336328

337329
uint64_t GetStopSourceLineCount(bool before) const;
338330

339-
StopDisassemblyType GetStopDisassemblyDisplay() const;
331+
lldb::StopDisassemblyType GetStopDisassemblyDisplay() const;
340332

341333
uint64_t GetDisassemblyLineCount() const;
342334

lldb/include/lldb/lldb-enumerations.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1383,6 +1383,14 @@ enum CommandReturnObjectCallbackResult {
13831383
eCommandReturnObjectPrintCallbackHandled = 1,
13841384
};
13851385

1386+
/// Used to determine when to show disassembly.
1387+
enum StopDisassemblyType {
1388+
eStopDisassemblyTypeNever = 0,
1389+
eStopDisassemblyTypeNoDebugInfo,
1390+
eStopDisassemblyTypeNoSource,
1391+
eStopDisassemblyTypeAlways
1392+
};
1393+
13861394
} // namespace lldb
13871395

13881396
#endif // LLDB_LLDB_ENUMERATIONS_H

lldb/source/Core/CoreProperties.td

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,13 @@ let Definition = "debugger" in {
9191
Global,
9292
DefaultUnsignedValue<4>,
9393
Desc<"The number of disassembly lines to show when displaying a stopped context.">;
94-
def StopDisassemblyDisplay: Property<"stop-disassembly-display", "Enum">,
95-
Global,
96-
DefaultEnumValue<"Debugger::eStopDisassemblyTypeNoDebugInfo">,
97-
EnumValues<"OptionEnumValues(g_show_disassembly_enum_values)">,
98-
Desc<"Control when to display disassembly when displaying a stopped context.">;
94+
def StopDisassemblyDisplay
95+
: Property<"stop-disassembly-display", "Enum">,
96+
Global,
97+
DefaultEnumValue<"eStopDisassemblyTypeNoDebugInfo">,
98+
EnumValues<"OptionEnumValues(g_show_disassembly_enum_values)">,
99+
Desc<"Control when to display disassembly when displaying a stopped "
100+
"context.">;
99101
def StopDisassemblyMaxSize: Property<"stop-disassembly-max-size", "UInt64">,
100102
Global,
101103
DefaultUnsignedValue<32000>,

lldb/source/Core/Debugger.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -112,24 +112,24 @@ static llvm::DefaultThreadPool *g_thread_pool = nullptr;
112112

113113
static constexpr OptionEnumValueElement g_show_disassembly_enum_values[] = {
114114
{
115-
Debugger::eStopDisassemblyTypeNever,
115+
lldb::eStopDisassemblyTypeNever,
116116
"never",
117117
"Never show disassembly when displaying a stop context.",
118118
},
119119
{
120-
Debugger::eStopDisassemblyTypeNoDebugInfo,
120+
lldb::eStopDisassemblyTypeNoDebugInfo,
121121
"no-debuginfo",
122122
"Show disassembly when there is no debug information.",
123123
},
124124
{
125-
Debugger::eStopDisassemblyTypeNoSource,
125+
lldb::eStopDisassemblyTypeNoSource,
126126
"no-source",
127127
"Show disassembly when there is no source information, or the source "
128128
"file "
129129
"is missing when displaying a stop context.",
130130
},
131131
{
132-
Debugger::eStopDisassemblyTypeAlways,
132+
lldb::eStopDisassemblyTypeAlways,
133133
"always",
134134
"Always show disassembly when displaying a stop context.",
135135
},
@@ -611,10 +611,10 @@ uint64_t Debugger::GetStopSourceLineCount(bool before) const {
611611
idx, g_debugger_properties[idx].default_uint_value);
612612
}
613613

614-
Debugger::StopDisassemblyType Debugger::GetStopDisassemblyDisplay() const {
614+
lldb::StopDisassemblyType Debugger::GetStopDisassemblyDisplay() const {
615615
const uint32_t idx = ePropertyStopDisassemblyDisplay;
616-
return GetPropertyAtIndexAs<Debugger::StopDisassemblyType>(
617-
idx, static_cast<Debugger::StopDisassemblyType>(
616+
return GetPropertyAtIndexAs<lldb::StopDisassemblyType>(
617+
idx, static_cast<lldb::StopDisassemblyType>(
618618
g_debugger_properties[idx].default_uint_value));
619619
}
620620

lldb/source/Target/StackFrame.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2030,8 +2030,7 @@ bool StackFrame::GetStatus(Stream &strm, bool show_frame_info, bool show_source,
20302030
if (show_source) {
20312031
ExecutionContext exe_ctx(shared_from_this());
20322032
bool have_source = false, have_debuginfo = false;
2033-
Debugger::StopDisassemblyType disasm_display =
2034-
Debugger::eStopDisassemblyTypeNever;
2033+
lldb::StopDisassemblyType disasm_display = lldb::eStopDisassemblyTypeNever;
20352034
Target *target = exe_ctx.GetTargetPtr();
20362035
if (target) {
20372036
Debugger &debugger = target->GetDebugger();
@@ -2064,20 +2063,20 @@ bool StackFrame::GetStatus(Stream &strm, bool show_frame_info, bool show_source,
20642063
}
20652064
}
20662065
switch (disasm_display) {
2067-
case Debugger::eStopDisassemblyTypeNever:
2066+
case lldb::eStopDisassemblyTypeNever:
20682067
break;
20692068

2070-
case Debugger::eStopDisassemblyTypeNoDebugInfo:
2069+
case lldb::eStopDisassemblyTypeNoDebugInfo:
20712070
if (have_debuginfo)
20722071
break;
20732072
[[fallthrough]];
20742073

2075-
case Debugger::eStopDisassemblyTypeNoSource:
2074+
case lldb::eStopDisassemblyTypeNoSource:
20762075
if (have_source)
20772076
break;
20782077
[[fallthrough]];
20792078

2080-
case Debugger::eStopDisassemblyTypeAlways:
2079+
case lldb::eStopDisassemblyTypeAlways:
20812080
if (target) {
20822081
const uint32_t disasm_lines = debugger.GetDisassemblyLineCount();
20832082
if (disasm_lines > 0) {
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
C_SOURCES := main.c other.c
2+
3+
include Makefile.rules
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
"""
2+
Test lldb-dap stack trace when some of the source paths are missing
3+
"""
4+
5+
from lldbsuite.test.lldbtest import line_number
6+
import lldbdap_testcase
7+
from contextlib import contextmanager
8+
import os
9+
10+
11+
OTHER_C_SOURCE_CODE = """
12+
int no_source_func(int n) {
13+
return n + 1; // Break here
14+
}
15+
"""
16+
17+
18+
@contextmanager
19+
def delete_file_on_exit(path):
20+
try:
21+
yield path
22+
finally:
23+
if os.path.exists(path):
24+
os.remove(path)
25+
26+
27+
class TestDAP_stackTraceMissingSourcePath(lldbdap_testcase.DAPTestCaseBase):
28+
def build_and_run_until_breakpoint(self):
29+
"""
30+
Build the program and run until the breakpoint is hit, and return the stack frames.
31+
"""
32+
other_source_file = "other.c"
33+
with delete_file_on_exit(other_source_file):
34+
with open(other_source_file, "w") as f:
35+
f.write(OTHER_C_SOURCE_CODE)
36+
37+
breakpoint_line = line_number(other_source_file, "// Break here")
38+
39+
program = self.getBuildArtifact("a.out")
40+
self.build_and_launch(program, commandEscapePrefix="")
41+
42+
breakpoint_ids = self.set_source_breakpoints(
43+
other_source_file, [breakpoint_line]
44+
)
45+
self.assertEqual(
46+
len(breakpoint_ids), 1, "expect correct number of breakpoints"
47+
)
48+
49+
self.continue_to_breakpoints(breakpoint_ids)
50+
51+
frames = self.get_stackFrames()
52+
self.assertLessEqual(2, len(frames), "expect at least 2 frames")
53+
54+
self.assertIn(
55+
"path",
56+
frames[0]["source"],
57+
"Expect source path to always be in frame (other.c)",
58+
)
59+
self.assertIn(
60+
"path",
61+
frames[1]["source"],
62+
"Expect source path in always be in frame (main.c)",
63+
)
64+
65+
return frames
66+
67+
def verify_frames_source(
68+
self, frames, main_frame_assembly: bool, other_frame_assembly: bool
69+
):
70+
if other_frame_assembly:
71+
self.assertFalse(
72+
frames[0]["source"]["path"].endswith("other.c"),
73+
"Expect original source path to not be in unavailable source frame (other.c)",
74+
)
75+
self.assertIn(
76+
"sourceReference",
77+
frames[0]["source"],
78+
"Expect sourceReference to be in unavailable source frame (other.c)",
79+
)
80+
else:
81+
self.assertTrue(
82+
frames[0]["source"]["path"].endswith("other.c"),
83+
"Expect original source path to be in normal source frame (other.c)",
84+
)
85+
self.assertNotIn(
86+
"sourceReference",
87+
frames[0]["source"],
88+
"Expect sourceReference to not be in normal source frame (other.c)",
89+
)
90+
91+
if main_frame_assembly:
92+
self.assertFalse(
93+
frames[1]["source"]["path"].endswith("main.c"),
94+
"Expect original source path to not be in unavailable source frame (main.c)",
95+
)
96+
self.assertIn(
97+
"sourceReference",
98+
frames[1]["source"],
99+
"Expect sourceReference to be in unavailable source frame (main.c)",
100+
)
101+
else:
102+
self.assertTrue(
103+
frames[1]["source"]["path"].endswith("main.c"),
104+
"Expect original source path to be in normal source frame (main.c)",
105+
)
106+
self.assertNotIn(
107+
"sourceReference",
108+
frames[1]["source"],
109+
"Expect sourceReference to not be in normal source code frame (main.c)",
110+
)
111+
112+
def test_stopDisassemblyDisplay(self):
113+
"""
114+
Test that with with all stop-disassembly-display values you get correct assembly / no assembly source code.
115+
"""
116+
self.build_and_run_until_breakpoint()
117+
frames = self.get_stackFrames()
118+
self.assertLessEqual(2, len(frames), "expect at least 2 frames")
119+
120+
self.assertIn(
121+
"path",
122+
frames[0]["source"],
123+
"Expect source path to always be in frame (other.c)",
124+
)
125+
self.assertIn(
126+
"path",
127+
frames[1]["source"],
128+
"Expect source path in always be in frame (main.c)",
129+
)
130+
131+
self.dap_server.request_evaluate(
132+
"settings set stop-disassembly-display never", context="repl"
133+
)
134+
frames = self.get_stackFrames()
135+
self.verify_frames_source(
136+
frames, main_frame_assembly=False, other_frame_assembly=False
137+
)
138+
139+
self.dap_server.request_evaluate(
140+
"settings set stop-disassembly-display always", context="repl"
141+
)
142+
frames = self.get_stackFrames()
143+
self.verify_frames_source(
144+
frames, main_frame_assembly=True, other_frame_assembly=True
145+
)
146+
147+
self.dap_server.request_evaluate(
148+
"settings set stop-disassembly-display no-source", context="repl"
149+
)
150+
frames = self.get_stackFrames()
151+
self.verify_frames_source(
152+
frames, main_frame_assembly=False, other_frame_assembly=True
153+
)
154+
155+
self.dap_server.request_evaluate(
156+
"settings set stop-disassembly-display no-debuginfo", context="repl"
157+
)
158+
frames = self.get_stackFrames()
159+
self.verify_frames_source(
160+
frames, main_frame_assembly=False, other_frame_assembly=False
161+
)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
int no_source_func(int n);
2+
3+
int main(int argc, char const *argv[]) {
4+
no_source_func(10);
5+
return 0;
6+
}

lldb/tools/lldb-dap/Handler/StackTraceRequestHandler.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
#include "DAP.h"
1010
#include "EventHelper.h"
1111
#include "JSONUtils.h"
12+
#include "LLDBUtils.h"
1213
#include "RequestHandler.h"
14+
#include "lldb/lldb-enumerations.h"
1315

1416
namespace lldb_dap {
1517

@@ -53,6 +55,8 @@ static bool FillStackFrames(DAP &dap, lldb::SBThread &thread,
5355
llvm::json::Array &stack_frames, int64_t &offset,
5456
const int64_t start_frame, const int64_t levels,
5557
const bool include_all) {
58+
lldb::StopDisassemblyType stop_disassembly_display =
59+
GetStopDisassemblyDisplay(dap.debugger);
5660
bool reached_end_of_stack = false;
5761
for (int64_t i = start_frame;
5862
static_cast<int64_t>(stack_frames.size()) < levels; i++) {
@@ -69,7 +73,8 @@ static bool FillStackFrames(DAP &dap, lldb::SBThread &thread,
6973
break;
7074
}
7175

72-
stack_frames.emplace_back(CreateStackFrame(frame, frame_format));
76+
stack_frames.emplace_back(
77+
CreateStackFrame(frame, frame_format, stop_disassembly_display));
7378
}
7479

7580
if (include_all && reached_end_of_stack) {

lldb/tools/lldb-dap/JSONUtils.cpp

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,30 @@ llvm::json::Value CreateSource(llvm::StringRef source_path) {
659659
return llvm::json::Value(std::move(source));
660660
}
661661

662+
bool ShouldDisplayAssemblySource(
663+
const lldb::SBLineEntry &line_entry,
664+
lldb::StopDisassemblyType stop_disassembly_display) {
665+
if (stop_disassembly_display == lldb::eStopDisassemblyTypeNever)
666+
return false;
667+
668+
if (stop_disassembly_display == lldb::eStopDisassemblyTypeAlways)
669+
return true;
670+
671+
// A line entry of 0 indicates the line is compiler generated i.e. no source
672+
// file is associated with the frame.
673+
auto file_spec = line_entry.GetFileSpec();
674+
if (!file_spec.IsValid() || line_entry.GetLine() == 0 ||
675+
line_entry.GetLine() == LLDB_INVALID_LINE_NUMBER)
676+
return true;
677+
678+
if (stop_disassembly_display == lldb::eStopDisassemblyTypeNoSource &&
679+
!file_spec.Exists()) {
680+
return true;
681+
}
682+
683+
return false;
684+
}
685+
662686
// "StackFrame": {
663687
// "type": "object",
664688
// "description": "A Stackframe contains the source location.",
@@ -720,8 +744,9 @@ llvm::json::Value CreateSource(llvm::StringRef source_path) {
720744
// },
721745
// "required": [ "id", "name", "line", "column" ]
722746
// }
723-
llvm::json::Value CreateStackFrame(lldb::SBFrame &frame,
724-
lldb::SBFormat &format) {
747+
llvm::json::Value
748+
CreateStackFrame(lldb::SBFrame &frame, lldb::SBFormat &format,
749+
lldb::StopDisassemblyType stop_disassembly_display) {
725750
llvm::json::Object object;
726751
int64_t frame_id = MakeDAPFrameID(frame);
727752
object.try_emplace("id", frame_id);
@@ -751,11 +776,7 @@ llvm::json::Value CreateStackFrame(lldb::SBFrame &frame,
751776
EmplaceSafeString(object, "name", frame_name);
752777

753778
auto line_entry = frame.GetLineEntry();
754-
// A line entry of 0 indicates the line is compiler generated i.e. no source
755-
// file is associated with the frame.
756-
if (line_entry.GetFileSpec().IsValid() &&
757-
(line_entry.GetLine() != 0 ||
758-
line_entry.GetLine() != LLDB_INVALID_LINE_NUMBER)) {
779+
if (!ShouldDisplayAssemblySource(line_entry, stop_disassembly_display)) {
759780
object.try_emplace("source", CreateSource(line_entry));
760781
object.try_emplace("line", line_entry.GetLine());
761782
auto column = line_entry.GetColumn();

0 commit comments

Comments
 (0)