Skip to content

Commit b81e30e

Browse files
committed
add persistence assembly breakpoint test
1 parent 3e35495 commit b81e30e

File tree

3 files changed

+102
-18
lines changed

3 files changed

+102
-18
lines changed

lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,24 +107,30 @@ def dump_dap_log(log_file):
107107

108108
class Source(object):
109109
def __init__(
110-
self, path: Optional[str] = None, source_reference: Optional[int] = None
110+
self, path: Optional[str] = None, source_reference: Optional[int] = None, raw_dict: Optional[dict[str, Any]] = None
111111
):
112112
self._name = None
113113
self._path = None
114114
self._source_reference = None
115+
self._raw_dict = None
115116

116117
if path is not None:
117118
self._name = os.path.basename(path)
118119
self._path = path
119120
elif source_reference is not None:
120121
self._source_reference = source_reference
122+
elif raw_dict is not None:
123+
self._raw_dict = raw_dict
121124
else:
122125
raise ValueError("Either path or source_reference must be provided")
123126

124127
def __str__(self):
125128
return f"Source(name={self.name}, path={self.path}), source_reference={self.source_reference})"
126129

127130
def as_dict(self):
131+
if self._raw_dict is not None:
132+
return self._raw_dict
133+
128134
source_dict = {}
129135
if self._name is not None:
130136
source_dict["name"] = self._name
@@ -135,6 +141,19 @@ def as_dict(self):
135141
return source_dict
136142

137143

144+
class Breakpoint(object):
145+
def __init__(self, obj):
146+
self._breakpoint = obj
147+
148+
def is_verified(self):
149+
"""Check if the breakpoint is verified."""
150+
return self._breakpoint.get("verified", False)
151+
152+
def source(self):
153+
"""Get the source of the breakpoint."""
154+
return self._breakpoint.get("source", {})
155+
156+
138157
class NotSupportedError(KeyError):
139158
"""Raised if a feature is not supported due to its capabilities."""
140159

