[Lldb-commits] [lldb] [lldb-dap] Add stdio redirection (PR #158609)

Druzhkov Sergei via lldb-commits lldb-commits at lists.llvm.org
Mon Sep 15 04:27:18 PDT 2025


https://github.com/DrSergei created https://github.com/llvm/llvm-project/pull/158609

As far as I understand, lldb-dap does not currently support stdio redirection. I have added support for this via a new field in the launch configuration named `stdio`. It was inspired by the same named field in [CodeLLDB](https://github.com/vadimcn/codelldb/blob/master/MANUAL.md#stdio-redirection).

>From 4c3e0090328b35fb8d0d92df3bd5378d9d811047 Mon Sep 17 00:00:00 2001
From: Druzhkov Sergei <serzhdruzhok at gmail.com>
Date: Mon, 15 Sep 2025 10:49:44 +0300
Subject: [PATCH] [lldb-dap] Add stdio redirection

---
 .../test/tools/lldb-dap/dap_server.py         |  3 +++
 .../tools/lldb-dap/launch/TestDAP_launch.py   | 21 ++++++++++++++++
 .../tools/lldb-dap/Handler/RequestHandler.cpp | 25 +++++++++++++++++++
 .../lldb-dap/Protocol/ProtocolRequests.cpp    |  3 ++-
 .../lldb-dap/Protocol/ProtocolRequests.h      |  2 ++
 lldb/tools/lldb-dap/README.md                 |  1 +
 lldb/tools/lldb-dap/package.json              |  8 ++++++
 7 files changed, 62 insertions(+), 1 deletion(-)

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 9fe8ca22e820b..daa3e76df6d82 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
@@ -1039,6 +1039,7 @@ def request_launch(
         disableSTDIO=False,
         shellExpandArguments=False,
         console: Optional[str] = None,
+        stdio: Optional[list[str]] = None,
         enableAutoVariableSummaries=False,
         displayExtendedBacktrace=False,
         enableSyntheticChildDebugging=False,
@@ -1090,6 +1091,8 @@ def request_launch(
             args_dict["sourceMap"] = sourceMap
         if console:
             args_dict["console"] = console
+        if stdio:
+            args_dict["stdio"] = stdio
         if postRunCommands:
             args_dict["postRunCommands"] = postRunCommands
         if customFrameFormat:
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
index 22fcd42b3d36a..a301e00f6755f 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
@@ -6,6 +6,7 @@
 from lldbsuite.test.lldbtest import *
 import lldbdap_testcase
 import os
+import pathlib
 import re
 import tempfile
 
@@ -624,3 +625,23 @@ def test_no_lldbinit_flag(self):
 
             # Verify the initCommands were executed
             self.verify_commands("initCommands", output, initCommands)
+
+    def test_stdio_redirection(self):
+        """
+        Test stdio redirection.
+        """
+        temp_file = tempfile.NamedTemporaryFile().name
+        self.build_and_create_debug_adapter()
+        program = self.getBuildArtifact("a.out")
+
+        self.launch(program, stdio=[None, temp_file, None])
+        self.continue_to_exit()
+
+        try:
+            with open(temp_file, "r") as f:
+                lines = f.readlines()
+                self.assertIn(
+                    program, lines[0], "make sure program path is in first argument"
+                )
+        finally:
+            pathlib.Path(temp_file).unlink(missing_ok=True)
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
index 4fadf1c22e0e3..4b727e1e33a64 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
@@ -177,6 +177,31 @@ llvm::Error BaseRequestHandler::LaunchProcess(
     launch_info.SetEnvironment(env, true);
   }
 
+  if (!arguments.stdio.empty() && !arguments.disableSTDIO) {
+    size_t n = std::max(arguments.stdio.size(), static_cast<size_t>(3));
+    for (size_t i = 0; i < n; i++) {
+      std::optional<std::string> path;
+      if (arguments.stdio.size() < i)
+        path = arguments.stdio.back();
+      else
+        path = arguments.stdio[i];
+      if (!path)
+        continue;
+      switch (i) {
+      case 0:
+        launch_info.AddOpenFileAction(i, path->c_str(), true, false);
+        break;
+      case 1:
+      case 2:
+        launch_info.AddOpenFileAction(i, path->c_str(), false, true);
+        break;
+      default:
+        launch_info.AddOpenFileAction(i, path->c_str(), true, true);
+        break;
+      }
+    }
+  }
+
   launch_info.SetDetachOnError(arguments.detachOnError);
   launch_info.SetShellExpandArguments(arguments.shellExpandArguments);
 
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
index e1806d6230a80..b455112cd37d9 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
@@ -303,7 +303,8 @@ bool fromJSON(const json::Value &Params, LaunchRequestArguments &LRA,
          O.mapOptional("disableSTDIO", LRA.disableSTDIO) &&
          O.mapOptional("shellExpandArguments", LRA.shellExpandArguments) &&
          O.mapOptional("runInTerminal", LRA.console) &&
-         O.mapOptional("console", LRA.console) && parseEnv(Params, LRA.env, P);
+         O.mapOptional("console", LRA.console) &&
+         O.mapOptional("stdio", LRA.stdio) && parseEnv(Params, LRA.env, P);
 }
 
 bool fromJSON(const json::Value &Params, AttachRequestArguments &ARA,
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
index 0848ee53b4410..92dada2295841 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
@@ -300,6 +300,8 @@ struct LaunchRequestArguments {
   /// terminal or external terminal.
   Console console = eConsoleInternal;
 
+  std::vector<std::optional<std::string>> stdio;
+
   /// @}
 };
 bool fromJSON(const llvm::json::Value &, LaunchRequestArguments &,
diff --git a/lldb/tools/lldb-dap/README.md b/lldb/tools/lldb-dap/README.md
index 39dabcc1342c8..52bb009b9c796 100644
--- a/lldb/tools/lldb-dap/README.md
+++ b/lldb/tools/lldb-dap/README.md
@@ -237,6 +237,7 @@ contain the following key/value pairs:
 | **stopOnEntry**                   | boolean     |     | Whether to stop program immediately after launching.
 | **runInTerminal** (deprecated)    | boolean     |     | Launch the program inside an integrated terminal in the IDE. Useful for debugging interactive command line programs.
 | **console**                       | string      |     | Specify where to launch the program: internal console (`internalConsole`), integrated terminal (`integratedTerminal`) or external terminal (`externalTerminal`). Supported from lldb-dap 21.0 version.
+| **stdio**                         | [string]    |     | Destination for program stdio streams (0 - stdin, 1 - stdout, 2 - stderr, ...). Using `null` value means no redirection. Supported from lldb-dap 22.0 version.
 | **launchCommands**                | [string]    |     | LLDB commands executed to launch the program.
 
 For JSON configurations of `"type": "attach"`, the JSON configuration can contain
diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json
index 0290a5f18f800..631b768b37c80 100644
--- a/lldb/tools/lldb-dap/package.json
+++ b/lldb/tools/lldb-dap/package.json
@@ -615,6 +615,14 @@
                 "description": "Specify where to launch the program: internal console, integrated terminal or external terminal.",
                 "default": "internalConsole"
               },
+              "stdio": {
+                "type": "array",
+                "items": {
+                  "type": "string"
+                },
+                "description": "Destination for program stdio streams (0 - stdin, 1 - stdout, 2 - stderr, ...). Using null value means no redirection.",
+                "default": []
+              },
               "timeout": {
                 "type": "number",
                 "description": "The time in seconds to wait for a program to stop at entry point when launching with \"launchCommands\". Defaults to 30 seconds."



More information about the lldb-commits mailing list