Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
804 changes: 458 additions & 346 deletions lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import os
import time
from typing import Optional
from typing import Optional, Callable, Any, List, Union
import uuid

import dap_server
Expand Down Expand Up @@ -67,7 +67,10 @@ def set_source_breakpoints_assembly(
self, source_reference, lines, data=None, wait_for_resolve=True
):
return self.set_source_breakpoints_from_source(
Source(source_reference=source_reference), lines, data, wait_for_resolve
Source.build(source_reference=source_reference),
lines,
data,
wait_for_resolve,
)

def set_source_breakpoints_from_source(
Expand Down Expand Up @@ -120,11 +123,19 @@ def wait_for_breakpoints_to_resolve(
f"Expected to resolve all breakpoints. Unresolved breakpoint ids: {unresolved_breakpoints}",
)

def waitUntil(self, condition_callback):
for _ in range(20):
if condition_callback():
def wait_until(
self,
predicate: Callable[[], bool],
delay: float = 0.5,
timeout: float = DEFAULT_TIMEOUT,
) -> bool:
"""Repeatedly run the predicate until either the predicate returns True
or a timeout has occurred."""
deadline = time.monotonic() + timeout
while deadline > time.monotonic():
if predicate():
return True
time.sleep(0.5)
time.sleep(delay)
return False

def assertCapabilityIsSet(self, key: str, msg: Optional[str] = None) -> None:
Expand All @@ -137,13 +148,16 @@ def assertCapabilityIsNotSet(self, key: str, msg: Optional[str] = None) -> None:
if key in self.dap_server.capabilities:
self.assertEqual(self.dap_server.capabilities[key], False, msg)

def verify_breakpoint_hit(self, breakpoint_ids, timeout=DEFAULT_TIMEOUT):
def verify_breakpoint_hit(
self, breakpoint_ids: List[Union[int, str]], timeout: float = DEFAULT_TIMEOUT
):
"""Wait for the process we are debugging to stop, and verify we hit
any breakpoint location in the "breakpoint_ids" array.
"breakpoint_ids" should be a list of breakpoint ID strings
(["1", "2"]). The return value from self.set_source_breakpoints()
or self.set_function_breakpoints() can be passed to this function"""
stopped_events = self.dap_server.wait_for_stopped(timeout)
normalized_bp_ids = [str(b) for b in breakpoint_ids]
for stopped_event in stopped_events:
if "body" in stopped_event:
body = stopped_event["body"]
Expand All @@ -154,22 +168,16 @@ def verify_breakpoint_hit(self, breakpoint_ids, timeout=DEFAULT_TIMEOUT):
and body["reason"] != "instruction breakpoint"
):
continue
if "description" not in body:
if "hitBreakpointIds" not in body:
continue
# Descriptions for breakpoints will be in the form
# "breakpoint 1.1", so look for any description that matches
# ("breakpoint 1.") in the description field as verification
# that one of the breakpoint locations was hit. DAP doesn't
# allow breakpoints to have multiple locations, but LLDB does.
# So when looking at the description we just want to make sure
# the right breakpoint matches and not worry about the actual
# location.
description = body["description"]
for breakpoint_id in breakpoint_ids:
match_desc = f"breakpoint {breakpoint_id}."
if match_desc in description:
hit_breakpoint_ids = body["hitBreakpointIds"]
for bp in hit_breakpoint_ids:
if str(bp) in normalized_bp_ids:
return
self.assertTrue(False, f"breakpoint not hit, stopped_events={stopped_events}")
self.assertTrue(
False,
f"breakpoint not hit, wanted breakpoint_ids {breakpoint_ids} in stopped_events {stopped_events}",
)

def verify_all_breakpoints_hit(self, breakpoint_ids, timeout=DEFAULT_TIMEOUT):
"""Wait for the process we are debugging to stop, and verify we hit
Expand Down Expand Up @@ -213,7 +221,7 @@ def verify_stop_exception_info(self, expected_description, timeout=DEFAULT_TIMEO
return True
return False

def verify_commands(self, flavor, output, commands):
def verify_commands(self, flavor: str, output: str, commands: list[str]):
self.assertTrue(output and len(output) > 0, "expect console output")
lines = output.splitlines()
prefix = "(lldb) "
Expand All @@ -226,10 +234,11 @@ def verify_commands(self, flavor, output, commands):
found = True
break
self.assertTrue(
found, "verify '%s' found in console output for '%s'" % (cmd, flavor)
found,
f"Command '{flavor}' - '{cmd}' not found in output: {output}",
)

def get_dict_value(self, d, key_path):
def get_dict_value(self, d: dict, key_path: list[str]) -> Any:
"""Verify each key in the key_path array is in contained in each
dictionary within "d". Assert if any key isn't in the
corresponding dictionary. This is handy for grabbing values from VS
Expand Down Expand Up @@ -298,28 +307,34 @@ def get_source_and_line(self, threadId=None, frameIndex=0):
return (source["path"], stackFrame["line"])
return ("", 0)

