Skip to content

Commit 1819798

Browse files
authored
[lldb-dap] Add stdio redirection (#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).
1 parent 3c4ab4f commit 1819798

File tree

7 files changed

+90
-1
lines changed

7 files changed

+90
-1
lines changed

lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,6 +1039,7 @@ def request_launch(
10391039
disableSTDIO=False,
10401040
shellExpandArguments=False,
10411041
console: Optional[str] = None,
1042+
stdio: Optional[list[str]] = None,
10421043
enableAutoVariableSummaries=False,
10431044
displayExtendedBacktrace=False,
10441045
enableSyntheticChildDebugging=False,
@@ -1090,6 +1091,8 @@ def request_launch(
10901091
args_dict["sourceMap"] = sourceMap
10911092
if console:
10921093
args_dict["console"] = console
1094+
if stdio:
1095+
args_dict["stdio"] = stdio
10931096
if postRunCommands:
10941097
args_dict["postRunCommands"] = postRunCommands
10951098
if customFrameFormat:

lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from lldbsuite.test.lldbtest import *
77
import lldbdap_testcase
88
import os
9+
import pathlib
910
import re
1011
import tempfile
1112

@@ -625,3 +626,18 @@ def test_no_lldbinit_flag(self):
625626

626627
# Verify the initCommands were executed
627628
self.verify_commands("initCommands", output, initCommands)
629+
630+
def test_stdio_redirection(self):
631+
"""
632+
Test stdio redirection.
633+
"""
634+
self.build_and_create_debug_adapter()
635+
program = self.getBuildArtifact("a.out")
636+
637+
with tempfile.NamedTemporaryFile("rt") as f:
638+
self.launch(program, stdio=[None, f.name, None])
639+
self.continue_to_exit()
640+
lines = f.readlines()
641+
self.assertIn(
642+
program, lines[0], "make sure program path is in first argument"
643+
)

lldb/tools/lldb-dap/Handler/RequestHandler.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,33 @@ static uint32_t SetLaunchFlag(uint32_t flags, bool flag,
5151
return flags;
5252
}
5353

54+
static void
55+
SetupIORedirection(const std::vector<std::optional<std::string>> &stdio,
56+
lldb::SBLaunchInfo &launch_info) {
57+
size_t n = std::max(stdio.size(), static_cast<size_t>(3));
58+
for (size_t i = 0; i < n; i++) {
59+
std::optional<std::string> path;
60+
if (stdio.size() < i)
61+
path = stdio.back();
62+
else
63+
path = stdio[i];
64+
if (!path)
65+
continue;
66+
switch (i) {
67+
case 0:
68+
launch_info.AddOpenFileAction(i, path->c_str(), true, false);
69+
break;
70+
case 1:
71+
case 2:
72+
launch_info.AddOpenFileAction(i, path->c_str(), false, true);
73+
break;
74+
default:
75+
launch_info.AddOpenFileAction(i, path->c_str(), true, true);
76+
break;
77+
}
78+
}
79+
}
80+
5481
static llvm::Error
5582
RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) {
5683
if (!dap.clientFeatures.contains(
@@ -177,6 +204,9 @@ llvm::Error BaseRequestHandler::LaunchProcess(
177204
launch_info.SetEnvironment(env, true);
178205
}
179206

207+
if (!arguments.stdio.empty() && !arguments.disableSTDIO)
208+
SetupIORedirection(arguments.stdio, launch_info);
209+
180210
launch_info.SetDetachOnError(arguments.detachOnError);
181211
launch_info.SetShellExpandArguments(arguments.shellExpandArguments);
182212

lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,8 @@ bool fromJSON(const json::Value &Params, LaunchRequestArguments &LRA,
303303
O.mapOptional("disableSTDIO", LRA.disableSTDIO) &&
304304
O.mapOptional("shellExpandArguments", LRA.shellExpandArguments) &&
305305
O.mapOptional("runInTerminal", LRA.console) &&
306-
O.mapOptional("console", LRA.console) && parseEnv(Params, LRA.env, P);
306+
O.mapOptional("console", LRA.console) &&
307+
O.mapOptional("stdio", LRA.stdio) && parseEnv(Params, LRA.env, P);
307308
}
308309

309310
bool fromJSON(const json::Value &Params, AttachRequestArguments &ARA,

lldb/tools/lldb-dap/Protocol/ProtocolRequests.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,8 @@ struct LaunchRequestArguments {
300300
/// terminal or external terminal.
301301
Console console = eConsoleInternal;
302302

303+
std::vector<std::optional<std::string>> stdio;
304+
303305
/// @}
304306
};
305307
bool fromJSON(const llvm::json::Value &, LaunchRequestArguments &,

lldb/tools/lldb-dap/README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,34 @@ adds `FOO=1` and `bar` to the environment:
4444
}
4545
```
4646

47+
#### Launch in integrated terminal
48+
49+
This will launch process in IDE's integrated terminal.
50+
51+
```javascript
52+
{
53+
"type": "lldb-dap",
54+
"request": "launch",
55+
"name": "Debug",
56+
"program": "/tmp/a.out",
57+
"console": "integratedTerminal"
58+
}
59+
```
60+
61+
#### Setup IO redirection
62+
63+
This will launch process and connect `stdin` to `in.txt`, both of `stdout` and `stderr` to `out.txt`.
64+
65+
```javascript
66+
{
67+
"type": "lldb-dap",
68+
"request": "launch",
69+
"name": "Debug",
70+
"program": "/tmp/a.out",
71+
"stdio": ["in.txt", "out.txt"]
72+
}
73+
```
74+
4775
### Attaching to a process
4876

4977
When attaching to a process using LLDB, you can attach in multiple ways:
@@ -237,6 +265,7 @@ contain the following key/value pairs:
237265
| **stopOnEntry** | boolean | | Whether to stop program immediately after launching.
238266
| **runInTerminal** (deprecated) | boolean | | Launch the program inside an integrated terminal in the IDE. Useful for debugging interactive command line programs.
239267
| **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.
268+
| **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.
240269
| **launchCommands** | [string] | | LLDB commands executed to launch the program.
241270

242271
For JSON configurations of `"type": "attach"`, the JSON configuration can contain

lldb/tools/lldb-dap/package.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,14 @@
615615
"description": "Specify where to launch the program: internal console, integrated terminal or external terminal.",
616616
"default": "internalConsole"
617617
},
618+
"stdio": {
619+
"type": "array",
620+
"items": {
621+
"type": "string"
622+
},
623+
"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.).",
624+
"default": []
625+
},
618626
"timeout": {
619627
"type": "number",
620628
"description": "The time in seconds to wait for a program to stop at entry point when launching with \"launchCommands\". Defaults to 30 seconds."

0 commit comments

Comments
 (0)