https://github.com/DrSergei updated https://github.com/llvm/llvm-project/pull/157530
>From a71a195cb42d59c0ad604419af76a4fcbe5f3b5a Mon Sep 17 00:00:00 2001 From: Druzhkov Sergei <serzhdruz...@gmail.com> Date: Mon, 8 Sep 2025 21:14:21 +0300 Subject: [PATCH 1/2] [lldb-dap] Add invalidated event --- .../test/tools/lldb-dap/dap_server.py | 4 +++ .../test/tools/lldb-dap/lldbdap_testcase.py | 20 +++++++++-- .../tools/lldb-dap/memory/TestDAP_memory.py | 4 +-- .../lldb-dap/variables/TestDAP_variables.py | 34 ++++++------------- lldb/tools/lldb-dap/EventHelper.cpp | 11 ++++++ lldb/tools/lldb-dap/EventHelper.h | 5 +++ .../Handler/SetVariableRequestHandler.cpp | 5 +++ .../Handler/WriteMemoryRequestHandler.cpp | 16 ++++++--- .../lldb-dap/Protocol/ProtocolEvents.cpp | 18 ++++++++++ lldb/tools/lldb-dap/Protocol/ProtocolEvents.h | 9 +++++ lldb/unittests/DAP/ProtocolTypesTest.cpp | 14 ++++++++ 11 files changed, 107 insertions(+), 33 deletions(-) diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py index 66aa070a537e0..8ad9d37dfcb37 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py @@ -215,6 +215,7 @@ def __init__( self.terminated: bool = False self.events: List[Event] = [] self.progress_events: List[Event] = [] + self.invalidated_event: Event = None self.reverse_requests: List[Request] = [] self.module_events: List[Dict] = [] self.sequence: int = 1 @@ -440,6 +441,8 @@ def _handle_event(self, packet: Event) -> None: elif event == "capabilities" and body: # Update the capabilities with new ones from the event. self.capabilities.update(body["capabilities"]) + elif event == "invalidated": + self.invalidated_event = packet def _handle_reverse_request(self, request: Request) -> None: if request in self.reverse_requests: @@ -1014,6 +1017,7 @@ def request_initialize(self, sourceInitFile=False): "supportsVariableType": True, "supportsStartDebuggingRequest": True, "supportsProgressReporting": True, + "supportsInvalidatedEvent": True, "$__lldb_sourceInitFile": sourceInitFile, }, } diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py index fffd4c23d6fcd..a0a009ae6cc9a 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py @@ -241,6 +241,13 @@ def verify_commands(self, flavor: str, output: str, commands: list[str]): f"Command '{flavor}' - '{cmd}' not found in output: {output}", ) + def verify_invalidated_event(self, expected_areas): + event = self.dap_server.invalidated_event + self.dap_server.invalidated_event = None + self.assertIsNotNone(event) + areas = event["body"].get("areas", []) + self.assertEqual(set(expected_areas), set(areas)) + 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 @@ -352,13 +359,20 @@ def get_local_as_int(self, name, threadId=None): else: return int(value) + def set_variable(self, varRef, name, value, id=None): + """Set a variable.""" + response = self.dap_server.request_setVariable(varRef, name, str(value), id=id) + if response["success"]: + self.verify_invalidated_event(["variables"]) + return response + def set_local(self, name, value, id=None): """Set a top level local variable only.""" - return self.dap_server.request_setVariable(1, name, str(value), id=id) + return self.set_variable(1, name, str(value), id=id) def set_global(self, name, value, id=None): """Set a top level global variable only.""" - return self.dap_server.request_setVariable(2, name, str(value), id=id) + return self.set_variable(2, name, str(value), id=id) def stepIn( self, @@ -577,4 +591,6 @@ def writeMemory(self, memoryReference, data=None, offset=0, allowPartial=False): response = self.dap_server.request_writeMemory( memoryReference, encodedData, offset=offset, allowPartial=allowPartial ) + if response["success"]: + self.verify_invalidated_event(["all"]) return response diff --git a/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py b/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py index f51056d7020c6..7c9ad0c0f75ee 100644 --- a/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py +++ b/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py @@ -72,9 +72,7 @@ def test_memory_refs_set_variable(self): ptr_value = self.get_local_as_int("rawptr") self.assertIn( "memoryReference", - self.dap_server.request_setVariable(1, "rawptr", ptr_value + 2)[ - "body" - ].keys(), + self.set_local("rawptr", ptr_value + 2)["body"].keys(), ) @skipIfWindows diff --git a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py index a3a4bdaaf40a6..13a694602f230 100644 --- a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py +++ b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py @@ -298,7 +298,7 @@ def do_test_scopes_variables_setVariable_evaluate( # Set a variable value whose name is synthetic, like a variable index # and verify the value by reading it variable_value = 100 - response = self.dap_server.request_setVariable(varRef, "[0]", variable_value) + response = self.set_variable(varRef, "[0]", variable_value) # Verify dap sent the correct response verify_response = { "type": "int", @@ -315,7 +315,7 @@ def do_test_scopes_variables_setVariable_evaluate( # Set a variable value whose name is a real child value, like "pt.x" # and verify the value by reading it varRef = varref_dict["pt"] - self.dap_server.request_setVariable(varRef, "x", 111) + self.set_variable(varRef, "x", 111) response = self.dap_server.request_variables(varRef, start=0, count=1) value = response["body"]["variables"][0]["value"] self.assertEqual( @@ -341,27 +341,15 @@ def do_test_scopes_variables_setVariable_evaluate( self.verify_variables(verify_locals, self.dap_server.get_local_variables()) # Now we verify that we correctly change the name of a variable with and without differentiator suffix - self.assertFalse(self.dap_server.request_setVariable(1, "x2", 9)["success"]) - self.assertFalse( - self.dap_server.request_setVariable(1, "x @ main.cpp:0", 9)["success"] - ) + self.assertFalse(self.set_local("x2", 9)["success"]) + self.assertFalse(self.set_local("x @ main.cpp:0", 9)["success"]) - self.assertTrue( - self.dap_server.request_setVariable(1, "x @ main.cpp:19", 19)["success"] - ) - self.assertTrue( - self.dap_server.request_setVariable(1, "x @ main.cpp:21", 21)["success"] - ) - self.assertTrue( - self.dap_server.request_setVariable(1, "x @ main.cpp:23", 23)["success"] - ) + self.assertTrue(self.set_local("x @ main.cpp:19", 19)["success"]) + self.assertTrue(self.set_local("x @ main.cpp:21", 21)["success"]) + self.assertTrue(self.set_local("x @ main.cpp:23", 23)["success"]) # The following should have no effect - self.assertFalse( - self.dap_server.request_setVariable(1, "x @ main.cpp:23", "invalid")[ - "success" - ] - ) + self.assertFalse(self.set_local("x @ main.cpp:23", "invalid")["success"]) verify_locals["x @ main.cpp:19"]["equals"]["value"] = "19" verify_locals["x @ main.cpp:21"]["equals"]["value"] = "21" @@ -370,7 +358,7 @@ def do_test_scopes_variables_setVariable_evaluate( self.verify_variables(verify_locals, self.dap_server.get_local_variables()) # The plain x variable shold refer to the innermost x - self.assertTrue(self.dap_server.request_setVariable(1, "x", 22)["success"]) + self.assertTrue(self.set_local("x", 22)["success"]) verify_locals["x @ main.cpp:23"]["equals"]["value"] = "22" self.verify_variables(verify_locals, self.dap_server.get_local_variables()) @@ -708,9 +696,7 @@ def test_return_variables(self): self.verify_variables(verify_locals, local_variables, varref_dict) break - self.assertFalse( - self.dap_server.request_setVariable(1, "(Return Value)", 20)["success"] - ) + self.assertFalse(self.set_local("(Return Value)", 20)["success"]) @skipIfWindows def test_indexedVariables(self): diff --git a/lldb/tools/lldb-dap/EventHelper.cpp b/lldb/tools/lldb-dap/EventHelper.cpp index ecd630cb530d6..927e2df8d681c 100644 --- a/lldb/tools/lldb-dap/EventHelper.cpp +++ b/lldb/tools/lldb-dap/EventHelper.cpp @@ -12,9 +12,11 @@ #include "JSONUtils.h" #include "LLDBUtils.h" #include "Protocol/ProtocolEvents.h" +#include "Protocol/ProtocolRequests.h" #include "Protocol/ProtocolTypes.h" #include "lldb/API/SBFileSpec.h" #include "llvm/Support/Error.h" +#include <utility> #if defined(_WIN32) #define NOMINMAX @@ -273,4 +275,13 @@ void SendProcessExitedEvent(DAP &dap, lldb::SBProcess &process) { dap.SendJSON(llvm::json::Value(std::move(event))); } +void SendInvalidatedEvent( + DAP &dap, std::vector<protocol::InvalidatedEventBody::Area> &&areas) { + if (!dap.clientFeatures.contains(protocol::eClientFeatureInvalidatedEvent)) + return; + protocol::InvalidatedEventBody body; + body.areas = std::move(areas); + dap.Send(protocol::Event{"invalidated", std::move(body)}); +} + } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/EventHelper.h b/lldb/tools/lldb-dap/EventHelper.h index 592c1b81c46af..24c4b6d3026fd 100644 --- a/lldb/tools/lldb-dap/EventHelper.h +++ b/lldb/tools/lldb-dap/EventHelper.h @@ -10,7 +10,9 @@ #define LLDB_TOOLS_LLDB_DAP_EVENTHELPER_H #include "DAPForward.h" +#include "Protocol/ProtocolEvents.h" #include "llvm/Support/Error.h" +#include <vector> namespace lldb_dap { struct DAP; @@ -32,6 +34,9 @@ void SendContinuedEvent(DAP &dap); void SendProcessExitedEvent(DAP &dap, lldb::SBProcess &process); +void SendInvalidatedEvent( + DAP &dap, std::vector<protocol::InvalidatedEventBody::Area> &&areas); + } // namespace lldb_dap #endif diff --git a/lldb/tools/lldb-dap/Handler/SetVariableRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetVariableRequestHandler.cpp index d07c0d6c9afa8..2a50dea0b4ada 100644 --- a/lldb/tools/lldb-dap/Handler/SetVariableRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/SetVariableRequestHandler.cpp @@ -9,6 +9,7 @@ #include "DAP.h" #include "EventHelper.h" #include "JSONUtils.h" +#include "Protocol/ProtocolEvents.h" #include "RequestHandler.h" using namespace lldb_dap::protocol; @@ -77,6 +78,10 @@ SetVariableRequestHandler::Run(const SetVariableArguments &args) const { if (ValuePointsToCode(variable)) body.valueLocationReference = new_var_ref; + // Also send invalidated event to signal client that some variables + // (e.g. references) can be changed. + SendInvalidatedEvent(dap, {InvalidatedEventBody::eAreaVariables}); + return body; } diff --git a/lldb/tools/lldb-dap/Handler/WriteMemoryRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/WriteMemoryRequestHandler.cpp index 313f59dceab24..3e34e488d1158 100644 --- a/lldb/tools/lldb-dap/Handler/WriteMemoryRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/WriteMemoryRequestHandler.cpp @@ -7,21 +7,24 @@ //===----------------------------------------------------------------------===// #include "DAP.h" +#include "EventHelper.h" #include "JSONUtils.h" +#include "Protocol/ProtocolEvents.h" #include "RequestHandler.h" #include "lldb/API/SBMemoryRegionInfo.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Base64.h" +using namespace lldb_dap::protocol; + namespace lldb_dap { // Writes bytes to memory at the provided location. // // Clients should only call this request if the corresponding capability // supportsWriteMemoryRequest is true. -llvm::Expected<protocol::WriteMemoryResponseBody> -WriteMemoryRequestHandler::Run( - const protocol::WriteMemoryArguments &args) const { +llvm::Expected<WriteMemoryResponseBody> +WriteMemoryRequestHandler::Run(const WriteMemoryArguments &args) const { const lldb::addr_t address = args.memoryReference + args.offset; lldb::SBProcess process = dap.target.GetProcess(); @@ -91,8 +94,13 @@ WriteMemoryRequestHandler::Run( if (bytes_written == 0) { return llvm::make_error<DAPError>(write_error.GetCString()); } - protocol::WriteMemoryResponseBody response; + WriteMemoryResponseBody response; response.bytesWritten = bytes_written; + + // Also send invalidated event to signal client that some things + // (e.g. variables) can be changed. + SendInvalidatedEvent(dap, {InvalidatedEventBody::eAreaAll}); + return response; } diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolEvents.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolEvents.cpp index 4faf65567c3ea..62760c9f38573 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolEvents.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolEvents.cpp @@ -33,4 +33,22 @@ json::Value toJSON(const ModuleEventBody &MEB) { return json::Object{{"reason", MEB.reason}, {"module", MEB.module}}; } +llvm::json::Value toJSON(const InvalidatedEventBody::Area &IEBA) { + switch (IEBA) { + case InvalidatedEventBody::eAreaAll: + return "all"; + case InvalidatedEventBody::eAreaStacks: + return "stacks"; + case InvalidatedEventBody::eAreaThreads: + return "threads"; + case InvalidatedEventBody::eAreaVariables: + return "variables"; + } + llvm_unreachable("unhandled invalidated event area!."); +} + +llvm::json::Value toJSON(const InvalidatedEventBody &IEB) { + return json::Object{{"areas", IEB.areas}}; +} + } // namespace lldb_dap::protocol diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolEvents.h b/lldb/tools/lldb-dap/Protocol/ProtocolEvents.h index ee9e03c499eae..a2d4704341e94 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolEvents.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolEvents.h @@ -22,6 +22,7 @@ #include "Protocol/ProtocolTypes.h" #include "llvm/Support/JSON.h" +#include <vector> namespace lldb_dap::protocol { @@ -56,6 +57,14 @@ struct ModuleEventBody { llvm::json::Value toJSON(const ModuleEventBody::Reason &); llvm::json::Value toJSON(const ModuleEventBody &); +struct InvalidatedEventBody { + enum Area : unsigned { eAreaAll, eAreaStacks, eAreaThreads, eAreaVariables }; + + std::vector<Area> areas; +}; +llvm::json::Value toJSON(const InvalidatedEventBody::Area &); +llvm::json::Value toJSON(const InvalidatedEventBody &); + } // end namespace lldb_dap::protocol #endif diff --git a/lldb/unittests/DAP/ProtocolTypesTest.cpp b/lldb/unittests/DAP/ProtocolTypesTest.cpp index c5d47fcb08da4..4d2956d4892ab 100644 --- a/lldb/unittests/DAP/ProtocolTypesTest.cpp +++ b/lldb/unittests/DAP/ProtocolTypesTest.cpp @@ -1073,3 +1073,17 @@ TEST(ProtocolTypesTest, CompletionsResponseBody) { ASSERT_THAT_EXPECTED(expected, llvm::Succeeded()); EXPECT_EQ(pp(*expected), pp(response)); } + +TEST(ProtocolTypesTest, InvalidatedEventBody) { + InvalidatedEventBody body; + body.areas = {InvalidatedEventBody::eAreaStacks, + InvalidatedEventBody::eAreaThreads}; + StringRef json = R"({ + "areas": [ + "stacks", + "threads" + ] +})"; + // Validate toJSON + EXPECT_EQ(json, pp(body)); +} >From 686d127f377cba3a3b80db530637e98ae27893be Mon Sep 17 00:00:00 2001 From: Druzhkov Sergei <serzhdruz...@gmail.com> Date: Tue, 9 Sep 2025 18:19:41 +0300 Subject: [PATCH 2/2] Add ArrayRef --- lldb/tools/lldb-dap/EventHelper.cpp | 4 ++-- lldb/tools/lldb-dap/EventHelper.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lldb/tools/lldb-dap/EventHelper.cpp b/lldb/tools/lldb-dap/EventHelper.cpp index 927e2df8d681c..6eb468e76b16c 100644 --- a/lldb/tools/lldb-dap/EventHelper.cpp +++ b/lldb/tools/lldb-dap/EventHelper.cpp @@ -276,11 +276,11 @@ void SendProcessExitedEvent(DAP &dap, lldb::SBProcess &process) { } void SendInvalidatedEvent( - DAP &dap, std::vector<protocol::InvalidatedEventBody::Area> &&areas) { + DAP &dap, llvm::ArrayRef<protocol::InvalidatedEventBody::Area> areas) { if (!dap.clientFeatures.contains(protocol::eClientFeatureInvalidatedEvent)) return; protocol::InvalidatedEventBody body; - body.areas = std::move(areas); + body.areas = areas; dap.Send(protocol::Event{"invalidated", std::move(body)}); } diff --git a/lldb/tools/lldb-dap/EventHelper.h b/lldb/tools/lldb-dap/EventHelper.h index 24c4b6d3026fd..0c57afbaf1f33 100644 --- a/lldb/tools/lldb-dap/EventHelper.h +++ b/lldb/tools/lldb-dap/EventHelper.h @@ -11,8 +11,8 @@ #include "DAPForward.h" #include "Protocol/ProtocolEvents.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/Support/Error.h" -#include <vector> namespace lldb_dap { struct DAP; @@ -35,7 +35,7 @@ void SendContinuedEvent(DAP &dap); void SendProcessExitedEvent(DAP &dap, lldb::SBProcess &process); void SendInvalidatedEvent( - DAP &dap, std::vector<protocol::InvalidatedEventBody::Area> &&areas); + DAP &dap, llvm::ArrayRef<protocol::InvalidatedEventBody::Area> areas); } // namespace lldb_dap _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits