Skip to content

Commit c5c11f3

Browse files
authored
[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.
1 parent ec24e23 commit c5c11f3

File tree

8 files changed

+177
-1
lines changed

8 files changed

+177
-1
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1267,7 +1267,7 @@ def run_vscode(dbg, args, options):
12671267
def main():
12681268
parser = optparse.OptionParser(
12691269
description=(
1270-
"A testing framework for the Visual Studio Code Debug " "Adaptor protocol"
1270+
"A testing framework for the Visual Studio Code Debug Adaptor protocol"
12711271
)
12721272
)
12731273

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
C_SOURCES := main.c
2+
3+
include Makefile.rules
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"""
2+
Test lldb-dap send-event integration.
3+
"""
4+
5+
import json
6+
7+
from lldbsuite.test.decorators import *
8+
from lldbsuite.test.lldbtest import *
9+
import lldbdap_testcase
10+
11+
12+
class TestDAP_sendEvent(lldbdap_testcase.DAPTestCaseBase):
13+
def test_send_event(self):
14+
"""
15+
Test sending a custom event.
16+
"""
17+
program = self.getBuildArtifact("a.out")
18+
source = "main.c"
19+
custom_event_body = {
20+
"key": 321,
21+
"arr": [True],
22+
}
23+
self.build_and_launch(
24+
program,
25+
stopCommands=[
26+
"lldb-dap send-event my-custom-event-no-body",
27+
"lldb-dap send-event my-custom-event '{}'".format(
28+
json.dumps(custom_event_body)
29+
),
30+
],
31+
)
32+
33+
breakpoint_line = line_number(source, "// breakpoint")
34+
35+
self.set_source_breakpoints(source, [breakpoint_line])
36+
self.continue_to_next_stop()
37+
38+
custom_event = self.dap_server.wait_for_event(
39+
filter=["my-custom-event-no-body"]
40+
)
41+
self.assertEquals(custom_event["event"], "my-custom-event-no-body")
42+
self.assertIsNone(custom_event.get("body", None))
43+
44+
custom_event = self.dap_server.wait_for_event(filter=["my-custom-event"])
45+
self.assertEquals(custom_event["event"], "my-custom-event")
46+
self.assertEquals(custom_event["body"], custom_event_body)
47+
48+
def test_send_internal_event(self):
49+
"""
50+
Test sending an internal event produces an error.
51+
"""
52+
program = self.getBuildArtifact("a.out")
53+
source = "main.c"
54+
self.build_and_launch(program)
55+
56+
breakpoint_line = line_number(source, "// breakpoint")
57+
58+
self.set_source_breakpoints(source, [breakpoint_line])
59+
self.continue_to_next_stop()
60+
61+
resp = self.dap_server.request_evaluate(
62+
"`lldb-dap send-event stopped", context="repl"
63+
)
64+
self.assertRegex(
65+
resp["body"]["result"],
66+
r"Invalid use of lldb-dap send-event, event \"stopped\" should be handled by lldb-dap internally.",
67+
)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include <stdio.h>
2+
3+
int main(int argc, char const *argv[]) {
4+
printf("example\n"); // breakpoint 1
5+
return 0;
6+
}

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,68 @@ bool ReplModeRequestHandler::DoExecute(lldb::SBDebugger debugger,
962962
return true;
963963
}
964964

965+
// Sends a DAP event with an optional body.
966+
//
967+
// See
968+
// https://code.visualstudio.com/api/references/vscode-api#debug.onDidReceiveDebugSessionCustomEvent
969+
bool SendEventRequestHandler::DoExecute(lldb::SBDebugger debugger,
970+
char **command,
971+
lldb::SBCommandReturnObject &result) {
972+
// Command format like: `send-event <name> <body>?`
973+
if (!command || !command[0] || llvm::StringRef(command[0]).empty()) {
974+
result.SetError("Not enough arguments found, expected format "
975+
"`lldb-dap send-event <name> <body>?`.");
976+
return false;
977+
}
978+
979+
llvm::StringRef name{command[0]};
980+
// Events that are stateful and should be handled by lldb-dap internally.
981+
const std::array internal_events{"breakpoint", "capabilities", "continued",
982+
"exited", "initialize", "loadedSource",
983+
"module", "process", "stopped",
984+
"terminated", "thread"};
985+
if (std::find(internal_events.begin(), internal_events.end(), name) !=
986+
std::end(internal_events)) {
987+
std::string msg =
988+
llvm::formatv("Invalid use of lldb-dap send-event, event \"{0}\" "
989+
"should be handled by lldb-dap internally.",
990+
name)
991+
.str();
992+
result.SetError(msg.c_str());
993+
return false;
994+
}
995+
996+
llvm::json::Object event(CreateEventObject(name));
997+
998+
if (command[1] && !llvm::StringRef(command[1]).empty()) {
999+
// See if we have unused arguments.
1000+
if (command[2]) {
1001+
result.SetError(
1002+
"Additional arguments found, expected `lldb-dap send-event "
1003+
"<name> <body>?`.");
1004+
return false;
1005+
}
1006+
1007+
llvm::StringRef raw_body{command[1]};
1008+
1009+
llvm::Expected<llvm::json::Value> body = llvm::json::parse(raw_body);
1010+
1011+
if (!body) {
1012+
llvm::Error err = body.takeError();
1013+
std::string msg = "Failed to parse custom event body: " +
1014+
llvm::toString(std::move(err));
1015+
result.SetError(msg.c_str());
1016+
return false;
1017+
}
1018+
1019+
event.try_emplace("body", std::move(*body));
1020+
}
1021+
1022+
g_dap.SendJSON(llvm::json::Value(std::move(event)));
1023+
result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
1024+
return true;
1025+
}
1026+
9651027
void DAP::SetFrameFormat(llvm::StringRef format) {
9661028
if (format.empty())
9671029
return;

lldb/tools/lldb-dap/DAP.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,11 @@ struct ReplModeRequestHandler : public lldb::SBCommandPluginInterface {
144144
lldb::SBCommandReturnObject &result) override;
145145
};
146146

147+
struct SendEventRequestHandler : public lldb::SBCommandPluginInterface {
148+
bool DoExecute(lldb::SBDebugger debugger, char **command,
149+
lldb::SBCommandReturnObject &result) override;
150+
};
151+
147152
struct DAP {
148153
std::string debug_adaptor_path;
149154
InputStream input;

lldb/tools/lldb-dap/README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,37 @@ The initial repl-mode can be configured with the cli flag `--repl-mode=<mode>`
290290
and may also be adjusted at runtime using the lldb command
291291
`lldb-dap repl-mode <mode>`.
292292

293+
#### `lldb-dap send-event`
294+
295+
lldb-dap includes a command to trigger a Debug Adapter Protocol event
296+
from a script.
297+
298+
The event maybe a custom DAP event or a standard event, if the event is not
299+
handled internally by `lldb-dap`.
300+
301+
This command has the format:
302+
303+
```
304+
lldb-dap send-event <name> <body>?
305+
```
306+
307+
For example you can use a launch configuration hook to trigger custom events like:
308+
309+
```json
310+
{
311+
"program": "exe",
312+
"stopCommands": [
313+
"lldb-dap send-event MyStopEvent",
314+
"lldb-dap send-event MyStopEvent '{\"key\": 321}",
315+
]
316+
}
317+
```
318+
319+
[See the specification](https://microsoft.github.io/debug-adapter-protocol/specification#Base_Protocol_Event)
320+
for more details on Debug Adapter Protocol events and the VS Code
321+
[debug.onDidReceiveDebugSessionCustomEvent](https://code.visualstudio.com/api/references/vscode-api#debug.onDidReceiveDebugSessionCustomEvent)
322+
API for handling a custom event from an extension.
323+
293324
## Contributing
294325

295326
`lldb-dap` and `lldb` are developed under the umbrella of the [LLVM project](https://llvm.org/).

lldb/tools/lldb-dap/lldb-dap.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1896,6 +1896,8 @@ void request_initialize(const llvm::json::Object &request) {
18961896
cmd.AddCommand(
18971897
"repl-mode", new ReplModeRequestHandler(),
18981898
"Get or set the repl behavior of lldb-dap evaluation requests.");
1899+
cmd.AddCommand("send-event", new SendEventRequestHandler(),
1900+
"Sends an DAP event to the client.");
18991901

19001902
g_dap.progress_event_thread = std::thread(ProgressEventThreadFunction);
19011903

0 commit comments

Comments
 (0)