Skip to content

Commit 51dc0cc

Browse files
authored
[lldb-dap] Handle stack frames without a module (#136777)
* Fix error in lldb-dap when the stack trace contains a frame without a module by simply showing the first 32 assembly instructions after the PC. * Adds a test with a simple example that triggers this case.
1 parent 7ff3d3b commit 51dc0cc

File tree

5 files changed

+115
-2
lines changed

5 files changed

+115
-2
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
C_SOURCES := main.c
2+
include Makefile.rules
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"""
2+
Test lldb-dap stack trace when module is missing
3+
"""
4+
5+
from lldbsuite.test.decorators import skipUnlessPlatform
6+
from lldbsuite.test.lldbtest import line_number
7+
import lldbdap_testcase
8+
import re
9+
10+
11+
class TestDAP_stackTraceMissingModule(lldbdap_testcase.DAPTestCaseBase):
12+
@skipUnlessPlatform(["linux"])
13+
def test_missingModule(self):
14+
"""
15+
Test that the stack frame without a module still has assembly source.
16+
"""
17+
program = self.getBuildArtifact("a.out")
18+
self.build_and_launch(program, commandEscapePrefix="")
19+
20+
source = "main.c"
21+
self.set_source_breakpoints(
22+
source,
23+
[line_number(source, "// Break here")],
24+
)
25+
self.continue_to_next_stop()
26+
27+
# Evaluate expr -- func
28+
expr_result = self.dap_server.request_evaluate(
29+
expression="expr -f pointer -- func",
30+
context="repl",
31+
)
32+
33+
expr_result_address = re.search(
34+
r"0x[0-9a-fA-F]+", expr_result["body"]["result"]
35+
)
36+
self.assertIsNotNone(
37+
expr_result_address, "Failed to get address of dynamic allocated func"
38+
)
39+
func_address = expr_result_address.group(0)
40+
41+
self.dap_server.request_evaluate(
42+
expression=f"breakpoint set --address {func_address}",
43+
context="repl",
44+
)
45+
46+
self.continue_to_next_stop()
47+
48+
frame_without_module = self.get_stackFrames()[0]
49+
50+
self.assertIn("line", frame_without_module, "Line number missing.")
51+
self.assertIn("column", frame_without_module, "Column number missing.")
52+
self.assertIn("source", frame_without_module, "Source location missing.")
53+
source = frame_without_module["source"]
54+
self.assertIn("sourceReference", source, "Source reference missing.")
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#include <stdint.h>
2+
#include <stdio.h>
3+
#include <string.h>
4+
#include <sys/mman.h>
5+
#include <unistd.h>
6+
7+
extern uint8_t __start_target_section[];
8+
extern uint8_t __stop_target_section[];
9+
10+
__attribute__((used, section("target_section"))) int target_function(void) {
11+
return 42;
12+
}
13+
14+
typedef int (*target_function_t)(void);
15+
16+
int main(void) {
17+
size_t target_function_size = __stop_target_section - __start_target_section;
18+
size_t page_size = sysconf(_SC_PAGESIZE);
19+
size_t page_aligned_size =
20+
(target_function_size + page_size - 1) & ~(page_size - 1);
21+
22+
void *executable_memory =
23+
mmap(NULL, page_aligned_size, PROT_READ | PROT_WRITE | PROT_EXEC,
24+
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
25+
if (executable_memory == MAP_FAILED) {
26+
perror("mmap");
27+
return 1;
28+
}
29+
30+
memcpy(executable_memory, __start_target_section, target_function_size);
31+
32+
target_function_t func = (target_function_t)executable_memory;
33+
int result = func(); // Break here
34+
printf("Result from target function: %d\n", result);
35+
36+
return 0;
37+
}

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "lldb/API/SBInstructionList.h"
1717
#include "lldb/API/SBProcess.h"
1818
#include "lldb/API/SBStream.h"
19+
#include "lldb/API/SBSymbol.h"
1920
#include "lldb/API/SBTarget.h"
2021
#include "lldb/API/SBThread.h"
2122
#include "llvm/Support/Error.h"
@@ -42,10 +43,19 @@ SourceRequestHandler::Run(const protocol::SourceArguments &args) const {
4243
if (!frame.IsValid())
4344
return llvm::make_error<DAPError>("source not found");
4445

45-
lldb::SBInstructionList insts = frame.GetSymbol().GetInstructions(dap.target);
4646
lldb::SBStream stream;
4747
lldb::SBExecutionContext exe_ctx(frame);
48-
insts.GetDescription(stream, exe_ctx);
48+
lldb::SBSymbol symbol = frame.GetSymbol();
49+
50+
if (symbol.IsValid()) {
51+
lldb::SBInstructionList insts = symbol.GetInstructions(dap.target);
52+
insts.GetDescription(stream, exe_ctx);
53+
} else {
54+
// No valid symbol, just return the disassembly.
55+
lldb::SBInstructionList insts =
56+
dap.target.ReadInstructions(frame.GetPCAddress(), 32);
57+
insts.GetDescription(stream, exe_ctx);
58+
}
4959

5060
return protocol::SourceResponseBody{/*content=*/stream.GetData(),
5161
/*mimeType=*/"text/x-lldb.disassembly"};

lldb/tools/lldb-dap/JSONUtils.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,16 @@ llvm::json::Value CreateStackFrame(lldb::SBFrame &frame,
783783
// Line numbers are 1-based.
784784
object.try_emplace("line", inst_line + 1);
785785
object.try_emplace("column", 1);
786+
} else {
787+
// No valid line entry or symbol.
788+
llvm::json::Object source;
789+
EmplaceSafeString(source, "name", frame_name);
790+
source.try_emplace("sourceReference", MakeDAPFrameID(frame));
791+
EmplaceSafeString(source, "presentationHint", "deemphasize");
792+
object.try_emplace("source", std::move(source));
793+
794+
object.try_emplace("line", 1);
795+
object.try_emplace("column", 1);
786796
}
787797

788798
const auto pc = frame.GetPC();

0 commit comments

Comments
 (0)