Skip to content

[lldb-dap] Show assembly depending on stop-disassembly-display settings #136494

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 1 addition & 9 deletions lldb/include/lldb/Core/Debugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,14 +232,6 @@ class Debugger : public std::enable_shared_from_this<Debugger>,

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

// Properties Functions
enum StopDisassemblyType {
eStopDisassemblyTypeNever = 0,
eStopDisassemblyTypeNoDebugInfo,
eStopDisassemblyTypeNoSource,
eStopDisassemblyTypeAlways
};

Status SetPropertyValue(const ExecutionContext *exe_ctx,
VarSetOperationType op, llvm::StringRef property_path,
llvm::StringRef value) override;
Expand Down Expand Up @@ -336,7 +328,7 @@ class Debugger : public std::enable_shared_from_this<Debugger>,

uint64_t GetStopSourceLineCount(bool before) const;

StopDisassemblyType GetStopDisassemblyDisplay() const;
lldb::StopDisassemblyType GetStopDisassemblyDisplay() const;

uint64_t GetDisassemblyLineCount() const;

Expand Down
8 changes: 8 additions & 0 deletions lldb/include/lldb/lldb-enumerations.h
Original file line number Diff line number Diff line change
Expand Up @@ -1383,6 +1383,14 @@ enum CommandReturnObjectCallbackResult {
eCommandReturnObjectPrintCallbackHandled = 1,
};

/// Used to determine when to show disassembly.
enum StopDisassemblyType {
eStopDisassemblyTypeNever = 0,
eStopDisassemblyTypeNoDebugInfo,
eStopDisassemblyTypeNoSource,
eStopDisassemblyTypeAlways
};

} // namespace lldb

