Skip to content

[lldb-dap] Creating an API for sending custom dap events from lldb-dap. #112384

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Oct 17, 2024

Conversation

ashgti
Copy link
Contributor

@ashgti ashgti commented Oct 15, 2024

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 custom events to the DAP client to allow extensions to handle these custom events.

@llvmbot
Copy link
Member

llvmbot commented Oct 15, 2024

@llvm/pr-subscribers-lldb

Author: John Harrison (ashgti)

Changes

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 custom events to the DAP client to allow extensions to handle these custom events.


Full diff: https://github.com/llvm/llvm-project/pull/112384.diff

8 Files Affected:

  • (modified) lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py (+1-1)
  • (added) lldb/test/API/tools/lldb-dap/custom-event/Makefile (+3)
  • (added) lldb/test/API/tools/lldb-dap/custom-event/TestDAP_customEvent.py (+46)
  • (added) lldb/test/API/tools/lldb-dap/custom-event/main.c (+6)
  • (modified) lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py (+2-4)
  • (modified) lldb/tools/lldb-dap/DAP.cpp (+46-15)
  • (modified) lldb/tools/lldb-dap/DAP.h (+5)
  • (modified) lldb/tools/lldb-dap/lldb-dap.cpp (+3-1)
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 b095171d8fd1a4..a43dd1aa7b9496 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
@@ -1230,7 +1230,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/custom-event/Makefile b/lldb/test/API/tools/lldb-dap/custom-event/Makefile
new file mode 100644
index 00000000000000..10495940055b63
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/custom-event/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/tools/lldb-dap/custom-event/TestDAP_customEvent.py b/lldb/test/API/tools/lldb-dap/custom-event/TestDAP_customEvent.py
new file mode 100644
index 00000000000000..2ae02b09c9f0fc
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/custom-event/TestDAP_customEvent.py
@@ -0,0 +1,46 @@
+"""
+Test lldb-dap custom-event integration.
+"""
+
+import json
+
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+import lldbdap_testcase
+
+
+class TestDAP_customEvent(lldbdap_testcase.DAPTestCaseBase):
+    def test_custom_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 custom-event my-custom-event-no-body",
+                "lldb-dap custom-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)
diff --git a/lldb/test/API/tools/lldb-dap/custom-event/main.c b/lldb/test/API/tools/lldb-dap/custom-event/main.c
new file mode 100644
index 00000000000000..27bc22b94794b6
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/custom-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/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py b/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py
index fd48e69cae5e25..fd452d91e472bd 100644
--- a/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py
+++ b/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py
@@ -1,12 +1,10 @@
 """
-Test lldb-dap startDebugging reverse request
+Test lldb-dap start-debugging reverse requests.
 """
 
 
-import dap_server
 from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
-from lldbsuite.test import lldbutil
 import lldbdap_testcase
 
 