@@ -170,7 +189,7 @@ def __init__(
170189
self.initialized = False
171190
self.frame_scopes = {}
172191
self.init_commands = init_commands
173-
self.resolved_breakpoints = {}
192+
self.resolved_breakpoints: dict[str, Breakpoint] = {}
174193

175194
@classmethod
176195
def encode_content(cls, s: str) -> bytes:
@@ -326,9 +345,7 @@ def _process_continued(self, all_threads_continued: bool):
326345
def _update_verified_breakpoints(self, breakpoints: list[Event]):
327346
for breakpoint in breakpoints:
328347
if "id" in breakpoint:
329-
self.resolved_breakpoints[str(breakpoint["id"])] = breakpoint.get(
330-
"verified", False
331-
)
348+
self.resolved_breakpoints[str(breakpoint["id"])] = Breakpoint(breakpoint)
332349

333350
def send_packet(self, command_dict: Request, set_sequence=True):
334351
"""Take the "command_dict" python dictionary and encode it as a JSON
@@ -484,7 +501,7 @@ def wait_for_breakpoints_to_be_verified(
484501
if breakpoint_event is None:
485502
break
486503

487-
return [id for id in breakpoint_ids if id not in self.resolved_breakpoints]
504+
return [id for id in breakpoint_ids if (id not in self.resolved_breakpoints or not self.resolved_breakpoints[id].is_verified())]
488505

489506
def wait_for_exited(self, timeout: Optional[float] = None):
490507
event_dict = self.wait_for_event("exited", timeout=timeout)

lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -59,24 +59,22 @@ def set_source_breakpoints(
5959
Each object in data is 1:1 mapping with the entry in lines.
6060
It contains optional location/hitCondition/logMessage parameters.
6161
"""
62-
response = self.dap_server.request_setBreakpoints(
63-
Source(source_path), lines, data
62+
return self.set_source_breakpoints_from_source(
63+
Source(path=source_path), lines, data, wait_for_resolve
6464
)
65-
if response is None or not response["success"]:
66-
return []
67-
breakpoints = response["body"]["breakpoints"]
68-
breakpoint_ids = []
69-
for breakpoint in breakpoints:
70-
breakpoint_ids.append("%i" % (breakpoint["id"]))
71-
if wait_for_resolve:
72-
self.wait_for_breakpoints_to_resolve(breakpoint_ids)
73-
return breakpoint_ids
7465

7566
def set_source_breakpoints_assembly(
7667
self, source_reference, lines, data=None, wait_for_resolve=True
68+
):
69+
return self.set_source_breakpoints_from_source(
70+
Source(source_reference=source_reference), lines, data, wait_for_resolve
71+
)
72+
73+
def set_source_breakpoints_from_source(
74+
self, source: Source, lines, data=None, wait_for_resolve=True
7775
):
7876
response = self.dap_server.request_setBreakpoints(
79-
Source(source_reference=source_reference),
77+
source,
8078
lines,
8179
data,
8280
)

lldb/test/API/tools/lldb-dap/breakpoint-assembly/TestDAP_breakpointAssembly.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,72 @@ def test_break_on_invalid_source_reference(self):
8383
break_point["message"],
8484
"Invalid sourceReference.",
8585
)
86+
87+
@skipIfWindows
88+
def test_persistent_assembly_breakpoint(self):
89+
"""Tests that assembly breakpoints are working persistently across sessions"""
90+
self.build()
91+
program = self.getBuildArtifact("a.out")
92+
self.create_debug_adapter()
93+
94+
# Run the first session and set a persistent assembly breakpoint
95+
try:
96+
self.dap_server.request_initialize()
97+
self.dap_server.request_launch(program)
98+
99+
assmebly_func_breakpoints = self.set_function_breakpoints(["assembly_func"])
100+
self.continue_to_breakpoints(assmebly_func_breakpoints)
101+
102+
assembly_func_frame = self.get_stackFrames()[0]
103+
source_reference = assembly_func_frame["source"]["sourceReference"]
104+
105+
# Set an assembly breakpoint in the middle of the assembly function
106+
persistent_breakpoint_line = 4
107+
persistent_breakpoint_ids = self.set_source_breakpoints_assembly(
108+
source_reference, [persistent_breakpoint_line]
109+
)
110+
111+
self.assertEqual(
112+
len(persistent_breakpoint_ids), 1, "Expected one assembly breakpoint to be set"
113+
)
114+
115+
persistent_breakpoint_source = self.dap_server.resolved_breakpoints[persistent_breakpoint_ids[0]].source()
116+
self.assertIn(
117+
"adapterData",
118+
persistent_breakpoint_source,
119+
"Expected assembly breakpoint to have persistent information",
120+
)
121+
self.assertIn(
122+
"persistenceData",
123+
persistent_breakpoint_source["adapterData"],
124+
"Expected assembly breakpoint to have persistent information",
125+
)
126+
127+
self.continue_to_breakpoints(persistent_breakpoint_ids)
128+
finally:
129+
self.dap_server.request_disconnect(terminateDebuggee=True)
130+
self.dap_server.terminate()
131+
132+
# Restart the session and verify the breakpoint is still there
133+
self.create_debug_adapter()
134+
try:
135+
self.dap_server.request_initialize()
136+
self.dap_server.request_launch(program)
137+
new_session_breakpoints_ids = self.set_source_breakpoints_from_source(
138+
Source(raw_dict=persistent_breakpoint_source), [persistent_breakpoint_line]
139+
)
140+
141+
self.assertEqual(
142+
len(new_session_breakpoints_ids),
143+
1,
144+
"Expected one breakpoint to be set in the new session",
145+
)
146+
147+
self.continue_to_breakpoints(new_session_breakpoints_ids)
148+
current_line = self.get_stackFrames()[0]["line"]
149+
self.assertEqual(current_line, persistent_breakpoint_line,
150+
"Expected to hit the persistent assembly breakpoint at the same line")
151+
finally:
152+
self.dap_server.request_disconnect(terminateDebuggee=True)
153+
self.dap_server.terminate()
154+

0 commit comments

Comments
 (0)