https://github.com/charles-zablit updated https://github.com/llvm/llvm-project/pull/176778
>From 0455d9aaaea6c9057d44244f05e1973d8fef8f61 Mon Sep 17 00:00:00 2001 From: Charles Zablit <[email protected]> Date: Mon, 19 Jan 2026 17:20:42 +0000 Subject: [PATCH 1/2] [lldb-dap] separate the --stdio flag in 3 separate flags --- .../tools/lldb-dap/Handler/RequestHandler.cpp | 44 ++++------ lldb/tools/lldb-dap/JSONUtils.cpp | 26 +++--- lldb/tools/lldb-dap/JSONUtils.h | 15 +++- .../lldb-dap/Protocol/ProtocolRequests.cpp | 17 ++-- .../lldb-dap/Protocol/ProtocolRequests.h | 10 ++- lldb/tools/lldb-dap/README.md | 7 +- lldb/tools/lldb-dap/tool/Options.td | 19 ++-- lldb/tools/lldb-dap/tool/lldb-dap.cpp | 88 ++++++++----------- 8 files changed, 114 insertions(+), 112 deletions(-) diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp index 5cb0055f034da..9d85ea765fd80 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp @@ -60,31 +60,16 @@ static uint32_t SetLaunchFlag(uint32_t flags, bool flag, return flags; } -static void -SetupIORedirection(const std::vector<std::optional<std::string>> &stdio, - lldb::SBLaunchInfo &launch_info) { - for (const auto &[idx, value_opt] : llvm::enumerate(stdio)) { - if (!value_opt) - continue; - const std::string &path = value_opt.value(); - assert(!path.empty() && "paths should not be empty"); - - const int fd = static_cast<int>(idx); - switch (fd) { - case 0: - launch_info.AddOpenFileAction(STDIN_FILENO, path.c_str(), true, false); - break; - case 1: - launch_info.AddOpenFileAction(STDOUT_FILENO, path.c_str(), false, true); - break; - case 2: - launch_info.AddOpenFileAction(STDERR_FILENO, path.c_str(), false, true); - break; - default: - launch_info.AddOpenFileAction(fd, path.c_str(), true, true); - break; - } - } +static void SetupIORedirection(const std::optional<std::string> &stdin_path, + const std::optional<std::string> &stdout_path, + const std::optional<std::string> &stderr_path, + lldb::SBLaunchInfo &launch_info) { + if (stdin_path) + launch_info.AddOpenFileAction(0, stdin_path->c_str(), true, false); + if (stdout_path) + launch_info.AddOpenFileAction(1, stdout_path->c_str(), false, true); + if (stderr_path) + launch_info.AddOpenFileAction(2, stderr_path->c_str(), false, true); } static llvm::Error @@ -116,7 +101,8 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) { llvm::json::Object reverse_request = CreateRunInTerminalReverseRequest( arguments.configuration.program, arguments.args, arguments.env, - arguments.cwd, comm_file.m_path, debugger_pid, arguments.stdio, + arguments.cwd, comm_file.m_path, debugger_pid, arguments.stdin_path, + arguments.stdout_path, arguments.stderr_path, arguments.console == protocol::eConsoleExternalTerminal); dap.SendReverseRequest<LogFailureResponseHandler>("runInTerminal", std::move(reverse_request)); @@ -214,8 +200,10 @@ llvm::Error BaseRequestHandler::LaunchProcess( launch_info.SetEnvironment(env, true); } - if (!arguments.stdio.empty() && !arguments.disableSTDIO) - SetupIORedirection(arguments.stdio, launch_info); + if ((arguments.stdin_path || arguments.stdout_path || arguments.stdin_path) && + !arguments.disableSTDIO) + SetupIORedirection(arguments.stdin_path, arguments.stdout_path, + arguments.stderr_path, launch_info); launch_info.SetDetachOnError(arguments.detachOnError); launch_info.SetShellExpandArguments(arguments.shellExpandArguments); diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp index 5c33c6aa591a6..382d4dbcaf7c2 100644 --- a/lldb/tools/lldb-dap/JSONUtils.cpp +++ b/lldb/tools/lldb-dap/JSONUtils.cpp @@ -707,7 +707,9 @@ 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, - const std::vector<std::optional<std::string>> &stdio, bool external) { + const std::optional<std::string> &stdin_path, + const std::optional<std::string> &stdout_path, + const std::optional<std::string> &stderr_path, bool external) { llvm::json::Object run_in_terminal_args; if (external) { // This indicates the IDE to open an external terminal window. @@ -725,17 +727,17 @@ llvm::json::Object CreateRunInTerminalReverseRequest( req_args.push_back(std::to_string(debugger_pid)); } - if (!stdio.empty()) { - req_args.emplace_back("--stdio"); - - std::stringstream ss; - std::string_view delimiter; - for (const std::optional<std::string> &file : stdio) { - ss << std::exchange(delimiter, ":"); - if (file) - ss << *file; - } - req_args.push_back(ss.str()); + if (stdin_path) { + req_args.push_back("--stdin-path"); + req_args.push_back(*stdin_path); + } + if (stdout_path) { + req_args.push_back("--stdout-path"); + req_args.push_back(*stdout_path); + } + if (stderr_path) { + req_args.push_back("--stderr-path"); + req_args.push_back(*stderr_path); } // WARNING: Any argument added after `launch-target` is passed to to the diff --git a/lldb/tools/lldb-dap/JSONUtils.h b/lldb/tools/lldb-dap/JSONUtils.h index 15449d6ece62a..0c2f51b776691 100644 --- a/lldb/tools/lldb-dap/JSONUtils.h +++ b/lldb/tools/lldb-dap/JSONUtils.h @@ -340,9 +340,14 @@ std::pair<int64_t, bool> UnpackLocation(int64_t location_id); /// launcher uses it on Linux tell the kernel that it should allow the /// debugger process to attach. /// -/// \param[in] stdio -/// An array of file paths for redirecting the program's standard IO -/// streams. +/// \param[in] stdin_path +/// A file path for redirecting the program's stdout stream. +/// +/// \param[in] stdout_path +/// A file path for redirecting the program's stdin stream. +/// +/// \param[in] stderr_path +/// A file path for redirecting the program's stderr stream. /// /// \param[in] external /// If set to true, the program will run in an external terminal window @@ -355,7 +360,9 @@ 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, - const std::vector<std::optional<std::string>> &stdio, bool external); + const std::optional<std::string> &stdin_path, + const std::optional<std::string> &stdout_path, + const std::optional<std::string> &stderr_path, bool external); /// Create a "Terminated" JSON object that contains statistics /// diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp index a72802a33fc9c..227d628aead56 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp @@ -307,15 +307,20 @@ bool fromJSON(const json::Value &Params, LaunchRequestArguments &LRA, O.mapOptional("shellExpandArguments", LRA.shellExpandArguments) && O.mapOptional("runInTerminal", LRA.console) && O.mapOptional("console", LRA.console) && - O.mapOptional("stdio", LRA.stdio) && parseEnv(Params, LRA.env, P); + O.mapOptional("stdin_path", LRA.stdin_path) && + O.mapOptional("stdout_path", LRA.stdout_path) && + O.mapOptional("stderr_path", LRA.stderr_path) && + parseEnv(Params, LRA.env, P); if (!success) return false; - for (std::optional<std::string> &io_path : LRA.stdio) { - // set empty paths to null. - if (io_path && llvm::StringRef(*io_path).trim().empty()) - io_path.reset(); - } + // set empty paths to null. + if (LRA.stdin_path && llvm::StringRef(*LRA.stdin_path).trim().empty()) + LRA.stdin_path.reset(); + if (LRA.stdout_path && llvm::StringRef(*LRA.stdout_path).trim().empty()) + LRA.stdout_path.reset(); + if (LRA.stderr_path && llvm::StringRef(*LRA.stderr_path).trim().empty()) + LRA.stderr_path.reset(); // Validate that we have a well formed launch request. if (!LRA.launchCommands.empty() && diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h index 28c9f48200e0c..a0e3d43144e1d 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h @@ -301,8 +301,14 @@ struct LaunchRequestArguments { /// terminal or external terminal. Console console = eConsoleInternal; - /// An array of file paths for redirecting the program's standard IO streams. - std::vector<std::optional<std::string>> stdio; + /// A file path for redirecting the program's stdin stream. + std::optional<std::string> stdin_path; + + /// A file path for redirecting the program's stdout stream. + std::optional<std::string> stdout_path; + + /// A file path for redirecting the program's stderr stream. + std::optional<std::string> stderr_path; /// @} }; diff --git a/lldb/tools/lldb-dap/README.md b/lldb/tools/lldb-dap/README.md index 8c7066a3b551b..002e367a8bd7c 100644 --- a/lldb/tools/lldb-dap/README.md +++ b/lldb/tools/lldb-dap/README.md @@ -68,7 +68,8 @@ This will launch process and connect `stdin` to `in.txt`, both of `stdout` and ` "request": "launch", "name": "Debug", "program": "/tmp/a.out", - "stdio": ["in.txt", "out.txt"] + "stdin-path": "in.txt", + "stdout-path": "out.txt" } ``` @@ -265,7 +266,9 @@ 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] | | Redirects the debuggee's standard I/O (stdin, stdout, stderr) to a file path, named pipe, or TTY device.<br/>Use null to redirect a stream to the default debug terminal.<br/>The first three values map to stdin, stdout, and stderr respectively, Additional values create extra file descriptors (4, 5, etc.).<br/><br/>Example: `stdio: [null, "./output_file"]`, the debuggee uses the default terminal for `stdin` and `stderr`, but writes `stdout` to `./output_file`.<br/>Added in version 22.0. +| **stdin-path** | [string] | | Redirects the debuggee's standard input (stdin) to a file path, named pipe, or TTY device.<br/>Use null to redirect the stream to the default debug terminal. +| **stdout-path** | [string] | | Redirects the debuggee's standard output (stdout) to a file path, named pipe, or TTY device.<br/>Use null to redirect the stream to the default debug terminal. +| **stderr-path** | [string] | | Redirects the debuggee's standard error (stderr) to a file path, named pipe, or TTY device.<br/>Use null to redirect the stream to the default debug terminal. | **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/tool/Options.td b/lldb/tools/lldb-dap/tool/Options.td index 339a64fed6c32..2dbcab8732d83 100644 --- a/lldb/tools/lldb-dap/tool/Options.td +++ b/lldb/tools/lldb-dap/tool/Options.td @@ -47,11 +47,20 @@ def debugger_pid: S<"debugger-pid">, HelpText<"The PID of the lldb-dap instance that sent the launchInTerminal " "request when using --launch-target.">; -def stdio: S<"stdio">, - MetaVarName<"<stdin:stdout:stderr:...>">, - HelpText<"An array of file paths for redirecting the program's standard IO " - "streams. A colon-separated list of entries. Empty value means no " - "redirection.">; +def stdin_path: S<"stdin-path">, + MetaVarName<"<stdin>">, + HelpText<"A file path for redirecting the program's stdin stream. Empty " + "value means no redirection.">; + +def stdout_path: S<"stdout-path">, + MetaVarName<"<stdout>">, + HelpText<"A file path for redirecting the program's stdout stream. Empty " + "value means no redirection.">; + +def stderr_path: S<"stderr-path">, + MetaVarName<"<stderr>">, + HelpText<"A file path for redirecting the program's stderr stream. Empty " + "value means no redirection.">; def repl_mode : S<"repl-mode">, diff --git a/lldb/tools/lldb-dap/tool/lldb-dap.cpp b/lldb/tools/lldb-dap/tool/lldb-dap.cpp index 15c63543e86f5..3e9a1a69eeaba 100644 --- a/lldb/tools/lldb-dap/tool/lldb-dap.cpp +++ b/lldb/tools/lldb-dap/tool/lldb-dap.cpp @@ -194,54 +194,34 @@ struct FDGroup { bool write = false; }; -static llvm::Error RedirectToFile(const FDGroup &fdg, llvm::StringRef file) { - if (!fdg.read && !fdg.write) - return llvm::Error::success(); - int target_fd = lldb_private::FileSystem::Instance().Open( - file.str().c_str(), fdg.GetFlags(), 0666); - if (target_fd == -1) - return llvm::errorCodeToError( - std::error_code(errno, std::generic_category())); - for (int fd : fdg.fds) { - if (target_fd == fd) - continue; - if (::dup2(target_fd, fd) == -1) +static llvm::Error SetupIORedirection(llvm::StringRef stdin_path, + llvm::StringRef stdout_path, + llvm::StringRef stderr_path) { + if (!stdin_path.empty()) { + int target_fd = lldb_private::FileSystem::Instance().Open( + stdin_path.str().c_str(), O_NOCTTY | O_RDONLY, 0666); + if (target_fd == -1) return llvm::errorCodeToError( std::error_code(errno, std::generic_category())); + ::close(target_fd); } - ::close(target_fd); - return llvm::Error::success(); -} - -static llvm::Error -SetupIORedirection(const llvm::SmallVectorImpl<llvm::StringRef> &files) { - llvm::SmallDenseMap<llvm::StringRef, FDGroup> groups; - for (size_t i = 0; i < files.size(); i++) { - if (files[i].empty()) - continue; - auto group = groups.find(files[i]); - if (group == groups.end()) - group = groups.insert({files[i], {{static_cast<int>(i)}}}).first; - else - group->second.fds.push_back(i); - switch (i) { - case 0: - group->second.read = true; - break; - case 1: - case 2: - group->second.write = true; - break; - default: - group->second.read = true; - group->second.write = true; - break; - } + if (!stdout_path.empty()) { + int target_fd = lldb_private::FileSystem::Instance().Open( + stdout_path.str().c_str(), O_NOCTTY | O_CREAT | O_WRONLY | O_TRUNC, + 0666); + if (target_fd == -1) + return llvm::errorCodeToError( + std::error_code(errno, std::generic_category())); + ::close(target_fd); } - for (const auto &[file, group] : groups) { - if (llvm::Error err = RedirectToFile(group, file)) - return llvm::createStringError( - llvm::formatv("{0}: {1}", file, llvm::toString(std::move(err)))); + if (!stderr_path.empty()) { + int target_fd = lldb_private::FileSystem::Instance().Open( + stderr_path.str().c_str(), O_NOCTTY | O_CREAT | O_WRONLY | O_TRUNC, + 0666); + if (target_fd == -1) + return llvm::errorCodeToError( + std::error_code(errno, std::generic_category())); + ::close(target_fd); } return llvm::Error::success(); } @@ -266,11 +246,11 @@ SetupIORedirection(const llvm::SmallVectorImpl<llvm::StringRef> &files) { // // In case of errors launching the target, a suitable error message will be // emitted to the debug adapter. -static llvm::Error LaunchRunInTerminalTarget(llvm::opt::Arg &target_arg, - llvm::StringRef comm_file, - lldb::pid_t debugger_pid, - llvm::StringRef stdio, - char *argv[]) { +static llvm::Error +LaunchRunInTerminalTarget(llvm::opt::Arg &target_arg, llvm::StringRef comm_file, + lldb::pid_t debugger_pid, llvm::StringRef stdin_path, + llvm::StringRef stdout_path, + llvm::StringRef stderr_path, char *argv[]) { #if defined(_WIN32) return llvm::createStringError( "runInTerminal is only supported on POSIX systems"); @@ -588,10 +568,12 @@ int main(int argc, char *argv[]) { break; } } - llvm::StringRef stdio = input_args.getLastArgValue(OPT_stdio); - if (llvm::Error err = - LaunchRunInTerminalTarget(*target_arg, comm_file->getValue(), pid, - stdio, argv + target_args_pos)) { + llvm::StringRef stdin_path = input_args.getLastArgValue(OPT_stdin_path); + llvm::StringRef stdout_path = input_args.getLastArgValue(OPT_stdout_path); + llvm::StringRef stderr_path = input_args.getLastArgValue(OPT_stderr_path); + if (llvm::Error err = LaunchRunInTerminalTarget( + *target_arg, comm_file->getValue(), pid, stdin_path, stdout_path, + stderr_path, argv + target_args_pos)) { llvm::errs() << llvm::toString(std::move(err)) << '\n'; return EXIT_FAILURE; } >From 062ad0a22a5c32389509ac6be9a90ca502f672c9 Mon Sep 17 00:00:00 2001 From: Charles Zablit <[email protected]> Date: Mon, 19 Jan 2026 17:24:26 +0000 Subject: [PATCH 2/2] remove leftover --stdio reference --- lldb/tools/lldb-dap/tool/lldb-dap.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lldb/tools/lldb-dap/tool/lldb-dap.cpp b/lldb/tools/lldb-dap/tool/lldb-dap.cpp index 3e9a1a69eeaba..8feaa0f187fe4 100644 --- a/lldb/tools/lldb-dap/tool/lldb-dap.cpp +++ b/lldb/tools/lldb-dap/tool/lldb-dap.cpp @@ -265,12 +265,9 @@ LaunchRunInTerminalTarget(llvm::opt::Arg &target_arg, llvm::StringRef comm_file, #endif lldb_private::FileSystem::Initialize(); - if (!stdio.empty()) { - constexpr size_t num_of_stdio = 3; - llvm::SmallVector<llvm::StringRef, num_of_stdio> stdio_files; - stdio.split(stdio_files, ':'); - stdio_files.resize(std::max(num_of_stdio, stdio_files.size())); - if (llvm::Error err = SetupIORedirection(stdio_files)) + if (!stdin_path.empty() || !stdout_path.empty() || !stderr_path.empty()) { + if (llvm::Error err = + SetupIORedirection(stdin_path, stdout_path, stderr_path)) return err; } else if ((isatty(STDIN_FILENO) != 0) && llvm::StringRef(getenv("TERM")).starts_with_insensitive("xterm")) { _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