def get_stdout(self, timeout=0.0):
return self.dap_server.get_output("stdout", timeout=timeout)
def get_stdout(self):
return self.dap_server.get_output("stdout")

def get_console(self, timeout=0.0):
return self.dap_server.get_output("console", timeout=timeout)
def get_console(self):
return self.dap_server.get_output("console")

def get_important(self, timeout=0.0):
return self.dap_server.get_output("important", timeout=timeout)
def get_important(self):
return self.dap_server.get_output("important")

def collect_stdout(self, timeout_secs, pattern=None):
def collect_stdout(
self, timeout: float = DEFAULT_TIMEOUT, pattern: Optional[str] = None
) -> str:
return self.dap_server.collect_output(
"stdout", timeout_secs=timeout_secs, pattern=pattern
"stdout", timeout=timeout, pattern=pattern
)

def collect_console(self, timeout_secs, pattern=None):
def collect_console(
self, timeout: float = DEFAULT_TIMEOUT, pattern: Optional[str] = None
) -> str:
return self.dap_server.collect_output(
"console", timeout_secs=timeout_secs, pattern=pattern
"console", timeout=timeout, pattern=pattern
)

def collect_important(self, timeout_secs, pattern=None):
def collect_important(
self, timeout: float = DEFAULT_TIMEOUT, pattern: Optional[str] = None
) -> str:
return self.dap_server.collect_output(
"important", timeout_secs=timeout_secs, pattern=pattern
"important", timeout=timeout, pattern=pattern
)

def get_local_as_int(self, name, threadId=None):
Expand Down
8 changes: 4 additions & 4 deletions lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def test_commands(self):
breakpoint_ids = self.set_function_breakpoints(functions)
self.assertEqual(len(breakpoint_ids), len(functions), "expect one breakpoint")
self.continue_to_breakpoints(breakpoint_ids)
output = self.collect_console(timeout_secs=10, pattern=stopCommands[-1])
output = self.collect_console(timeout=10, pattern=stopCommands[-1])
self.verify_commands("stopCommands", output, stopCommands)

# Continue after launch and hit the "pause()" call and stop the target.
Expand All @@ -163,7 +163,7 @@ def test_commands(self):
time.sleep(0.5)
self.dap_server.request_pause()
self.dap_server.wait_for_stopped()
output = self.collect_console(timeout_secs=10, pattern=stopCommands[-1])
output = self.collect_console(timeout=10, pattern=stopCommands[-1])
self.verify_commands("stopCommands", output, stopCommands)

# Continue until the program exits
Expand All @@ -172,7 +172,7 @@ def test_commands(self):
# "exitCommands" that were run after the second breakpoint was hit
# and the "terminateCommands" due to the debugging session ending
output = self.collect_console(
timeout_secs=10.0,
timeout=10.0,
pattern=terminateCommands[0],
)
self.verify_commands("exitCommands", output, exitCommands)
Expand Down Expand Up @@ -223,7 +223,7 @@ def test_terminate_commands(self):
# "terminateCommands"
self.dap_server.request_disconnect(terminateDebuggee=True)
output = self.collect_console(
timeout_secs=1.0,
timeout=1.0,
pattern=terminateCommands[0],
)
self.verify_commands("terminateCommands", output, terminateCommands)
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
Test lldb-dap setBreakpoints request in assembly source references.
"""


from lldbsuite.test.decorators import *
from dap_server import Source
import lldbdap_testcase
Expand Down Expand Up @@ -52,7 +51,7 @@ def test_break_on_invalid_source_reference(self):

# Verify that setting a breakpoint on an invalid source reference fails
response = self.dap_server.request_setBreakpoints(
Source(source_reference=-1), [1]
Source.build(source_reference=-1), [1]
)
self.assertIsNotNone(response)
breakpoints = response["body"]["breakpoints"]
Expand All @@ -69,7 +68,7 @@ def test_break_on_invalid_source_reference(self):

# Verify that setting a breakpoint on a source reference that is not created fails
response = self.dap_server.request_setBreakpoints(
Source(source_reference=200), [1]
Source.build(source_reference=200), [1]
)
self.assertIsNotNone(response)
breakpoints = response["body"]["breakpoints"]
Expand Down Expand Up @@ -116,7 +115,7 @@ def test_persistent_assembly_breakpoint(self):

persistent_breakpoint_source = self.dap_server.resolved_breakpoints[
persistent_breakpoint_ids[0]
].source()
]["source"]
self.assertIn(
"adapterData",
persistent_breakpoint_source,
Expand All @@ -139,7 +138,7 @@ def test_persistent_assembly_breakpoint(self):
self.dap_server.request_initialize()
self.dap_server.request_launch(program)
new_session_breakpoints_ids = self.set_source_breakpoints_from_source(
Source(raw_dict=persistent_breakpoint_source),
Source(persistent_breakpoint_source),
[persistent_breakpoint_line],
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def test_breakpoint_events(self):
# Set breakpoints and verify that they got set correctly
dap_breakpoint_ids = []
response = self.dap_server.request_setBreakpoints(
Source(main_source_path), [main_bp_line]
Source.build(path=main_source_path), [main_bp_line]
)
self.assertTrue(response["success"])
breakpoints = response["body"]["breakpoints"]
Expand All @@ -70,7 +70,7 @@ def test_breakpoint_events(self):
)

response = self.dap_server.request_setBreakpoints(
Source(foo_source_path), [foo_bp1_line]
Source.build(path=foo_source_path), [foo_bp1_line]
)
self.assertTrue(response["success"])
breakpoints = response["body"]["breakpoints"]
Expand Down
33 changes: 22 additions & 11 deletions lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
Test lldb-dap setBreakpoints request
"""