#endif // LLDB_LLDB_ENUMERATIONS_H
12 changes: 7 additions & 5 deletions lldb/source/Core/CoreProperties.td
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,13 @@ let Definition = "debugger" in {
Global,
DefaultUnsignedValue<4>,
Desc<"The number of disassembly lines to show when displaying a stopped context.">;
def StopDisassemblyDisplay: Property<"stop-disassembly-display", "Enum">,
Global,
DefaultEnumValue<"Debugger::eStopDisassemblyTypeNoDebugInfo">,
EnumValues<"OptionEnumValues(g_show_disassembly_enum_values)">,
Desc<"Control when to display disassembly when displaying a stopped context.">;
def StopDisassemblyDisplay
: Property<"stop-disassembly-display", "Enum">,
Global,
DefaultEnumValue<"eStopDisassemblyTypeNoDebugInfo">,
EnumValues<"OptionEnumValues(g_show_disassembly_enum_values)">,
Desc<"Control when to display disassembly when displaying a stopped "
"context.">;
def StopDisassemblyMaxSize: Property<"stop-disassembly-max-size", "UInt64">,
Global,
DefaultUnsignedValue<32000>,
Expand Down
14 changes: 7 additions & 7 deletions lldb/source/Core/Debugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,24 +112,24 @@ static llvm::DefaultThreadPool *g_thread_pool = nullptr;

static constexpr OptionEnumValueElement g_show_disassembly_enum_values[] = {
{
Debugger::eStopDisassemblyTypeNever,
lldb::eStopDisassemblyTypeNever,
"never",
"Never show disassembly when displaying a stop context.",
},
{
Debugger::eStopDisassemblyTypeNoDebugInfo,
lldb::eStopDisassemblyTypeNoDebugInfo,
"no-debuginfo",
"Show disassembly when there is no debug information.",
},
{
Debugger::eStopDisassemblyTypeNoSource,
lldb::eStopDisassemblyTypeNoSource,
"no-source",
"Show disassembly when there is no source information, or the source "
"file "
"is missing when displaying a stop context.",
},
{
Debugger::eStopDisassemblyTypeAlways,
lldb::eStopDisassemblyTypeAlways,
"always",
"Always show disassembly when displaying a stop context.",
},
Expand Down Expand Up @@ -611,10 +611,10 @@ uint64_t Debugger::GetStopSourceLineCount(bool before) const {
idx, g_debugger_properties[idx].default_uint_value);
}

Debugger::StopDisassemblyType Debugger::GetStopDisassemblyDisplay() const {
lldb::StopDisassemblyType Debugger::GetStopDisassemblyDisplay() const {
const uint32_t idx = ePropertyStopDisassemblyDisplay;
return GetPropertyAtIndexAs<Debugger::StopDisassemblyType>(
idx, static_cast<Debugger::StopDisassemblyType>(
return GetPropertyAtIndexAs<lldb::StopDisassemblyType>(
idx, static_cast<lldb::StopDisassemblyType>(
g_debugger_properties[idx].default_uint_value));
}

Expand Down
11 changes: 5 additions & 6 deletions lldb/source/Target/StackFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2030,8 +2030,7 @@ bool StackFrame::GetStatus(Stream &strm, bool show_frame_info, bool show_source,
if (show_source) {
ExecutionContext exe_ctx(shared_from_this());
bool have_source = false, have_debuginfo = false;
Debugger::StopDisassemblyType disasm_display =
Debugger::eStopDisassemblyTypeNever;
lldb::StopDisassemblyType disasm_display = lldb::eStopDisassemblyTypeNever;
Target *target = exe_ctx.GetTargetPtr();
if (target) {
Debugger &debugger = target->GetDebugger();
Expand Down Expand Up @@ -2064,20 +2063,20 @@ bool StackFrame::GetStatus(Stream &strm, bool show_frame_info, bool show_source,
}
}
switch (disasm_display) {
case Debugger::eStopDisassemblyTypeNever:
case lldb::eStopDisassemblyTypeNever:
break;

case Debugger::eStopDisassemblyTypeNoDebugInfo:
case lldb::eStopDisassemblyTypeNoDebugInfo:
if (have_debuginfo)
break;
[[fallthrough]];

case Debugger::eStopDisassemblyTypeNoSource:
case lldb::eStopDisassemblyTypeNoSource:
if (have_source)
break;
[[fallthrough]];

case Debugger::eStopDisassemblyTypeAlways:
case lldb::eStopDisassemblyTypeAlways:
if (target) {
const uint32_t disasm_lines = debugger.GetDisassemblyLineCount();
if (disasm_lines > 0) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
C_SOURCES := main.c other.c

include Makefile.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
"""
Test lldb-dap stack trace when some of the source paths are missing
"""

from lldbsuite.test.lldbtest import line_number
import lldbdap_testcase
from contextlib import contextmanager
import os


OTHER_C_SOURCE_CODE = """
int no_source_func(int n) {
return n + 1; // Break here
}
"""


@contextmanager
def delete_file_on_exit(path):
try:
yield path
finally:
if os.path.exists(path):
os.remove(path)


class TestDAP_stackTraceMissingSourcePath(lldbdap_testcase.DAPTestCaseBase):
def build_and_run_until_breakpoint(self):
"""
Build the program and run until the breakpoint is hit, and return the stack frames.
"""
other_source_file = "other.c"
with delete_file_on_exit(other_source_file):
with open(other_source_file, "w") as f:
f.write(OTHER_C_SOURCE_CODE)

breakpoint_line = line_number(other_source_file, "// Break here")

program = self.getBuildArtifact("a.out")
self.build_and_launch(program, commandEscapePrefix="")

breakpoint_ids = self.set_source_breakpoints(
other_source_file, [breakpoint_line]
)
self.assertEqual(
len(breakpoint_ids), 1, "expect correct number of breakpoints"
)

self.continue_to_breakpoints(breakpoint_ids)

frames = self.get_stackFrames()
self.assertLessEqual(2, len(frames), "expect at least 2 frames")

self.assertIn(
"path",
frames[0]["source"],
"Expect source path to always be in frame (other.c)",
)
self.assertIn(
"path",
frames[1]["source"],
"Expect source path in always be in frame (main.c)",
)

return frames

def verify_frames_source(
self, frames, main_frame_assembly: bool, other_frame_assembly: bool
):
if other_frame_assembly:
self.assertFalse(
frames[0]["source"]["path"].endswith("other.c"),
"Expect original source path to not be in unavailable source frame (other.c)",
)
self.assertIn(
"sourceReference",
frames[0]["source"],
"Expect sourceReference to be in unavailable source frame (other.c)",
)
else:
self.assertTrue(
frames[0]["source"]["path"].endswith("other.c"),
"Expect original source path to be in normal source frame (other.c)",
)
self.assertNotIn(
"sourceReference",
frames[0]["source"],
"Expect sourceReference to not be in normal source frame (other.c)",
)

if main_frame_assembly:
self.assertFalse(
frames[1]["source"]["path"].endswith("main.c"),
"Expect original source path to not be in unavailable source frame (main.c)",
)
self.assertIn(
"sourceReference",
frames[1]["source"],
"Expect sourceReference to be in unavailable source frame (main.c)",
)
else:
self.assertTrue(
frames[1]["source"]["path"].endswith("main.c"),
"Expect original source path to be in normal source frame (main.c)",
)
self.assertNotIn(
"sourceReference",
frames[1]["source"],
"Expect sourceReference to not be in normal source code frame (main.c)",
)

def test_stopDisassemblyDisplay(self):
"""
Test that with with all stop-disassembly-display values you get correct assembly / no assembly source code.
"""
self.build_and_run_until_breakpoint()
frames = self.get_stackFrames()
self.assertLessEqual(2, len(frames), "expect at least 2 frames")

self.assertIn(
"path",
frames[0]["source"],
"Expect source path to always be in frame (other.c)",
)
self.assertIn(
"path",
frames[1]["source"],
"Expect source path in always be in frame (main.c)",
)

self.dap_server.request_evaluate(
"settings set stop-disassembly-display never", context="repl"
)
frames = self.get_stackFrames()
self.verify_frames_source(
frames, main_frame_assembly=False, other_frame_assembly=False
)

self.dap_server.request_evaluate(
"settings set stop-disassembly-display always", context="repl"
)
frames = self.get_stackFrames()
self.verify_frames_source(
frames, main_frame_assembly=True, other_frame_assembly=True
)

self.dap_server.request_evaluate(
"settings set stop-disassembly-display no-source", context="repl"
)
frames = self.get_stackFrames()
self.verify_frames_source(
frames, main_frame_assembly=False, other_frame_assembly=True
)

self.dap_server.request_evaluate(
"settings set stop-disassembly-display no-debuginfo", context="repl"
)
frames = self.get_stackFrames()
self.verify_frames_source(
frames, main_frame_assembly=False, other_frame_assembly=False
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
int no_source_func(int n);

int main(int argc, char const *argv[]) {
no_source_func(10);
return 0;
}
7 changes: 6 additions & 1 deletion lldb/tools/lldb-dap/Handler/StackTraceRequestHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
#include "DAP.h"
#include "EventHelper.h"
#include "JSONUtils.h"
#include "LLDBUtils.h"
#include "RequestHandler.h"
#include "lldb/lldb-enumerations.h"

namespace lldb_dap {

Expand Down Expand Up @@ -53,6 +55,8 @@ static bool FillStackFrames(DAP &dap, lldb::SBThread &thread,
llvm::json::Array &stack_frames, int64_t &offset,
const int64_t start_frame, const int64_t levels,
const bool include_all) {
lldb::StopDisassemblyType stop_disassembly_display =
GetStopDisassemblyDisplay(dap.debugger);
bool reached_end_of_stack = false;
for (int64_t i = start_frame;
static_cast<int64_t>(stack_frames.size()) < levels; i++) {
Expand All @@ -69,7 +73,8 @@ static bool FillStackFrames(DAP &dap, lldb::SBThread &thread,
break;
}

stack_frames.emplace_back(CreateStackFrame(frame, frame_format));
stack_frames.emplace_back(
CreateStackFrame(frame, frame_format, stop_disassembly_display));
}

if (include_all && reached_end_of_stack) {
Expand Down
35 changes: 28 additions & 7 deletions lldb/tools/lldb-dap/JSONUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,30 @@ llvm::json::Value CreateSource(llvm::StringRef source_path) {
return llvm::json::Value(std::move(source));
}

bool ShouldDisplayAssemblySource(
const lldb::SBLineEntry &line_entry,
lldb::StopDisassemblyType stop_disassembly_display) {
if (stop_disassembly_display == lldb::eStopDisassemblyTypeNever)
return false;

if (stop_disassembly_display == lldb::eStopDisassemblyTypeAlways)
return true;

// A line entry of 0 indicates the line is compiler generated i.e. no source
// file is associated with the frame.
auto file_spec = line_entry.GetFileSpec();
if (!file_spec.IsValid() || line_entry.GetLine() == 0 ||
line_entry.GetLine() == LLDB_INVALID_LINE_NUMBER)
return true;

if (stop_disassembly_display == lldb::eStopDisassemblyTypeNoSource &&
!file_spec.Exists()) {
return true;
}

return false;
}

// "StackFrame": {
// "type": "object",
// "description": "A Stackframe contains the source location.",
Expand Down Expand Up @@ -720,8 +744,9 @@ llvm::json::Value CreateSource(llvm::StringRef source_path) {
// },
// "required": [ "id", "name", "line", "column" ]
// }
llvm::json::Value CreateStackFrame(lldb::SBFrame &frame,
lldb::SBFormat &format) {
llvm::json::Value
CreateStackFrame(lldb::SBFrame &frame, lldb::SBFormat &format,
lldb::StopDisassemblyType stop_disassembly_display) {
llvm::json::Object object;
int64_t frame_id = MakeDAPFrameID(frame);
object.try_emplace("id", frame_id);
Expand Down Expand Up @@ -751,11 +776,7 @@ llvm::json::Value CreateStackFrame(lldb::SBFrame &frame,
EmplaceSafeString(object, "name", frame_name);

auto line_entry = frame.GetLineEntry();
// A line entry of 0 indicates the line is compiler generated i.e. no source
// file is associated with the frame.
if (line_entry.GetFileSpec().IsValid() &&
(line_entry.GetLine() != 0 ||
line_entry.GetLine() != LLDB_INVALID_LINE_NUMBER)) {
if (!ShouldDisplayAssemblySource(line_entry, stop_disassembly_display)) {
object.try_emplace("source", CreateSource(line_entry));
object.try_emplace("line", line_entry.GetLine());
auto column = line_entry.GetColumn();
Expand Down
Loading
Loading