Skip to content

Commit 7b59d42

Browse files
author
Jeffrey Tan
committed
Add Test
1 parent 0f9f57f commit 7b59d42

File tree

4 files changed

+100
-3
lines changed

4 files changed

+100
-3
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
C_SOURCES := main.c
2+
3+
include Makefile.rules
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
"""
2+
Test lldb-dap stackTrace request for compiler generated code
3+
"""
4+
5+
import os
6+
7+
import lldbdap_testcase
8+
from lldbsuite.test.decorators import *
9+
from lldbsuite.test.lldbtest import *
10+
11+
12+
class TestDAP_stackTraceCompilerGeneratedCode(lldbdap_testcase.DAPTestCaseBase):
13+
@skipIfWindows
14+
def test_non_leaf_frame_compiler_generate_code(self):
15+
"""
16+
Test that non-leaf frames with compiler-generated code are properly resolved.
17+
18+
This test verifies that LLDB correctly handles stack frames that contain
19+
compiler-generated code (code without valid source location information).
20+
Specifically, it tests that when a non-leaf frame contains compiler-generated
21+
code, LLDB properly resolves the source location to the last valid source line
22+
before the compiler-generated code, rather than failing to symbolicate the frame.
23+
24+
Critical Assumption:
25+
====================
26+
This test assumes that there is NO extra machine code between the call
27+
instruction and the start of the next source line. If the compiler were
28+
to insert additional instructions (e.g., for stack frame setup, register
29+
spilling, or other optimizations) between:
30+
- The call to bar()
31+
- The compiler-generated "return 0;" code
32+
33+
Then the return address might point to valid intermediate code, and this
34+
test scenario would not properly verify the fix for compiler-generated code.
35+
36+
The #line 0 directive ensures the compiler marks subsequent code as having
37+
invalid source information, creating the test scenario where LLDB must
38+
fall back to the previous valid source line.
39+
"""
40+
program = self.getBuildArtifact("a.out")
41+
self.build_and_launch(program)
42+
source = "main.c"
43+
44+
# Set breakpoint inside bar() function
45+
lines = [line_number(source, "// breakpoint here")]
46+
breakpoint_ids = self.set_source_breakpoints(source, lines)
47+
self.assertEqual(
48+
len(breakpoint_ids), len(lines), "expect correct number of breakpoints"
49+
)
50+
51+
self.continue_to_breakpoints(breakpoint_ids)
52+
53+
# Get the stack frames: [0] = bar(), [1] = foo(), [2] = main()
54+
stack_frames = self.get_stackFrames()
55+
self.assertGreater(len(stack_frames), 2, "Expected more than 2 stack frames")
56+
57+
# Examine the foo() frame (stack_frames[1])
58+
# This is the critical frame containing compiler-generated code
59+
foo_frame = stack_frames[1]
60+
print(f"foo_frame: {foo_frame}")
61+
62+
# Verify that the frame's line number points to the bar() call,
63+
# not to the compiler-generated code after it
64+
foo_call_bar_source_line = foo_frame.get("line")
65+
self.assertEqual(
66+
foo_call_bar_source_line,
67+
line_number(source, "foo call bar"),
68+
"Expected foo call bar to be the source line of the frame",
69+
)
70+
71+
# Verify the source file name is correctly resolved
72+
foo_source_name = foo_frame.get("source", {}).get("name")
73+
self.assertEqual(
74+
foo_source_name, "main.c", "Expected foo source name to be main.c"
75+
)
76+
77+
# When lldb fails to symbolicate a frame it will emit a fake assembly
78+
# source with path of format <module>`<symbol> or <module>`<address> with
79+
# sourceReference to retrieve disassembly source file.
80+
# Verify that this didn't happen - the path should be a real file path.
81+
foo_path = foo_frame.get("source", {}).get("path")
82+
self.assertNotIn("`", foo_path, "Expected foo source path to not contain `")
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
void bar() {
2+
int val = 32; // breakpoint here
3+
}
4+
5+
int foo() {
6+
bar(); // foo call bar
7+
#line 0 "test.cpp"
8+
return 0;
9+
}
10+
11+
int main(int argc, char const *argv[]) {
12+
foo();
13+
return 0;
14+
}

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -665,9 +665,7 @@ std::optional<protocol::Source> DAP::ResolveSource(const lldb::SBFrame &frame) {
665665
// EXAMPLE: If PC is at 0x1004 (frame return address after the call
666666
// instruction) with no line info, but 0x1003 (in the middle of previous call
667667
// instruction) is at line 42, symbol context returns line 42.
668-
//
669-
// NOTE: This issue is non-deterministic and depends on compiler debug info
670-
// generation, making it difficult to create a reliable automated test.
668+
671669
const lldb::SBLineEntry frame_line_entry = frame.GetLineEntry();
672670
if (DisplayAssemblySource(debugger, frame_line_entry)) {
673671
const lldb::SBAddress frame_pc = frame.GetPCAddress();

0 commit comments

Comments
 (0)