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..4d6b4a49a5161 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,18 @@ 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. + """ + self.build_and_create_debug_adapter() + program = self.getBuildArtifact("a.out") + + with tempfile.NamedTemporaryFile("rt") as f: + self.launch(program, stdio=[None, f.name, None]) + self.continue_to_exit() + lines = f.readlines() + self.assertIn( + program, lines[0], "make sure program path is in first argument" + ) diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp index 4fadf1c22e0e3..773891353db6a 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp @@ -51,6 +51,33 @@ static uint32_t SetLaunchFlag(uint32_t flags, bool flag, return flags; } +static void +SetupIORedirection(const std::vector> &stdio, + lldb::SBLaunchInfo &launch_info) { + size_t n = std::max(stdio.size(), static_cast(3)); + for (size_t i = 0; i < n; i++) { + std::optional path; + if (stdio.size() < i) + path = stdio.back(); + else + path = 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; + } + } +} + static llvm::Error RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) { if (!dap.clientFeatures.contains( @@ -177,6 +204,9 @@ llvm::Error BaseRequestHandler::LaunchProcess( launch_info.SetEnvironment(env, true); } + if (!arguments.stdio.empty() && !arguments.disableSTDIO) + SetupIORedirection(arguments.stdio, launch_info); + 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> 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..e83384f89bc1f 100644 --- a/lldb/tools/lldb-dap/README.md +++ b/lldb/tools/lldb-dap/README.md @@ -44,6 +44,34 @@ adds `FOO=1` and `bar` to the environment: } ``` +#### Launch in integrated terminal + +This will launch process in IDE's integrated terminal. + +```javascript +{ + "type": "lldb-dap", + "request": "launch", + "name": "Debug", + "program": "/tmp/a.out", + "console": "integratedTerminal" +} +``` + +#### Setup IO redirection + +This will launch process and connect `stdin` to `in.txt`, both of `stdout` and `stderr` to `out.txt`. + +```javascript +{ + "type": "lldb-dap", + "request": "launch", + "name": "Debug", + "program": "/tmp/a.out", + "stdio": ["in.txt", "out.txt"] +} +``` + ### Attaching to a process When attaching to a process using LLDB, you can attach in multiple ways: @@ -237,6 +265,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] | | The stdio property specifies the redirection targets for the debuggee's stdio streams. A null value redirects a stream to the default debug terminal. String can be a path to file, named pipe or TTY device. If less than three values are provided, the list will be padded with the last value. Specifying more than three values will create additional file descriptors (4, 5, etc.). 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..6566ba3bdee13 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": "The stdio property specifies the redirection targets for the debuggee's stdio streams. A null value redirects a stream to the default debug terminal. String can be a path to file, named pipe or TTY device. If less than three values are provided, the list will be padded with the last value. Specifying more than three values will create additional file descriptors (4, 5, etc.).", + "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."