Skip to content

Commit ad865d9

Browse files
committed
[lldb-vscode] Allow an empty 'breakpoints' field to clear breakpoints.
Per the DAP spec for SetBreakpoints [1], the way to clear breakpoints is: `To clear all breakpoint for a source, specify an empty array.` However, leaving the breakpoints field unset is also a well formed request (note the `breakpoints?:` in the `SetBreakpointsArguments` definition). If it's unset, we have a couple choices: 1. Crash (current behavior) 2. Clear breakpoints 3. Return an error response that the breakpoints field is missing. I propose we do (2) instead of (1), and treat an unset breakpoints field the same as an empty breakpoints field. [1] https://microsoft.github.io/debug-adapter-protocol/specification#Requests_SetBreakpoints Reviewed By: wallace, labath Differential Revision: https://reviews.llvm.org/D88513
1 parent 655af65 commit ad865d9

File tree

3 files changed

+78
-29
lines changed

3 files changed

+78
-29
lines changed

lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -728,24 +728,26 @@ def request_scopes(self, frameId):
728728
def request_setBreakpoints(self, file_path, line_array, condition=None,
729729
hitCondition=None):
730730
(dir, base) = os.path.split(file_path)
731-
breakpoints = []
732-
for line in line_array:
733-
bp = {'line': line}
734-
if condition is not None:
735-
bp['condition'] = condition
736-
if hitCondition is not None:
737-
bp['hitCondition'] = hitCondition
738-
breakpoints.append(bp)
739731
source_dict = {
740732
'name': base,
741733
'path': file_path
742734
}
743735
args_dict = {
744736
'source': source_dict,
745-
'breakpoints': breakpoints,
746-
'lines': '%s' % (line_array),
747737
'sourceModified': False,
748738
}
739+
if line_array is not None:
740+
args_dict['lines'] = '%s' % line_array
741+
breakpoints = []
742+
for line in line_array:
743+
bp = {'line': line}
744+
if condition is not None:
745+
bp['condition'] = condition
746+
if hitCondition is not None:
747+
bp['hitCondition'] = hitCondition
748+
breakpoints.append(bp)
749+
args_dict['breakpoints'] = breakpoints
750+
749751
command_dict = {
750752
'command': 'setBreakpoints',
751753
'type': 'request',

lldb/test/API/tools/lldb-vscode/breakpoint/TestVSCode_setBreakpoints.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,48 @@ def test_set_and_clear(self):
219219
self.assertTrue(breakpoint['verified'],
220220
"expect breakpoint still verified")
221221

222+
@skipIfWindows
223+
@skipIfRemote
224+
def test_clear_breakpoints_unset_breakpoints(self):
225+
'''Test clearing breakpoints like test_set_and_clear, but clear
226+
breakpoints by omitting the breakpoints array instead of sending an
227+
empty one.'''
228+
lines = [line_number('main.cpp', 'break 12'),
229+
line_number('main.cpp', 'break 13')]
230+
231+
# Visual Studio Code Debug Adaptors have no way to specify the file
232+
# without launching or attaching to a process, so we must start a
233+
# process in order to be able to set breakpoints.
234+
program = self.getBuildArtifact("a.out")
235+
self.build_and_launch(program)
236+
237+
# Set one breakpoint and verify that it got set correctly.
238+
response = self.vscode.request_setBreakpoints(self.main_path, lines)
239+
line_to_id = {}
240+
breakpoints = response['body']['breakpoints']
241+
self.assertEquals(len(breakpoints), len(lines),
242+
"expect %u source breakpoints" % (len(lines)))
243+
for (breakpoint, index) in zip(breakpoints, range(len(lines))):
244+
line = breakpoint['line']
245+
self.assertTrue(line, lines[index])
246+
# Store the "id" of the breakpoint that was set for later
247+
line_to_id[line] = breakpoint['id']
248+
self.assertTrue(line in lines, "line expected in lines array")
249+
self.assertTrue(breakpoint['verified'],
250+
"expect breakpoint verified")
251+
252+
# Now clear all breakpoints for the source file by not setting the
253+
# lines array.
254+
lines = None
255+
response = self.vscode.request_setBreakpoints(self.main_path, lines)
256+
breakpoints = response['body']['breakpoints']
257+
self.assertEquals(len(breakpoints), 0, "expect no source breakpoints")
258+
259+
# Verify with the target that all breakpoints have been cleared.
260+
response = self.vscode.request_testGetTargetBreakpoints()
261+
breakpoints = response['body']['breakpoints']
262+
self.assertEquals(len(breakpoints), 0, "expect no source breakpoints")
263+
222264
@skipIfWindows
223265
@skipIfRemote
224266
def test_functionality(self):

lldb/tools/lldb-vscode/lldb-vscode.cpp

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1936,27 +1936,32 @@ void request_setBreakpoints(const llvm::json::Object &request) {
19361936

19371937
// Decode the source breakpoint infos for this "setBreakpoints" request
19381938
SourceBreakpointMap request_bps;
1939-
for (const auto &bp : *breakpoints) {
1940-
auto bp_obj = bp.getAsObject();
1941-
if (bp_obj) {
1942-
SourceBreakpoint src_bp(*bp_obj);
1943-
request_bps[src_bp.line] = src_bp;
1944-
1945-
// We check if this breakpoint already exists to update it
1946-
auto existing_source_bps = g_vsc.source_breakpoints.find(path);
1947-
if (existing_source_bps != g_vsc.source_breakpoints.end()) {
1948-
const auto &existing_bp = existing_source_bps->second.find(src_bp.line);
1949-
if (existing_bp != existing_source_bps->second.end()) {
1950-
existing_bp->second.UpdateBreakpoint(src_bp);
1951-
AppendBreakpoint(existing_bp->second.bp, response_breakpoints, path,
1952-
src_bp.line);
1953-
continue;
1939+
// "breakpoints" may be unset, in which case we treat it the same as being set
1940+
// to an empty array.
1941+
if (breakpoints) {
1942+
for (const auto &bp : *breakpoints) {
1943+
auto bp_obj = bp.getAsObject();
1944+
if (bp_obj) {
1945+
SourceBreakpoint src_bp(*bp_obj);
1946+
request_bps[src_bp.line] = src_bp;
1947+
1948+
// We check if this breakpoint already exists to update it
1949+
auto existing_source_bps = g_vsc.source_breakpoints.find(path);
1950+
if (existing_source_bps != g_vsc.source_breakpoints.end()) {
1951+
const auto &existing_bp =
1952+
existing_source_bps->second.find(src_bp.line);
1953+
if (existing_bp != existing_source_bps->second.end()) {
1954+
existing_bp->second.UpdateBreakpoint(src_bp);
1955+
AppendBreakpoint(existing_bp->second.bp, response_breakpoints, path,
1956+
src_bp.line);
1957+
continue;
1958+
}
19541959
}
1960+
// At this point the breakpoint is new
1961+
src_bp.SetBreakpoint(path.data());
1962+
AppendBreakpoint(src_bp.bp, response_breakpoints, path, src_bp.line);
1963+
g_vsc.source_breakpoints[path][src_bp.line] = std::move(src_bp);
19551964
}
1956-
// At this point the breakpoint is new
1957-
src_bp.SetBreakpoint(path.data());
1958-
AppendBreakpoint(src_bp.bp, response_breakpoints, path, src_bp.line);
1959-
g_vsc.source_breakpoints[path][src_bp.line] = std::move(src_bp);
19601965
}
19611966
}
19621967

0 commit comments

Comments
 (0)