[Lldb-commits] [lldb] [lldb-dap] Adding support for cancelling a request. (PR #130169)

John Harrison via lldb-commits lldb-commits at lists.llvm.org
Thu Mar 6 12:11:58 PST 2025


https://github.com/ashgti created https://github.com/llvm/llvm-project/pull/130169

Adding support for cancelling requests.

There are two forms of request cancellation.

* Preemptively cancelling a request that is in the queue.
* Actively cancelling the in progress request as a best effort attempt using `SBDebugger.RequestInterrupt()`.


>From 228369443a418e23f82ddc47371f7531bd4a9cb7 Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Thu, 6 Mar 2025 10:18:36 +0100
Subject: [PATCH 1/4] [lldb-dap] Refactoring IOStream into Transport handler.

Instead of having two discrete InputStream and OutputStream helpers,
this merges the two into a unifed 'Transport' handler.

This handler is responsible for reading the DAP message headers, parsing
the resulting JSON and converting the messages into
`lldb_dap::protocol::Message`s for both input and output.
---
 .../test/tools/lldb-dap/dap_server.py         |   4 +-
 .../TestDAP_terminatedEvent.py                |   2 +-
 lldb/tools/lldb-dap/CMakeLists.txt            |   4 +-
 lldb/tools/lldb-dap/DAP.cpp                   | 135 ++++------------
 lldb/tools/lldb-dap/DAP.h                     |  18 +--
 lldb/tools/lldb-dap/IOStream.cpp              |  73 ---------
 lldb/tools/lldb-dap/IOStream.h                |  42 -----
 lldb/tools/lldb-dap/JSONUtils.cpp             |   3 +-
 lldb/tools/lldb-dap/Transport.cpp             | 152 ++++++++++++++++++
 lldb/tools/lldb-dap/Transport.h               |  56 +++++++
 10 files changed, 248 insertions(+), 241 deletions(-)
 delete mode 100644 lldb/tools/lldb-dap/IOStream.cpp
 delete mode 100644 lldb/tools/lldb-dap/IOStream.h
 create mode 100644 lldb/tools/lldb-dap/Transport.cpp
 create mode 100644 lldb/tools/lldb-dap/Transport.h

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 9471594b66012..0fea3419d9725 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
@@ -337,7 +337,7 @@ def send_recv(self, command):
                     self.send_packet(
                         {
                             "type": "response",
-                            "seq": -1,
+                            "seq": 0,
                             "request_seq": response_or_request["seq"],
                             "success": True,
                             "command": "runInTerminal",
@@ -349,7 +349,7 @@ def send_recv(self, command):
                     self.send_packet(
                         {
                             "type": "response",
-                            "seq": -1,
+                            "seq": 0,
                             "request_seq": response_or_request["seq"],
                             "success": True,
                             "command": "startDebugging",
diff --git a/lldb/test/API/tools/lldb-dap/terminated-event/TestDAP_terminatedEvent.py b/lldb/test/API/tools/lldb-dap/terminated-event/TestDAP_terminatedEvent.py
index 6d1c25e8e4534..b0abe2a38dac4 100644
--- a/lldb/test/API/tools/lldb-dap/terminated-event/TestDAP_terminatedEvent.py
+++ b/lldb/test/API/tools/lldb-dap/terminated-event/TestDAP_terminatedEvent.py
@@ -43,7 +43,7 @@ def test_terminated_event(self):
         self.continue_to_breakpoints(breakpoint_ids)
         self.continue_to_exit()
 
-        statistics = self.dap_server.wait_for_terminated()["statistics"]
+        statistics = self.dap_server.wait_for_terminated()["body"]["$__lldb_statistics"]
         self.assertGreater(statistics["totalDebugInfoByteSize"], 0)
         self.assertGreater(statistics["totalDebugInfoEnabled"], 0)
         self.assertGreater(statistics["totalModuleCountHasDebugInfo"], 0)
diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index 9a2d604f4d573..8a76cb58dbcab 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -28,14 +28,14 @@ add_lldb_tool(lldb-dap
   FifoFiles.cpp
   FunctionBreakpoint.cpp
   InstructionBreakpoint.cpp
-  IOStream.cpp
   JSONUtils.cpp
   LLDBUtils.cpp
   OutputRedirector.cpp
   ProgressEvent.cpp
+  Protocol.cpp
   RunInTerminal.cpp
   SourceBreakpoint.cpp
-  Protocol.cpp
+  Transport.cpp
   Watchpoint.cpp
 
   Handler/ResponseHandler.cpp
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index b21b83a79aec7..4acf3ba7fafec 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -11,6 +11,7 @@
 #include "JSONUtils.h"
 #include "LLDBUtils.h"
 #include "OutputRedirector.h"
+#include "Transport.h"
 #include "lldb/API/SBBreakpoint.h"
 #include "lldb/API/SBCommandInterpreter.h"
 #include "lldb/API/SBCommandReturnObject.h"
@@ -65,7 +66,7 @@ DAP::DAP(std::string name, llvm::StringRef path, std::ofstream *log,
          lldb::IOObjectSP input, lldb::IOObjectSP output, ReplMode repl_mode,
          std::vector<std::string> pre_init_commands)
     : name(std::move(name)), debug_adapter_path(path), log(log),
-      input(std::move(input)), output(std::move(output)),
+      transport(this->name, std::move(input), std::move(output)),
       broadcaster("lldb-dap"), exception_breakpoints(),
       pre_init_commands(std::move(pre_init_commands)),
       focus_tid(LLDB_INVALID_THREAD_ID), stop_at_entry(false), is_attach(false),
@@ -237,65 +238,25 @@ void DAP::StopEventHandlers() {
   }
 }
 
-// Send the JSON in "json_str" to the "out" stream. Correctly send the
-// "Content-Length:" field followed by the length, followed by the raw
-// JSON bytes.
-void DAP::SendJSON(const std::string &json_str) {
-  output.write_full("Content-Length: ");
-  output.write_full(llvm::utostr(json_str.size()));
-  output.write_full("\r\n\r\n");
-  output.write_full(json_str);
-}
-
 // Serialize the JSON value into a string and send the JSON packet to
 // the "out" stream.
 void DAP::SendJSON(const llvm::json::Value &json) {
-  std::string json_str;
-  llvm::raw_string_ostream strm(json_str);
-  strm << json;
-  static std::mutex mutex;
-  std::lock_guard<std::mutex> locker(mutex);
-  SendJSON(json_str);
-
-  if (log) {
-    auto now = std::chrono::duration<double>(
-        std::chrono::system_clock::now().time_since_epoch());
-    *log << llvm::formatv("{0:f9} {1} <-- ", now.count(), name).str()
-         << std::endl
-         << "Content-Length: " << json_str.size() << "\r\n\r\n"
-         << llvm::formatv("{0:2}", json).str() << std::endl;
-  }
-}
-
-// Read a JSON packet from the "in" stream.
-std::string DAP::ReadJSON() {
-  std::string length_str;
-  std::string json_str;
-  int length;
-
-  if (!input.read_expected(log, "Content-Length: "))
-    return json_str;
-
-  if (!input.read_line(log, length_str))
-    return json_str;
-
-  if (!llvm::to_integer(length_str, length))
-    return json_str;
-
-  if (!input.read_expected(log, "\r\n"))
-    return json_str;
-
-  if (!input.read_full(log, length, json_str))
-    return json_str;
-
-  if (log) {
-    auto now = std::chrono::duration<double>(
-        std::chrono::system_clock::now().time_since_epoch());
-    *log << llvm::formatv("{0:f9} {1} --> ", now.count(), name).str()
-         << std::endl
-         << "Content-Length: " << length << "\r\n\r\n";
+  protocol::Message M;
+  llvm::json::Path::Root root;
+  if (!protocol::fromJSON(json, M, root)) {
+    if (log) {
+      std::string error;
+      llvm::raw_string_ostream OS(error);
+      root.printErrorContext(json, OS);
+      *log << "encoding failure: " << error << "\n";
+    }
+    return;
   }
-  return json_str;
+  auto status = transport.Write(log, M);
+  if (status.Fail() && log)
+    *log << llvm::formatv("failed to send {0}: {1}\n", llvm::json::Value(M),
+                          status.AsCString())
+                .str();
 }
 
 // "OutputEvent": {
@@ -722,40 +683,9 @@ void DAP::SetTarget(const lldb::SBTarget target) {
   }
 }
 
-PacketStatus DAP::GetNextObject(llvm::json::Object &object) {
-  std::string json = ReadJSON();
-  if (json.empty())
-    return PacketStatus::EndOfFile;
-
-  llvm::StringRef json_sref(json);
-  llvm::Expected<llvm::json::Value> json_value = llvm::json::parse(json_sref);
-  if (!json_value) {
-    auto error = json_value.takeError();
-    if (log) {
-      std::string error_str;
-      llvm::raw_string_ostream strm(error_str);
-      strm << error;
-      *log << "error: failed to parse JSON: " << error_str << std::endl
-           << json << std::endl;
-    }
-    return PacketStatus::JSONMalformed;
-  }
-
-  if (log) {
-    *log << llvm::formatv("{0:2}", *json_value).str() << std::endl;
-  }
-
-  llvm::json::Object *object_ptr = json_value->getAsObject();
-  if (!object_ptr) {
-    if (log)
-      *log << "error: json packet isn't a object" << std::endl;
-    return PacketStatus::JSONNotObject;
-  }
-  object = *object_ptr;
-  return PacketStatus::Success;
-}
-
-bool DAP::HandleObject(const llvm::json::Object &object) {
+bool DAP::HandleObject(const protocol::Message &M) {
+  llvm::json::Value v = toJSON(M);
+  llvm::json::Object object = *v.getAsObject();
   const auto packet_type = GetString(object, "type");
   if (packet_type == "request") {
     const auto command = GetString(object, "command");
@@ -858,25 +788,18 @@ lldb::SBError DAP::Disconnect(bool terminateDebuggee) {
 }
 
 llvm::Error DAP::Loop() {
-  auto cleanup = llvm::make_scope_exit([this]() {
-    if (output.descriptor)
-      output.descriptor->Close();
-    StopEventHandlers();
-  });
+  auto cleanup = llvm::make_scope_exit([this]() { StopEventHandlers(); });
   while (!disconnecting) {
-    llvm::json::Object object;
-    lldb_dap::PacketStatus status = GetNextObject(object);
-
-    if (status == lldb_dap::PacketStatus::EndOfFile) {
-      break;
-    }
-
-    if (status != lldb_dap::PacketStatus::Success) {
-      return llvm::createStringError(llvm::inconvertibleErrorCode(),
-                                     "failed to send packet");
+    protocol::Message next;
+    auto status = transport.Read(log, next);
+    if (status.Fail()) {
+      // On EOF, simply break out of the loop.
+      if (status.GetError() == Transport::kEOF)
+        break;
+      return status.takeError();
     }
 
-    if (!HandleObject(object)) {
+    if (!HandleObject(next)) {
       return llvm::createStringError(llvm::inconvertibleErrorCode(),
                                      "unhandled packet");
     }
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index 8b2e498a28c95..ed730ee5d1961 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -14,11 +14,12 @@
 #include "FunctionBreakpoint.h"
 #include "Handler/RequestHandler.h"
 #include "Handler/ResponseHandler.h"
-#include "IOStream.h"
 #include "InstructionBreakpoint.h"
 #include "OutputRedirector.h"
 #include "ProgressEvent.h"
+#include "Protocol.h"
 #include "SourceBreakpoint.h"
+#include "Transport.h"
 #include "lldb/API/SBBroadcaster.h"
 #include "lldb/API/SBCommandInterpreter.h"
 #include "lldb/API/SBDebugger.h"
@@ -39,7 +40,6 @@
 #include "llvm/Support/Error.h"
 #include "llvm/Support/JSON.h"
 #include "llvm/Support/Threading.h"
-#include <map>
 #include <memory>
 #include <mutex>
 #include <optional>
@@ -148,8 +148,7 @@ struct DAP {
   std::string name;
   llvm::StringRef debug_adapter_path;
   std::ofstream *log;
-  InputStream input;
-  OutputStream output;
+  Transport transport;
   lldb::SBFile in;
   OutputRedirector out;
   OutputRedirector err;
@@ -233,8 +232,6 @@ struct DAP {
   // the "out" stream.
   void SendJSON(const llvm::json::Value &json);
 
-  std::string ReadJSON();
-
   void SendOutput(OutputType o, const llvm::StringRef output);
 
   void SendProgressEvent(uint64_t progress_id, const char *message,
@@ -307,8 +304,7 @@ struct DAP {
   /// listeing for its breakpoint events.
   void SetTarget(const lldb::SBTarget target);
 
-  PacketStatus GetNextObject(llvm::json::Object &object);
-  bool HandleObject(const llvm::json::Object &object);
+  bool HandleObject(const protocol::Message &M);
 
   /// Disconnect the DAP session.
   lldb::SBError Disconnect();
@@ -382,12 +378,6 @@ struct DAP {
   InstructionBreakpoint *GetInstructionBreakpoint(const lldb::break_id_t bp_id);
 
   InstructionBreakpoint *GetInstructionBPFromStopReason(lldb::SBThread &thread);
-
-private:
-  // Send the JSON in "json_str" to the "out" stream. Correctly send the
-  // "Content-Length:" field followed by the length, followed by the raw
-  // JSON bytes.
-  void SendJSON(const std::string &json_str);
 };
 
 } // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/IOStream.cpp b/lldb/tools/lldb-dap/IOStream.cpp
deleted file mode 100644
index ee22a297ec248..0000000000000
--- a/lldb/tools/lldb-dap/IOStream.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-//===-- IOStream.cpp --------------------------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "IOStream.h"
-#include "lldb/Utility/IOObject.h"
-#include "lldb/Utility/Status.h"
-#include <fstream>
-#include <string>
-
-using namespace lldb_dap;
-
-bool OutputStream::write_full(llvm::StringRef str) {
-  if (!descriptor)
-    return false;
-
-  size_t num_bytes = str.size();
-  auto status = descriptor->Write(str.data(), num_bytes);
-  return status.Success();
-}
-
-bool InputStream::read_full(std::ofstream *log, size_t length,
-                            std::string &text) {
-  if (!descriptor)
-    return false;
-
-  std::string data;
-  data.resize(length);
-
-  auto status = descriptor->Read(data.data(), length);
-  if (status.Fail())
-    return false;
-
-  text += data.substr(0, length);
-  return true;
-}
-
-bool InputStream::read_line(std::ofstream *log, std::string &line) {
-  line.clear();
-  while (true) {
-    std::string next;
-    if (!read_full(log, 1, next))
-      return false;
-
-    // If EOF is encoutnered, '' is returned, break out of this loop.
-    if (next.empty())
-      return false;
-
-    line += next;
-
-    if (llvm::StringRef(line).ends_with("\r\n"))
-      break;
-  }
-  line.erase(line.size() - 2);
-  return true;
-}
-
-bool InputStream::read_expected(std::ofstream *log, llvm::StringRef expected) {
-  std::string result;
-  if (!read_full(log, expected.size(), result))
-    return false;
-  if (expected != result) {
-    if (log)
-      *log << "Warning: Expected '" << expected.str() << "', got '" << result
-           << "\n";
-    return false;
-  }
-  return true;
-}
diff --git a/lldb/tools/lldb-dap/IOStream.h b/lldb/tools/lldb-dap/IOStream.h
deleted file mode 100644
index e9fb8e11c92da..0000000000000
--- a/lldb/tools/lldb-dap/IOStream.h
+++ /dev/null
@@ -1,42 +0,0 @@
-//===-- IOStream.h ----------------------------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLDB_TOOLS_LLDB_DAP_IOSTREAM_H
-#define LLDB_TOOLS_LLDB_DAP_IOSTREAM_H
-
-#include "lldb/lldb-forward.h"
-#include "llvm/ADT/StringRef.h"
-#include <fstream>
-#include <string>
-
-namespace lldb_dap {
-
-struct InputStream {
-  lldb::IOObjectSP descriptor;
-
-  explicit InputStream(lldb::IOObjectSP descriptor)
-      : descriptor(std::move(descriptor)) {}
-
-  bool read_full(std::ofstream *log, size_t length, std::string &text);
-
-  bool read_line(std::ofstream *log, std::string &line);
-
-  bool read_expected(std::ofstream *log, llvm::StringRef expected);
-};
-
-struct OutputStream {
-  lldb::IOObjectSP descriptor;
-
-  explicit OutputStream(lldb::IOObjectSP descriptor)
-      : descriptor(std::move(descriptor)) {}
-
-  bool write_full(llvm::StringRef str);
-};
-} // namespace lldb_dap
-
-#endif
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index 7094bf60bfbc2..932145b1799bd 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -1526,7 +1526,8 @@ static void addStatistic(lldb::SBTarget &target, llvm::json::Object &event) {
     const char *key = keys.GetStringAtIndex(i);
     FilterAndGetValueForKey(statistics, key, stats_body);
   }
-  event.try_emplace("statistics", std::move(stats_body));
+  llvm::json::Object body{{"$__lldb_statistics", std::move(stats_body)}};
+  event.try_emplace("body", std::move(body));
 }
 
 llvm::json::Object CreateTerminatedEventObject(lldb::SBTarget &target) {
diff --git a/lldb/tools/lldb-dap/Transport.cpp b/lldb/tools/lldb-dap/Transport.cpp
new file mode 100644
index 0000000000000..f46bd78192332
--- /dev/null
+++ b/lldb/tools/lldb-dap/Transport.cpp
@@ -0,0 +1,152 @@
+//===-- Transport.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Transport.h"
+#include "Protocol.h"
+#include "lldb/Utility/IOObject.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/raw_ostream.h"
+#include <utility>
+
+using namespace llvm;
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_dap;
+using namespace lldb_dap::protocol;
+
+static Status ReadFull(IOObjectSP &descriptor, size_t length,
+                       std::string &text) {
+  if (!descriptor || !descriptor->IsValid())
+    return Status("transport input is closed");
+
+  std::string data;
+  data.resize(length);
+
+  auto status = descriptor->Read(data.data(), length);
+  if (status.Fail())
+    return status;
+
+  // If we got back zero then we have reached EOF.
+  if (length == 0)
+    return Status(Transport::kEOF, lldb::eErrorTypeGeneric, "end-of-file");
+
+  text += data.substr(0, length);
+  return Status();
+}
+
+static Status ReadUpTo(IOObjectSP &descriptor, std::string &line,
+                       const std::string &delimiter) {
+  line.clear();
+  while (true) {
+    std::string next;
+    auto status = ReadFull(descriptor, 1, next);
+    if (status.Fail())
+      return status;
+
+    line += next;
+
+    if (llvm::StringRef(line).ends_with(delimiter))
+      break;
+  }
+  line.erase(line.size() - delimiter.size());
+  return Status();
+}
+
+static Status ReadExpected(IOObjectSP &descriptor, llvm::StringRef expected) {
+  std::string result;
+  auto status = ReadFull(descriptor, expected.size(), result);
+  if (status.Fail())
+    return status;
+  if (expected != result) {
+    return Status::FromErrorStringWithFormatv("expected %s, got %s", expected,
+                                              result);
+  }
+  return Status();
+}
+
+namespace lldb_dap {
+
+Transport::Transport(StringRef client_name, IOObjectSP input, IOObjectSP output)
+    : m_client_name(client_name), m_input(std::move(input)),
+      m_output(std::move(output)) {}
+
+Status Transport::Read(std::ofstream *log, Message &M) {
+  // If we don't find the expected header we have reached EOF.
+  auto status = ReadExpected(m_input, "Content-Length: ");
+  if (status.Fail())
+    return status;
+
+  std::string rawLength;
+  status = ReadUpTo(m_input, rawLength, "\r\n\r\n");
+  if (status.Fail())
+    return status;
+
+  size_t length;
+  if (!to_integer(rawLength, length))
+    return Status::FromErrorStringWithFormatv("invalid content length {0}",
+                                              rawLength);
+
+  std::string rawJSON;
+  status = ReadFull(m_input, length, rawJSON);
+  if (status.Fail())
+    return status;
+  if (rawJSON.length() != length)
+    return Status::FromErrorStringWithFormatv(
+        "malformed request, expected {0} bytes, got {1} bytes", length,
+        rawJSON.length());
+
+  if (log) {
+    auto now = std::chrono::duration<double>(
+        std::chrono::system_clock::now().time_since_epoch());
+    *log << formatv("{0:f9} <-- ({1}) {2}\n", now.count(), m_client_name,
+                    rawJSON)
+                .str();
+  }
+
+  auto JSON = json::parse(rawJSON);
+  if (auto Err = JSON.takeError()) {
+    return Status::FromErrorStringWithFormatv("malformed JSON {0}\n{1}",
+                                              rawJSON, Err);
+  }
+
+  llvm::json::Path::Root Root;
+  if (!fromJSON(*JSON, M, Root)) {
+    std::string error;
+    raw_string_ostream OS(error);
+    Root.printErrorContext(*JSON, OS);
+    return Status::FromErrorStringWithFormatv("malformed request: {0}", error);
+  }
+  return Status();
+}
+
+lldb_private::Status Transport::Write(std::ofstream *log,
+                                      const protocol::Message &M) {
+  if (!m_output || !m_output->IsValid())
+    return Status("transport output is closed");
+
+  std::string JSON = formatv("{0}", toJSON(M)).str();
+
+  if (log) {
+    auto now = std::chrono::duration<double>(
+        std::chrono::system_clock::now().time_since_epoch());
+    *log << formatv("{0:f9} --> ({1}) {2}\n", now.count(), m_client_name, JSON)
+                .str();
+  }
+
+  std::string Output;
+  raw_string_ostream OS(Output);
+  OS << "Content-Length: " << JSON.length() << "\r\n\r\n" << JSON;
+  size_t num_bytes = Output.size();
+  return m_output->Write(Output.data(), num_bytes);
+}
+
+} // end namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Transport.h b/lldb/tools/lldb-dap/Transport.h
new file mode 100644
index 0000000000000..041ceca7446c6
--- /dev/null
+++ b/lldb/tools/lldb-dap/Transport.h
@@ -0,0 +1,56 @@
+//===-- Transport.h -------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Debug Adapter Protocol transport layer for encoding and decoding protocol
+// messages.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_TOOLS_LLDB_DAP_TRANSPORT_H
+#define LLDB_TOOLS_LLDB_DAP_TRANSPORT_H
+
+#include "Protocol.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-forward.h"
+#include "llvm/ADT/StringRef.h"
+#include <fstream>
+
+namespace lldb_dap {
+
+/// A transport class that performs the Debug Adapter Protocol communication
+/// with the client.
+class Transport {
+public:
+  Transport(llvm::StringRef client_name, lldb::IOObjectSP input,
+            lldb::IOObjectSP output);
+  ~Transport() = default;
+
+  Transport(const Transport &rhs) = delete;
+  void operator=(const Transport &rhs) = delete;
+
+  static const lldb_private::Status::ValueType kEOF =
+      0x1001; ///< ValueObject::GetError() returns this if EOF is encountered.
+
+  /// Writes a Debug Adater Protocol message to the output stream.
+  lldb_private::Status Write(std::ofstream *log, const protocol::Message &M);
+
+  /// Reads the next Debug Adater Protocol message from the input stream.
+  lldb_private::Status Read(std::ofstream *log, protocol::Message &M);
+
+  /// Closes the transport.
+  void CloseOutput();
+
+private:
+  llvm::StringRef m_client_name;
+  lldb::IOObjectSP m_input;
+  lldb::IOObjectSP m_output;
+};
+
+} // namespace lldb_dap
+
+#endif

>From 36d0fec9569b4313055f5557b7d824c8a8188700 Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Thu, 6 Mar 2025 13:21:10 +0100
Subject: [PATCH 2/4] [lldb-dap] Updating RequestHandler to encode/decode
 arguments and response.

This is a work in progress refactor to add explicit types instead of
generic 'llvm::json::Value' types to the DAP protocol.

This updates RequestHandler to have take the type of the arguments and
response body for serialization for requests.

The 'source' request is updated to show how the new flow works.
---
 lldb/tools/lldb-dap/DAP.cpp                   |  57 +++--
 lldb/tools/lldb-dap/DAP.h                     |   4 +-
 lldb/tools/lldb-dap/DAPForward.h              |   2 +
 .../tools/lldb-dap/Handler/RequestHandler.cpp |  11 +-
 lldb/tools/lldb-dap/Handler/RequestHandler.h  | 214 ++++++++++++------
 .../lldb-dap/Handler/SourceRequestHandler.cpp | 104 ++-------
 lldb/tools/lldb-dap/Protocol.cpp              |  44 ++++
 lldb/tools/lldb-dap/Protocol.h                | 131 +++++++++++
 8 files changed, 381 insertions(+), 186 deletions(-)

diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 4acf3ba7fafec..ce118f36808dc 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -7,10 +7,12 @@
 //===----------------------------------------------------------------------===//
 
 #include "DAP.h"
+#include "Handler/RequestHandler.h"
 #include "Handler/ResponseHandler.h"
 #include "JSONUtils.h"
 #include "LLDBUtils.h"
 #include "OutputRedirector.h"
+#include "Protocol.h"
 #include "Transport.h"
 #include "lldb/API/SBBreakpoint.h"
 #include "lldb/API/SBCommandInterpreter.h"
@@ -24,6 +26,7 @@
 #include "lldb/lldb-defines.h"
 #include "lldb/lldb-enumerations.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/Twine.h"
@@ -39,6 +42,7 @@
 #include <fstream>
 #include <memory>
 #include <mutex>
+#include <string>
 #include <utility>
 
 #if defined(_WIN32)
@@ -684,31 +688,24 @@ void DAP::SetTarget(const lldb::SBTarget target) {
 }
 
 bool DAP::HandleObject(const protocol::Message &M) {
-  llvm::json::Value v = toJSON(M);
-  llvm::json::Object object = *v.getAsObject();
-  const auto packet_type = GetString(object, "type");
-  if (packet_type == "request") {
-    const auto command = GetString(object, "command");
-
-    auto new_handler_pos = request_handlers.find(command);
+  if (const auto *req = std::get_if<protocol::Request>(&M)) {
+    auto new_handler_pos = request_handlers.find(req->command);
     if (new_handler_pos != request_handlers.end()) {
-      (*new_handler_pos->second)(object);
+      (*new_handler_pos->second)(*req);
       return true; // Success
     }
 
     if (log)
-      *log << "error: unhandled command \"" << command.data() << "\""
+      *log << "error: unhandled command \"" << req->command << "\""
            << std::endl;
     return false; // Fail
   }
 
-  if (packet_type == "response") {
-    auto id = GetInteger<int64_t>(object, "request_seq").value_or(0);
-
+  if (const auto *resp = std::get_if<protocol::Response>(&M)) {
     std::unique_ptr<ResponseHandler> response_handler;
     {
       std::lock_guard<std::mutex> locker(call_mutex);
-      auto inflight = inflight_reverse_requests.find(id);
+      auto inflight = inflight_reverse_requests.find(resp->request_seq);
       if (inflight != inflight_reverse_requests.end()) {
         response_handler = std::move(inflight->second);
         inflight_reverse_requests.erase(inflight);
@@ -716,20 +713,31 @@ bool DAP::HandleObject(const protocol::Message &M) {
     }
 
     if (!response_handler)
-      response_handler = std::make_unique<UnknownResponseHandler>("", id);
+      response_handler =
+          std::make_unique<UnknownResponseHandler>("", resp->request_seq);
 
     // Result should be given, use null if not.
-    if (GetBoolean(object, "success").value_or(false)) {
-      llvm::json::Value Result = nullptr;
-      if (auto *B = object.get("body")) {
-        Result = std::move(*B);
-      }
-      (*response_handler)(Result);
+    if (resp->success) {
+      (*response_handler)(resp->rawBody);
     } else {
-      llvm::StringRef message = GetString(object, "message");
-      if (message.empty()) {
-        message = "Unknown error, response failed";
+      std::string message = "Unknown error, response failed";
+      if (resp->message) {
+        message = std::visit(
+            llvm::makeVisitor(
+                [](const std::string &message) -> std::string {
+                  return message;
+                },
+                [](const protocol::Response::Message &message) -> std::string {
+                  switch (message) {
+                  case protocol::Response::Message::cancelled:
+                    return "cancelled";
+                  case protocol::Response::Message::notStopped:
+                    return "notStopped";
+                  }
+                }),
+            *resp->message);
       }
+
       (*response_handler)(llvm::createStringError(
           std::error_code(-1, std::generic_category()), message));
     }
@@ -737,6 +745,9 @@ bool DAP::HandleObject(const protocol::Message &M) {
     return true;
   }
 
+  if (log)
+    *log << "Unsupported protocol message" << std::endl;
+
   return false;
 }
 
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index ed730ee5d1961..fbabe763b8171 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -12,8 +12,6 @@
 #include "DAPForward.h"
 #include "ExceptionBreakpoint.h"
 #include "FunctionBreakpoint.h"
-#include "Handler/RequestHandler.h"
-#include "Handler/ResponseHandler.h"
 #include "InstructionBreakpoint.h"
 #include "OutputRedirector.h"
 #include "ProgressEvent.h"
@@ -188,7 +186,7 @@ struct DAP {
   // the old process here so we can detect this case and keep running.
   lldb::pid_t restarting_process_id;
   bool configuration_done_sent;
-  llvm::StringMap<std::unique_ptr<RequestHandler>> request_handlers;
+  llvm::StringMap<std::unique_ptr<BaseRequestHandler>> request_handlers;
   bool waiting_for_run_in_terminal;
   ProgressEventReporter progress_event_reporter;
   // Keep track of the last stop thread index IDs as threads won't go away
diff --git a/lldb/tools/lldb-dap/DAPForward.h b/lldb/tools/lldb-dap/DAPForward.h
index 0196d83dcd6a9..667aef23abd0f 100644
--- a/lldb/tools/lldb-dap/DAPForward.h
+++ b/lldb/tools/lldb-dap/DAPForward.h
@@ -19,6 +19,8 @@ struct SourceBreakpoint;
 struct Watchpoint;
 struct InstructionBreakpoint;
 struct DAP;
+class BaseRequestHandler;
+class ResponseHandler;
 } // namespace lldb_dap
 
 namespace lldb {
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
index e43fa36d25e3f..13cf166e200e8 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
@@ -6,8 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "RequestHandler.h"
+#include "Handler/RequestHandler.h"
 #include "DAP.h"
+#include "Handler/ResponseHandler.h"
 #include "JSONUtils.h"
 #include "LLDBUtils.h"
 #include "RunInTerminal.h"
@@ -45,7 +46,7 @@ static uint32_t SetLaunchFlag(uint32_t flags, const llvm::json::Object *obj,
 
 // Both attach and launch take either a sourcePath or a sourceMap
 // argument (or neither), from which we need to set the target.source-map.
-void RequestHandler::SetSourceMapFromArguments(
+void BaseRequestHandler::SetSourceMapFromArguments(
     const llvm::json::Object &arguments) const {
   const char *sourceMapHelp =
       "source must be be an array of two-element arrays, "
@@ -159,7 +160,7 @@ static llvm::Error RunInTerminal(DAP &dap,
 }
 
 lldb::SBError
-RequestHandler::LaunchProcess(const llvm::json::Object &request) const {
+BaseRequestHandler::LaunchProcess(const llvm::json::Object &request) const {
   lldb::SBError error;
   const auto *arguments = request.getObject("arguments");
   auto launchCommands = GetStrings(arguments, "launchCommands");
@@ -228,13 +229,13 @@ RequestHandler::LaunchProcess(const llvm::json::Object &request) const {
   return error;
 }
 
-void RequestHandler::PrintWelcomeMessage() const {
+void BaseRequestHandler::PrintWelcomeMessage() const {
 #ifdef LLDB_DAP_WELCOME_MESSAGE
   dap.SendOutput(OutputType::Console, LLDB_DAP_WELCOME_MESSAGE);
 #endif
 }
 
-bool RequestHandler::HasInstructionGranularity(
+bool BaseRequestHandler::HasInstructionGranularity(
     const llvm::json::Object &arguments) const {
   if (std::optional<llvm::StringRef> value = arguments.getString("granularity"))
     return value == "instruction";
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index b44367518bcb9..da65b2de9ab99 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -9,25 +9,36 @@
 #ifndef LLDB_TOOLS_LLDB_DAP_HANDLER_HANDLER_H
 #define LLDB_TOOLS_LLDB_DAP_HANDLER_HANDLER_H
 
+#include "DAP.h"
+#include "Protocol.h"
 #include "lldb/API/SBError.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/JSON.h"
 
 namespace lldb_dap {
 struct DAP;
 
-class RequestHandler {
+/// Base class for request handlers. Do not extend this directly: Extend
+/// the RequestHandler template subclass instead.
+class BaseRequestHandler {
 public:
-  RequestHandler(DAP &dap) : dap(dap) {}
+  BaseRequestHandler(DAP &dap) : dap(dap) {}
 
-  /// RequestHandler are not copyable.
+  /// BaseRequestHandler are not copyable.
   /// @{
-  RequestHandler(const RequestHandler &) = delete;
-  RequestHandler &operator=(const RequestHandler &) = delete;
+  BaseRequestHandler(const BaseRequestHandler &) = delete;
+  BaseRequestHandler &operator=(const BaseRequestHandler &) = delete;
   /// @}
 
-  virtual ~RequestHandler() = default;
+  virtual ~BaseRequestHandler() = default;
 
+  virtual void operator()(const protocol::Request &request) const {
+    auto req = toJSON(request);
+    (*this)(*req.getAsObject());
+  }
+
+  /// FIXME: Migrate callers to typed RequestHandler for improved type handling.
   virtual void operator()(const llvm::json::Object &request) const = 0;
 
 protected:
@@ -57,235 +68,288 @@ class RequestHandler {
   DAP &dap;
 };
 
-class AttachRequestHandler : public RequestHandler {
-public:
-  using RequestHandler::RequestHandler;
+/// Base class for handling DAP requests. Handlers should declare their
+/// arguments and response body types like:
+///
+/// class MyRequestHandler : public RequestHandler<Arguments, ResponseBody> {
+///   ....
+/// };
+template <typename Args, typename Body>
+class RequestHandler : public BaseRequestHandler {
+  using BaseRequestHandler::BaseRequestHandler;
+
+  void operator()(const llvm::json::Object &request) const override {
+    /* no-op, the other overload handles json coding. */
+  }
+
+  void operator()(const protocol::Request &request) const override {
+    protocol::Response response;
+    response.request_seq = request.seq;
+    response.command = request.command;
+    Args arguments;
+    llvm::json::Path::Root root;
+    if (request.rawArguments &&
+        !fromJSON(request.rawArguments, arguments, root)) {
+      std::string parseFailure;
+      llvm::raw_string_ostream OS(parseFailure);
+      root.printErrorContext(request.rawArguments, OS);
+      response.success = false;
+      response.message = parseFailure;
+      dap.SendJSON(std::move(response));
+      return;
+    }
+
+    auto ResponseBody = Run(arguments);
+    // FIXME: Add a dedicated DAPError for enhanced errors that are user
+    // visibile.
+    if (auto Err = ResponseBody.takeError()) {
+      response.success = false;
+      // FIXME: Build ErrorMessage based on error details instead of using the
+      // 'message' field.
+      response.message = llvm::toString(std::move(Err));
+    } else {
+      response.success = true;
+      response.rawBody = std::move(*ResponseBody);
+    }
+
+    dap.SendJSON(std::move(response));
+  };
+
+  virtual llvm::Expected<Body> Run(const Args &) const = 0;
+};
+
+class AttachRequestHandler : public BaseRequestHandler {
+public:
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "attach"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class BreakpointLocationsRequestHandler : public RequestHandler {
+class BreakpointLocationsRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "breakpointLocations"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class CompletionsRequestHandler : public RequestHandler {
+class CompletionsRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "completions"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class ContinueRequestHandler : public RequestHandler {
+class ContinueRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "continue"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class ConfigurationDoneRequestHandler : public RequestHandler {
+class ConfigurationDoneRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "configurationDone"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class DisconnectRequestHandler : public RequestHandler {
+class DisconnectRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "disconnect"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class EvaluateRequestHandler : public RequestHandler {
+class EvaluateRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "evaluate"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class ExceptionInfoRequestHandler : public RequestHandler {
+class ExceptionInfoRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "exceptionInfo"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class InitializeRequestHandler : public RequestHandler {
+class InitializeRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "initialize"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class LaunchRequestHandler : public RequestHandler {
+class LaunchRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "launch"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class RestartRequestHandler : public RequestHandler {
+class RestartRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "restart"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class NextRequestHandler : public RequestHandler {
+class NextRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "next"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class StepInRequestHandler : public RequestHandler {
+class StepInRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "stepIn"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class StepInTargetsRequestHandler : public RequestHandler {
+class StepInTargetsRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "stepInTargets"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class StepOutRequestHandler : public RequestHandler {
+class StepOutRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "stepOut"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class SetBreakpointsRequestHandler : public RequestHandler {
+class SetBreakpointsRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "setBreakpoints"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class SetExceptionBreakpointsRequestHandler : public RequestHandler {
+class SetExceptionBreakpointsRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "setExceptionBreakpoints"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class SetFunctionBreakpointsRequestHandler : public RequestHandler {
+class SetFunctionBreakpointsRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "setFunctionBreakpoints"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class DataBreakpointInfoRequestHandler : public RequestHandler {
+class DataBreakpointInfoRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "dataBreakpointInfo"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class SetDataBreakpointsRequestHandler : public RequestHandler {
+class SetDataBreakpointsRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "setDataBreakpoints"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class SetInstructionBreakpointsRequestHandler : public RequestHandler {
+class SetInstructionBreakpointsRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() {
     return "setInstructionBreakpoints";
   }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class CompileUnitsRequestHandler : public RequestHandler {
+class CompileUnitsRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "compileUnits"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class ModulesRequestHandler : public RequestHandler {
+class ModulesRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "modules"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class PauseRequestHandler : public RequestHandler {
+class PauseRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "pause"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class ScopesRequestHandler : public RequestHandler {
+class ScopesRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "scopes"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class SetVariableRequestHandler : public RequestHandler {
+class SetVariableRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "setVariable"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class SourceRequestHandler : public RequestHandler {
+class SourceRequestHandler
+    : public RequestHandler<protocol::SourceArguments,
+                            protocol::SourceResponseBody> {
 public:
   using RequestHandler::RequestHandler;
   static llvm::StringLiteral getCommand() { return "source"; }
-  void operator()(const llvm::json::Object &request) const override;
+  llvm::Expected<protocol::SourceResponseBody>
+  Run(const protocol::SourceArguments &args) const override;
 };
 
-class StackTraceRequestHandler : public RequestHandler {
+class StackTraceRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "stackTrace"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class ThreadsRequestHandler : public RequestHandler {
+class ThreadsRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "threads"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class VariablesRequestHandler : public RequestHandler {
+class VariablesRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "variables"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class LocationsRequestHandler : public RequestHandler {
+class LocationsRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "locations"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class DisassembleRequestHandler : public RequestHandler {
+class DisassembleRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "disassemble"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class ReadMemoryRequestHandler : public RequestHandler {
+class ReadMemoryRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() { return "readMemory"; }
   void operator()(const llvm::json::Object &request) const override;
 };
@@ -294,9 +358,9 @@ class ReadMemoryRequestHandler : public RequestHandler {
 /// currently set in the target. This helps us to test "setBreakpoints" and
 /// "setFunctionBreakpoints" requests to verify we have the correct set of
 /// breakpoints currently set in LLDB.
-class TestGetTargetBreakpointsRequestHandler : public RequestHandler {
+class TestGetTargetBreakpointsRequestHandler : public BaseRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using BaseRequestHandler::BaseRequestHandler;
   static llvm::StringLiteral getCommand() {
     return "_testGetTargetBreakpoints";
   }
diff --git a/lldb/tools/lldb-dap/Handler/SourceRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SourceRequestHandler.cpp
index 493543b395fd1..101f18ef5220a 100644
--- a/lldb/tools/lldb-dap/Handler/SourceRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/SourceRequestHandler.cpp
@@ -7,17 +7,15 @@
 //===----------------------------------------------------------------------===//
 
 #include "DAP.h"
-#include "EventHelper.h"
-#include "JSONUtils.h"
+#include "Handler/RequestHandler.h"
 #include "LLDBUtils.h"
-#include "RequestHandler.h"
 #include "lldb/API/SBFrame.h"
 #include "lldb/API/SBInstructionList.h"
 #include "lldb/API/SBProcess.h"
 #include "lldb/API/SBStream.h"
 #include "lldb/API/SBTarget.h"
 #include "lldb/API/SBThread.h"
-#include "llvm/Support/JSON.h"
+#include "llvm/Support/Error.h"
 
 namespace lldb_dap {
 
@@ -38,84 +36,30 @@ namespace lldb_dap {
 //     "required": [ "command", "arguments"  ]
 //   }]
 // },
-// "SourceArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'source' request.",
-//   "properties": {
-//     "source": {
-//       "$ref": "#/definitions/Source",
-//       "description": "Specifies the source content to load. Either
-//       source.path or source.sourceReference must be specified."
-//     },
-//     "sourceReference": {
-//       "type": "integer",
-//       "description": "The reference to the source. This is the same as
-//       source.sourceReference. This is provided for backward compatibility
-//       since old backends do not understand the 'source' attribute."
-//     }
-//   },
-//   "required": [ "sourceReference" ]
-// },
-// "SourceResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to 'source' request.",
-//     "properties": {
-//       "body": {
-//         "type": "object",
-//         "properties": {
-//           "content": {
-//             "type": "string",
-//             "description": "Content of the source reference."
-//           },
-//           "mimeType": {
-//             "type": "string",
-//             "description": "Optional content type (mime type) of the source."
-//           }
-//         },
-//         "required": [ "content" ]
-//       }
-//     },
-//     "required": [ "body" ]
-//   }]
-// }
-void SourceRequestHandler::operator()(const llvm::json::Object &request) const {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  const auto *arguments = request.getObject("arguments");
-  const auto *source = arguments->getObject("source");
-  llvm::json::Object body;
-  const auto source_ref =
-      GetInteger<uint64_t>(source, "sourceReference")
-          .value_or(
-              GetInteger<uint64_t>(arguments, "sourceReference").value_or(0));
+llvm::Expected<protocol::SourceResponseBody>
+SourceRequestHandler::Run(const protocol::SourceArguments &args) const {
+  const auto source =
+      args.source->sourceReference.value_or(args.sourceReference);
+
+  if (!source)
+    return llvm::createStringError(
+        "invalid arguments, expected source.sourceReference to be set");
+
+  lldb::SBProcess process = dap.target.GetProcess();
+  // Upper 32 bits is the thread index ID
+  lldb::SBThread thread =
+      process.GetThreadByIndexID(GetLLDBThreadIndexID(source));
+  // Lower 32 bits is the frame index
+  lldb::SBFrame frame = thread.GetFrameAtIndex(GetLLDBFrameID(source));
+  if (!frame.IsValid())
+    return llvm::createStringError("source not found");
 
-  if (source_ref) {
-    lldb::SBProcess process = dap.target.GetProcess();
-    // Upper 32 bits is the thread index ID
-    lldb::SBThread thread =
-        process.GetThreadByIndexID(GetLLDBThreadIndexID(source_ref));
-    // Lower 32 bits is the frame index
-    lldb::SBFrame frame = thread.GetFrameAtIndex(GetLLDBFrameID(source_ref));
-    if (!frame.IsValid()) {
-      response["success"] = false;
-      response["message"] = "source not found";
-    } else {
-      lldb::SBInstructionList insts =
-          frame.GetSymbol().GetInstructions(dap.target);
-      lldb::SBStream stream;
-      insts.GetDescription(stream);
-      body["content"] = stream.GetData();
-      body["mimeType"] = "text/x-lldb.disassembly";
-      response.try_emplace("body", std::move(body));
-    }
-  } else {
-    response["success"] = false;
-    response["message"] =
-        "invalid arguments, expected source.sourceReference to be set";
-  }
+  lldb::SBInstructionList insts = frame.GetSymbol().GetInstructions(dap.target);
+  lldb::SBStream stream;
+  insts.GetDescription(stream);
 
-  dap.SendJSON(llvm::json::Value(std::move(response)));
+  return protocol::SourceResponseBody{/*content=*/stream.GetData(),
+                                      /*mimeType=*/"text/x-lldb.disassembly"};
 }
 
 } // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Protocol.cpp b/lldb/tools/lldb-dap/Protocol.cpp
index b516c0cb19ebf..68cfa40580b31 100644
--- a/lldb/tools/lldb-dap/Protocol.cpp
+++ b/lldb/tools/lldb-dap/Protocol.cpp
@@ -287,5 +287,49 @@ json::Value toJSON(const Message &M) {
   return std::visit([](auto &M) { return toJSON(M); }, M);
 }
 
+bool fromJSON(const llvm::json::Value &Params, Source::PresentationHint &PH,
+              llvm::json::Path P) {
+  auto rawHint = Params.getAsString();
+  if (!rawHint) {
+    P.report("expected a string");
+    return false;
+  }
+  std::optional<Source::PresentationHint> hint =
+      llvm::StringSwitch<std::optional<Source::PresentationHint>>(*rawHint)
+          .Case("normal", Source::PresentationHint::normal)
+          .Case("emphasize", Source::PresentationHint::emphasize)
+          .Case("deemphasize", Source::PresentationHint::deemphasize)
+          .Default(std::nullopt);
+  if (!hint) {
+    P.report("unexpected value");
+    return false;
+  }
+  PH = *hint;
+  return true;
+}
+
+bool fromJSON(const llvm::json::Value &Params, Source &S, llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
+  return O && O.mapOptional("name", S.name) && O.mapOptional("path", S.path) &&
+         O.mapOptional("presentationHint", S.presentationHint) &&
+         O.mapOptional("sourceReference", S.sourceReference);
+}
+
+bool fromJSON(const llvm::json::Value &Params, SourceArguments &SA,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
+  return O && O.mapOptional("source", SA.source) &&
+         O.map("sourceReference", SA.sourceReference);
+}
+
+llvm::json::Value toJSON(const SourceResponseBody &SA) {
+  llvm::json::Object Result{{"content", SA.content}};
+
+  if (SA.mimeType)
+    Result.insert({"mimeType", SA.mimeType});
+
+  return std::move(Result);
+}
+
 } // namespace protocol
 } // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Protocol.h b/lldb/tools/lldb-dap/Protocol.h
index a9a5532fa6bfa..a1219400fdea5 100644
--- a/lldb/tools/lldb-dap/Protocol.h
+++ b/lldb/tools/lldb-dap/Protocol.h
@@ -240,6 +240,137 @@ using Message = std::variant<Request, Response, Event>;
 bool fromJSON(const llvm::json::Value &, Message &, llvm::json::Path);
 llvm::json::Value toJSON(const Message &);
 
+// MARK: Types
+
+// "Source": {
+//   "type": "object",
+//   "description": "A `Source` is a descriptor for source code.\nIt is returned
+//   from the debug adapter as part of a `StackFrame` and it is used by clients
+//   when specifying breakpoints.", "properties": {
+//     "name": {
+//       "type": "string",
+//       "description": "The short name of the source. Every source returned
+//       from the debug adapter has a name.\nWhen sending a source to the debug
+//       adapter this name is optional."
+//     },
+//     "path": {
+//       "type": "string",
+//       "description": "The path of the source to be shown in the UI.\nIt is
+//       only used to locate and load the content of the source if no
+//       `sourceReference` is specified (or its value is 0)."
+//     },
+//     "sourceReference": {
+//       "type": "integer",
+//       "description": "If the value > 0 the contents of the source must be
+//       retrieved through the `source` request (even if a path is
+//       specified).\nSince a `sourceReference` is only valid for a session, it
+//       can not be used to persist a source.\nThe value should be less than or
+//       equal to 2147483647 (2^31-1)."
+//     },
+//     "presentationHint": {
+//       "type": "string",
+//       "description": "A hint for how to present the source in the UI.\nA
+//       value of `deemphasize` can be used to indicate that the source is not
+//       available or that it is skipped on stepping.", "enum": [ "normal",
+//       "emphasize", "deemphasize" ]
+//     },
+//     "origin": {
+//       "type": "string",
+//       "description": "The origin of this source. For example, 'internal
+//       module', 'inlined content from source map', etc."
+//     },
+//     "sources": {
+//       "type": "array",
+//       "items": {
+//         "$ref": "#/definitions/Source"
+//       },
+//       "description": "A list of sources that are related to this source.
+//       These may be the source that generated this source."
+//     },
+//     "adapterData": {
+//       "type": [ "array", "boolean", "integer", "null", "number", "object",
+//       "string" ], "description": "Additional data that a debug adapter might
+//       want to loop through the client.\nThe client should leave the data
+//       intact and persist it across sessions. The client should not interpret
+//       the data."
+//     },
+//     "checksums": {
+//       "type": "array",
+//       "items": {
+//         "$ref": "#/definitions/Checksum"
+//       },
+//       "description": "The checksums associated with this file."
+//     }
+//   }
+// },
+struct Source {
+  enum class PresentationHint { normal, emphasize, deemphasize };
+
+  std::optional<std::string> name;
+  std::optional<std::string> path;
+  std::optional<int64_t> sourceReference;
+  std::optional<PresentationHint> presentationHint;
+
+  // unsupproted keys origin, sources, adapterData, checksums
+};
+bool fromJSON(const llvm::json::Value &, Source &, llvm::json::Path);
+llvm::json::Value toJSON(const Source &);
+
+// MARK: Requests
+
+// "SourceArguments": {
+//   "type": "object",
+//   "description": "Arguments for `source` request.",
+//   "properties": {
+//     "source": {
+//       "$ref": "#/definitions/Source",
+//       "description": "Specifies the source content to load. Either
+//       `source.path` or `source.sourceReference` must be specified."
+//     },
+//     "sourceReference": {
+//       "type": "integer",
+//       "description": "The reference to the source. This is the same as
+//       `source.sourceReference`.\nThis is provided for backward compatibility
+//       since old clients do not understand the `source` attribute."
+//     }
+//   },
+//   "required": [ "sourceReference" ]
+// },
+struct SourceArguments {
+  std::optional<Source> source;
+  int64_t sourceReference;
+};
+bool fromJSON(const llvm::json::Value &, SourceArguments &, llvm::json::Path);
+
+// "SourceResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to `source` request.",
+//     "properties": {
+//       "body": {
+//         "type": "object",
+//         "properties": {
+//           "content": {
+//             "type": "string",
+//             "description": "Content of the source reference."
+//           },
+//           "mimeType": {
+//             "type": "string",
+//             "description": "Content type (MIME type) of the source."
+//           }
+//         },
+//         "required": [ "content" ]
+//       }
+//     },
+//     "required": [ "body" ]
+//   }]
+// },
+struct SourceResponseBody {
+  std::string content;
+  std::optional<std::string> mimeType;
+};
+llvm::json::Value toJSON(const SourceResponseBody &);
+
 } // namespace lldb_dap::protocol
 
 #endif

>From b9fb9805f33634627b2e8fd55419b78729204fe8 Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Thu, 6 Mar 2025 14:13:58 +0100
Subject: [PATCH 3/4] [lldb-dap] Adding support for well typed events.

This adds a mechanism for registering well typed events with the DAP.

For a proof of concept, this updates the 'exited' event to use the new
protocol serialization handlers and updates the call sites to use the
new helper.
---
 lldb/tools/lldb-dap/DAP.h                     | 31 +++++++++++++++++--
 lldb/tools/lldb-dap/EventHelper.cpp           | 10 +-----
 lldb/tools/lldb-dap/EventHelper.h             |  2 --
 .../Handler/InitializeRequestHandler.cpp      |  3 +-
 lldb/tools/lldb-dap/Protocol.cpp              |  4 +++
 lldb/tools/lldb-dap/Protocol.h                | 30 ++++++++++++++++++
 lldb/tools/lldb-dap/lldb-dap.cpp              |  4 +++
 7 files changed, 69 insertions(+), 15 deletions(-)

diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index fbabe763b8171..22b5fdaabee90 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -58,6 +58,9 @@ typedef llvm::StringMap<FunctionBreakpoint> FunctionBreakpointMap;
 typedef llvm::DenseMap<lldb::addr_t, InstructionBreakpoint>
     InstructionBreakpointMap;
 
+/// A debug adapter initiated event.
+template <typename T> using OutgoingEvent = std::function<void(const T &)>;
+
 enum class OutputType { Console, Stdout, Stderr, Telemetry };
 
 /// Buffer size for handling output events.
@@ -123,21 +126,21 @@ struct Variables {
 
 struct StartDebuggingRequestHandler : public lldb::SBCommandPluginInterface {
   DAP &dap;
-  explicit StartDebuggingRequestHandler(DAP &d) : dap(d) {};
+  explicit StartDebuggingRequestHandler(DAP &d) : dap(d){};
   bool DoExecute(lldb::SBDebugger debugger, char **command,
                  lldb::SBCommandReturnObject &result) override;
 };
 
 struct ReplModeRequestHandler : public lldb::SBCommandPluginInterface {
   DAP &dap;
-  explicit ReplModeRequestHandler(DAP &d) : dap(d) {};
+  explicit ReplModeRequestHandler(DAP &d) : dap(d){};
   bool DoExecute(lldb::SBDebugger debugger, char **command,
                  lldb::SBCommandReturnObject &result) override;
 };
 
 struct SendEventRequestHandler : public lldb::SBCommandPluginInterface {
   DAP &dap;
-  explicit SendEventRequestHandler(DAP &d) : dap(d) {};
+  explicit SendEventRequestHandler(DAP &d) : dap(d){};
   bool DoExecute(lldb::SBDebugger debugger, char **command,
                  lldb::SBCommandReturnObject &result) override;
 };
@@ -207,6 +210,14 @@ struct DAP {
   // will contain that expression.
   std::string last_nonempty_var_expression;
 
+  /// Typed Events Handlers
+  /// @{
+
+  /// onExited sends an event that the debuggee has exited.
+  OutgoingEvent<protocol::ExitedEventBody> onExited;
+
+  /// @}
+
   DAP(std::string name, llvm::StringRef path, std::ofstream *log,
       lldb::IOObjectSP input, lldb::IOObjectSP output, ReplMode repl_mode,
       std::vector<std::string> pre_init_commands);
@@ -345,6 +356,10 @@ struct DAP {
     request_handlers[Handler::getCommand()] = std::make_unique<Handler>(*this);
   }
 
+  /// Registeres an event handler for sending Debug Adapter Protocol events.
+  template <typename Body>
+  OutgoingEvent<Body> RegisterEvent(llvm::StringLiteral Event);
+
   /// Debuggee will continue from stopped state.
   void WillContinue() { variables.Clear(); }
 
@@ -378,6 +393,16 @@ struct DAP {
   InstructionBreakpoint *GetInstructionBPFromStopReason(lldb::SBThread &thread);
 };
 
+template <typename Body>
+OutgoingEvent<Body> DAP::RegisterEvent(llvm::StringLiteral Event) {
+  return [&, Event](const Body &B) {
+    protocol::Event Evt;
+    Evt.event = Event;
+    Evt.rawBody = std::move(B);
+    SendJSON(std::move(Evt));
+  };
+}
+
 } // namespace lldb_dap
 
 #endif
diff --git a/lldb/tools/lldb-dap/EventHelper.cpp b/lldb/tools/lldb-dap/EventHelper.cpp
index 705eb0a457d9c..cea9363f98f94 100644
--- a/lldb/tools/lldb-dap/EventHelper.cpp
+++ b/lldb/tools/lldb-dap/EventHelper.cpp
@@ -10,6 +10,7 @@
 #include "DAP.h"
 #include "JSONUtils.h"
 #include "LLDBUtils.h"
+#include "Protocol.h"
 #include "lldb/API/SBFileSpec.h"
 
 #if defined(_WIN32)
@@ -235,13 +236,4 @@ void SendContinuedEvent(DAP &dap) {
   dap.SendJSON(llvm::json::Value(std::move(event)));
 }
 
-// Send a "exited" event to indicate the process has exited.
-void SendProcessExitedEvent(DAP &dap, lldb::SBProcess &process) {
-  llvm::json::Object event(CreateEventObject("exited"));
-  llvm::json::Object body;
-  body.try_emplace("exitCode", (int64_t)process.GetExitStatus());
-  event.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(event)));
-}
-
 } // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/EventHelper.h b/lldb/tools/lldb-dap/EventHelper.h
index 90b009c73089e..a734fdd334aa2 100644
--- a/lldb/tools/lldb-dap/EventHelper.h
+++ b/lldb/tools/lldb-dap/EventHelper.h
@@ -26,8 +26,6 @@ void SendStdOutStdErr(DAP &dap, lldb::SBProcess &process);
 
 void SendContinuedEvent(DAP &dap);
 
-void SendProcessExitedEvent(DAP &dap, lldb::SBProcess &process);
-
 } // namespace lldb_dap
 
 #endif
diff --git a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
index 5bb73a7ec0d85..ea1d6283b527a 100644
--- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
@@ -9,6 +9,7 @@
 #include "DAP.h"
 #include "EventHelper.h"
 #include "JSONUtils.h"
+#include "Protocol.h"
 #include "RequestHandler.h"
 #include "lldb/API/SBEvent.h"
 #include "lldb/API/SBListener.h"
@@ -178,7 +179,7 @@ static void EventThreadFunction(DAP &dap) {
               // Run any exit LLDB commands the user specified in the
               // launch.json
               dap.RunExitCommands();
-              SendProcessExitedEvent(dap, process);
+              dap.onExited(protocol::ExitedEventBody{/*exitCode=*/process.GetExitStatus()});
               dap.SendTerminatedEvent();
               done = true;
             }
diff --git a/lldb/tools/lldb-dap/Protocol.cpp b/lldb/tools/lldb-dap/Protocol.cpp
index 68cfa40580b31..30632ffbddf6e 100644
--- a/lldb/tools/lldb-dap/Protocol.cpp
+++ b/lldb/tools/lldb-dap/Protocol.cpp
@@ -315,6 +315,10 @@ bool fromJSON(const llvm::json::Value &Params, Source &S, llvm::json::Path P) {
          O.mapOptional("sourceReference", S.sourceReference);
 }
 
+llvm::json::Value toJSON(const ExitedEventBody &EEB) {
+  return llvm::json::Object{{"exitCode", EEB.exitCode}};
+}
+
 bool fromJSON(const llvm::json::Value &Params, SourceArguments &SA,
               llvm::json::Path P) {
   llvm::json::ObjectMapper O(Params, P);
diff --git a/lldb/tools/lldb-dap/Protocol.h b/lldb/tools/lldb-dap/Protocol.h
index a1219400fdea5..e51ba378b18f4 100644
--- a/lldb/tools/lldb-dap/Protocol.h
+++ b/lldb/tools/lldb-dap/Protocol.h
@@ -316,6 +316,36 @@ struct Source {
 bool fromJSON(const llvm::json::Value &, Source &, llvm::json::Path);
 llvm::json::Value toJSON(const Source &);
 
+// MARK: Events
+
+// "ExitedEvent": {
+//   "allOf": [ { "$ref": "#/definitions/Event" }, {
+//     "type": "object",
+//     "description": "The event indicates that the debuggee has exited and
+//     returns its exit code.", "properties": {
+//       "event": {
+//         "type": "string",
+//         "enum": [ "exited" ]
+//       },
+//       "body": {
+//         "type": "object",
+//         "properties": {
+//           "exitCode": {
+//             "type": "integer",
+//             "description": "The exit code returned from the debuggee."
+//           }
+//         },
+//         "required": [ "exitCode" ]
+//       }
+//     },
+//     "required": [ "event", "body" ]
+//   }]
+// }
+struct ExitedEventBody {
+  int exitCode;
+};
+llvm::json::Value toJSON(const ExitedEventBody &);
+
 // MARK: Requests
 
 // "SourceArguments": {
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index a5d9978e30248..560154596c0bb 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -9,6 +9,7 @@
 #include "DAP.h"
 #include "EventHelper.h"
 #include "Handler/RequestHandler.h"
+#include "Protocol.h"
 #include "RunInTerminal.h"
 #include "lldb/API/SBDebugger.h"
 #include "lldb/API/SBStream.h"
@@ -151,6 +152,9 @@ static void RegisterRequestCallbacks(DAP &dap) {
 
   // Testing requests
   dap.RegisterRequest<TestGetTargetBreakpointsRequestHandler>();
+
+  // Event handlers
+  dap.onExited = dap.RegisterEvent<protocol::ExitedEventBody>("exited");
 }
 
 static void PrintHelp(LLDBDAPOptTable &table, llvm::StringRef tool_name) {

>From 8700ee9486789f687d0a6bdd30ea6e1bffeb3a10 Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Thu, 27 Feb 2025 15:17:15 -0800
Subject: [PATCH 4/4] [lldb-dap] Adding support for cancelling a request.

Adding support for cancelling requests.

There are two forms of request cancellation.

* Preemptively cancelling a request that is in the queue.
* Actively cancelling the in progress request as a best effort attempt using `SBDebugger.RequestInterrupt()`.
---
 lldb/test/API/tools/lldb-dap/cancel/Makefile  |   3 +
 .../tools/lldb-dap/cancel/TestDAP_cancel.py   |  92 +++++++++++++
 lldb/test/API/tools/lldb-dap/cancel/main.c    |   6 +
 lldb/tools/lldb-dap/CMakeLists.txt            |   1 +
 lldb/tools/lldb-dap/DAP.cpp                   | 125 +++++++++++++++++-
 lldb/tools/lldb-dap/DAP.h                     |   5 +
 .../lldb-dap/Handler/CancelRequestHandler.cpp |  56 ++++++++
 .../Handler/InitializeRequestHandler.cpp      |   3 +
 lldb/tools/lldb-dap/Handler/RequestHandler.h  |  10 ++
 lldb/tools/lldb-dap/Protocol.cpp              |   7 +
 lldb/tools/lldb-dap/Protocol.h                |  68 ++++++++++
 lldb/tools/lldb-dap/Transport.cpp             |   2 +-
 lldb/tools/lldb-dap/Transport.h               |   3 -
 lldb/tools/lldb-dap/lldb-dap.cpp              |   1 +
 14 files changed, 371 insertions(+), 11 deletions(-)
 create mode 100644 lldb/test/API/tools/lldb-dap/cancel/Makefile
 create mode 100644 lldb/test/API/tools/lldb-dap/cancel/TestDAP_cancel.py
 create mode 100644 lldb/test/API/tools/lldb-dap/cancel/main.c
 create mode 100644 lldb/tools/lldb-dap/Handler/CancelRequestHandler.cpp

diff --git a/lldb/test/API/tools/lldb-dap/cancel/Makefile b/lldb/test/API/tools/lldb-dap/cancel/Makefile
new file mode 100644
index 0000000000000..10495940055b6
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/cancel/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/tools/lldb-dap/cancel/TestDAP_cancel.py b/lldb/test/API/tools/lldb-dap/cancel/TestDAP_cancel.py
new file mode 100644
index 0000000000000..d6e964fad3ddd
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/cancel/TestDAP_cancel.py
@@ -0,0 +1,92 @@
+"""
+Test lldb-dap cancel request
+"""
+
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+import lldbdap_testcase
+
+
+class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase):
+    def send_async_req(self, command: str, arguments={}) -> int:
+        seq = self.dap_server.sequence
+        self.dap_server.send_packet(
+            {
+                "type": "request",
+                "command": command,
+                "arguments": arguments,
+            }
+        )
+        return seq
+
+    def async_blocking_request(self, duration: float) -> int:
+        """
+        Sends an evaluate request that will sleep for the specified duration to
+        block the request handling thread.
+        """
+        return self.send_async_req(
+            command="evaluate",
+            arguments={
+                "expression": "`script import time; time.sleep({})".format(duration),
+                "context": "repl",
+            },
+        )
+
+    def async_cancel(self, requestId: int) -> int:
+        return self.send_async_req(command="cancel", arguments={"requestId": requestId})
+
+    def test_pending_request(self):
+        """
+        Tests cancelling a pending request.
+        """
+        program = self.getBuildArtifact("a.out")
+        self.build_and_launch(program, stopOnEntry=True)
+        self.continue_to_next_stop()
+
+        # Use a relatively short timeout since this is only to ensure the
+        # following request is queued.
+        blocking_seq = self.async_blocking_request(duration=1.0)
+        # Use a longer timeout to ensure we catch if the request was interrupted
+        # properly.
+        pending_seq = self.async_blocking_request(duration=self.timeoutval)
+        cancel_seq = self.async_cancel(requestId=pending_seq)
+
+        blocking_resp = self.dap_server.recv_packet(filter_type=["response"])
+        self.assertEqual(blocking_resp["request_seq"], blocking_seq)
+        self.assertEqual(blocking_resp["command"], "evaluate")
+        self.assertEqual(blocking_resp["success"], True)
+
+        pending_resp = self.dap_server.recv_packet(filter_type=["response"])
+        self.assertEqual(pending_resp["request_seq"], pending_seq)
+        self.assertEqual(pending_resp["command"], "evaluate")
+        self.assertEqual(pending_resp["success"], False)
+        self.assertEqual(pending_resp["message"], "cancelled")
+
+        cancel_resp = self.dap_server.recv_packet(filter_type=["response"])
+        self.assertEqual(cancel_resp["request_seq"], cancel_seq)
+        self.assertEqual(cancel_resp["command"], "cancel")
+        self.assertEqual(cancel_resp["success"], True)
+        self.continue_to_exit()
+
+    def test_inflight_request(self):
+        """
+        Tests cancelling an inflight request.
+        """
+        program = self.getBuildArtifact("a.out")
+        self.build_and_launch(program, stopOnEntry=True)
+        self.continue_to_next_stop()
+
+        blocking_seq = self.async_blocking_request(duration=self.timeoutval)
+        cancel_seq = self.async_cancel(requestId=blocking_seq)
+
+        blocking_resp = self.dap_server.recv_packet(filter_type=["response"])
+        self.assertEqual(blocking_resp["request_seq"], blocking_seq)
+        self.assertEqual(blocking_resp["command"], "evaluate")
+        self.assertEqual(blocking_resp["success"], False)
+        self.assertEqual(blocking_resp["message"], "cancelled")
+
+        cancel_resp = self.dap_server.recv_packet(filter_type=["response"])
+        self.assertEqual(cancel_resp["request_seq"], cancel_seq)
+        self.assertEqual(cancel_resp["command"], "cancel")
+        self.assertEqual(cancel_resp["success"], True)
+        self.continue_to_exit()
diff --git a/lldb/test/API/tools/lldb-dap/cancel/main.c b/lldb/test/API/tools/lldb-dap/cancel/main.c
new file mode 100644
index 0000000000000..ecc0d99ec8db7
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/cancel/main.c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+int main(int argc, char const *argv[]) {
+  printf("Hello world!\n");
+  return 0;
+}
diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index 8a76cb58dbcab..d2001a88d405f 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -41,6 +41,7 @@ add_lldb_tool(lldb-dap
   Handler/ResponseHandler.cpp
   Handler/AttachRequestHandler.cpp
   Handler/BreakpointLocationsHandler.cpp
+  Handler/CancelRequestHandler.cpp
   Handler/CompileUnitsRequestHandler.cpp
   Handler/CompletionsHandler.cpp
   Handler/ConfigurationDoneRequestHandler.cpp
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index ce118f36808dc..56b699ca066f7 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -37,6 +37,7 @@
 #include <algorithm>
 #include <cassert>
 #include <chrono>
+#include <condition_variable>
 #include <cstdarg>
 #include <cstdio>
 #include <fstream>
@@ -256,7 +257,27 @@ void DAP::SendJSON(const llvm::json::Value &json) {
     }
     return;
   }
-  auto status = transport.Write(log, M);
+  Send(M);
+}
+
+void DAP::Send(const protocol::Message &M) {
+  lldb_private::Status status;
+  // If the debugger was interrupted while handling a response, mark the
+  // response as cancelled since it may contain partial information.
+  if (const auto *resp = std::get_if<protocol::Response>(&M);
+      debugger.InterruptRequested()) {
+    status = transport.Write(
+        log, protocol::Response{
+                 /*request_seq=*/resp->request_seq,
+                 /*command=*/resp->command,
+                 /*success=*/false,
+                 /*message=*/protocol::Response::Message::cancelled,
+                 /*rawBody=*/std::nullopt,
+             });
+  } else {
+    status = transport.Write(log, M);
+  }
+
   if (status.Fail() && log)
     *log << llvm::formatv("failed to send {0}: {1}\n", llvm::json::Value(M),
                           status.AsCString())
@@ -798,22 +819,112 @@ lldb::SBError DAP::Disconnect(bool terminateDebuggee) {
   return error;
 }
 
+template <typename T>
+static std::optional<T> getArgumentsIfRequest(const protocol::Message &pm,
+                                              llvm::StringLiteral command) {
+  auto *const req = std::get_if<protocol::Request>(&pm);
+  if (!req || req->command != command)
+    return std::nullopt;
+
+  T args;
+  llvm::json::Path::Root root;
+  if (!fromJSON(req->rawArguments, args, root)) {
+    return std::nullopt;
+  }
+
+  return std::move(args);
+}
+
 llvm::Error DAP::Loop() {
-  auto cleanup = llvm::make_scope_exit([this]() { StopEventHandlers(); });
+  std::deque<protocol::Message> queue;
+  std::condition_variable queue_cv;
+  std::mutex queue_mutex;
+  std::thread queue_reader([&]() {
+    auto cleanup = llvm::make_scope_exit([&]() {
+      // Ensure we're marked as disconnecting when the reader exits.
+      disconnecting = true;
+      queue_cv.notify_all();
+    });
+    while (!disconnecting) {
+      protocol::Message next;
+      auto status = transport.Read(log, next);
+      if (status.Fail()) {
+        if (status.GetError() != Transport::kEOF && log)
+          *log << "DAP transport failure: " << status.AsCString() << std::endl;
+        break;
+      }
+
+      {
+        std::lock_guard<std::mutex> lock(queue_mutex);
+
+        // If a cancel is requested for the active request, make a best
+        // effort attempt to interrupt.
+        if (const auto args = getArgumentsIfRequest<protocol::CancelArguments>(
+                next, "cancel");
+            args && active_seq == args->requestId)
+          debugger.RequestInterrupt();
+
+        queue.push_back(std::move(next));
+      }
+      queue_cv.notify_one();
+    }
+  });
+
+  auto cleanup = llvm::make_scope_exit([&]() {
+    StopEventHandlers();
+    queue_reader.join();
+  });
+
   while (!disconnecting) {
     protocol::Message next;
-    auto status = transport.Read(log, next);
-    if (status.Fail()) {
-      // On EOF, simply break out of the loop.
-      if (status.GetError() == Transport::kEOF)
+    {
+      std::unique_lock<std::mutex> lock(queue_mutex);
+      queue_cv.wait(lock, [&] { return disconnecting || !queue.empty(); });
+
+      if (queue.empty())
         break;
-      return status.takeError();
+
+      next = queue.front();
+      queue.pop_front();
+
+      if (protocol::Request *req = std::get_if<protocol::Request>(&next)) {
+        active_seq = req->seq;
+
+        // Check if we should preempt this request from a queued cancel.
+        bool cancelled = false;
+        for (const auto &message : queue) {
+          if (const auto args =
+                  getArgumentsIfRequest<protocol::CancelArguments>(message,
+                                                                   "cancel");
+              args && args->requestId == req->seq) {
+            cancelled = true;
+            break;
+          }
+        }
+
+        // Preempt the request and immeidately respond with cancelled.
+        if (cancelled) {
+          Send(protocol::Response{
+              /*request_seq=*/req->seq,
+              /*command=*/req->command,
+              /*success=*/false,
+              /*message=*/protocol::Response::Message::cancelled,
+              /*rawBody=*/std::nullopt,
+          });
+          continue;
+        }
+      } else
+        active_seq = 0;
     }
 
     if (!HandleObject(next)) {
       return llvm::createStringError(llvm::inconvertibleErrorCode(),
                                      "unhandled packet");
     }
+
+    // Clear interrupt marker prior to handling the next request.
+    if (debugger.InterruptRequested())
+      debugger.CancelInterruptRequest();
   }
 
   return llvm::Error::success();
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index 22b5fdaabee90..04441a25f2c62 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -391,6 +391,11 @@ struct DAP {
   InstructionBreakpoint *GetInstructionBreakpoint(const lldb::break_id_t bp_id);
 
   InstructionBreakpoint *GetInstructionBPFromStopReason(lldb::SBThread &thread);
+
+private:
+  void Send(const protocol::Message &M);
+
+  std::atomic<int64_t> active_seq;
 };
 
 template <typename Body>
diff --git a/lldb/tools/lldb-dap/Handler/CancelRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/CancelRequestHandler.cpp
new file mode 100644
index 0000000000000..d36ca6e143f5a
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/CancelRequestHandler.cpp
@@ -0,0 +1,56 @@
+//===-- SourceRequestHandler.cpp ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Protocol.h"
+#include "RequestHandler.h"
+#include "llvm/Support/Error.h"
+
+using namespace lldb_dap;
+using namespace lldb_dap::protocol;
+
+namespace lldb_dap {
+
+// "CancelRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "The `cancel` request is used by the client in two
+//     situations:\n- to indicate that it is no longer interested in the result
+//     produced by a specific request issued earlier\n- to cancel a progress
+//     sequence.\nClients should only call this request if the corresponding
+//     capability `supportsCancelRequest` is true.\nThis request has a hint
+//     characteristic: a debug adapter can only be expected to make a 'best
+//     effort' in honoring this request but there are no guarantees.\nThe
+//     `cancel` request may return an error if it could not cancel an operation
+//     but a client should refrain from presenting this error to end users.\nThe
+//     request that got cancelled still needs to send a response back. This can
+//     either be a normal result (`success` attribute true) or an error response
+//     (`success` attribute false and the `message` set to
+//     `cancelled`).\nReturning partial results from a cancelled request is
+//     possible but please note that a client has no generic way for detecting
+//     that a response is partial or not.\nThe progress that got cancelled still
+//     needs to send a `progressEnd` event back.\n A client should not assume
+//     that progress just got cancelled after sending the `cancel` request.",
+//     "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "cancel" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/CancelArguments"
+//       }
+//     },
+//     "required": [ "command" ]
+//   }]
+// },
+llvm::Expected<CancelResponseBody>
+CancelRequestHandler::Run(const CancelArguments &arguments) const {
+  /* no-op, simple ack of the request. */
+  return nullptr;
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
index ea1d6283b527a..d58b7bc4f7900 100644
--- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
@@ -422,6 +422,7 @@ void InitializeRequestHandler::operator()(
   // The debug adapter support for instruction breakpoint.
   body.try_emplace("supportsInstructionBreakpoints", true);
 
+
   llvm::json::Array completion_characters;
   completion_characters.emplace_back(".");
   completion_characters.emplace_back(" ");
@@ -464,6 +465,8 @@ void InitializeRequestHandler::operator()(
   body.try_emplace("supportsDataBreakpoints", true);
   // The debug adapter supports the `readMemory` request.
   body.try_emplace("supportsReadMemoryRequest", true);
+  // The debug adapter supports the `cancel` request.
+  body.try_emplace("supportsCancelRequest", true);
 
   // Put in non-DAP specification lldb specific information.
   llvm::json::Object lldb_json;
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index da65b2de9ab99..b00dd4cd4b288 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -354,6 +354,16 @@ class ReadMemoryRequestHandler : public BaseRequestHandler {
   void operator()(const llvm::json::Object &request) const override;
 };
 
+class CancelRequestHandler
+    : public RequestHandler<protocol::CancelArguments,
+                            protocol::CancelResponseBody> {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "cancel"; }
+  llvm::Expected<protocol::CancelResponseBody>
+  Run(const protocol::CancelArguments &args) const override;
+};
+
 /// A request used in testing to get the details on all breakpoints that are
 /// currently set in the target. This helps us to test "setBreakpoints" and
 /// "setFunctionBreakpoints" requests to verify we have the correct set of
diff --git a/lldb/tools/lldb-dap/Protocol.cpp b/lldb/tools/lldb-dap/Protocol.cpp
index 30632ffbddf6e..1d1b87e6eaff3 100644
--- a/lldb/tools/lldb-dap/Protocol.cpp
+++ b/lldb/tools/lldb-dap/Protocol.cpp
@@ -319,6 +319,13 @@ llvm::json::Value toJSON(const ExitedEventBody &EEB) {
   return llvm::json::Object{{"exitCode", EEB.exitCode}};
 }
 
+bool fromJSON(const llvm::json::Value &Params, CancelArguments &CA,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
+  return O && O.mapOptional("requestId", CA.requestId) &&
+         O.mapOptional("progressId", CA.progressId);
+}
+
 bool fromJSON(const llvm::json::Value &Params, SourceArguments &SA,
               llvm::json::Path P) {
   llvm::json::ObjectMapper O(Params, P);
diff --git a/lldb/tools/lldb-dap/Protocol.h b/lldb/tools/lldb-dap/Protocol.h
index e51ba378b18f4..29878dc7e570a 100644
--- a/lldb/tools/lldb-dap/Protocol.h
+++ b/lldb/tools/lldb-dap/Protocol.h
@@ -21,6 +21,7 @@
 #define LLDB_TOOLS_LLDB_DAP_PROTOCOL_H
 
 #include "llvm/Support/JSON.h"
+#include <cstddef>
 #include <cstdint>
 #include <optional>
 #include <string>
@@ -152,6 +153,8 @@ struct Response {
 bool fromJSON(const llvm::json::Value &, Response &, llvm::json::Path);
 llvm::json::Value toJSON(const Response &);
 
+using VoidResponseBody = std::nullptr_t;
+
 // "Message": {
 //   "type": "object",
 //   "description": "A structured message object. Used to return errors from
@@ -348,6 +351,71 @@ llvm::json::Value toJSON(const ExitedEventBody &);
 
 // MARK: Requests
 
+// "CancelRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "The `cancel` request is used by the client in two
+//     situations:\n- to indicate that it is no longer interested in the result
+//     produced by a specific request issued earlier\n- to cancel a progress
+//     sequence.\nClients should only call this request if the corresponding
+//     capability `supportsCancelRequest` is true.\nThis request has a hint
+//     characteristic: a debug adapter can only be expected to make a 'best
+//     effort' in honoring this request but there are no guarantees.\nThe
+//     `cancel` request may return an error if it could not cancel an operation
+//     but a client should refrain from presenting this error to end users.\nThe
+//     request that got cancelled still needs to send a response back. This can
+//     either be a normal result (`success` attribute true) or an error response
+//     (`success` attribute false and the `message` set to
+//     `cancelled`).\nReturning partial results from a cancelled request is
+//     possible but please note that a client has no generic way for detecting
+//     that a response is partial or not.\nThe progress that got cancelled still
+//     needs to send a `progressEnd` event back.\n A client should not assume
+//     that progress just got cancelled after sending the `cancel` request.",
+//     "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "cancel" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/CancelArguments"
+//       }
+//     },
+//     "required": [ "command" ]
+//   }]
+// },
+// "CancelArguments": {
+//   "type": "object",
+//   "description": "Arguments for `cancel` request.",
+//   "properties": {
+//     "requestId": {
+//       "type": "integer",
+//       "description": "The ID (attribute `seq`) of the request to cancel. If
+//       missing no request is cancelled.\nBoth a `requestId` and a `progressId`
+//       can be specified in one request."
+//     },
+//     "progressId": {
+//       "type": "string",
+//       "description": "The ID (attribute `progressId`) of the progress to
+//       cancel. If missing no progress is cancelled.\nBoth a `requestId` and a
+//       `progressId` can be specified in one request."
+//     }
+//   }
+// },
+struct CancelArguments {
+  std::optional<int64_t> requestId;
+  std::optional<int64_t> progressId;
+};
+bool fromJSON(const llvm::json::Value &, CancelArguments &, llvm::json::Path);
+
+// "CancelResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to `cancel` request. This is just an
+//     acknowledgement, so no body field is required."
+//   }]
+// },
+using CancelResponseBody = VoidResponseBody;
+
 // "SourceArguments": {
 //   "type": "object",
 //   "description": "Arguments for `source` request.",
diff --git a/lldb/tools/lldb-dap/Transport.cpp b/lldb/tools/lldb-dap/Transport.cpp
index f46bd78192332..a02b2f4a6db83 100644
--- a/lldb/tools/lldb-dap/Transport.cpp
+++ b/lldb/tools/lldb-dap/Transport.cpp
@@ -67,7 +67,7 @@ static Status ReadExpected(IOObjectSP &descriptor, llvm::StringRef expected) {
   if (status.Fail())
     return status;
   if (expected != result) {
-    return Status::FromErrorStringWithFormatv("expected %s, got %s", expected,
+    return Status::FromErrorStringWithFormatv("expected {0}, got {1}", expected,
                                               result);
   }
   return Status();
diff --git a/lldb/tools/lldb-dap/Transport.h b/lldb/tools/lldb-dap/Transport.h
index 041ceca7446c6..14b0b237ed0cf 100644
--- a/lldb/tools/lldb-dap/Transport.h
+++ b/lldb/tools/lldb-dap/Transport.h
@@ -42,9 +42,6 @@ class Transport {
   /// Reads the next Debug Adater Protocol message from the input stream.
   lldb_private::Status Read(std::ofstream *log, protocol::Message &M);
 
-  /// Closes the transport.
-  void CloseOutput();
-
 private:
   llvm::StringRef m_client_name;
   lldb::IOObjectSP m_input;
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 560154596c0bb..8a61b329b1551 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -116,6 +116,7 @@ class LLDBDAPOptTable : public llvm::opt::GenericOptTable {
 static void RegisterRequestCallbacks(DAP &dap) {
   dap.RegisterRequest<AttachRequestHandler>();
   dap.RegisterRequest<BreakpointLocationsRequestHandler>();
+  dap.RegisterRequest<CancelRequestHandler>();
   dap.RegisterRequest<CompletionsRequestHandler>();
   dap.RegisterRequest<ConfigurationDoneRequestHandler>();
   dap.RegisterRequest<ContinueRequestHandler>();



More information about the lldb-commits mailing list