https://github.com/DrSergei updated https://github.com/llvm/llvm-project/pull/215228
>From 6d1c6459ea0da00ea7073b455959f012b8510061 Mon Sep 17 00:00:00 2001 From: Sergei Druzhkov <[email protected]> Date: Mon, 10 Aug 2026 12:17:03 +0300 Subject: [PATCH 1/3] [lldb-dap] Preserve watchpoints from console --- .../TestDAP_setDataBreakpoints.py | 41 +++++++++++++++++++ lldb/tools/lldb-dap/DAP.h | 1 + .../SetDataBreakpointsRequestHandler.cpp | 8 +++- lldb/tools/lldb-dap/Watchpoint.h | 2 + 4 files changed, 51 insertions(+), 1 deletion(-) diff --git a/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py b/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py index 225a947674351..bad48b0ffbf0f 100644 --- a/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py +++ b/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py @@ -182,6 +182,47 @@ def test_functionality(self): stop_event = session.continue_to_breakpoint(self.expect_not_none(bp_cond.id)) self.assertEqual(session.top_frame_from(stop_event).locals["x"].value, "10") + @skipIfWindows + def test_console_watchpoint_preserved(self): + """Test setDataBreakpoints must not delete watchpoints created via the console.""" + source = "main.cpp" + program = self.getBuildArtifact("a.out") + session = self.build_and_create_session() + first_loop_break_line = line_number(source, "// first loop breakpoint") + with session.configure(LaunchArgs(program)) as ctx: + session.resolve_source_breakpoints(source, [first_loop_break_line]) + stop_event = session.verify_stopped_on_breakpoint(after=ctx.process_event) + top_frame = session.top_frame_from(stop_event) + + # Create a watchpoint via the LLDB console. + resp_body = session.evaluate("`watchpoint set variable x", context="repl") + session.verify_evaluate(resp_body, matches=r".*Watchpoint created.*") + resp_body = session.evaluate("`watchpoint list", context="repl") + session.verify_evaluate(resp_body, matches=r".*Watchpoint 1:.*") + + # Set a data breakpoint via DAP. + arr = top_frame.locals["arr"] + arr_var_ref = self.expect_not_none(arr.variablesReference) + response_arr_2 = session.data_breakpoint_info( + "[2]", arr_var_ref, top_frame.frame.id + ) + arr_2_data_id = self.expect_not_none(response_arr_2.body.dataId) + set_response = session.set_data_breakpoints( + [DataBreakpoint(dataId=arr_2_data_id, accessType="write")] + ) + [bp_arr_2] = set_response.body.breakpoints + self.assertTrue(bp_arr_2.verified) + + resp_body = session.evaluate("`watchpoint list", context="repl") + session.verify_evaluate(resp_body, matches=r".*Watchpoint 1:.*") + + session.set_data_breakpoints([]) + resp_body = session.evaluate("`watchpoint list", context="repl") + session.verify_evaluate(resp_body, matches=r".*Watchpoint 1:.*") + + session.evaluate("`watchpoint delete 1", context="repl") + session.continue_to_exit() + @skipIfWindows def test_bytes(self): """Tests setting data breakpoints on memory range.""" diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index 3e2b1d4782147..c09b149ae3f53 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -107,6 +107,7 @@ struct DAP final : public DAPTransport::MessageHandler { FunctionBreakpointMap function_breakpoints; InstructionBreakpointMap instruction_breakpoints; std::vector<ExceptionBreakpoint> exception_breakpoints; + std::vector<lldb::watch_id_t> data_breakpoints; /// Map step in target id to list of function targets that user can choose. llvm::DenseMap<lldb::addr_t, std::string> step_in_targets; diff --git a/lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp index 1caaa23bf06f6..0540dfcd9d255 100644 --- a/lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp @@ -25,7 +25,10 @@ SetDataBreakpointsRequestHandler::Run( const protocol::SetDataBreakpointsArguments &args) const { std::vector<protocol::Breakpoint> response_breakpoints; - dap.target.DeleteAllWatchpoints(); + for (lldb::watch_id_t watch_id : dap.data_breakpoints) + dap.target.DeleteWatchpoint(watch_id); + dap.data_breakpoints.clear(); + std::vector<Watchpoint> watchpoints; for (const auto &bp : args.breakpoints) watchpoints.emplace_back(dap, bp); @@ -38,6 +41,9 @@ SetDataBreakpointsRequestHandler::Run( if (addresses.count(iter->GetAddress()) == 0) { iter->SetWatchpoint(); addresses.insert(iter->GetAddress()); + if (lldb::watch_id_t watch_id = iter->GetID(); + watch_id != LLDB_INVALID_WATCH_ID) + dap.data_breakpoints.push_back(watch_id); } } for (auto wp : watchpoints) diff --git a/lldb/tools/lldb-dap/Watchpoint.h b/lldb/tools/lldb-dap/Watchpoint.h index d943e1218bdcd..f73b89e674329 100644 --- a/lldb/tools/lldb-dap/Watchpoint.h +++ b/lldb/tools/lldb-dap/Watchpoint.h @@ -34,6 +34,8 @@ class Watchpoint : public BreakpointBase { lldb::addr_t GetAddress() const { return m_addr; } + lldb::watch_id_t GetID() { return m_wp.GetID(); } + protected: lldb::addr_t m_addr; size_t m_size; >From 9f479360cf0993483cc44cbfe6d48621c7c8c16c Mon Sep 17 00:00:00 2001 From: Sergei Druzhkov <[email protected]> Date: Wed, 12 Aug 2026 18:32:13 +0300 Subject: [PATCH 2/3] Fix review comments --- .../lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py b/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py index bad48b0ffbf0f..a3cf2bbb5b360 100644 --- a/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py +++ b/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py @@ -5,7 +5,7 @@ from lldbsuite.test.decorators import skipIfWasm, skipIfWindows from lldbsuite.test.lldbtest import line_number from lldbsuite.test.tools.lldb_dap import DAPTestCaseBase -from lldbsuite.test.tools.lldb_dap.types import DataBreakpoint, LaunchArgs +from lldbsuite.test.tools.lldb_dap.types import DataBreakpoint, LaunchArgs, StoppedReason @skipIfWasm # data breakpoints map to watchpoints. @@ -220,6 +220,10 @@ def test_console_watchpoint_preserved(self): resp_body = session.evaluate("`watchpoint list", context="repl") session.verify_evaluate(resp_body, matches=r".*Watchpoint 1:.*") + # Verify watchpoint from console. + stop_event = session.continue_to_next_stop(exp_reason=StoppedReason.DATA_BREAKPOINT) + self.assertEqual(session.top_frame_from(stop_event).locals["x"].value, "2") + session.evaluate("`watchpoint delete 1", context="repl") session.continue_to_exit() >From 196d22d178ff8fe31146171948ee3975560c87b3 Mon Sep 17 00:00:00 2001 From: Sergei Druzhkov <[email protected]> Date: Wed, 12 Aug 2026 18:48:29 +0300 Subject: [PATCH 3/3] Fix formatting --- .../lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py b/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py index a3cf2bbb5b360..0383daf290a5a 100644 --- a/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py +++ b/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py @@ -221,7 +221,9 @@ def test_console_watchpoint_preserved(self): session.verify_evaluate(resp_body, matches=r".*Watchpoint 1:.*") # Verify watchpoint from console. - stop_event = session.continue_to_next_stop(exp_reason=StoppedReason.DATA_BREAKPOINT) + stop_event = session.continue_to_next_stop( + exp_reason=StoppedReason.DATA_BREAKPOINT + ) self.assertEqual(session.top_frame_from(stop_event).locals["x"].value, "2") session.evaluate("`watchpoint delete 1", context="repl") _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