from dap_server import Source
import shutil
from lldbsuite.test.decorators import *
Expand Down Expand Up @@ -58,7 +57,7 @@ def test_source_map(self):

# breakpoint in main.cpp
response = self.dap_server.request_setBreakpoints(
Source(new_main_path), [main_line]
Source.build(path=new_main_path), [main_line]
)
breakpoints = response["body"]["breakpoints"]
self.assertEqual(len(breakpoints), 1)
Expand All @@ -70,7 +69,7 @@ def test_source_map(self):

# 2nd breakpoint, which is from a dynamically loaded library
response = self.dap_server.request_setBreakpoints(
Source(new_other_path), [other_line]
Source.build(path=new_other_path), [other_line]
)
breakpoints = response["body"]["breakpoints"]
breakpoint = breakpoints[0]
Expand All @@ -85,7 +84,7 @@ def test_source_map(self):

# 2nd breakpoint again, which should be valid at this point
response = self.dap_server.request_setBreakpoints(
Source(new_other_path), [other_line]
Source.build(path=new_other_path), [other_line]
)
breakpoints = response["body"]["breakpoints"]
breakpoint = breakpoints[0]
Expand Down Expand Up @@ -129,7 +128,9 @@ def test_set_and_clear(self):
self.build_and_launch(program)

# Set 3 breakpoints and verify that they got set correctly
response = self.dap_server.request_setBreakpoints(Source(self.main_path), lines)
response = self.dap_server.request_setBreakpoints(
Source.build(path=self.main_path), lines
)
line_to_id = {}
breakpoints = response["body"]["breakpoints"]
self.assertEqual(
Expand All @@ -154,7 +155,9 @@ def test_set_and_clear(self):
lines.remove(second_line)
# Set 2 breakpoints and verify that the previous breakpoints that were
# set above are still set.
response = self.dap_server.request_setBreakpoints(Source(self.main_path), lines)
response = self.dap_server.request_setBreakpoints(
Source.build(path=self.main_path), lines
)
breakpoints = response["body"]["breakpoints"]
self.assertEqual(
len(breakpoints),
Expand Down Expand Up @@ -199,7 +202,9 @@ def test_set_and_clear(self):
# Now clear all breakpoints for the source file by passing down an
# empty lines array
lines = []
response = self.dap_server.request_setBreakpoints(Source(self.main_path), lines)
response = self.dap_server.request_setBreakpoints(
Source.build(path=self.main_path), lines
)
breakpoints = response["body"]["breakpoints"]
self.assertEqual(
len(breakpoints),
Expand All @@ -219,7 +224,9 @@ def test_set_and_clear(self):
# Now set a breakpoint again in the same source file and verify it
# was added.
lines = [second_line]
response = self.dap_server.request_setBreakpoints(Source(self.main_path), lines)
response = self.dap_server.request_setBreakpoints(
Source.build(path=self.main_path), lines
)
if response:
breakpoints = response["body"]["breakpoints"]
self.assertEqual(
Expand Down Expand Up @@ -270,7 +277,9 @@ def test_clear_breakpoints_unset_breakpoints(self):
self.build_and_launch(program)

# Set one breakpoint and verify that it got set correctly.
response = self.dap_server.request_setBreakpoints(Source(self.main_path), lines)
response = self.dap_server.request_setBreakpoints(
Source.build(path=self.main_path), lines
)
line_to_id = {}
breakpoints = response["body"]["breakpoints"]
self.assertEqual(
Expand All @@ -286,7 +295,9 @@ def test_clear_breakpoints_unset_breakpoints(self):
# Now clear all breakpoints for the source file by not setting the
# lines array.
lines = None
response = self.dap_server.request_setBreakpoints(Source(self.main_path), lines)
response = self.dap_server.request_setBreakpoints(
Source.build(path=self.main_path), lines
)
breakpoints = response["body"]["breakpoints"]
self.assertEqual(len(breakpoints), 0, "expect no source breakpoints")

Expand Down Expand Up @@ -362,7 +373,7 @@ def test_column_breakpoints(self):
# Set two breakpoints on the loop line at different columns.
columns = [13, 39]
response = self.dap_server.request_setBreakpoints(
Source(self.main_path),
Source.build(path=self.main_path),
[loop_line, loop_line],
list({"column": c} for c in columns),
)
Expand Down
Loading
Loading