Skip to content
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
C_SOURCES := main.c
include Makefile.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""
Test lldb-dap stack trace when module is missing
"""

from lldbsuite.test.decorators import skipUnlessPlatform
from lldbsuite.test.lldbtest import line_number
import lldbdap_testcase
import re


class TestDAP_stackTraceMissingModule(lldbdap_testcase.DAPTestCaseBase):
@skipUnlessPlatform(["linux"])
def test_missingModule(self):
"""
Test that the stack frame without a module still has assembly source.
"""
program = self.getBuildArtifact("a.out")
self.build_and_launch(program, commandEscapePrefix="")

source = "main.c"
self.set_source_breakpoints(
source,
[line_number(source, "// Break here")],
)
self.continue_to_next_stop()

# Evaluate expr -- func
expr_result = self.dap_server.request_evaluate(
expression="expr -f pointer -- func",
context="repl",
)

expr_result_address = re.search(
r"0x[0-9a-fA-F]+", expr_result["body"]["result"]
)
self.assertIsNotNone(
expr_result_address, "Failed to get address of dynamic allocated func"
)
func_address = expr_result_address.group(0)

self.dap_server.request_evaluate(
expression=f"breakpoint set --address {func_address}",
context="repl",
)

self.continue_to_next_stop()

frame_without_module = self.get_stackFrames()[0]

self.assertIn("line", frame_without_module, "Line number missing.")
self.assertIn("column", frame_without_module, "Column number missing.")
self.assertIn("source", frame_without_module, "Source location missing.")
source = frame_without_module["source"]
self.assertIn("sourceReference", source, "Source reference missing.")
37 changes: 37 additions & 0 deletions lldb/test/API/tools/lldb-dap/stackTraceMissingModule/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>

extern uint8_t __start_target_section[];
extern uint8_t __stop_target_section[];

__attribute__((used, section("target_section"))) int target_function(void) {
return 42;
}

typedef int (*target_function_t)(void);

int main(void) {
size_t target_function_size = __stop_target_section - __start_target_section;
size_t page_size = sysconf(_SC_PAGESIZE);
size_t page_aligned_size =
(target_function_size + page_size - 1) & ~(page_size - 1);

void *executable_memory =
mmap(NULL, page_aligned_size, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (executable_memory == MAP_FAILED) {
perror("mmap");
return 1;
}

memcpy(executable_memory, __start_target_section, target_function_size);

target_function_t func = (target_function_t)executable_memory;
int result = func(); // Break here
printf("Result from target function: %d\n", result);

return 0;
}
14 changes: 12 additions & 2 deletions lldb/tools/lldb-dap/Handler/SourceRequestHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "lldb/API/SBInstructionList.h"
#include "lldb/API/SBProcess.h"
#include "lldb/API/SBStream.h"
#include "lldb/API/SBSymbol.h"
#include "lldb/API/SBTarget.h"
#include "lldb/API/SBThread.h"
#include "llvm/Support/Error.h"
Expand All @@ -42,10 +43,19 @@ SourceRequestHandler::Run(const protocol::SourceArguments &args) const {
if (!frame.IsValid())
return llvm::make_error<DAPError>("source not found");

lldb::SBInstructionList insts = frame.GetSymbol().GetInstructions(dap.target);
lldb::SBStream stream;
lldb::SBExecutionContext exe_ctx(frame);
insts.GetDescription(stream, exe_ctx);
lldb::SBSymbol symbol = frame.GetSymbol();

if (symbol.IsValid()) {
lldb::SBInstructionList insts = symbol.GetInstructions(dap.target);
insts.GetDescription(stream, exe_ctx);
} else {
// No valid symbol, just return the disassembly.
lldb::SBInstructionList insts =
dap.target.ReadInstructions(frame.GetPCAddress(), 32);
insts.GetDescription(stream, exe_ctx);
}

return protocol::SourceResponseBody{/*content=*/stream.GetData(),
/*mimeType=*/"text/x-lldb.disassembly"};
Expand Down
10 changes: 10 additions & 0 deletions lldb/tools/lldb-dap/JSONUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,16 @@ llvm::json::Value CreateStackFrame(lldb::SBFrame &frame,
// Line numbers are 1-based.
object.try_emplace("line", inst_line + 1);
object.try_emplace("column", 1);
} else {
// No valid line entry or symbol.
llvm::json::Object source;
EmplaceSafeString(source, "name", frame_name);
source.try_emplace("sourceReference", MakeDAPFrameID(frame));
EmplaceSafeString(source, "presentationHint", "deemphasize");
object.try_emplace("source", std::move(source));

object.try_emplace("line", 1);
object.try_emplace("column", 1);
Comment on lines +794 to +795
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
object.try_emplace("line", 1);
object.try_emplace("column", 1);
object.try_emplace("line", LLDB_INVALID_LINE_NUMBER);
object.try_emplace("column", LLDB_INVALID_LINE_NUMBER);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it still needs to be 1, this way it will be possible to debug the code in the IDE:

Screencast.From.2025-04-24.08-48-53.webm

The only annoying thing is that each step removes the previous assembly lines, but I couldn't think of a straightforward way to tackle it because the frame changes too in every step

}

const auto pc = frame.GetPC();
Expand Down
Loading