Skip to content

[lldb-dap] Reimplement runInTerminal with signals #142374

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

SuibianP
Copy link
Contributor

@SuibianP SuibianP commented Jun 2, 2025

runInTerminal is currently implemented using JSON messages over FIFOs, which feels too clunky for the simple job of passing a PID.

Instead, take advantage of si_pid available from sa_sigaction handler, and send a user signal instead. Both synchronisation and timeout are preserved with the protocol below,

  1. Debugger waits for SIGUSR1 under timeout with pselect
  2. Launcher forks into child, sends SIGUSR1 to debugger and suspends
  3. Debugger receives SIGUSR1, captures the sender (launcher child) PID, attaches to and resumes it
  4. Launcher child resumes and exec's into target
  5. Launcher parent waitpid's and kills irresponsive child after timeout

With this, the entirety of FifoFiles and RunInTerminal can be dropped.

Refs: #121269 (comment)

@llvmbot
Copy link
Member

llvmbot commented Jun 2, 2025

@llvm/pr-subscribers-lldb

Author: Hu Jialun (SuibianP)

Changes

runInTerminal is currently implemented using JSON messages over FIFOs, which feels too clunky for the simple job of passing a PID.

Instead, take advantage of si_pid available from sa_sigaction handler, and send a user signal instead. Both synchronisation and timeout are preserved with the protocol below,

  1. Debugger waits for SIGUSR1 under timeout with pselect
  2. Launcher forks into child, sends SIGUSR1 to debugger and suspends
  3. Debugger receives SIGUSR1, captures the sender (launcher child) PID, attaches to and resumes it
  4. Launcher child resumes and exec's into target
  5. Launcher parent waitpid's and kills irresponsive child after timeout

With this, the entirety of FifoFiles and RunInTerminal can be dropped.

Refs: #121269 (comment)


Patch is 36.38 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/142374.diff

11 Files Affected:

  • (modified) lldb/test/API/tools/lldb-dap/runInTerminal/TestDAP_runInTerminal.py (-117)
  • (modified) lldb/tools/lldb-dap/CMakeLists.txt (-2)
  • (removed) lldb/tools/lldb-dap/FifoFiles.cpp (-101)
  • (removed) lldb/tools/lldb-dap/FifoFiles.h (-85)
  • (modified) lldb/tools/lldb-dap/Handler/RequestHandler.cpp (+72-27)
  • (modified) lldb/tools/lldb-dap/JSONUtils.cpp (+2-3)
  • (modified) lldb/tools/lldb-dap/JSONUtils.h (+1-4)
  • (modified) lldb/tools/lldb-dap/Options.td (+5-10)
  • (removed) lldb/tools/lldb-dap/RunInTerminal.cpp (-170)
  • (removed) lldb/tools/lldb-dap/RunInTerminal.h (-131)
  • (modified) lldb/tools/lldb-dap/tool/lldb-dap.cpp (+43-44)
diff --git a/lldb/test/API/tools/lldb-dap/runInTerminal/TestDAP_runInTerminal.py b/lldb/test/API/tools/lldb-dap/runInTerminal/TestDAP_runInTerminal.py
index 65c931210d400..3b6571621e541 100644
--- a/lldb/test/API/tools/lldb-dap/runInTerminal/TestDAP_runInTerminal.py
+++ b/lldb/test/API/tools/lldb-dap/runInTerminal/TestDAP_runInTerminal.py
@@ -131,120 +131,3 @@ def test_runInTerminalInvalidTarget(self):
             "'INVALIDPROGRAM' does not exist",
             response["body"]["error"]["format"],
         )
