If a SetDataBreakpointsRequest contains a list data breakpoints which
have duplicate starting addresses, the current behaviour is returning
`{verified: true}` to both watchpoints with duplicated starting
addresses. This confuses the client and what actually happens in lldb is
the second one overwrite the first one.
This fixes it by letting the last watchpoint at given address have
`{verified: true}` and all previous watchpoints at the same address
should have `{verfied: false}` at response.
177 lines
7.9 KiB
Python
177 lines
7.9 KiB
Python
"""
|
|
Test lldb-dap dataBreakpointInfo and setDataBreakpoints requests
|
|
"""
|
|
|
|
from lldbsuite.test.decorators import *
|
|
from lldbsuite.test.lldbtest import *
|
|
import lldbdap_testcase
|
|
|
|
|
|
class TestDAP_setDataBreakpoints(lldbdap_testcase.DAPTestCaseBase):
|
|
def setUp(self):
|
|
lldbdap_testcase.DAPTestCaseBase.setUp(self)
|
|
self.accessTypes = ["read", "write", "readWrite"]
|
|
|
|
@skipIfWindows
|
|
@skipIfRemote
|
|
def test_duplicate_start_addresses(self):
|
|
"""Test setDataBreakpoints with multiple watchpoints starting at the same addresses."""
|
|
program = self.getBuildArtifact("a.out")
|
|
self.build_and_launch(program)
|
|
source = "main.cpp"
|
|
first_loop_break_line = line_number(source, "// first loop breakpoint")
|
|
self.set_source_breakpoints(source, [first_loop_break_line])
|
|
self.continue_to_next_stop()
|
|
self.dap_server.get_stackFrame()
|
|
# Test setting write watchpoint using expressions: &x, arr+2
|
|
response_x = self.dap_server.request_dataBreakpointInfo(0, "&x")
|
|
response_arr_2 = self.dap_server.request_dataBreakpointInfo(0, "arr+2")
|
|
# Test response from dataBreakpointInfo request.
|
|
self.assertEquals(response_x["body"]["dataId"].split("/")[1], "4")
|
|
self.assertEquals(response_x["body"]["accessTypes"], self.accessTypes)
|
|
self.assertEquals(response_arr_2["body"]["dataId"].split("/")[1], "4")
|
|
self.assertEquals(response_arr_2["body"]["accessTypes"], self.accessTypes)
|
|
# The first one should be overwritten by the third one as they start at
|
|
# the same address. This is indicated by returning {verified: False} for
|
|
# the first one.
|
|
dataBreakpoints = [
|
|
{"dataId": response_x["body"]["dataId"], "accessType": "read"},
|
|
{"dataId": response_arr_2["body"]["dataId"], "accessType": "write"},
|
|
{"dataId": response_x["body"]["dataId"], "accessType": "write"},
|
|
]
|
|
set_response = self.dap_server.request_setDataBreakpoint(dataBreakpoints)
|
|
self.assertEquals(
|
|
set_response["body"]["breakpoints"],
|
|
[{"verified": False}, {"verified": True}, {"verified": True}],
|
|
)
|
|
|
|
self.continue_to_next_stop()
|
|
x_val = self.dap_server.get_local_variable_value("x")
|
|
i_val = self.dap_server.get_local_variable_value("i")
|
|
self.assertEquals(x_val, "2")
|
|
self.assertEquals(i_val, "1")
|
|
|
|
self.continue_to_next_stop()
|
|
arr_2 = self.dap_server.get_local_variable_child("arr", "[2]")
|
|
i_val = self.dap_server.get_local_variable_value("i")
|
|
self.assertEquals(arr_2["value"], "42")
|
|
self.assertEquals(i_val, "2")
|
|
|
|
@skipIfWindows
|
|
@skipIfRemote
|
|
def test_expression(self):
|
|
"""Tests setting data breakpoints on expression."""
|
|
program = self.getBuildArtifact("a.out")
|
|
self.build_and_launch(program)
|
|
source = "main.cpp"
|
|
first_loop_break_line = line_number(source, "// first loop breakpoint")
|
|
self.set_source_breakpoints(source, [first_loop_break_line])
|
|
self.continue_to_next_stop()
|
|
self.dap_server.get_stackFrame()
|
|
# Test setting write watchpoint using expressions: &x, arr+2
|
|
response_x = self.dap_server.request_dataBreakpointInfo(0, "&x")
|
|
response_arr_2 = self.dap_server.request_dataBreakpointInfo(0, "arr+2")
|
|
# Test response from dataBreakpointInfo request.
|
|
self.assertEquals(response_x["body"]["dataId"].split("/")[1], "4")
|
|
self.assertEquals(response_x["body"]["accessTypes"], self.accessTypes)
|
|
self.assertEquals(response_arr_2["body"]["dataId"].split("/")[1], "4")
|
|
self.assertEquals(response_arr_2["body"]["accessTypes"], self.accessTypes)
|
|
dataBreakpoints = [
|
|
{"dataId": response_x["body"]["dataId"], "accessType": "write"},
|
|
{"dataId": response_arr_2["body"]["dataId"], "accessType": "write"},
|
|
]
|
|
set_response = self.dap_server.request_setDataBreakpoint(dataBreakpoints)
|
|
self.assertEquals(
|
|
set_response["body"]["breakpoints"],
|
|
[{"verified": True}, {"verified": True}],
|
|
)
|
|
|
|
self.continue_to_next_stop()
|
|
x_val = self.dap_server.get_local_variable_value("x")
|
|
i_val = self.dap_server.get_local_variable_value("i")
|
|
self.assertEquals(x_val, "2")
|
|
self.assertEquals(i_val, "1")
|
|
|
|
self.continue_to_next_stop()
|
|
arr_2 = self.dap_server.get_local_variable_child("arr", "[2]")
|
|
i_val = self.dap_server.get_local_variable_value("i")
|
|
self.assertEquals(arr_2["value"], "42")
|
|
self.assertEquals(i_val, "2")
|
|
|
|
@skipIfWindows
|
|
@skipIfRemote
|
|
def test_functionality(self):
|
|
"""Tests setting data breakpoints on variable."""
|
|
program = self.getBuildArtifact("a.out")
|
|
self.build_and_launch(program)
|
|
source = "main.cpp"
|
|
first_loop_break_line = line_number(source, "// first loop breakpoint")
|
|
self.set_source_breakpoints(source, [first_loop_break_line])
|
|
self.continue_to_next_stop()
|
|
self.dap_server.get_local_variables()
|
|
# Test write watchpoints on x, arr[2]
|
|
response_x = self.dap_server.request_dataBreakpointInfo(1, "x")
|
|
arr = self.dap_server.get_local_variable("arr")
|
|
response_arr_2 = self.dap_server.request_dataBreakpointInfo(
|
|
arr["variablesReference"], "[2]"
|
|
)
|
|
|
|
# Test response from dataBreakpointInfo request.
|
|
self.assertEquals(response_x["body"]["dataId"].split("/")[1], "4")
|
|
self.assertEquals(response_x["body"]["accessTypes"], self.accessTypes)
|
|
self.assertEquals(response_arr_2["body"]["dataId"].split("/")[1], "4")
|
|
self.assertEquals(response_arr_2["body"]["accessTypes"], self.accessTypes)
|
|
dataBreakpoints = [
|
|
{"dataId": response_x["body"]["dataId"], "accessType": "write"},
|
|
{"dataId": response_arr_2["body"]["dataId"], "accessType": "write"},
|
|
]
|
|
set_response = self.dap_server.request_setDataBreakpoint(dataBreakpoints)
|
|
self.assertEquals(
|
|
set_response["body"]["breakpoints"],
|
|
[{"verified": True}, {"verified": True}],
|
|
)
|
|
|
|
self.continue_to_next_stop()
|
|
x_val = self.dap_server.get_local_variable_value("x")
|
|
i_val = self.dap_server.get_local_variable_value("i")
|
|
self.assertEquals(x_val, "2")
|
|
self.assertEquals(i_val, "1")
|
|
|
|
self.continue_to_next_stop()
|
|
arr_2 = self.dap_server.get_local_variable_child("arr", "[2]")
|
|
i_val = self.dap_server.get_local_variable_value("i")
|
|
self.assertEquals(arr_2["value"], "42")
|
|
self.assertEquals(i_val, "2")
|
|
self.dap_server.request_setDataBreakpoint([])
|
|
|
|
# Test hit condition
|
|
second_loop_break_line = line_number(source, "// second loop breakpoint")
|
|
breakpoint_ids = self.set_source_breakpoints(source, [second_loop_break_line])
|
|
self.continue_to_breakpoints(breakpoint_ids)
|
|
dataBreakpoints = [
|
|
{
|
|
"dataId": response_x["body"]["dataId"],
|
|
"accessType": "write",
|
|
"hitCondition": "2",
|
|
}
|
|
]
|
|
set_response = self.dap_server.request_setDataBreakpoint(dataBreakpoints)
|
|
self.assertEquals(set_response["body"]["breakpoints"], [{"verified": True}])
|
|
self.continue_to_next_stop()
|
|
x_val = self.dap_server.get_local_variable_value("x")
|
|
self.assertEquals(x_val, "3")
|
|
|
|
# Test condition
|
|
dataBreakpoints = [
|
|
{
|
|
"dataId": response_x["body"]["dataId"],
|
|
"accessType": "write",
|
|
"condition": "x==10",
|
|
}
|
|
]
|
|
set_response = self.dap_server.request_setDataBreakpoint(dataBreakpoints)
|
|
self.assertEquals(set_response["body"]["breakpoints"], [{"verified": True}])
|
|
self.continue_to_next_stop()
|
|
x_val = self.dap_server.get_local_variable_value("x")
|
|
self.assertEquals(x_val, "10")
|