https://github.com/jeffreytan81 updated 
https://github.com/llvm/llvm-project/pull/165944

>From 0f9f57ffb0bcdfb0ac8706a545c23c8160dc4de1 Mon Sep 17 00:00:00 2001
From: Jeffrey Tan <[email protected]>
Date: Fri, 31 Oct 2025 17:12:00 -0700
Subject: [PATCH 1/2] Fix lldb-dap non-leaf frame source resolution issue

---
 lldb/tools/lldb-dap/DAP.cpp           | 23 ++++++++++++++++++-----
 lldb/tools/lldb-dap/ProtocolUtils.cpp |  7 +++----
 lldb/tools/lldb-dap/ProtocolUtils.h   |  3 ++-
 3 files changed, 23 insertions(+), 10 deletions(-)

diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index f009a902f79e7..ebafda95ff583 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -657,18 +657,31 @@ std::optional<protocol::Source> DAP::ResolveSource(const 
lldb::SBFrame &frame) {
   if (!frame.IsValid())
     return std::nullopt;
 
-  const lldb::SBAddress frame_pc = frame.GetPCAddress();
-  if (DisplayAssemblySource(debugger, frame_pc))
+  // IMPORTANT: Get line entry from symbol context, NOT from PC address.
+  // When a frame's PC points to a return address (the instruction
+  // after a call), that address may have line number 0 for compiler generated
+  // code.
+  //
+  // EXAMPLE: If PC is at 0x1004 (frame return address after the call
+  // instruction) with no line info, but 0x1003 (in the middle of previous call
+  // instruction) is at line 42, symbol context returns line 42.
+  //
+  // NOTE: This issue is non-deterministic and depends on compiler debug info
+  // generation, making it difficult to create a reliable automated test.
+  const lldb::SBLineEntry frame_line_entry = frame.GetLineEntry();
+  if (DisplayAssemblySource(debugger, frame_line_entry)) {
+    const lldb::SBAddress frame_pc = frame.GetPCAddress();
     return ResolveAssemblySource(frame_pc);
+  }
 
-  return CreateSource(frame.GetLineEntry().GetFileSpec());
+  return CreateSource(frame_line_entry.GetFileSpec());
 }
 
 std::optional<protocol::Source> DAP::ResolveSource(lldb::SBAddress address) {
-  if (DisplayAssemblySource(debugger, address))
+  lldb::SBLineEntry line_entry = GetLineEntryForAddress(target, address);
+  if (DisplayAssemblySource(debugger, line_entry))
     return ResolveAssemblySource(address);
 
-  lldb::SBLineEntry line_entry = GetLineEntryForAddress(target, address);
   if (!line_entry.IsValid())
     return std::nullopt;
 
diff --git a/lldb/tools/lldb-dap/ProtocolUtils.cpp 
b/lldb/tools/lldb-dap/ProtocolUtils.cpp
index 868c67ca72986..acf31b03f7af0 100644
--- a/lldb/tools/lldb-dap/ProtocolUtils.cpp
+++ b/lldb/tools/lldb-dap/ProtocolUtils.cpp
@@ -27,7 +27,7 @@ using namespace lldb_dap::protocol;
 namespace lldb_dap {
 
 static bool ShouldDisplayAssemblySource(
-    lldb::SBAddress address,
+    lldb::SBLineEntry line_entry,
     lldb::StopDisassemblyType stop_disassembly_display) {
   if (stop_disassembly_display == lldb::eStopDisassemblyTypeNever)
     return false;
@@ -37,7 +37,6 @@ static bool ShouldDisplayAssemblySource(
 
   // A line entry of 0 indicates the line is compiler generated i.e. no source
   // file is associated with the frame.
-  auto line_entry = address.GetLineEntry();
   auto file_spec = line_entry.GetFileSpec();
   if (!file_spec.IsValid() || line_entry.GetLine() == 0 ||
       line_entry.GetLine() == LLDB_INVALID_LINE_NUMBER)
@@ -174,10 +173,10 @@ bool IsAssemblySource(const protocol::Source &source) {
 }
 
 bool DisplayAssemblySource(lldb::SBDebugger &debugger,
-                           lldb::SBAddress address) {
+                           lldb::SBLineEntry line_entry) {
   const lldb::StopDisassemblyType stop_disassembly_display =
       GetStopDisassemblyDisplay(debugger);
-  return ShouldDisplayAssemblySource(address, stop_disassembly_display);
+  return ShouldDisplayAssemblySource(line_entry, stop_disassembly_display);
 }
 
 std::string GetLoadAddressString(const lldb::addr_t addr) {
diff --git a/lldb/tools/lldb-dap/ProtocolUtils.h 
b/lldb/tools/lldb-dap/ProtocolUtils.h
index a1f7ae0661914..f4d576ba9f608 100644
--- a/lldb/tools/lldb-dap/ProtocolUtils.h
+++ b/lldb/tools/lldb-dap/ProtocolUtils.h
@@ -53,7 +53,8 @@ std::optional<protocol::Source> CreateSource(const 
lldb::SBFileSpec &file);
 /// Checks if the given source is for assembly code.
 bool IsAssemblySource(const protocol::Source &source);
 
-bool DisplayAssemblySource(lldb::SBDebugger &debugger, lldb::SBAddress 
address);
+bool DisplayAssemblySource(lldb::SBDebugger &debugger,
+                           lldb::SBLineEntry line_entry);
 
 /// Get the address as a 16-digit hex string, e.g. "0x0000000000012345"
 std::string GetLoadAddressString(const lldb::addr_t addr);

>From ca2a8d8b7b708952ec86a6e278bf2c91fbef0b5d Mon Sep 17 00:00:00 2001
From: Jeffrey Tan <[email protected]>
Date: Wed, 5 Nov 2025 12:28:14 -0800
Subject: [PATCH 2/2] Add Test

---
 .../stackTraceCompilerGeneratedCode/Makefile  |  3 +
 ...TestDAP_stackTraceCompilerGeneratedCode.py | 82 +++++++++++++++++++
 .../stackTraceCompilerGeneratedCode/main.c    | 14 ++++
 lldb/tools/lldb-dap/DAP.cpp                   |  4 +-
 4 files changed, 100 insertions(+), 3 deletions(-)
 create mode 100644 
lldb/test/API/tools/lldb-dap/stackTraceCompilerGeneratedCode/Makefile
 create mode 100644 
lldb/test/API/tools/lldb-dap/stackTraceCompilerGeneratedCode/TestDAP_stackTraceCompilerGeneratedCode.py
 create mode 100644 
lldb/test/API/tools/lldb-dap/stackTraceCompilerGeneratedCode/main.c

diff --git 
a/lldb/test/API/tools/lldb-dap/stackTraceCompilerGeneratedCode/Makefile 
b/lldb/test/API/tools/lldb-dap/stackTraceCompilerGeneratedCode/Makefile
new file mode 100644
index 0000000000000..10495940055b6
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/stackTraceCompilerGeneratedCode/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git 
a/lldb/test/API/tools/lldb-dap/stackTraceCompilerGeneratedCode/TestDAP_stackTraceCompilerGeneratedCode.py
 
b/lldb/test/API/tools/lldb-dap/stackTraceCompilerGeneratedCode/TestDAP_stackTraceCompilerGeneratedCode.py
new file mode 100644
index 0000000000000..4820d549445ab
--- /dev/null
+++ 
b/lldb/test/API/tools/lldb-dap/stackTraceCompilerGeneratedCode/TestDAP_stackTraceCompilerGeneratedCode.py
@@ -0,0 +1,82 @@
+"""
+Test lldb-dap stackTrace request for compiler generated code
+"""
+
+import os
+
+import lldbdap_testcase
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+
+
+class 
TestDAP_stackTraceCompilerGeneratedCode(lldbdap_testcase.DAPTestCaseBase):
+    @skipIfWindows
+    def test_non_leaf_frame_compiler_generate_code(self):
+        """
+        Test that non-leaf frames with compiler-generated code are properly 
resolved.
+
+        This test verifies that LLDB correctly handles stack frames that 
contain
+        compiler-generated code (code without valid source location 
information).
+        Specifically, it tests that when a non-leaf frame contains 
compiler-generated
+        code, LLDB properly resolves the source location to the last valid 
source line
+        before the compiler-generated code, rather than failing to symbolicate 
the frame.
+
+        Critical Assumption:
+        ====================
+        This test assumes that there is NO extra machine code between the call
+        instruction and the start of the next source line. If the compiler were
+        to insert additional instructions (e.g., for stack frame setup, 
register
+        spilling, or other optimizations) between:
+                - The call to bar()
+                - The compiler-generated "return 0;" code
+
+        Then the return address might point to valid intermediate code, and 
this
+        test scenario would not properly verify the fix for compiler-generated 
code.
+
+        The #line 0 directive ensures the compiler marks subsequent code as 
having
+        invalid source information, creating the test scenario where LLDB must
+        fall back to the previous valid source line.
+        """
+        program = self.getBuildArtifact("a.out")
+        self.build_and_launch(program)
+        source = "main.c"
+
+        # Set breakpoint inside bar() function
+        lines = [line_number(source, "// breakpoint here")]
+        breakpoint_ids = self.set_source_breakpoints(source, lines)
+        self.assertEqual(
+            len(breakpoint_ids), len(lines), "expect correct number of 
breakpoints"
+        )
+
+        self.continue_to_breakpoints(breakpoint_ids)
+
+        # Get the stack frames: [0] = bar(), [1] = foo(), [2] = main()
+        stack_frames = self.get_stackFrames()
+        self.assertGreater(len(stack_frames), 2, "Expected more than 2 stack 
frames")
+
+        # Examine the foo() frame (stack_frames[1])
+        # This is the critical frame containing compiler-generated code
+        foo_frame = stack_frames[1]
+        print(f"foo_frame: {foo_frame}")
+
+        # Verify that the frame's line number points to the bar() call,
+        # not to the compiler-generated code after it
+        foo_call_bar_source_line = foo_frame.get("line")
+        self.assertEqual(
+            foo_call_bar_source_line,
+            line_number(source, "foo call bar"),
+            "Expected foo call bar to be the source line of the frame",
+        )
+
+        # Verify the source file name is correctly resolved
+        foo_source_name = foo_frame.get("source", {}).get("name")
+        self.assertEqual(
+            foo_source_name, "main.c", "Expected foo source name to be main.c"
+        )
+
+        # When lldb fails to symbolicate a frame it will emit a fake assembly
+        # source with path of format <module>`<symbol> or <module>`<address> 
with
+        # sourceReference to retrieve disassembly source file.
+        # Verify that this didn't happen - the path should be a real file path.
+        foo_path = foo_frame.get("source", {}).get("path")
+        self.assertNotIn("`", foo_path, "Expected foo source path to not 
contain `")
diff --git 
a/lldb/test/API/tools/lldb-dap/stackTraceCompilerGeneratedCode/main.c 
b/lldb/test/API/tools/lldb-dap/stackTraceCompilerGeneratedCode/main.c
new file mode 100644
index 0000000000000..2ac5e506384dd
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/stackTraceCompilerGeneratedCode/main.c
@@ -0,0 +1,14 @@
+void bar() { 
+  int val = 32; // breakpoint here
+}
+
+int foo() {
+  bar(); // foo call bar
+#line 0 "test.cpp"
+  return 0;
+}
+
+int main(int argc, char const *argv[]) {
+  foo();
+  return 0;
+}
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index ebafda95ff583..995c006f1bf55 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -665,9 +665,7 @@ std::optional<protocol::Source> DAP::ResolveSource(const 
lldb::SBFrame &frame) {
   // EXAMPLE: If PC is at 0x1004 (frame return address after the call
   // instruction) with no line info, but 0x1003 (in the middle of previous call
   // instruction) is at line 42, symbol context returns line 42.
-  //
-  // NOTE: This issue is non-deterministic and depends on compiler debug info
-  // generation, making it difficult to create a reliable automated test.
+
   const lldb::SBLineEntry frame_line_entry = frame.GetLineEntry();
   if (DisplayAssemblySource(debugger, frame_line_entry)) {
     const lldb::SBAddress frame_pc = frame.GetPCAddress();

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to