@@ -25,7 +23,7 @@ def test_startDebugging(self):
         self.set_source_breakpoints(source, [breakpoint_line])
         self.continue_to_next_stop()
         self.dap_server.request_evaluate(
-            "`lldb-dap startDebugging attach '{\"pid\":321}'", context="repl"
+            "`lldb-dap start-debugging attach '{\"pid\":321}'", context="repl"
         )
 
         self.continue_to_exit()
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 6012ee52110b73..b8887ac367f44f 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -863,42 +863,35 @@ int64_t Variables::InsertExpandableVariable(lldb::SBValue variable,
 bool StartDebuggingRequestHandler::DoExecute(
     lldb::SBDebugger debugger, char **command,
     lldb::SBCommandReturnObject &result) {
-  // Command format like: `startDebugging <launch|attach> <configuration>`
+  // Command format like: `start-debugging <launch|attach> <configuration>`
   if (!command) {
-    result.SetError("Invalid use of startDebugging");
-    result.SetStatus(lldb::eReturnStatusFailed);
+    result.SetError("Invalid use of start-debugging, expected format "
+                    "`start-debugging <launch|attach> <configuration>`.");
     return false;
   }
 
   if (!command[0] || llvm::StringRef(command[0]).empty()) {
-    result.SetError("startDebugging request type missing.");
-    result.SetStatus(lldb::eReturnStatusFailed);
+    result.SetError("start-debugging request type missing.");
     return false;
   }
 
   if (!command[1] || llvm::StringRef(command[1]).empty()) {
-    result.SetError("configuration missing.");
-    result.SetStatus(lldb::eReturnStatusFailed);
+    result.SetError("start-debugging debug configuration missing.");
     return false;
   }
 
   llvm::StringRef request{command[0]};
   std::string raw_configuration{command[1]};
 
-  int i = 2;
-  while (command[i]) {
-    raw_configuration.append(" ").append(command[i]);
-  }
-
   llvm::Expected<llvm::json::Value> configuration =
       llvm::json::parse(raw_configuration);
 
   if (!configuration) {
     llvm::Error err = configuration.takeError();
-    std::string msg =
-        "Failed to parse json configuration: " + llvm::toString(std::move(err));
+    std::string msg = "Failed to parse json configuration: " +
+                      llvm::toString(std::move(err)) + "\n\n" +
+                      raw_configuration;
     result.SetError(msg.c_str());
-    result.SetStatus(lldb::eReturnStatusFailed);
     return false;
   }
 
@@ -966,6 +959,44 @@ bool ReplModeRequestHandler::DoExecute(lldb::SBDebugger debugger,
   return true;
 }
 
+// Sends a custom DAP event with an optional body.
+//
+// See
+// https://code.visualstudio.com/api/references/vscode-api#debug.onDidReceiveDebugSessionCustomEvent
+bool CustomDAPEventRequestHandler::DoExecute(
+    lldb::SBDebugger debugger, char **command,
+    lldb::SBCommandReturnObject &result) {
+  // Command format like: `custom-event <name> <body>?`
+  if (!command || !command[0] || llvm::StringRef(command[0]).empty()) {
+    result.SetError("Invalid use of custom-event, expected format "
+                    "`custom-event <name> <body>?`.");
+    return false;
+  }
+
+  llvm::StringRef name{command[0]};
+  llvm::json::Object event(CreateEventObject(name));
+
+  if (command[1] && !llvm::StringRef(command[1]).empty()) {
+    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 f4fdec6e895ad1..92108f8bbe86c3 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -150,6 +150,11 @@ struct ReplModeRequestHandler : public lldb::SBCommandPluginInterface {
                  lldb::SBCommandReturnObject &result) override;
 };
 
+struct CustomDAPEventRequestHandler : 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/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index c5c4b09f15622b..d3adc8f7fbcd35 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -1629,13 +1629,15 @@ void request_initialize(const llvm::json::Object &request) {
       "lldb-dap", "Commands for managing lldb-dap.");
   if (GetBoolean(arguments, "supportsStartDebuggingRequest", false)) {
     cmd.AddCommand(
-        "startDebugging", new StartDebuggingRequestHandler(),
+        "start-debugging", new StartDebuggingRequestHandler(),
         "Sends a startDebugging request from the debug adapter to the client "
         "to start a child debug session of the same type as the caller.");
   }
   cmd.AddCommand(
       "repl-mode", new ReplModeRequestHandler(),
       "Get or set the repl behavior of lldb-dap evaluation requests.");
+  cmd.AddCommand("custom-event", new CustomDAPEventRequestHandler(),
+                 "Fires a custom lldb-dap event.");
 
   g_dap.progress_event_thread = std::thread(ProgressEventThreadFunction);
 

@ashgti ashgti force-pushed the custom-dap-event branch 2 times, most recently from be42193 to 61f5407 Compare October 15, 2024 19:20
Copy link
Member

@walter-erquinigo walter-erquinigo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks pretty good. Just a minor nit

@@ -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("custom-event", new CustomDAPEventRequestHandler(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about custom-dap-event?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The command is in the lldb-dap command group, so I think that would become a bit redundant as lldb-dap custom-dap-event, but with vogelsgesang's comment below, how about lldb-dap send-event?

Copy link

github-actions bot commented Oct 16, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link

github-actions bot commented Oct 16, 2024

✅ With the latest revision this PR passed the Python code formatter.

@ashgti ashgti merged commit c5c11f3 into llvm:main Oct 17, 2024
4 of 6 checks passed
adrian-prantl pushed a commit to adrian-prantl/llvm-project that referenced this pull request Dec 20, 2024
…db-dap. (llvm#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.

(cherry picked from commit c5c11f3)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants