[Lldb-commits] [lldb] c5c11f3 - [lldb-dap] Creating an API for sending dap events from a script in lldb-dap. (#112384)
via lldb-commits
lldb-commits at lists.llvm.org
Wed Oct 16 17:19:55 PDT 2024
Author: John Harrison
Date: 2024-10-16T17:19:51-07:00
New Revision: c5c11f340436a88cfc2165f2dcd64e4d63285068
URL: https://github.com/llvm/llvm-project/commit/c5c11f340436a88cfc2165f2dcd64e4d63285068
DIFF: https://github.com/llvm/llvm-project/commit/c5c11f340436a88cfc2165f2dcd64e4d63285068.diff
LOG: [lldb-dap] Creating an API for sending dap events from a script in lldb-dap. (#112384)
Custom DAP events can be detected using
https://code.visualstudio.com/api/references/vscode-api#debug.onDidReceiveDebugSessionCustomEvent.
This API allows an lldb python script to send events to the DAP
client to allow extensions to handle these custom events.
Added:
lldb/test/API/tools/lldb-dap/send-event/Makefile
lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py
lldb/test/API/tools/lldb-dap/send-event/main.c
Modified:
lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
lldb/tools/lldb-dap/DAP.cpp
lldb/tools/lldb-dap/DAP.h
lldb/tools/lldb-dap/README.md
lldb/tools/lldb-dap/lldb-dap.cpp
Removed:
################################################################################
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 1d5e6e0d75c7cb..63748a71f1122d 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
@@ -1267,7 +1267,7 @@ def run_vscode(dbg, args, options):
def main():
parser = optparse.OptionParser(
description=(
- "A testing framework for the Visual Studio Code Debug " "Adaptor protocol"
+ "A testing framework for the Visual Studio Code Debug Adaptor protocol"
)
)
diff --git a/lldb/test/API/tools/lldb-dap/send-event/Makefile b/lldb/test/API/tools/lldb-dap/send-event/Makefile
new file mode 100644
index 00000000000000..10495940055b63
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/send-event/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py b/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py
new file mode 100644
index 00000000000000..de47651bb2fa1b
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py
@@ -0,0 +1,67 @@
+"""
+Test lldb-dap send-event integration.
+"""
+
+import json
+
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+import lldbdap_testcase
+
+
+class TestDAP_sendEvent(lldbdap_testcase.DAPTestCaseBase):
+ def test_send_event(self):
+ """
+ Test sending a custom event.
+ """
+ program = self.getBuildArtifact("a.out")
+ source = "main.c"
+ custom_event_body = {
+ "key": 321,
+ "arr": [True],
+ }
+ self.build_and_launch(
+ program,
+ stopCommands=[
+ "lldb-dap send-event my-custom-event-no-body",
+ "lldb-dap send-event my-custom-event '{}'".format(
+ json.dumps(custom_event_body)
+ ),
+ ],
+ )
+
+ breakpoint_line = line_number(source, "// breakpoint")
+
+ self.set_source_breakpoints(source, [breakpoint_line])
+ self.continue_to_next_stop()
+
+ custom_event = self.dap_server.wait_for_event(
+ filter=["my-custom-event-no-body"]
+ )
+ self.assertEquals(custom_event["event"], "my-custom-event-no-body")
+ self.assertIsNone(custom_event.get("body", None))
+
+ custom_event = self.dap_server.wait_for_event(filter=["my-custom-event"])
+ self.assertEquals(custom_event["event"], "my-custom-event")
+ self.assertEquals(custom_event["body"], custom_event_body)
+
+ def test_send_internal_event(self):
+ """
+ Test sending an internal event produces an error.
+ """
+ program = self.getBuildArtifact("a.out")
+ source = "main.c"
+ self.build_and_launch(program)
+
+ breakpoint_line = line_number(source, "// breakpoint")
+
+ self.set_source_breakpoints(source, [breakpoint_line])
+ self.continue_to_next_stop()
+
+ resp = self.dap_server.request_evaluate(
+ "`lldb-dap send-event stopped", context="repl"
+ )
+ self.assertRegex(
+ resp["body"]["result"],
+ r"Invalid use of lldb-dap send-event, event \"stopped\" should be handled by lldb-dap internally.",
+ )
diff --git a/lldb/test/API/tools/lldb-dap/send-event/main.c b/lldb/test/API/tools/lldb-dap/send-event/main.c
new file mode 100644
index 00000000000000..27bc22b94794b6
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/send-event/main.c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+int main(int argc, char const *argv[]) {
+ printf("example\n"); // breakpoint 1
+ return 0;
+}
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 119779d7bfecba..68559e382006db 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -962,6 +962,68 @@ bool ReplModeRequestHandler::DoExecute(lldb::SBDebugger debugger,
return true;
}
+// Sends a DAP event with an optional body.
+//
+// See
+// https://code.visualstudio.com/api/references/vscode-api#debug.onDidReceiveDebugSessionCustomEvent
+bool SendEventRequestHandler::DoExecute(lldb::SBDebugger debugger,
+ char **command,
+ lldb::SBCommandReturnObject &result) {
+ // Command format like: `send-event <name> <body>?`
+ if (!command || !command[0] || llvm::StringRef(command[0]).empty()) {
+ result.SetError("Not enough arguments found, expected format "
+ "`lldb-dap send-event <name> <body>?`.");
+ return false;
+ }
+
+ llvm::StringRef name{command[0]};
+ // Events that are stateful and should be handled by lldb-dap internally.
+ const std::array internal_events{"breakpoint", "capabilities", "continued",
+ "exited", "initialize", "loadedSource",
+ "module", "process", "stopped",
+ "terminated", "thread"};
+ if (std::find(internal_events.begin(), internal_events.end(), name) !=
+ std::end(internal_events)) {
+ std::string msg =
+ llvm::formatv("Invalid use of lldb-dap send-event, event \"{0}\" "
+ "should be handled by lldb-dap internally.",
+ name)
+ .str();
+ result.SetError(msg.c_str());
+ return false;
+ }
+
+ llvm::json::Object event(CreateEventObject(name));
+
+ if (command[1] && !llvm::StringRef(command[1]).empty()) {
+ // See if we have unused arguments.
+ if (command[2]) {
+ result.SetError(
+ "Additional arguments found, expected `lldb-dap send-event "
+ "<name> <body>?`.");
+ return false;
+ }
+
+ llvm::StringRef raw_body{command[1]};
+
+ llvm::Expected<llvm::json::Value> body = llvm::json::parse(raw_body);
+
+ if (!body) {
+ llvm::Error err = body.takeError();
+ std::string msg = "Failed to parse custom event body: " +
+ llvm::toString(std::move(err));
+ result.SetError(msg.c_str());
+ return false;
+ }
+
+ event.try_emplace("body", std::move(*body));
+ }
+
+ g_dap.SendJSON(llvm::json::Value(std::move(event)));
+ result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
+ return true;
+}
+
void DAP::SetFrameFormat(llvm::StringRef format) {
if (format.empty())
return;
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index ba6d3d80410e3d..acc10ade75fd14 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -144,6 +144,11 @@ struct ReplModeRequestHandler : public lldb::SBCommandPluginInterface {
lldb::SBCommandReturnObject &result) override;
};
+struct SendEventRequestHandler : public lldb::SBCommandPluginInterface {
+ bool DoExecute(lldb::SBDebugger debugger, char **command,
+ lldb::SBCommandReturnObject &result) override;
+};
+
struct DAP {
std::string debug_adaptor_path;
InputStream input;
diff --git a/lldb/tools/lldb-dap/README.md b/lldb/tools/lldb-dap/README.md
index 11086eb222d2eb..42b5f501e32c65 100644
--- a/lldb/tools/lldb-dap/README.md
+++ b/lldb/tools/lldb-dap/README.md
@@ -290,6 +290,37 @@ The initial repl-mode can be configured with the cli flag `--repl-mode=<mode>`
and may also be adjusted at runtime using the lldb command
`lldb-dap repl-mode <mode>`.
+#### `lldb-dap send-event`
+
+lldb-dap includes a command to trigger a Debug Adapter Protocol event
+from a script.
+
+The event maybe a custom DAP event or a standard event, if the event is not
+handled internally by `lldb-dap`.
+
+This command has the format:
+
+```
+lldb-dap send-event <name> <body>?
+```
+
+For example you can use a launch configuration hook to trigger custom events like:
+
+```json
+{
+ "program": "exe",
+ "stopCommands": [
+ "lldb-dap send-event MyStopEvent",
+ "lldb-dap send-event MyStopEvent '{\"key\": 321}",
+ ]
+}
+```
+
+[See the specification](https://microsoft.github.io/debug-adapter-protocol/specification#Base_Protocol_Event)
+for more details on Debug Adapter Protocol events and the VS Code
+[debug.onDidReceiveDebugSessionCustomEvent](https://code.visualstudio.com/api/references/vscode-api#debug.onDidReceiveDebugSessionCustomEvent)
+API for handling a custom event from an extension.
+
## Contributing
`lldb-dap` and `lldb` are developed under the umbrella of the [LLVM project](https://llvm.org/).
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 5e351ab11ab6ca..f70b0d3d4cbee0 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -1896,6 +1896,8 @@ void request_initialize(const llvm::json::Object &request) {
cmd.AddCommand(
"repl-mode", new ReplModeRequestHandler(),
"Get or set the repl behavior of lldb-dap evaluation requests.");
+ cmd.AddCommand("send-event", new SendEventRequestHandler(),
+ "Sends an DAP event to the client.");
g_dap.progress_event_thread = std::thread(ProgressEventThreadFunction);
More information about the lldb-commits
mailing list