-
-    @skipIfWindows
-    @skipIf(oslist=["linux"], archs=no_match(["x86_64"]))
-    def test_missingArgInRunInTerminalLauncher(self):
-        if not self.isTestSupported():
-            return
-        proc = subprocess.run(
-            [self.lldbDAPExec, "--launch-target", "INVALIDPROGRAM"],
-            capture_output=True,
-            universal_newlines=True,
-        )
-        self.assertNotEqual(proc.returncode, 0)
-        self.assertIn(
-            '"--launch-target" requires "--comm-file" to be specified', proc.stderr
-        )
-
-    @skipIfWindows
-    @skipIf(oslist=["linux"], archs=no_match(["x86_64"]))
-    def test_FakeAttachedRunInTerminalLauncherWithInvalidProgram(self):
-        if not self.isTestSupported():
-            return
-        comm_file = os.path.join(self.getBuildDir(), "comm-file")
-        os.mkfifo(comm_file)
-
-        proc = subprocess.Popen(
-            [
-                self.lldbDAPExec,
-                "--comm-file",
-                comm_file,
-                "--launch-target",
-                "INVALIDPROGRAM",
-            ],
-            universal_newlines=True,
-            stderr=subprocess.PIPE,
-        )
-
-        self.readPidMessage(comm_file)
-        self.sendDidAttachMessage(comm_file)
-        self.assertIn("No such file or directory", self.readErrorMessage(comm_file))
-
-        _, stderr = proc.communicate()
-        self.assertIn("No such file or directory", stderr)
-
-    @skipIfWindows
-    @skipIf(oslist=["linux"], archs=no_match(["x86_64"]))
-    def test_FakeAttachedRunInTerminalLauncherWithValidProgram(self):
-        if not self.isTestSupported():
-            return
-        comm_file = os.path.join(self.getBuildDir(), "comm-file")
-        os.mkfifo(comm_file)
-
-        proc = subprocess.Popen(
-            [
-                self.lldbDAPExec,
-                "--comm-file",
-                comm_file,
-                "--launch-target",
-                "echo",
-                "foo",
-            ],
-            universal_newlines=True,
-            stdout=subprocess.PIPE,
-        )
-
-        self.readPidMessage(comm_file)
-        self.sendDidAttachMessage(comm_file)
-
-        stdout, _ = proc.communicate()
-        self.assertIn("foo", stdout)
-
-    @skipIfWindows
-    @skipIf(oslist=["linux"], archs=no_match(["x86_64"]))
-    def test_FakeAttachedRunInTerminalLauncherAndCheckEnvironment(self):
-        if not self.isTestSupported():
-            return
-        comm_file = os.path.join(self.getBuildDir(), "comm-file")
-        os.mkfifo(comm_file)
-
-        proc = subprocess.Popen(
-            [self.lldbDAPExec, "--comm-file", comm_file, "--launch-target", "env"],
-            universal_newlines=True,
-            stdout=subprocess.PIPE,
-            env={**os.environ, "FOO": "BAR"},
-        )
-
-        self.readPidMessage(comm_file)
-        self.sendDidAttachMessage(comm_file)
-
-        stdout, _ = proc.communicate()
-        self.assertIn("FOO=BAR", stdout)
-
-    @skipIfWindows
-    @skipIf(oslist=["linux"], archs=no_match(["x86_64"]))
-    def test_NonAttachedRunInTerminalLauncher(self):
-        if not self.isTestSupported():
-            return
-        comm_file = os.path.join(self.getBuildDir(), "comm-file")
-        os.mkfifo(comm_file)
-
-        proc = subprocess.Popen(
-            [
-                self.lldbDAPExec,
-                "--comm-file",
-                comm_file,
-                "--launch-target",
-                "echo",
-                "foo",
-            ],
-            universal_newlines=True,
-            stderr=subprocess.PIPE,
-            env={**os.environ, "LLDB_DAP_RIT_TIMEOUT_IN_MS": "1000"},
-        )
-
-        self.readPidMessage(comm_file)
-
-        _, stderr = proc.communicate()
-        self.assertIn("Timed out trying to get messages from the debug adapter", stderr)
diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index 40cba16c141f0..3e7e88afa9ced 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -14,7 +14,6 @@ add_lldb_library(lldbDAP
   DAPLog.cpp
   EventHelper.cpp
   ExceptionBreakpoint.cpp
-  FifoFiles.cpp
   FunctionBreakpoint.cpp
   InstructionBreakpoint.cpp
   JSONUtils.cpp
@@ -22,7 +21,6 @@ add_lldb_library(lldbDAP
   OutputRedirector.cpp
   ProgressEvent.cpp
   ProtocolUtils.cpp
-  RunInTerminal.cpp
   SourceBreakpoint.cpp
   Transport.cpp
   Variables.cpp
diff --git a/lldb/tools/lldb-dap/FifoFiles.cpp b/lldb/tools/lldb-dap/FifoFiles.cpp
deleted file mode 100644
index 1f1bba80bd3b1..0000000000000
--- a/lldb/tools/lldb-dap/FifoFiles.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-//===-- FifoFiles.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 "FifoFiles.h"
-#include "JSONUtils.h"
-
-#if !defined(_WIN32)
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#endif
-
-#include <chrono>
-#include <fstream>
-#include <future>
-#include <optional>
-
-using namespace llvm;
-
-namespace lldb_dap {
-
-FifoFile::FifoFile(StringRef path) : m_path(path) {}
-
-FifoFile::~FifoFile() {
-#if !defined(_WIN32)
-  unlink(m_path.c_str());
-#endif
-}
-
-Expected<std::shared_ptr<FifoFile>> CreateFifoFile(StringRef path) {
-#if defined(_WIN32)
-  return createStringError(inconvertibleErrorCode(), "Unimplemented");
-#else
-  if (int err = mkfifo(path.data(), 0600))
-    return createStringError(std::error_code(err, std::generic_category()),
-                             "Couldn't create fifo file: %s", path.data());
-  return std::make_shared<FifoFile>(path);
-#endif
-}
-
-FifoFileIO::FifoFileIO(StringRef fifo_file, StringRef other_endpoint_name)
-    : m_fifo_file(fifo_file), m_other_endpoint_name(other_endpoint_name) {}
-
-Expected<json::Value> FifoFileIO::ReadJSON(std::chrono::milliseconds timeout) {
-  // We use a pointer for this future, because otherwise its normal destructor
-  // would wait for the getline to end, rendering the timeout useless.
-  std::optional<std::string> line;
-  std::future<void> *future =
-      new std::future<void>(std::async(std::launch::async, [&]() {
-        std::ifstream reader(m_fifo_file, std::ifstream::in);
-        std::string buffer;
-        std::getline(reader, buffer);
-        if (!buffer.empty())
-          line = buffer;
-      }));
-  if (future->wait_for(timeout) == std::future_status::timeout || !line)
-    // Indeed this is a leak, but it's intentional. "future" obj destructor
-    //  will block on waiting for the worker thread to join. And the worker
-    //  thread might be stuck in blocking I/O. Intentionally leaking the  obj
-    //  as a hack to avoid blocking main thread, and adding annotation to
-    //  supress static code inspection warnings
-
-    // coverity[leaked_storage]
-    return createStringError(inconvertibleErrorCode(),
-                             "Timed out trying to get messages from the " +
-                                 m_other_endpoint_name);
-  delete future;
-  return json::parse(*line);
-}
-
-Error FifoFileIO::SendJSON(const json::Value &json,
-                           std::chrono::milliseconds timeout) {
-  bool done = false;
-  std::future<void> *future =
-      new std::future<void>(std::async(std::launch::async, [&]() {
-        std::ofstream writer(m_fifo_file, std::ofstream::out);
-        writer << JSONToString(json) << std::endl;
-        done = true;
-      }));
-  if (future->wait_for(timeout) == std::future_status::timeout || !done) {
-    // Indeed this is a leak, but it's intentional. "future" obj destructor will
-    // block on waiting for the worker thread to join. And the worker thread
-    // might be stuck in blocking I/O. Intentionally leaking the  obj as a hack
-    // to avoid blocking main thread, and adding annotation to supress static
-    // code inspection warnings"
-
-    // coverity[leaked_storage]
-    return createStringError(inconvertibleErrorCode(),
-                             "Timed out trying to send messages to the " +
-                                 m_other_endpoint_name);
-  }
-  delete future;
-  return Error::success();
-}
-
-} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/FifoFiles.h b/lldb/tools/lldb-dap/FifoFiles.h
deleted file mode 100644
index 633ebeb2aedd4..0000000000000
--- a/lldb/tools/lldb-dap/FifoFiles.h
+++ /dev/null
@@ -1,85 +0,0 @@
-//===-- FifoFiles.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_FIFOFILES_H
-#define LLDB_TOOLS_LLDB_DAP_FIFOFILES_H
-
-#include "llvm/Support/Error.h"
-#include "llvm/Support/JSON.h"
-
-#include <chrono>
-
-namespace lldb_dap {
-
-/// Struct that controls the life of a fifo file in the filesystem.
-///
-/// The file is destroyed when the destructor is invoked.
-struct FifoFile {
-  FifoFile(llvm::StringRef path);
-
-  ~FifoFile();
-
-  std::string m_path;
-};
-
-/// Create a fifo file in the filesystem.
-///
-/// \param[in] path
-///     The path for the fifo file.
-///
-/// \return
-///     A \a std::shared_ptr<FifoFile> if the file could be created, or an
-///     \a llvm::Error in case of failures.
-llvm::Expected<std::shared_ptr<FifoFile>> CreateFifoFile(llvm::StringRef path);
-
-class FifoFileIO {
-public:
-  /// \param[in] fifo_file
-  ///     The path to an input fifo file that exists in the file system.
-  ///
-  /// \param[in] other_endpoint_name
-  ///     A human readable name for the other endpoint that will communicate
-  ///     using this file. This is used for error messages.
-  FifoFileIO(llvm::StringRef fifo_file, llvm::StringRef other_endpoint_name);
-
-  /// Read the next JSON object from the underlying input fifo file.
-  ///
-  /// The JSON object is expected to be a single line delimited with \a
-  /// std::endl.
-  ///
-  /// \return
-  ///     An \a llvm::Error object indicating the success or failure of this
-  ///     operation. Failures arise if the timeout is hit, the next line of text
-  ///     from the fifo file is not a valid JSON object, or is it impossible to
-  ///     read from the file.
-  llvm::Expected<llvm::json::Value> ReadJSON(std::chrono::milliseconds timeout);
-
-  /// Serialize a JSON object and write it to the underlying output fifo file.
-  ///
-  /// \param[in] json
-  ///     The JSON object to send. It will be printed as a single line delimited
-  ///     with \a std::endl.
-  ///
-  /// \param[in] timeout
-  ///     A timeout for how long we should until for the data to be consumed.
-  ///
-  /// \return
-  ///     An \a llvm::Error object indicating whether the data was consumed by
-  ///     a reader or not.
-  llvm::Error SendJSON(
-      const llvm::json::Value &json,
-      std::chrono::milliseconds timeout = std::chrono::milliseconds(20000));
-
-private:
-  std::string m_fifo_file;
-  std::string m_other_endpoint_name;
-};
-
-} // namespace lldb_dap
-
-#endif // LLDB_TOOLS_LLDB_DAP_FIFOFILES_H
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
index 93bc80a38e29d..cfcbabbb16a93 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
@@ -14,10 +14,12 @@
 #include "LLDBUtils.h"
 #include "Protocol/ProtocolBase.h"
 #include "Protocol/ProtocolRequests.h"
-#include "RunInTerminal.h"
 #include "lldb/API/SBDefines.h"
 #include "lldb/API/SBEnvironment.h"
 #include "llvm/Support/Error.h"
+#include <cerrno>
+#include <csignal>
+#include <limits>
 #include <mutex>
 
 #if !defined(_WIN32)
@@ -51,6 +53,70 @@ static uint32_t SetLaunchFlag(uint32_t flags, bool flag,
   return flags;
 }
 
+// Assume single thread
+class PidReceiver {
+private:
+  inline static volatile std::sig_atomic_t pid;
+  sigset_t oldmask;
+  struct sigaction oldaction;
+
+  static_assert(std::numeric_limits<volatile sig_atomic_t>::max() >=
+                    std::numeric_limits<pid_t>::max(),
+                "sig_atomic_t must be able to hold a pid_t value");
+
+  static void sig_handler(int, siginfo_t *info, void *) {
+    // Store the PID from the signal info
+    pid = info->si_pid;
+  }
+
+  PidReceiver(const PidReceiver &) = delete;
+  PidReceiver &operator=(const PidReceiver &) = delete;
+  PidReceiver(PidReceiver &&) = delete;
+
+public:
+  PidReceiver() {
+    pid = LLDB_INVALID_PROCESS_ID;
+    // sigprocmask and sigaction can only fail by programmer error
+    // Defer SIGUSR1, otherwise we might receive it out of pselect and hang
+    sigset_t mask;
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGUSR1);
+    assert(!sigprocmask(SIG_BLOCK, &mask, &oldmask));
+
+    // Set up sigaction for SIGUSR1 with SA_SIGINFO
+    struct sigaction sa;
+    std::memset(&sa, 0, sizeof(sa));
+    sa.sa_sigaction = sig_handler;
+    sa.sa_flags = SA_SIGINFO;
+    sigemptyset(&sa.sa_mask);
+    assert(!sigaction(SIGUSR1, &sa, &oldaction));
+  }
+
+  ~PidReceiver() {
+    // Restore the old signal mask
+    assert(!sigprocmask(SIG_SETMASK, &oldmask, nullptr));
+    assert(!sigaction(SIGUSR1, &oldaction, nullptr));
+  }
+
+  llvm::Expected<lldb::pid_t> WaitForPid() {
+    // Wait for the signal to be received
+    while (pid == LLDB_INVALID_PROCESS_ID) {
+      struct timespec timeout;
+      timeout.tv_sec = 5;
+      timeout.tv_nsec = 0;
+      auto ret = pselect(0, nullptr, nullptr, nullptr, &timeout, &oldmask);
+      if (ret == -1 && errno != EINTR) {
+        return llvm::createStringError(
+            std::error_code(errno, std::generic_category()), "pselect failed");
+      } else if (ret == 0) {
+        return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                       "Timed out waiting for signal");
+      }
+    }
+    return pid;
+  }
+};
+
 static llvm::Error
 RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) {
   if (!dap.clientFeatures.contains(
@@ -65,26 +131,19 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) {
   dap.is_attach = true;
   lldb::SBAttachInfo attach_info;
 
-  llvm::Expected<std::shared_ptr<FifoFile>> comm_file_or_err =
-      CreateRunInTerminalCommFile();
-  if (!comm_file_or_err)
-    return comm_file_or_err.takeError();
-  FifoFile &comm_file = *comm_file_or_err.get();
-
-  RunInTerminalDebugAdapterCommChannel comm_channel(comm_file.m_path);
-
   lldb::pid_t debugger_pid = LLDB_INVALID_PROCESS_ID;
 #if !defined(_WIN32)
   debugger_pid = getpid();
 #endif
 
+  auto pid_receiver = PidReceiver();
   llvm::json::Object reverse_request = CreateRunInTerminalReverseRequest(
       arguments.configuration.program, arguments.args, arguments.env,
-      arguments.cwd, comm_file.m_path, debugger_pid);
+      arguments.cwd, debugger_pid);
   dap.SendReverseRequest<LogFailureResponseHandler>("runInTerminal",
                                                     std::move(reverse_request));
 
-  if (llvm::Expected<lldb::pid_t> pid = comm_channel.GetLauncherPid())
+  if (llvm::Expected<lldb::pid_t> pid = pid_receiver.WaitForPid())
     attach_info.SetProcessID(*pid);
   else
     return pid.takeError();
@@ -95,13 +154,7 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) {
 
   if (error.Fail())
     return llvm::createStringError(llvm::inconvertibleErrorCode(),
-                                   "Failed to attach to the target process. %s",
-                                   comm_channel.GetLauncherError().c_str());
-  // This will notify the runInTerminal launcher that we attached.
-  // We have to make this async, as the function won't return until the launcher
-  // resumes and reads the data.
-  std::future<lldb::SBError> did_attach_message_success =
-      comm_channel.NotifyDidAttach();
+                                   "Failed to attach to the target process.");
 
   // We just attached to the runInTerminal launcher, which was waiting to be
   // attached. We now resume it, so it can receive the didAttach notification
@@ -115,15 +168,7 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) {
   // we return the debugger to its sync state.
   scope_sync_mode.reset();
 
-  // If sending the notification failed, the launcher should be dead by now and
-  // the async didAttach notification should have an error message, so we
-  // return it. Otherwise, everything was a success.
-  did_attach_message_success.wait();
-  error = did_attach_message_success.get();
-  if (error.Success())
-    return llvm::Error::success();
-  return llvm::createStringError(llvm::inconvertibleErrorCode(),
-                                 error.GetCString());
+  return llvm::Error::success();
 }
 
 void BaseRequestHandler::Run(const Request &request) {
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index 573f3eba00f62..bf10742e18bb8 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -1238,15 +1238,14 @@ llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit &unit) {
 llvm::json::Object CreateRunInTerminalReverseRequest(
     llvm::StringRef program, const std::vector<std::string> &args,
     const llvm::StringMap<std::string> &env, llvm::StringRef cwd,
-    llvm::StringRef comm_file, lldb::pid_t debugger_pid) {
+    lldb::pid_t debugger_pid) {
   llvm::json::Object run_in_terminal_args;
   // This indicates the IDE to open an embedded terminal, instead of opening
   // the terminal in a new window.
   run_in_terminal_args.try_emplace("kind", "integrated");
 
   // The program path must be the first entry in the "args" field
-  std::vector<std::string> req_args = {DAP::debug_adapter_path.str(),
-                                       "--comm-file", comm_file.str()};
+  std::vector<std::string> req_args = {DAP::debug_adapter_path.str()};
   if (debugger_pid != LLDB_INVALID_PROCESS_ID) {
     req_args.push_back("--debugger-pid");
     req_args.push_back(std::to_string(debugger_pid));
diff --git a/lldb/tools/lldb-dap/JSONUtils.h b/lldb/tools/lldb-dap/JSONUtils.h
index 08699a94bbd87..4ed66e6992be2 100644
--- a/lldb/tools/lldb-dap/JSONUtils.h
+++ b/lldb/tools/lldb-dap/JSONUtils.h
@@ -466,9 +466,6 @@ llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit &unit);
 /// \param[in] cwd
 ///     The working directory for the run in terminal request.
 ///
-/// \param[in] comm_file
-///     The fifo file used to communicate the with the target launcher.
-///
 /// \param[in] debugger_pid
 ///     The PID of the lldb-dap instance that will attach to the target. The
 ///     launcher uses it on Linux tell the kernel that it should allow the
@@ -480,7 +477,7 @@ llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit &unit);
 llvm::json::Object CreateRunInTerminalReverseRequest(
     llvm::StringRef program, const std::vector<std::string> &args,
     const llvm::StringMap<std::string> &env, llvm::StringRef cwd,
-    llvm::StringRef comm_file, lldb::pid_t debugger_pid);
+    lldb::pid_t debugger_pid);
 
 /// Create a "Terminated" JSON object that contains statistics
 ///
