-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[lldb-dap] Handle stack frames without a module #136777
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
[lldb-dap] Handle stack frames without a module #136777
Conversation
@llvm/pr-subscribers-lldb Author: Ely Ronnen (eronnen) Changes
Full diff: https://github.com/llvm/llvm-project/pull/136777.diff 5 Files Affected:
diff --git a/lldb/test/API/tools/lldb-dap/stackTraceMissingModule/Makefile b/lldb/test/API/tools/lldb-dap/stackTraceMissingModule/Makefile
new file mode 100644
index 0000000000000..c9319d6e6888a
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/stackTraceMissingModule/Makefile
@@ -0,0 +1,2 @@
+C_SOURCES := main.c
+include Makefile.rules
diff --git a/lldb/test/API/tools/lldb-dap/stackTraceMissingModule/TestDAP_stackTraceMissingModule.py b/lldb/test/API/tools/lldb-dap/stackTraceMissingModule/TestDAP_stackTraceMissingModule.py
new file mode 100644
index 0000000000000..1eb00f9935a22
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/stackTraceMissingModule/TestDAP_stackTraceMissingModule.py
@@ -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.")
diff --git a/lldb/test/API/tools/lldb-dap/stackTraceMissingModule/main.c b/lldb/test/API/tools/lldb-dap/stackTraceMissingModule/main.c
new file mode 100644
index 0000000000000..d706231fbd5c2
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/stackTraceMissingModule/main.c
@@ -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;
+}
diff --git a/lldb/tools/lldb-dap/Handler/SourceRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SourceRequestHandler.cpp
index 1a7a13d9f267a..99c3692f15283 100644
--- a/lldb/tools/lldb-dap/Handler/SourceRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/SourceRequestHandler.cpp
@@ -15,6 +15,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"
@@ -41,9 +42,18 @@ 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;
- insts.GetDescription(stream);
+ lldb::SBSymbol symbol = frame.GetSymbol();
+
+ if (symbol.IsValid()) {
+ lldb::SBInstructionList insts = symbol.GetInstructions(dap.target);
+ insts.GetDescription(stream);
+ } else {
+ // No valid symbol, just return the disassembly
+ lldb::SBInstructionList insts =
+ dap.target.ReadInstructions(frame.GetPCAddress(), 32);
+ insts.GetDescription(stream);
+ }
return protocol::SourceResponseBody{/*content=*/stream.GetData(),
/*mimeType=*/"text/x-lldb.disassembly"};
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index 33f10c93d2ada..30d277abce11e 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -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);
}
const auto pc = frame.GetPC();
|
93764e3
to
e513345
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice test. LGTM modulo comments.
lldb::SBInstructionList insts = symbol.GetInstructions(dap.target); | ||
insts.GetDescription(stream, exe_ctx); | ||
} else { | ||
// No valid symbol, just return the disassembly |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// No valid symbol, just return the disassembly | |
// No valid symbol, just return the disassembly. |
When writing comments, write them as English prose, using proper capitalization, punctuation, etc.
lldb/tools/lldb-dap/JSONUtils.cpp
Outdated
@@ -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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// No valid line entry or symbol | |
// No valid line entry or symbol. |
object.try_emplace("line", 1); | ||
object.try_emplace("column", 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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); |
There was a problem hiding this comment.
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
* 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.
* 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.
* 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.
* 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.
* 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.
Uh oh!
There was an error while loading. Please reload this page.