Skip to content

Commit 2743af5

Browse files
committed
[lldb-dap] Add support for data breakpoint.
This implements functionality to handle `DataBreakpointInfo` request and `SetDataBreakpoints` request. It doesn't handle the case when `name` is an expression, see Todo comment for details.
1 parent 40fca27 commit 2743af5

File tree

10 files changed

+492
-0
lines changed

10 files changed

+492
-0
lines changed

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,17 @@ def get_local_variable_value(self, name, frameIndex=0, threadId=None):
501501
return variable["value"]
502502
return None
503503

504+
def get_local_variable_child(self, name, child_name, frameIndex=0, threadId=None):
505+
local = self.get_local_variable(name, frameIndex, threadId)
506+
if local["variablesReference"] == 0:
507+
return None
508+
children = self.request_variables(local["variablesReference"])[
509+
"body"]["variables"]
510+
for child in children:
511+
if child["name"] == child_name:
512+
return child
513+
return None
514+
504515
def replay_packets(self, replay_file_path):
505516
f = open(replay_file_path, "r")
506517
mode = "invalid"
@@ -895,6 +906,32 @@ def request_setFunctionBreakpoints(self, names, condition=None, hitCondition=Non
895906
}
896907
return self.send_recv(command_dict)
897908

909+
def request_dataBreakpointInfo(self, variablesReference, name):
910+
args_dict = {"variablesReference": variablesReference, "name": name}
911+
command_dict = {
912+
"command": "dataBreakpointInfo",
913+
"type": "request",
914+
"arguments": args_dict,
915+
}
916+
return self.send_recv(command_dict)
917+
918+
def request_setDataBreakpoint(self, dataBreakpoints):
919+
"""dataBreakpoints is a list of dictionary with following fields:
920+
{
921+
dataId: (address in hex)/(size in bytes)
922+
accessType: read/write/readWrite
923+
[condition]: string
924+
[hitCondition]: string
925+
}
926+
"""
927+
args_dict = {"breakpoints": dataBreakpoints}
928+
command_dict = {
929+
"command": "setDataBreakpoints",
930+
"type": "request",
931+
"arguments": args_dict,
932+
}
933+
return self.send_recv(command_dict)
934+
898935
def request_compileUnits(self, moduleId):
899936
args_dict = {"moduleId": moduleId}
900937
command_dict = {
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
CXX_SOURCES := main.cpp
2+
3+
include Makefile.rules
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
"""
2+
Test lldb-dap dataBreakpointInfo and setDataBreakpoints requests
3+
"""
4+
5+
6+
from lldbsuite.test.decorators import *
7+
from lldbsuite.test.lldbtest import *
8+
import lldbdap_testcase
9+
10+
11+
class TestDAP_setDataBreakpoints(lldbdap_testcase.DAPTestCaseBase):
12+
def setUp(self):
13+
lldbdap_testcase.DAPTestCaseBase.setUp(self)
14+
self.accessTypes = ["read", "write", "readWrite"]
15+
16+
@skipIfWindows
17+
@skipIfRemote
18+
def test_functionality(self):
19+
"""Tests setting data breakpoints.
20+
"""
21+
program = self.getBuildArtifact("a.out")
22+
self.build_and_launch(program)
23+
source = "main.cpp"
24+
first_loop_break_line = line_number(
25+
source, "// first loop breakpoint")
26+
breakpoint_ids = self.set_source_breakpoints(
27+
source, [first_loop_break_line])
28+
self.continue_to_breakpoints(breakpoint_ids)
29+
self.dap_server.get_local_variables()
30+
# Test write watchpoints on x, arr[2]
31+
response_x = self.dap_server.request_dataBreakpointInfo(1, "x")
32+
arr = self.dap_server.get_local_variable("arr")
33+
response_arr_2 = self.dap_server.request_dataBreakpointInfo(
34+
arr["variablesReference"], "[2]")
35+
36+
# Test response from dataBreakpointInfo request.
37+
self.assertEquals(response_x["body"]["dataId"].split("/")[1], "4")
38+
self.assertEquals(response_x["body"]["accessTypes"], self.accessTypes)
39+
self.assertEquals(response_arr_2["body"]["dataId"].split("/")[1], "1")
40+
self.assertEquals(response_arr_2["body"]
41+
["accessTypes"], self.accessTypes)
42+
dataBreakpoints = [
43+
{
44+
"dataId": response_x["body"]["dataId"],
45+
"accessType": "write"
46+
},
47+
{
48+
"dataId": response_arr_2["body"]["dataId"],
49+
"accessType": "write"
50+
}
51+
]
52+
self.dap_server.request_setDataBreakpoint(dataBreakpoints)
53+
54+
self.dap_server.request_continue()
55+
self.dap_server.wait_for_stopped()
56+
x_val = self.dap_server.get_local_variable_value("x")
57+
i_val = self.dap_server.get_local_variable_value("i")
58+
self.assertEquals(x_val, "2")
59+
self.assertEquals(i_val, "1")
60+
61+
self.dap_server.request_continue()
62+
self.dap_server.wait_for_stopped()
63+
arr_2 = self.dap_server.get_local_variable_child("arr", "[2]")
64+
i_val = self.dap_server.get_local_variable_value("i")
65+
self.assertEquals(arr_2["value"], "'z'")
66+
self.assertEquals(i_val, "2")
67+
self.dap_server.request_setDataBreakpoint([])
68+
69+
# Test hit condition
70+
second_loop_break_line = line_number(
71+
source, "// second loop breakpoint")
72+
breakpoint_ids = self.set_source_breakpoints(
73+
source, [second_loop_break_line])
74+
self.continue_to_breakpoints(breakpoint_ids)
75+
dataBreakpoints = [
76+
{
77+
"dataId": response_x["body"]["dataId"],
78+
"accessType": "write",
79+
"hitCondition": "2"
80+
}
81+
]
82+
self.dap_server.request_setDataBreakpoint(dataBreakpoints)
83+
self.dap_server.request_continue()
84+
self.dap_server.wait_for_stopped()
85+
x_val = self.dap_server.get_local_variable_value("x")
86+
self.assertEquals(x_val, "3")
87+
88+
# Test condition
89+
dataBreakpoints = [
90+
{
91+
"dataId": response_x["body"]["dataId"],
92+
"accessType": "write",
93+
"condition": "x==10"
94+
}
95+
]
96+
self.dap_server.request_setDataBreakpoint(dataBreakpoints)
97+
self.dap_server.request_continue()
98+
self.dap_server.wait_for_stopped()
99+
x_val = self.dap_server.get_local_variable_value("x")
100+
self.assertEquals(x_val, "10")
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
int main(int argc, char const *argv[]) {
2+
// Test for data breakpoint
3+
int x = 0;
4+
char arr[4] = {'a', 'b', 'c', 'd'};
5+
for (int i = 0; i < 5; ++i) { // first loop breakpoint
6+
if (i == 1) {
7+
x = i + 1;
8+
} else if (i == 2) {
9+
arr[i] = 'z';
10+
}
11+
}
12+
13+
x = 1;
14+
for (int i = 0; i < 10; ++i) { // second loop breakpoint
15+
++x;
16+
}
17+
}

lldb/tools/lldb-dap/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ add_lldb_tool(lldb-dap
3737
RunInTerminal.cpp
3838
SourceBreakpoint.cpp
3939
DAP.cpp
40+
Watchpoint.cpp
4041

4142
LINK_LIBS
4243
liblldb

lldb/tools/lldb-dap/DAPForward.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ struct BreakpointBase;
1414
struct ExceptionBreakpoint;
1515
struct FunctionBreakpoint;
1616
struct SourceBreakpoint;
17+
struct Watchpoint;
1718
} // namespace lldb_dap
1819

1920
namespace lldb {
@@ -39,6 +40,7 @@ class SBStringList;
3940
class SBTarget;
4041
class SBThread;
4142
class SBValue;
43+
class SBWatchpoint;
4244
} // namespace lldb
4345

4446
#endif

lldb/tools/lldb-dap/Watchpoint.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//===-- Watchpoint.cpp ------------------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "Watchpoint.h"
10+
#include "DAP.h"
11+
#include "JSONUtils.h"
12+
#include "llvm/ADT/StringExtras.h"
13+
14+
namespace lldb_dap {
15+
Watchpoint::Watchpoint(const llvm::json::Object &obj) : BreakpointBase(obj) {
16+
llvm::StringRef dataId = GetString(obj, "dataId");
17+
std::string accessType = GetString(obj, "accessType").str();
18+
auto [addr_str, size_str] = dataId.split('/');
19+
lldb::addr_t addr;
20+
size_t size;
21+
llvm::to_integer(addr_str, addr, 16);
22+
llvm::to_integer(size_str, size);
23+
lldb::SBWatchpointOptions options;
24+
options.SetWatchpointTypeRead(accessType != "write");
25+
if (accessType != "read")
26+
options.SetWatchpointTypeWrite(lldb::eWatchpointWriteTypeOnModify);
27+
wp = g_dap.target.WatchpointCreateByAddress(addr, size, options, error);
28+
SetCondition();
29+
SetHitCondition();
30+
}
31+
32+
void Watchpoint::SetCondition() { wp.SetCondition(condition.c_str()); }
33+
34+
void Watchpoint::SetHitCondition() {
35+
uint64_t hitCount = 0;
36+
if (llvm::to_integer(hitCondition, hitCount))
37+
wp.SetIgnoreCount(hitCount - 1);
38+
}
39+
40+
void Watchpoint::CreateJsonObject(llvm::json::Object &object) {
41+
if (error.Success()) {
42+
object.try_emplace("verified", true);
43+
} else {
44+
object.try_emplace("verified", false);
45+
EmplaceSafeString(object, "message", error.GetCString());
46+
}
47+
}
48+
} // namespace lldb_dap

lldb/tools/lldb-dap/Watchpoint.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===-- Watchpoint.h --------------------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLDB_TOOLS_LLDB_DAP_WATCHPOINT_H
10+
#define LLDB_TOOLS_LLDB_DAP_WATCHPOINT_H
11+
12+
#include "BreakpointBase.h"
13+
#include "lldb/API/SBError.h"
14+
#include "lldb/API/SBWatchpoint.h"
15+
#include "lldb/API/SBWatchpointOptions.h"
16+
17+
namespace lldb_dap {
18+
19+
struct Watchpoint : public BreakpointBase {
20+
// The LLDB breakpoint associated wit this watchpoint.
21+
lldb::SBWatchpoint wp;
22+
lldb::SBError error;
23+
24+
Watchpoint() = default;
25+
Watchpoint(const llvm::json::Object &obj);
26+
Watchpoint(lldb::SBWatchpoint wp) : wp(wp) {}
27+
28+
void SetCondition() override;
29+
void SetHitCondition() override;
30+
void CreateJsonObject(llvm::json::Object &object) override;
31+
};
32+
} // namespace lldb_dap
33+
34+
#endif

0 commit comments

Comments
 (0)