diff --git a/lldb/tools/lldb-dap/Options.td b/lldb/tools/lldb-dap/Options.td
index aecf91797ac70..669d6a7c0df05 100644
--- a/lldb/tools/lldb-dap/Options.td
+++ b/lldb/tools/lldb-dap/Options.td
@@ -31,16 +31,11 @@ def connection
           "Connections are specified like 'tcp://[host]:port' or "
           "'unix:///path'.">;
 
-def launch_target: S<"laun...
[truncated]

Copy link

github-actions bot commented Jun 2, 2025

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

@SuibianP SuibianP force-pushed the lldb-dap-runinterminal-signal branch 3 times, most recently from b71032a to 7555bcf Compare June 3, 2025 02:19
@JDevlieghere JDevlieghere requested review from ashgti and labath June 3, 2025 22:21
runInTerminal is currently implemented using JSON messages over FIFOs,
which feels too clunky for the simple job of passing a PID.

Instead, take advantage of si_pid available from sa_sigaction handler,
and send a user signal instead. Both synchronisation and timeout are
preserved with the protocol below,

1. Debugger waits for SIGUSR1 under timeout with pselect
2. Launcher forks into child, sends SIGUSR1 to debugger and suspends
3. Debugger receives SIGUSR1, captures the sender (launcher child) PID,
   attaches to and resumes it
4. Launcher child resumes and exec's into target
5. Launcher parent waitpid's and kills irresponsive child after timeout

With this, the entirety of FifoFiles and RunInTerminal can be dropped.

Refs: llvm#121269 (comment)
@SuibianP SuibianP force-pushed the lldb-dap-runinterminal-signal branch from 7555bcf to 098fb7f Compare June 6, 2025 11:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants