cmd,proc,terminal,debugger: Support default file descriptor redirects
Adds features to support default file descriptor redirects for the target process: 1. A new command line flag '--redirect' and '-r' are added to specify file redirects for the target process 2. New syntax is added to the 'restart' command to specify file redirects. 3. Interactive instances will check if stdin/stdout and stderr are terminals and print a helpful error message if they aren't.
This commit is contained in:
parent
f90134eb4d
commit
7555d1c063
@ -412,17 +412,23 @@ Restart process.
|
|||||||
|
|
||||||
For recorded targets the command takes the following forms:
|
For recorded targets the command takes the following forms:
|
||||||
|
|
||||||
restart resets ot the start of the recording
|
restart resets ot the start of the recording
|
||||||
restart [checkpoint] resets the recording to the given checkpoint
|
restart [checkpoint] resets the recording to the given checkpoint
|
||||||
restart -r [newargv...] re-records the target process
|
restart -r [newargv...] [redirects...] re-records the target process
|
||||||
|
|
||||||
For live targets the command takes the following forms:
|
For live targets the command takes the following forms:
|
||||||
|
|
||||||
restart [newargv...] restarts the process
|
restart [newargv...] [redirects...] restarts the process
|
||||||
|
|
||||||
If newargv is omitted the process is restarted (or re-recorded) with the same argument vector.
|
If newargv is omitted the process is restarted (or re-recorded) with the same argument vector.
|
||||||
If -noargs is specified instead, the argument vector is cleared.
|
If -noargs is specified instead, the argument vector is cleared.
|
||||||
|
|
||||||
|
A list of file redirections can be specified after the new argument list to override the redirections defined using the '--redirect' command line option. A syntax similar to Unix shells is used:
|
||||||
|
|
||||||
|
<input.txt redirects the standard input of the target process from input.txt
|
||||||
|
>output.txt redirects the standard output of the target process to output.txt
|
||||||
|
2>error.txt redirects the standard error of the target process to error.txt
|
||||||
|
|
||||||
|
|
||||||
Aliases: r
|
Aliases: r
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ threads() | Equivalent to API call [ListThreads](https://godoc.org/github.com/go
|
|||||||
types(Filter) | Equivalent to API call [ListTypes](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListTypes)
|
types(Filter) | Equivalent to API call [ListTypes](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListTypes)
|
||||||
process_pid() | Equivalent to API call [ProcessPid](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ProcessPid)
|
process_pid() | Equivalent to API call [ProcessPid](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ProcessPid)
|
||||||
recorded() | Equivalent to API call [Recorded](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.Recorded)
|
recorded() | Equivalent to API call [Recorded](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.Recorded)
|
||||||
restart(Position, ResetArgs, NewArgs, Rerecord, Rebuild) | Equivalent to API call [Restart](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.Restart)
|
restart(Position, ResetArgs, NewArgs, Rerecord, Rebuild, NewRedirects) | Equivalent to API call [Restart](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.Restart)
|
||||||
set_expr(Scope, Symbol, Value) | Equivalent to API call [Set](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.Set)
|
set_expr(Scope, Symbol, Value) | Equivalent to API call [Set](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.Set)
|
||||||
stacktrace(Id, Depth, Full, Defers, Opts, Cfg) | Equivalent to API call [Stacktrace](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.Stacktrace)
|
stacktrace(Id, Depth, Full, Defers, Opts, Cfg) | Equivalent to API call [Stacktrace](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.Stacktrace)
|
||||||
state(NonBlocking) | Equivalent to API call [State](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.State)
|
state(NonBlocking) | Equivalent to API call [State](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.State)
|
||||||
|
@ -19,19 +19,21 @@ Pass flags to the program you are debugging using `--`, for example:
|
|||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
--accept-multiclient Allows a headless server to accept multiple client connections.
|
--accept-multiclient Allows a headless server to accept multiple client connections.
|
||||||
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
--allow-non-terminal-interactive Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
|
||||||
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
||||||
--build-flags string Build flags, to be passed to the compiler.
|
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
||||||
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
--build-flags string Build flags, to be passed to the compiler.
|
||||||
--headless Run debug server only, in headless mode.
|
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
||||||
--init string Init file, executed by the terminal client.
|
--headless Run debug server only, in headless mode.
|
||||||
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
--init string Init file, executed by the terminal client.
|
||||||
--log Enable debugging server logging.
|
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
||||||
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
--log Enable debugging server logging.
|
||||||
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
||||||
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
||||||
--wd string Working directory for running the program.
|
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
||||||
|
-r, --redirect stringArray Specifies redirect rules for target process (see 'dlv help redirect')
|
||||||
|
--wd string Working directory for running the program.
|
||||||
```
|
```
|
||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
@ -25,19 +25,21 @@ dlv attach pid [executable]
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--accept-multiclient Allows a headless server to accept multiple client connections.
|
--accept-multiclient Allows a headless server to accept multiple client connections.
|
||||||
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
--allow-non-terminal-interactive Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
|
||||||
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
||||||
--build-flags string Build flags, to be passed to the compiler.
|
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
||||||
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
--build-flags string Build flags, to be passed to the compiler.
|
||||||
--headless Run debug server only, in headless mode.
|
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
||||||
--init string Init file, executed by the terminal client.
|
--headless Run debug server only, in headless mode.
|
||||||
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
--init string Init file, executed by the terminal client.
|
||||||
--log Enable debugging server logging.
|
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
||||||
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
--log Enable debugging server logging.
|
||||||
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
||||||
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
||||||
--wd string Working directory for running the program.
|
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
||||||
|
-r, --redirect stringArray Specifies redirect rules for target process (see 'dlv help redirect')
|
||||||
|
--wd string Working directory for running the program.
|
||||||
```
|
```
|
||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
@ -18,19 +18,21 @@ are:
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--accept-multiclient Allows a headless server to accept multiple client connections.
|
--accept-multiclient Allows a headless server to accept multiple client connections.
|
||||||
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
--allow-non-terminal-interactive Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
|
||||||
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
||||||
--build-flags string Build flags, to be passed to the compiler.
|
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
||||||
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
--build-flags string Build flags, to be passed to the compiler.
|
||||||
--headless Run debug server only, in headless mode.
|
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
||||||
--init string Init file, executed by the terminal client.
|
--headless Run debug server only, in headless mode.
|
||||||
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
--init string Init file, executed by the terminal client.
|
||||||
--log Enable debugging server logging.
|
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
||||||
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
--log Enable debugging server logging.
|
||||||
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
||||||
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
||||||
--wd string Working directory for running the program.
|
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
||||||
|
-r, --redirect stringArray Specifies redirect rules for target process (see 'dlv help redirect')
|
||||||
|
--wd string Working directory for running the program.
|
||||||
```
|
```
|
||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
@ -14,19 +14,21 @@ dlv connect addr
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--accept-multiclient Allows a headless server to accept multiple client connections.
|
--accept-multiclient Allows a headless server to accept multiple client connections.
|
||||||
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
--allow-non-terminal-interactive Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
|
||||||
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
||||||
--build-flags string Build flags, to be passed to the compiler.
|
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
||||||
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
--build-flags string Build flags, to be passed to the compiler.
|
||||||
--headless Run debug server only, in headless mode.
|
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
||||||
--init string Init file, executed by the terminal client.
|
--headless Run debug server only, in headless mode.
|
||||||
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
--init string Init file, executed by the terminal client.
|
||||||
--log Enable debugging server logging.
|
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
||||||
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
--log Enable debugging server logging.
|
||||||
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
||||||
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
||||||
--wd string Working directory for running the program.
|
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
||||||
|
-r, --redirect stringArray Specifies redirect rules for target process (see 'dlv help redirect')
|
||||||
|
--wd string Working directory for running the program.
|
||||||
```
|
```
|
||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
@ -20,19 +20,21 @@ dlv core <executable> <core>
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--accept-multiclient Allows a headless server to accept multiple client connections.
|
--accept-multiclient Allows a headless server to accept multiple client connections.
|
||||||
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
--allow-non-terminal-interactive Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
|
||||||
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
||||||
--build-flags string Build flags, to be passed to the compiler.
|
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
||||||
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
--build-flags string Build flags, to be passed to the compiler.
|
||||||
--headless Run debug server only, in headless mode.
|
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
||||||
--init string Init file, executed by the terminal client.
|
--headless Run debug server only, in headless mode.
|
||||||
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
--init string Init file, executed by the terminal client.
|
||||||
--log Enable debugging server logging.
|
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
||||||
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
--log Enable debugging server logging.
|
||||||
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
||||||
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
||||||
--wd string Working directory for running the program.
|
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
||||||
|
-r, --redirect stringArray Specifies redirect rules for target process (see 'dlv help redirect')
|
||||||
|
--wd string Working directory for running the program.
|
||||||
```
|
```
|
||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
@ -21,19 +21,21 @@ dlv dap
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--accept-multiclient Allows a headless server to accept multiple client connections.
|
--accept-multiclient Allows a headless server to accept multiple client connections.
|
||||||
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
--allow-non-terminal-interactive Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
|
||||||
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
||||||
--build-flags string Build flags, to be passed to the compiler.
|
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
||||||
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
--build-flags string Build flags, to be passed to the compiler.
|
||||||
--headless Run debug server only, in headless mode.
|
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
||||||
--init string Init file, executed by the terminal client.
|
--headless Run debug server only, in headless mode.
|
||||||
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
--init string Init file, executed by the terminal client.
|
||||||
--log Enable debugging server logging.
|
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
||||||
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
--log Enable debugging server logging.
|
||||||
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
||||||
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
||||||
--wd string Working directory for running the program.
|
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
||||||
|
-r, --redirect stringArray Specifies redirect rules for target process (see 'dlv help redirect')
|
||||||
|
--wd string Working directory for running the program.
|
||||||
```
|
```
|
||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
@ -27,19 +27,21 @@ dlv debug [package]
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--accept-multiclient Allows a headless server to accept multiple client connections.
|
--accept-multiclient Allows a headless server to accept multiple client connections.
|
||||||
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
--allow-non-terminal-interactive Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
|
||||||
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
||||||
--build-flags string Build flags, to be passed to the compiler.
|
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
||||||
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
--build-flags string Build flags, to be passed to the compiler.
|
||||||
--headless Run debug server only, in headless mode.
|
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
||||||
--init string Init file, executed by the terminal client.
|
--headless Run debug server only, in headless mode.
|
||||||
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
--init string Init file, executed by the terminal client.
|
||||||
--log Enable debugging server logging.
|
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
||||||
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
--log Enable debugging server logging.
|
||||||
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
||||||
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
||||||
--wd string Working directory for running the program.
|
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
||||||
|
-r, --redirect stringArray Specifies redirect rules for target process (see 'dlv help redirect')
|
||||||
|
--wd string Working directory for running the program.
|
||||||
```
|
```
|
||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
@ -27,19 +27,21 @@ dlv exec <path/to/binary>
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--accept-multiclient Allows a headless server to accept multiple client connections.
|
--accept-multiclient Allows a headless server to accept multiple client connections.
|
||||||
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
--allow-non-terminal-interactive Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
|
||||||
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
||||||
--build-flags string Build flags, to be passed to the compiler.
|
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
||||||
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
--build-flags string Build flags, to be passed to the compiler.
|
||||||
--headless Run debug server only, in headless mode.
|
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
||||||
--init string Init file, executed by the terminal client.
|
--headless Run debug server only, in headless mode.
|
||||||
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
--init string Init file, executed by the terminal client.
|
||||||
--log Enable debugging server logging.
|
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
||||||
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
--log Enable debugging server logging.
|
||||||
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
||||||
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
||||||
--wd string Working directory for running the program.
|
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
||||||
|
-r, --redirect stringArray Specifies redirect rules for target process (see 'dlv help redirect')
|
||||||
|
--wd string Working directory for running the program.
|
||||||
```
|
```
|
||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
@ -33,19 +33,21 @@ and dap modes.
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--accept-multiclient Allows a headless server to accept multiple client connections.
|
--accept-multiclient Allows a headless server to accept multiple client connections.
|
||||||
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
--allow-non-terminal-interactive Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
|
||||||
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
||||||
--build-flags string Build flags, to be passed to the compiler.
|
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
||||||
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
--build-flags string Build flags, to be passed to the compiler.
|
||||||
--headless Run debug server only, in headless mode.
|
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
||||||
--init string Init file, executed by the terminal client.
|
--headless Run debug server only, in headless mode.
|
||||||
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
--init string Init file, executed by the terminal client.
|
||||||
--log Enable debugging server logging.
|
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
||||||
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
--log Enable debugging server logging.
|
||||||
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
||||||
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
||||||
--wd string Working directory for running the program.
|
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
||||||
|
-r, --redirect stringArray Specifies redirect rules for target process (see 'dlv help redirect')
|
||||||
|
--wd string Working directory for running the program.
|
||||||
```
|
```
|
||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
43
Documentation/usage/dlv_redirect.md
Normal file
43
Documentation/usage/dlv_redirect.md
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
## dlv redirect
|
||||||
|
|
||||||
|
Help about file redirection.
|
||||||
|
|
||||||
|
### Synopsis
|
||||||
|
|
||||||
|
|
||||||
|
The standard file descriptors of the target process can be controlled using the '-r' and '--tty' arguments.
|
||||||
|
|
||||||
|
The --tty argument allows redirecting all standard descriptors to a terminal, specified as an argument to --tty.
|
||||||
|
|
||||||
|
The syntax for '-r' argument is:
|
||||||
|
|
||||||
|
-r [source:]destination
|
||||||
|
|
||||||
|
Where source is one of 'stdin', 'stdout' or 'stderr' and destination is the path to a file. If the source is omitted stdin is used implicitly.
|
||||||
|
|
||||||
|
File redirects can also be changed using the 'restart' command.
|
||||||
|
|
||||||
|
|
||||||
|
### Options inherited from parent commands
|
||||||
|
|
||||||
|
```
|
||||||
|
--accept-multiclient Allows a headless server to accept multiple client connections.
|
||||||
|
--allow-non-terminal-interactive Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
|
||||||
|
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
||||||
|
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
||||||
|
--build-flags string Build flags, to be passed to the compiler.
|
||||||
|
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
||||||
|
--headless Run debug server only, in headless mode.
|
||||||
|
--init string Init file, executed by the terminal client.
|
||||||
|
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
||||||
|
--log Enable debugging server logging.
|
||||||
|
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
||||||
|
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
||||||
|
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
||||||
|
-r, --redirect stringArray Specifies redirect rules for target process (see 'dlv help redirect')
|
||||||
|
--wd string Working directory for running the program.
|
||||||
|
```
|
||||||
|
|
||||||
|
### SEE ALSO
|
||||||
|
* [dlv](dlv.md) - Delve is a debugger for the Go programming language.
|
||||||
|
|
@ -18,19 +18,21 @@ dlv replay [trace directory]
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--accept-multiclient Allows a headless server to accept multiple client connections.
|
--accept-multiclient Allows a headless server to accept multiple client connections.
|
||||||
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
--allow-non-terminal-interactive Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
|
||||||
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
||||||
--build-flags string Build flags, to be passed to the compiler.
|
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
||||||
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
--build-flags string Build flags, to be passed to the compiler.
|
||||||
--headless Run debug server only, in headless mode.
|
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
||||||
--init string Init file, executed by the terminal client.
|
--headless Run debug server only, in headless mode.
|
||||||
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
--init string Init file, executed by the terminal client.
|
||||||
--log Enable debugging server logging.
|
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
||||||
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
--log Enable debugging server logging.
|
||||||
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
||||||
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
||||||
--wd string Working directory for running the program.
|
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
||||||
|
-r, --redirect stringArray Specifies redirect rules for target process (see 'dlv help redirect')
|
||||||
|
--wd string Working directory for running the program.
|
||||||
```
|
```
|
||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
@ -14,19 +14,21 @@ dlv run
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--accept-multiclient Allows a headless server to accept multiple client connections.
|
--accept-multiclient Allows a headless server to accept multiple client connections.
|
||||||
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
--allow-non-terminal-interactive Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
|
||||||
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
||||||
--build-flags string Build flags, to be passed to the compiler.
|
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
||||||
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
--build-flags string Build flags, to be passed to the compiler.
|
||||||
--headless Run debug server only, in headless mode.
|
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
||||||
--init string Init file, executed by the terminal client.
|
--headless Run debug server only, in headless mode.
|
||||||
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
--init string Init file, executed by the terminal client.
|
||||||
--log Enable debugging server logging.
|
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
||||||
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
--log Enable debugging server logging.
|
||||||
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
||||||
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
||||||
--wd string Working directory for running the program.
|
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
||||||
|
-r, --redirect stringArray Specifies redirect rules for target process (see 'dlv help redirect')
|
||||||
|
--wd string Working directory for running the program.
|
||||||
```
|
```
|
||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
@ -25,19 +25,21 @@ dlv test [package]
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--accept-multiclient Allows a headless server to accept multiple client connections.
|
--accept-multiclient Allows a headless server to accept multiple client connections.
|
||||||
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
--allow-non-terminal-interactive Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
|
||||||
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
||||||
--build-flags string Build flags, to be passed to the compiler.
|
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
||||||
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
--build-flags string Build flags, to be passed to the compiler.
|
||||||
--headless Run debug server only, in headless mode.
|
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
||||||
--init string Init file, executed by the terminal client.
|
--headless Run debug server only, in headless mode.
|
||||||
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
--init string Init file, executed by the terminal client.
|
||||||
--log Enable debugging server logging.
|
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
||||||
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
--log Enable debugging server logging.
|
||||||
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
||||||
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
||||||
--wd string Working directory for running the program.
|
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
||||||
|
-r, --redirect stringArray Specifies redirect rules for target process (see 'dlv help redirect')
|
||||||
|
--wd string Working directory for running the program.
|
||||||
```
|
```
|
||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
@ -32,19 +32,21 @@ dlv trace [package] regexp
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--accept-multiclient Allows a headless server to accept multiple client connections.
|
--accept-multiclient Allows a headless server to accept multiple client connections.
|
||||||
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
--allow-non-terminal-interactive Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
|
||||||
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
||||||
--build-flags string Build flags, to be passed to the compiler.
|
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
||||||
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
--build-flags string Build flags, to be passed to the compiler.
|
||||||
--headless Run debug server only, in headless mode.
|
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
||||||
--init string Init file, executed by the terminal client.
|
--headless Run debug server only, in headless mode.
|
||||||
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
--init string Init file, executed by the terminal client.
|
||||||
--log Enable debugging server logging.
|
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
||||||
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
--log Enable debugging server logging.
|
||||||
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
||||||
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
||||||
--wd string Working directory for running the program.
|
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
||||||
|
-r, --redirect stringArray Specifies redirect rules for target process (see 'dlv help redirect')
|
||||||
|
--wd string Working directory for running the program.
|
||||||
```
|
```
|
||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
@ -14,19 +14,21 @@ dlv version
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--accept-multiclient Allows a headless server to accept multiple client connections.
|
--accept-multiclient Allows a headless server to accept multiple client connections.
|
||||||
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
--allow-non-terminal-interactive Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
|
||||||
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
--api-version int Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
|
||||||
--build-flags string Build flags, to be passed to the compiler.
|
--backend string Backend selection (see 'dlv help backend'). (default "default")
|
||||||
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
--build-flags string Build flags, to be passed to the compiler.
|
||||||
--headless Run debug server only, in headless mode.
|
--check-go-version Checks that the version of Go in use is compatible with Delve. (default true)
|
||||||
--init string Init file, executed by the terminal client.
|
--headless Run debug server only, in headless mode.
|
||||||
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
--init string Init file, executed by the terminal client.
|
||||||
--log Enable debugging server logging.
|
-l, --listen string Debugging server listen address. (default "127.0.0.1:0")
|
||||||
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
--log Enable debugging server logging.
|
||||||
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
--log-dest string Writes logs to the specified file or file descriptor (see 'dlv help log').
|
||||||
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
--log-output string Comma separated list of components that should produce debug output (see 'dlv help log')
|
||||||
--wd string Working directory for running the program.
|
--only-same-user Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
|
||||||
|
-r, --redirect stringArray Specifies redirect rules for target process (see 'dlv help redirect')
|
||||||
|
--wd string Working directory for running the program.
|
||||||
```
|
```
|
||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
1
_fixtures/redirect-input.txt
Normal file
1
_fixtures/redirect-input.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Redirect test
|
13
_fixtures/redirect.go
Normal file
13
_fixtures/redirect.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
buf, _ := ioutil.ReadAll(os.Stdin)
|
||||||
|
fmt.Fprintf(os.Stdout, "%s %v\n", buf, time.Now())
|
||||||
|
}
|
59
cmd/dlv/cmds/cmds_test.go
Normal file
59
cmd/dlv/cmds/cmds_test.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package cmds
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseRedirects(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
in []string
|
||||||
|
tgt [3]string
|
||||||
|
tgterr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
[]string{"one.txt"},
|
||||||
|
[3]string{"one.txt", "", ""},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"one.txt", "two.txt"},
|
||||||
|
[3]string{},
|
||||||
|
"redirect error: stdin redirected twice",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"stdout:one.txt"},
|
||||||
|
[3]string{"", "one.txt", ""},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"stdout:one.txt", "stderr:two.txt", "stdin:three.txt"},
|
||||||
|
[3]string{"three.txt", "one.txt", "two.txt"},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"stdout:one.txt", "stderr:two.txt", "three.txt"},
|
||||||
|
[3]string{"three.txt", "one.txt", "two.txt"},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Logf("input: %q", tc.in)
|
||||||
|
out, err := parseRedirects(tc.in)
|
||||||
|
t.Logf("output: %q error %v", out, err)
|
||||||
|
if tc.tgterr != "" {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected error %q, got output %q", tc.tgterr, out)
|
||||||
|
} else if errstr := err.Error(); errstr != tc.tgterr {
|
||||||
|
t.Errorf("Expected error %q, got error %q", tc.tgterr, errstr)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i := range tc.tgt {
|
||||||
|
if tc.tgt[i] != out[i] {
|
||||||
|
t.Errorf("Expected %q, got %q (mismatch at index %d)", tc.tgt, out, i)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -26,6 +26,7 @@ import (
|
|||||||
"github.com/go-delve/delve/service/debugger"
|
"github.com/go-delve/delve/service/debugger"
|
||||||
"github.com/go-delve/delve/service/rpc2"
|
"github.com/go-delve/delve/service/rpc2"
|
||||||
"github.com/go-delve/delve/service/rpccommon"
|
"github.com/go-delve/delve/service/rpccommon"
|
||||||
|
"github.com/mattn/go-isatty"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -74,6 +75,11 @@ var (
|
|||||||
traceTestBinary bool
|
traceTestBinary bool
|
||||||
traceStackDepth int
|
traceStackDepth int
|
||||||
|
|
||||||
|
// redirect specifications for target process
|
||||||
|
redirects []string
|
||||||
|
|
||||||
|
allowNonTerminalInteractive bool
|
||||||
|
|
||||||
conf *config.Config
|
conf *config.Config
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -123,6 +129,8 @@ func New(docCall bool) *cobra.Command {
|
|||||||
rootCommand.PersistentFlags().BoolVarP(&checkGoVersion, "check-go-version", "", true, "Checks that the version of Go in use is compatible with Delve.")
|
rootCommand.PersistentFlags().BoolVarP(&checkGoVersion, "check-go-version", "", true, "Checks that the version of Go in use is compatible with Delve.")
|
||||||
rootCommand.PersistentFlags().BoolVarP(&checkLocalConnUser, "only-same-user", "", true, "Only connections from the same user that started this instance of Delve are allowed to connect.")
|
rootCommand.PersistentFlags().BoolVarP(&checkLocalConnUser, "only-same-user", "", true, "Only connections from the same user that started this instance of Delve are allowed to connect.")
|
||||||
rootCommand.PersistentFlags().StringVar(&backend, "backend", "default", `Backend selection (see 'dlv help backend').`)
|
rootCommand.PersistentFlags().StringVar(&backend, "backend", "default", `Backend selection (see 'dlv help backend').`)
|
||||||
|
rootCommand.PersistentFlags().StringArrayVarP(&redirects, "redirect", "r", []string{}, "Specifies redirect rules for target process (see 'dlv help redirect')")
|
||||||
|
rootCommand.PersistentFlags().BoolVar(&allowNonTerminalInteractive, "allow-non-terminal-interactive", false, "Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr")
|
||||||
|
|
||||||
// 'attach' subcommand.
|
// 'attach' subcommand.
|
||||||
attachCommand := &cobra.Command{
|
attachCommand := &cobra.Command{
|
||||||
@ -358,6 +366,23 @@ otherwise as a file path.
|
|||||||
This option will also redirect the "server listening at" message in headless
|
This option will also redirect the "server listening at" message in headless
|
||||||
and dap modes.
|
and dap modes.
|
||||||
|
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
|
||||||
|
rootCommand.AddCommand(&cobra.Command{
|
||||||
|
Use: "redirect",
|
||||||
|
Short: "Help about file redirection.",
|
||||||
|
Long: `The standard file descriptors of the target process can be controlled using the '-r' and '--tty' arguments.
|
||||||
|
|
||||||
|
The --tty argument allows redirecting all standard descriptors to a terminal, specified as an argument to --tty.
|
||||||
|
|
||||||
|
The syntax for '-r' argument is:
|
||||||
|
|
||||||
|
-r [source:]destination
|
||||||
|
|
||||||
|
Where source is one of 'stdin', 'stdout' or 'stderr' and destination is the path to a file. If the source is omitted stdin is used implicitly.
|
||||||
|
|
||||||
|
File redirects can also be changed using the 'restart' command.
|
||||||
`,
|
`,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -745,9 +770,34 @@ func execute(attachPid int, processArgs []string, conf *config.Config, coreFile
|
|||||||
acceptMulti = false
|
acceptMulti = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !headless && !allowNonTerminalInteractive {
|
||||||
|
for _, f := range []struct {
|
||||||
|
name string
|
||||||
|
file *os.File
|
||||||
|
}{{"Stdin", os.Stdin}, {"Stdout", os.Stdout}, {"Stderr", os.Stderr}} {
|
||||||
|
if f.file == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !isatty.IsTerminal(f.file.Fd()) {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s is not a terminal, use '-r' to specify redirects for the target process or --allow-non-terminal-interactive=true if you really want to specify a redirect for Delve\n", f.name)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(redirects) > 0 && tty != "" {
|
||||||
|
fmt.Fprintf(os.Stderr, "Can not use -r and --tty together\n")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
redirects, err := parseRedirects(redirects)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
var listener net.Listener
|
var listener net.Listener
|
||||||
var clientConn net.Conn
|
var clientConn net.Conn
|
||||||
var err error
|
|
||||||
|
|
||||||
// Make a TCP listener
|
// Make a TCP listener
|
||||||
if headless {
|
if headless {
|
||||||
@ -791,6 +841,7 @@ func execute(attachPid int, processArgs []string, conf *config.Config, coreFile
|
|||||||
DebugInfoDirectories: conf.DebugInfoDirectories,
|
DebugInfoDirectories: conf.DebugInfoDirectories,
|
||||||
CheckGoVersion: checkGoVersion,
|
CheckGoVersion: checkGoVersion,
|
||||||
TTY: tty,
|
TTY: tty,
|
||||||
|
Redirects: redirects,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
default:
|
default:
|
||||||
@ -832,3 +883,24 @@ func execute(attachPid int, processArgs []string, conf *config.Config, coreFile
|
|||||||
|
|
||||||
return connect(listener.Addr().String(), clientConn, conf, kind)
|
return connect(listener.Addr().String(), clientConn, conf, kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseRedirects(redirects []string) ([3]string, error) {
|
||||||
|
r := [3]string{}
|
||||||
|
names := [3]string{"stdin", "stdout", "stderr"}
|
||||||
|
for _, redirect := range redirects {
|
||||||
|
idx := 0
|
||||||
|
for i, name := range names {
|
||||||
|
pfx := name + ":"
|
||||||
|
if strings.HasPrefix(redirect, pfx) {
|
||||||
|
idx = i
|
||||||
|
redirect = redirect[len(pfx):]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r[idx] != "" {
|
||||||
|
return r, fmt.Errorf("redirect error: %s redirected twice", names[idx])
|
||||||
|
}
|
||||||
|
r[idx] = redirect
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
@ -121,7 +121,7 @@ func testOutput(t *testing.T, dlvbin, output string, delveCmds []string) (stdout
|
|||||||
var stdoutBuf, stderrBuf bytes.Buffer
|
var stdoutBuf, stderrBuf bytes.Buffer
|
||||||
buildtestdir := filepath.Join(protest.FindFixturesDir(), "buildtest")
|
buildtestdir := filepath.Join(protest.FindFixturesDir(), "buildtest")
|
||||||
|
|
||||||
c := []string{dlvbin, "debug"}
|
c := []string{dlvbin, "debug", "--allow-non-terminal-interactive=true"}
|
||||||
debugbin := filepath.Join(buildtestdir, "__debug_bin")
|
debugbin := filepath.Join(buildtestdir, "__debug_bin")
|
||||||
if output != "" {
|
if output != "" {
|
||||||
c = append(c, "--output", output)
|
c = append(c, "--output", output)
|
||||||
@ -734,7 +734,7 @@ func TestDlvTestChdir(t *testing.T) {
|
|||||||
defer os.RemoveAll(tmpdir)
|
defer os.RemoveAll(tmpdir)
|
||||||
|
|
||||||
fixtures := protest.FindFixturesDir()
|
fixtures := protest.FindFixturesDir()
|
||||||
cmd := exec.Command(dlvbin, "test", filepath.Join(fixtures, "buildtest"), "--", "-test.v")
|
cmd := exec.Command(dlvbin, "--allow-non-terminal-interactive=true", "test", filepath.Join(fixtures, "buildtest"), "--", "-test.v")
|
||||||
cmd.Stdin = strings.NewReader("continue\nexit\n")
|
cmd.Stdin = strings.NewReader("continue\nexit\n")
|
||||||
out, err := cmd.CombinedOutput()
|
out, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -342,7 +342,7 @@ func getLdEnvVars() []string {
|
|||||||
// LLDBLaunch starts an instance of lldb-server and connects to it, asking
|
// LLDBLaunch starts an instance of lldb-server and connects to it, asking
|
||||||
// it to launch the specified target program with the specified arguments
|
// it to launch the specified target program with the specified arguments
|
||||||
// (cmd) on the specified directory wd.
|
// (cmd) on the specified directory wd.
|
||||||
func LLDBLaunch(cmd []string, wd string, foreground bool, debugInfoDirs []string, tty string) (*proc.Target, error) {
|
func LLDBLaunch(cmd []string, wd string, foreground bool, debugInfoDirs []string, tty string, redirects [3]string) (*proc.Target, error) {
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
return nil, ErrUnsupportedOS
|
return nil, ErrUnsupportedOS
|
||||||
}
|
}
|
||||||
@ -371,12 +371,32 @@ func LLDBLaunch(cmd []string, wd string, foreground bool, debugInfoDirs []string
|
|||||||
ldEnvVars := getLdEnvVars()
|
ldEnvVars := getLdEnvVars()
|
||||||
args := make([]string, 0, len(cmd)+4+len(ldEnvVars))
|
args := make([]string, 0, len(cmd)+4+len(ldEnvVars))
|
||||||
args = append(args, ldEnvVars...)
|
args = append(args, ldEnvVars...)
|
||||||
if foreground {
|
|
||||||
args = append(args, "--stdio-path", "/dev/tty")
|
|
||||||
}
|
|
||||||
if tty != "" {
|
if tty != "" {
|
||||||
args = append(args, "--stdio-path", tty)
|
args = append(args, "--stdio-path", tty)
|
||||||
|
} else {
|
||||||
|
found := [3]bool{}
|
||||||
|
names := [3]string{"stdin", "stdout", "stderr"}
|
||||||
|
for i := range redirects {
|
||||||
|
if redirects[i] != "" {
|
||||||
|
found[i] = true
|
||||||
|
args = append(args, fmt.Sprintf("--%s-path", names[i]), redirects[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if foreground {
|
||||||
|
if !found[0] && !found[1] && !found[2] {
|
||||||
|
args = append(args, "--stdio-path", "/dev/tty")
|
||||||
|
} else {
|
||||||
|
for i := range found {
|
||||||
|
if !found[i] {
|
||||||
|
args = append(args, fmt.Sprintf("--%s-path", names[i]), "/dev/tty")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if logflags.LLDBServerOutput() {
|
if logflags.LLDBServerOutput() {
|
||||||
args = append(args, "-g", "-l", "stdout")
|
args = append(args, "-g", "-l", "stdout")
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ import (
|
|||||||
// program. Returns a run function which will actually record the program, a
|
// program. Returns a run function which will actually record the program, a
|
||||||
// stop function which will prematurely terminate the recording of the
|
// stop function which will prematurely terminate the recording of the
|
||||||
// program.
|
// program.
|
||||||
func RecordAsync(cmd []string, wd string, quiet bool) (run func() (string, error), stop func() error, err error) {
|
func RecordAsync(cmd []string, wd string, quiet bool, redirects [3]string) (run func() (string, error), stop func() error, err error) {
|
||||||
if err := checkRRAvailabe(); err != nil {
|
if err := checkRRAvailabe(); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@ -35,10 +35,10 @@ func RecordAsync(cmd []string, wd string, quiet bool) (run func() (string, error
|
|||||||
args = append(args, "record", "--print-trace-dir=3")
|
args = append(args, "record", "--print-trace-dir=3")
|
||||||
args = append(args, cmd...)
|
args = append(args, cmd...)
|
||||||
rrcmd := exec.Command("rr", args...)
|
rrcmd := exec.Command("rr", args...)
|
||||||
rrcmd.Stdin = os.Stdin
|
var closefn func()
|
||||||
if !quiet {
|
rrcmd.Stdin, rrcmd.Stdout, rrcmd.Stderr, closefn, err = openRedirects(redirects, quiet)
|
||||||
rrcmd.Stdout = os.Stdout
|
if err != nil {
|
||||||
rrcmd.Stderr = os.Stderr
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
rrcmd.ExtraFiles = []*os.File{wfd}
|
rrcmd.ExtraFiles = []*os.File{wfd}
|
||||||
rrcmd.Dir = wd
|
rrcmd.Dir = wd
|
||||||
@ -51,6 +51,7 @@ func RecordAsync(cmd []string, wd string, quiet bool) (run func() (string, error
|
|||||||
|
|
||||||
run = func() (string, error) {
|
run = func() (string, error) {
|
||||||
err := rrcmd.Run()
|
err := rrcmd.Run()
|
||||||
|
closefn()
|
||||||
_ = wfd.Close()
|
_ = wfd.Close()
|
||||||
tracedir := <-tracedirChan
|
tracedir := <-tracedirChan
|
||||||
return tracedir, err
|
return tracedir, err
|
||||||
@ -63,10 +64,57 @@ func RecordAsync(cmd []string, wd string, quiet bool) (run func() (string, error
|
|||||||
return run, stop, nil
|
return run, stop, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func openRedirects(redirects [3]string, quiet bool) (stdin, stdout, stderr *os.File, closefn func(), err error) {
|
||||||
|
toclose := []*os.File{}
|
||||||
|
|
||||||
|
if redirects[0] != "" {
|
||||||
|
stdin, err = os.Open(redirects[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
toclose = append(toclose, stdin)
|
||||||
|
} else {
|
||||||
|
stdin = os.Stdin
|
||||||
|
}
|
||||||
|
|
||||||
|
create := func(path string, dflt *os.File) *os.File {
|
||||||
|
if path == "" {
|
||||||
|
if quiet {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return dflt
|
||||||
|
}
|
||||||
|
var f *os.File
|
||||||
|
f, err = os.Create(path)
|
||||||
|
if f != nil {
|
||||||
|
toclose = append(toclose, f)
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout = create(redirects[1], os.Stdout)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
stderr = create(redirects[2], os.Stderr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
closefn = func() {
|
||||||
|
for _, f := range toclose {
|
||||||
|
_ = f.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stdin, stdout, stderr, closefn, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Record uses rr to record the execution of the specified program and
|
// Record uses rr to record the execution of the specified program and
|
||||||
// returns the trace directory's path.
|
// returns the trace directory's path.
|
||||||
func Record(cmd []string, wd string, quiet bool) (tracedir string, err error) {
|
func Record(cmd []string, wd string, quiet bool, redirects [3]string) (tracedir string, err error) {
|
||||||
run, _, err := RecordAsync(cmd, wd, quiet)
|
run, _, err := RecordAsync(cmd, wd, quiet, redirects)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -286,8 +334,8 @@ func splitQuotedFields(in string) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RecordAndReplay acts like calling Record and then Replay.
|
// RecordAndReplay acts like calling Record and then Replay.
|
||||||
func RecordAndReplay(cmd []string, wd string, quiet bool, debugInfoDirs []string) (*proc.Target, string, error) {
|
func RecordAndReplay(cmd []string, wd string, quiet bool, debugInfoDirs []string, redirects [3]string) (*proc.Target, string, error) {
|
||||||
tracedir, err := Record(cmd, wd, quiet)
|
tracedir, err := Record(cmd, wd, quiet, redirects)
|
||||||
if tracedir == "" {
|
if tracedir == "" {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ func withTestRecording(name string, t testing.TB, fn func(t *proc.Target, fixtur
|
|||||||
t.Skip("test skipped, rr not found")
|
t.Skip("test skipped, rr not found")
|
||||||
}
|
}
|
||||||
t.Log("recording")
|
t.Log("recording")
|
||||||
p, tracedir, err := gdbserial.RecordAndReplay([]string{fixture.Path}, ".", true, []string{})
|
p, tracedir, err := gdbserial.RecordAndReplay([]string{fixture.Path}, ".", true, []string{}, [3]string{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("Launch():", err)
|
t.Fatal("Launch():", err)
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
var ErrNativeBackendDisabled = errors.New("native backend disabled during compilation")
|
var ErrNativeBackendDisabled = errors.New("native backend disabled during compilation")
|
||||||
|
|
||||||
// Launch returns ErrNativeBackendDisabled.
|
// Launch returns ErrNativeBackendDisabled.
|
||||||
func Launch(_ []string, _ string, _ bool, _ []string, _ string) (*proc.Target, error) {
|
func Launch(_ []string, _ string, _ bool, _ []string, _ string, _ [3]string) (*proc.Target, error) {
|
||||||
return nil, ErrNativeBackendDisabled
|
return nil, ErrNativeBackendDisabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,3 +320,47 @@ func (dbp *nativeProcess) writeSoftwareBreakpoint(thread *nativeThread, addr uin
|
|||||||
_, err := thread.WriteMemory(uintptr(addr), dbp.bi.Arch.BreakpointInstruction())
|
_, err := thread.WriteMemory(uintptr(addr), dbp.bi.Arch.BreakpointInstruction())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func openRedirects(redirects [3]string, foreground bool) (stdin, stdout, stderr *os.File, closefn func(), err error) {
|
||||||
|
toclose := []*os.File{}
|
||||||
|
|
||||||
|
if redirects[0] != "" {
|
||||||
|
stdin, err = os.Open(redirects[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
toclose = append(toclose, stdin)
|
||||||
|
} else if foreground {
|
||||||
|
stdin = os.Stdin
|
||||||
|
}
|
||||||
|
|
||||||
|
create := func(path string, dflt *os.File) *os.File {
|
||||||
|
if path == "" {
|
||||||
|
return dflt
|
||||||
|
}
|
||||||
|
var f *os.File
|
||||||
|
f, err = os.Create(path)
|
||||||
|
if f != nil {
|
||||||
|
toclose = append(toclose, f)
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout = create(redirects[1], os.Stdout)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
stderr = create(redirects[2], os.Stderr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
closefn = func() {
|
||||||
|
for _, f := range toclose {
|
||||||
|
_ = f.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stdin, stdout, stderr, closefn, nil
|
||||||
|
}
|
||||||
|
@ -37,7 +37,7 @@ type osProcessDetails struct {
|
|||||||
// custom fork/exec process in order to take advantage of
|
// custom fork/exec process in order to take advantage of
|
||||||
// PT_SIGEXC on Darwin which will turn Unix signals into
|
// PT_SIGEXC on Darwin which will turn Unix signals into
|
||||||
// Mach exceptions.
|
// Mach exceptions.
|
||||||
func Launch(cmd []string, wd string, foreground bool, _ []string, _ string) (*proc.Target, error) {
|
func Launch(cmd []string, wd string, foreground bool, _ []string, _ string, _ [3]string) (*proc.Target, error) {
|
||||||
argv0Go, err := filepath.Abs(cmd[0])
|
argv0Go, err := filepath.Abs(cmd[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -6,7 +6,6 @@ package native
|
|||||||
import "C"
|
import "C"
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strings"
|
"strings"
|
||||||
@ -43,13 +42,18 @@ type osProcessDetails struct {
|
|||||||
// to be supplied to that process. `wd` is working directory of the program.
|
// to be supplied to that process. `wd` is working directory of the program.
|
||||||
// If the DWARF information cannot be found in the binary, Delve will look
|
// If the DWARF information cannot be found in the binary, Delve will look
|
||||||
// for external debug files in the directories passed in.
|
// for external debug files in the directories passed in.
|
||||||
func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string, tty string) (*proc.Target, error) {
|
func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string, tty string, redirects [3]string) (*proc.Target, error) {
|
||||||
var (
|
var (
|
||||||
process *exec.Cmd
|
process *exec.Cmd
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
if !isatty.IsTerminal(os.Stdin.Fd()) {
|
stdin, stdout, stderr, closefn, err := openRedirects(redirects, foreground)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if stdin == nil || !isatty.IsTerminal(stdin.Fd()) {
|
||||||
// exec.(*Process).Start will fail if we try to send a process to
|
// exec.(*Process).Start will fail if we try to send a process to
|
||||||
// foreground but we are not attached to a terminal.
|
// foreground but we are not attached to a terminal.
|
||||||
foreground = false
|
foreground = false
|
||||||
@ -64,13 +68,13 @@ func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string, tt
|
|||||||
dbp.execPtraceFunc(func() {
|
dbp.execPtraceFunc(func() {
|
||||||
process = exec.Command(cmd[0])
|
process = exec.Command(cmd[0])
|
||||||
process.Args = cmd
|
process.Args = cmd
|
||||||
process.Stdout = os.Stdout
|
process.Stdin = stdin
|
||||||
process.Stderr = os.Stderr
|
process.Stdout = stdout
|
||||||
|
process.Stderr = stderr
|
||||||
process.SysProcAttr = &syscall.SysProcAttr{Ptrace: true, Setpgid: true, Foreground: foreground}
|
process.SysProcAttr = &syscall.SysProcAttr{Ptrace: true, Setpgid: true, Foreground: foreground}
|
||||||
process.Env = proc.DisableAsyncPreemptEnv()
|
process.Env = proc.DisableAsyncPreemptEnv()
|
||||||
if foreground {
|
if foreground {
|
||||||
signal.Ignore(syscall.SIGTTOU, syscall.SIGTTIN)
|
signal.Ignore(syscall.SIGTTOU, syscall.SIGTTIN)
|
||||||
process.Stdin = os.Stdin
|
|
||||||
}
|
}
|
||||||
if tty != "" {
|
if tty != "" {
|
||||||
dbp.ctty, err = attachProcessToTTY(process, tty)
|
dbp.ctty, err = attachProcessToTTY(process, tty)
|
||||||
@ -83,6 +87,7 @@ func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string, tt
|
|||||||
}
|
}
|
||||||
err = process.Start()
|
err = process.Start()
|
||||||
})
|
})
|
||||||
|
closefn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -49,13 +49,18 @@ type osProcessDetails struct {
|
|||||||
// to be supplied to that process. `wd` is working directory of the program.
|
// to be supplied to that process. `wd` is working directory of the program.
|
||||||
// If the DWARF information cannot be found in the binary, Delve will look
|
// If the DWARF information cannot be found in the binary, Delve will look
|
||||||
// for external debug files in the directories passed in.
|
// for external debug files in the directories passed in.
|
||||||
func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string, tty string) (*proc.Target, error) {
|
func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string, tty string, redirects [3]string) (*proc.Target, error) {
|
||||||
var (
|
var (
|
||||||
process *exec.Cmd
|
process *exec.Cmd
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
if !isatty.IsTerminal(os.Stdin.Fd()) {
|
stdin, stdout, stderr, closefn, err := openRedirects(redirects, foreground)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if stdin == nil || !isatty.IsTerminal(stdin.Fd()) {
|
||||||
// exec.(*Process).Start will fail if we try to send a process to
|
// exec.(*Process).Start will fail if we try to send a process to
|
||||||
// foreground but we are not attached to a terminal.
|
// foreground but we are not attached to a terminal.
|
||||||
foreground = false
|
foreground = false
|
||||||
@ -70,8 +75,9 @@ func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string, tt
|
|||||||
dbp.execPtraceFunc(func() {
|
dbp.execPtraceFunc(func() {
|
||||||
process = exec.Command(cmd[0])
|
process = exec.Command(cmd[0])
|
||||||
process.Args = cmd
|
process.Args = cmd
|
||||||
process.Stdout = os.Stdout
|
process.Stdin = stdin
|
||||||
process.Stderr = os.Stderr
|
process.Stdout = stdout
|
||||||
|
process.Stderr = stderr
|
||||||
process.SysProcAttr = &syscall.SysProcAttr{
|
process.SysProcAttr = &syscall.SysProcAttr{
|
||||||
Ptrace: true,
|
Ptrace: true,
|
||||||
Setpgid: true,
|
Setpgid: true,
|
||||||
@ -79,7 +85,6 @@ func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string, tt
|
|||||||
}
|
}
|
||||||
if foreground {
|
if foreground {
|
||||||
signal.Ignore(syscall.SIGTTOU, syscall.SIGTTIN)
|
signal.Ignore(syscall.SIGTTOU, syscall.SIGTTIN)
|
||||||
process.Stdin = os.Stdin
|
|
||||||
}
|
}
|
||||||
if tty != "" {
|
if tty != "" {
|
||||||
dbp.ctty, err = attachProcessToTTY(process, tty)
|
dbp.ctty, err = attachProcessToTTY(process, tty)
|
||||||
@ -92,6 +97,7 @@ func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string, tt
|
|||||||
}
|
}
|
||||||
err = process.Start()
|
err = process.Start()
|
||||||
})
|
})
|
||||||
|
closefn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ type osProcessDetails struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Launch creates and begins debugging a new process.
|
// Launch creates and begins debugging a new process.
|
||||||
func Launch(cmd []string, wd string, foreground bool, _ []string, _ string) (*proc.Target, error) {
|
func Launch(cmd []string, wd string, foreground bool, _ []string, _ string, redirects [3]string) (*proc.Target, error) {
|
||||||
argv0Go, err := filepath.Abs(cmd[0])
|
argv0Go, err := filepath.Abs(cmd[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -29,12 +29,17 @@ func Launch(cmd []string, wd string, foreground bool, _ []string, _ string) (*pr
|
|||||||
|
|
||||||
env := proc.DisableAsyncPreemptEnv()
|
env := proc.DisableAsyncPreemptEnv()
|
||||||
|
|
||||||
|
stdin, stdout, stderr, closefn, err := openRedirects(redirects, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
var p *os.Process
|
var p *os.Process
|
||||||
dbp := newProcess(0)
|
dbp := newProcess(0)
|
||||||
dbp.execPtraceFunc(func() {
|
dbp.execPtraceFunc(func() {
|
||||||
attr := &os.ProcAttr{
|
attr := &os.ProcAttr{
|
||||||
Dir: wd,
|
Dir: wd,
|
||||||
Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
|
Files: []*os.File{stdin, stdout, stderr},
|
||||||
Sys: &syscall.SysProcAttr{
|
Sys: &syscall.SysProcAttr{
|
||||||
CreationFlags: _DEBUG_ONLY_THIS_PROCESS,
|
CreationFlags: _DEBUG_ONLY_THIS_PROCESS,
|
||||||
},
|
},
|
||||||
@ -42,6 +47,7 @@ func Launch(cmd []string, wd string, foreground bool, _ []string, _ string) (*pr
|
|||||||
}
|
}
|
||||||
p, err = os.StartProcess(argv0Go, cmd, attr)
|
p, err = os.StartProcess(argv0Go, cmd, attr)
|
||||||
})
|
})
|
||||||
|
closefn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ func TestLoadingExternalDebugInfo(t *testing.T) {
|
|||||||
fixture := protest.BuildFixture("locationsprog", 0)
|
fixture := protest.BuildFixture("locationsprog", 0)
|
||||||
defer os.Remove(fixture.Path)
|
defer os.Remove(fixture.Path)
|
||||||
stripAndCopyDebugInfo(fixture, t)
|
stripAndCopyDebugInfo(fixture, t)
|
||||||
p, err := native.Launch(append([]string{fixture.Path}, ""), "", false, []string{filepath.Dir(fixture.Path)}, "")
|
p, err := native.Launch(append([]string{fixture.Path}, ""), "", false, []string{filepath.Dir(fixture.Path)}, "", [3]string{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -69,13 +69,13 @@ func withTestProcessArgs(name string, t testing.TB, wd string, args []string, bu
|
|||||||
|
|
||||||
switch testBackend {
|
switch testBackend {
|
||||||
case "native":
|
case "native":
|
||||||
p, err = native.Launch(append([]string{fixture.Path}, args...), wd, false, []string{}, "")
|
p, err = native.Launch(append([]string{fixture.Path}, args...), wd, false, []string{}, "", [3]string{})
|
||||||
case "lldb":
|
case "lldb":
|
||||||
p, err = gdbserial.LLDBLaunch(append([]string{fixture.Path}, args...), wd, false, []string{}, "")
|
p, err = gdbserial.LLDBLaunch(append([]string{fixture.Path}, args...), wd, false, []string{}, "", [3]string{})
|
||||||
case "rr":
|
case "rr":
|
||||||
protest.MustHaveRecordingAllowed(t)
|
protest.MustHaveRecordingAllowed(t)
|
||||||
t.Log("recording")
|
t.Log("recording")
|
||||||
p, tracedir, err = gdbserial.RecordAndReplay(append([]string{fixture.Path}, args...), wd, true, []string{})
|
p, tracedir, err = gdbserial.RecordAndReplay(append([]string{fixture.Path}, args...), wd, true, []string{}, [3]string{})
|
||||||
t.Logf("replaying %q", tracedir)
|
t.Logf("replaying %q", tracedir)
|
||||||
default:
|
default:
|
||||||
t.Fatal("unknown backend")
|
t.Fatal("unknown backend")
|
||||||
@ -2102,9 +2102,9 @@ func TestUnsupportedArch(t *testing.T) {
|
|||||||
|
|
||||||
switch testBackend {
|
switch testBackend {
|
||||||
case "native":
|
case "native":
|
||||||
p, err = native.Launch([]string{outfile}, ".", false, []string{}, "")
|
p, err = native.Launch([]string{outfile}, ".", false, []string{}, "", [3]string{})
|
||||||
case "lldb":
|
case "lldb":
|
||||||
p, err = gdbserial.LLDBLaunch([]string{outfile}, ".", false, []string{}, "")
|
p, err = gdbserial.LLDBLaunch([]string{outfile}, ".", false, []string{}, "", [3]string{})
|
||||||
default:
|
default:
|
||||||
t.Skip("test not valid for this backend")
|
t.Skip("test not valid for this backend")
|
||||||
}
|
}
|
||||||
|
@ -132,16 +132,22 @@ See also: "help on", "help cond" and "help clear"`},
|
|||||||
|
|
||||||
For recorded targets the command takes the following forms:
|
For recorded targets the command takes the following forms:
|
||||||
|
|
||||||
restart resets ot the start of the recording
|
restart resets ot the start of the recording
|
||||||
restart [checkpoint] resets the recording to the given checkpoint
|
restart [checkpoint] resets the recording to the given checkpoint
|
||||||
restart -r [newargv...] re-records the target process
|
restart -r [newargv...] [redirects...] re-records the target process
|
||||||
|
|
||||||
For live targets the command takes the following forms:
|
For live targets the command takes the following forms:
|
||||||
|
|
||||||
restart [newargv...] restarts the process
|
restart [newargv...] [redirects...] restarts the process
|
||||||
|
|
||||||
If newargv is omitted the process is restarted (or re-recorded) with the same argument vector.
|
If newargv is omitted the process is restarted (or re-recorded) with the same argument vector.
|
||||||
If -noargs is specified instead, the argument vector is cleared.
|
If -noargs is specified instead, the argument vector is cleared.
|
||||||
|
|
||||||
|
A list of file redirections can be specified after the new argument list to override the redirections defined using the '--redirect' command line option. A syntax similar to Unix shells is used:
|
||||||
|
|
||||||
|
<input.txt redirects the standard input of the target process from input.txt
|
||||||
|
>output.txt redirects the standard output of the target process to output.txt
|
||||||
|
2>error.txt redirects the standard error of the target process to error.txt
|
||||||
`},
|
`},
|
||||||
{aliases: []string{"rebuild"}, group: runCmds, cmdFn: c.rebuild, allowedPrefixes: revPrefix, helpMsg: "Rebuild the target executable and restarts it. It does not work if the executable was not built by delve."},
|
{aliases: []string{"rebuild"}, group: runCmds, cmdFn: c.rebuild, allowedPrefixes: revPrefix, helpMsg: "Rebuild the target executable and restarts it. It does not work if the executable was not built by delve."},
|
||||||
{aliases: []string{"continue", "c"}, group: runCmds, cmdFn: c.cont, allowedPrefixes: revPrefix, helpMsg: "Run until breakpoint or program termination."},
|
{aliases: []string{"continue", "c"}, group: runCmds, cmdFn: c.cont, allowedPrefixes: revPrefix, helpMsg: "Run until breakpoint or program termination."},
|
||||||
@ -971,6 +977,7 @@ func restartRecorded(t *Term, ctx callContext, args string) error {
|
|||||||
rerecord := false
|
rerecord := false
|
||||||
resetArgs := false
|
resetArgs := false
|
||||||
newArgv := []string{}
|
newArgv := []string{}
|
||||||
|
newRedirects := [3]string{}
|
||||||
restartPos := ""
|
restartPos := ""
|
||||||
|
|
||||||
if len(v) > 0 {
|
if len(v) > 0 {
|
||||||
@ -978,7 +985,7 @@ func restartRecorded(t *Term, ctx callContext, args string) error {
|
|||||||
rerecord = true
|
rerecord = true
|
||||||
if len(v) == 2 {
|
if len(v) == 2 {
|
||||||
var err error
|
var err error
|
||||||
resetArgs, newArgv, err = parseNewArgv(v[1])
|
resetArgs, newArgv, newRedirects, err = parseNewArgv(v[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -991,7 +998,7 @@ func restartRecorded(t *Term, ctx callContext, args string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := restartIntl(t, rerecord, restartPos, resetArgs, newArgv); err != nil {
|
if err := restartIntl(t, rerecord, restartPos, resetArgs, newArgv, newRedirects); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1015,12 +1022,12 @@ func parseOptionalCount(arg string) (int64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func restartLive(t *Term, ctx callContext, args string) error {
|
func restartLive(t *Term, ctx callContext, args string) error {
|
||||||
resetArgs, newArgv, err := parseNewArgv(args)
|
resetArgs, newArgv, newRedirects, err := parseNewArgv(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := restartIntl(t, false, "", resetArgs, newArgv); err != nil {
|
if err := restartIntl(t, false, "", resetArgs, newArgv, newRedirects); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1028,8 +1035,8 @@ func restartLive(t *Term, ctx callContext, args string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func restartIntl(t *Term, rerecord bool, restartPos string, resetArgs bool, newArgv []string) error {
|
func restartIntl(t *Term, rerecord bool, restartPos string, resetArgs bool, newArgv []string, newRedirects [3]string) error {
|
||||||
discarded, err := t.client.RestartFrom(rerecord, restartPos, resetArgs, newArgv, false)
|
discarded, err := t.client.RestartFrom(rerecord, restartPos, resetArgs, newArgv, newRedirects, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -1039,9 +1046,9 @@ func restartIntl(t *Term, rerecord bool, restartPos string, resetArgs bool, newA
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseNewArgv(args string) (resetArgs bool, newArgv []string, err error) {
|
func parseNewArgv(args string) (resetArgs bool, newArgv []string, newRedirects [3]string, err error) {
|
||||||
if args == "" {
|
if args == "" {
|
||||||
return false, nil, nil
|
return false, nil, [3]string{}, nil
|
||||||
}
|
}
|
||||||
v, err := argv.Argv(args,
|
v, err := argv.Argv(args,
|
||||||
func(s string) (string, error) {
|
func(s string) (string, error) {
|
||||||
@ -1049,22 +1056,58 @@ func parseNewArgv(args string) (resetArgs bool, newArgv []string, err error) {
|
|||||||
},
|
},
|
||||||
nil)
|
nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil, err
|
return false, nil, [3]string{}, err
|
||||||
}
|
}
|
||||||
if len(v) != 1 {
|
if len(v) != 1 {
|
||||||
return false, nil, fmt.Errorf("Illegal commandline '%s'", args)
|
return false, nil, [3]string{}, fmt.Errorf("illegal commandline '%s'", args)
|
||||||
}
|
}
|
||||||
w := v[0]
|
w := v[0]
|
||||||
if len(w) == 0 {
|
if len(w) == 0 {
|
||||||
return false, nil, nil
|
return false, nil, [3]string{}, nil
|
||||||
}
|
}
|
||||||
if w[0] == "-noargs" {
|
if w[0] == "-noargs" {
|
||||||
if len(w) > 1 {
|
if len(w) > 1 {
|
||||||
return false, nil, fmt.Errorf("Too many arguments to restart")
|
return false, nil, [3]string{}, fmt.Errorf("too many arguments to restart")
|
||||||
}
|
}
|
||||||
return true, nil, nil
|
return true, nil, [3]string{}, nil
|
||||||
}
|
}
|
||||||
return true, w, nil
|
redirs := [3]string{}
|
||||||
|
for len(w) > 0 {
|
||||||
|
var found bool
|
||||||
|
var err error
|
||||||
|
w, found, err = parseOneRedirect(w, &redirs)
|
||||||
|
if err != nil {
|
||||||
|
return false, nil, [3]string{}, err
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, w, redirs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseOneRedirect(w []string, redirs *[3]string) ([]string, bool, error) {
|
||||||
|
prefixes := []string{"<", ">", "2>"}
|
||||||
|
names := []string{"stdin", "stdout", "stderr"}
|
||||||
|
if len(w) >= 2 {
|
||||||
|
for _, prefix := range prefixes {
|
||||||
|
if w[len(w)-2] == prefix {
|
||||||
|
w[len(w)-2] += w[len(w)-1]
|
||||||
|
w = w[:len(w)-1]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i, prefix := range prefixes {
|
||||||
|
if strings.HasPrefix(w[len(w)-1], prefix) {
|
||||||
|
if redirs[i] != "" {
|
||||||
|
return nil, false, fmt.Errorf("redirect error: %s redirected twice", names[i])
|
||||||
|
}
|
||||||
|
redirs[i] = w[len(w)-1][len(prefix):]
|
||||||
|
return w[:len(w)-1], true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return w, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func printcontextNoState(t *Term) {
|
func printcontextNoState(t *Term) {
|
||||||
|
@ -1138,3 +1138,50 @@ func TestPrintCastToInterface(t *testing.T) {
|
|||||||
t.Logf("%q", out)
|
t.Logf("%q", out)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseNewArgv(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
in string
|
||||||
|
tgtargs string
|
||||||
|
tgtredir string
|
||||||
|
tgterr string
|
||||||
|
}{
|
||||||
|
{"-noargs", "", " | | ", ""},
|
||||||
|
{"-noargs arg1", "", "", "too many arguments to restart"},
|
||||||
|
{"arg1 arg2", "arg1 | arg2", " | | ", ""},
|
||||||
|
{"arg1 arg2 <input.txt", "arg1 | arg2", "input.txt | | ", ""},
|
||||||
|
{"arg1 arg2 < input.txt", "arg1 | arg2", "input.txt | | ", ""},
|
||||||
|
{"<input.txt", "", "input.txt | | ", ""},
|
||||||
|
{"< input.txt", "", "input.txt | | ", ""},
|
||||||
|
{"arg1 < input.txt > output.txt 2> error.txt", "arg1", "input.txt | output.txt | error.txt", ""},
|
||||||
|
{"< input.txt > output.txt 2> error.txt", "", "input.txt | output.txt | error.txt", ""},
|
||||||
|
{"arg1 <input.txt >output.txt 2>error.txt", "arg1", "input.txt | output.txt | error.txt", ""},
|
||||||
|
{"<input.txt >output.txt 2>error.txt", "", "input.txt | output.txt | error.txt", ""},
|
||||||
|
{"<input.txt <input2.txt", "", "", "redirect error: stdin redirected twice"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
resetArgs, newArgv, newRedirects, err := parseNewArgv(tc.in)
|
||||||
|
t.Logf("%q -> %q %q %v\n", tc.in, newArgv, newRedirects, err)
|
||||||
|
if tc.tgterr != "" {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected error %q, got no error", tc.tgterr)
|
||||||
|
} else if errstr := err.Error(); errstr != tc.tgterr {
|
||||||
|
t.Errorf("Expected error %q, got error %q", tc.tgterr, errstr)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !resetArgs {
|
||||||
|
t.Errorf("parse error, resetArgs is false")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
argvstr := strings.Join(newArgv, " | ")
|
||||||
|
if argvstr != tc.tgtargs {
|
||||||
|
t.Errorf("Expected new arguments %q, got %q", tc.tgtargs, argvstr)
|
||||||
|
}
|
||||||
|
redirstr := strings.Join(newRedirects[:], " | ")
|
||||||
|
if redirstr != tc.tgtredir {
|
||||||
|
t.Errorf("Expected new redirects %q, got %q", tc.tgtredir, redirstr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1097,6 +1097,12 @@ func (env *Env) starlarkPredeclare() starlark.StringDict {
|
|||||||
return starlark.None, decorateError(thread, err)
|
return starlark.None, decorateError(thread, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(args) > 5 && args[5] != starlark.None {
|
||||||
|
err := unmarshalStarlarkValue(args[5], &rpcArgs.NewRedirects, "NewRedirects")
|
||||||
|
if err != nil {
|
||||||
|
return starlark.None, decorateError(thread, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
for _, kv := range kwargs {
|
for _, kv := range kwargs {
|
||||||
var err error
|
var err error
|
||||||
switch kv[0].(starlark.String) {
|
switch kv[0].(starlark.String) {
|
||||||
@ -1110,6 +1116,8 @@ func (env *Env) starlarkPredeclare() starlark.StringDict {
|
|||||||
err = unmarshalStarlarkValue(kv[1], &rpcArgs.Rerecord, "Rerecord")
|
err = unmarshalStarlarkValue(kv[1], &rpcArgs.Rerecord, "Rerecord")
|
||||||
case "Rebuild":
|
case "Rebuild":
|
||||||
err = unmarshalStarlarkValue(kv[1], &rpcArgs.Rebuild, "Rebuild")
|
err = unmarshalStarlarkValue(kv[1], &rpcArgs.Rebuild, "Rebuild")
|
||||||
|
case "NewRedirects":
|
||||||
|
err = unmarshalStarlarkValue(kv[1], &rpcArgs.NewRedirects, "NewRedirects")
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("unknown argument %q", kv[0])
|
err = fmt.Errorf("unknown argument %q", kv[0])
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ type Client interface {
|
|||||||
// Restarts program. Set true if you want to rebuild the process we are debugging.
|
// Restarts program. Set true if you want to rebuild the process we are debugging.
|
||||||
Restart(rebuild bool) ([]api.DiscardedBreakpoint, error)
|
Restart(rebuild bool) ([]api.DiscardedBreakpoint, error)
|
||||||
// Restarts program from the specified position.
|
// Restarts program from the specified position.
|
||||||
RestartFrom(rerecord bool, pos string, resetArgs bool, newArgs []string, rebuild bool) ([]api.DiscardedBreakpoint, error)
|
RestartFrom(rerecord bool, pos string, resetArgs bool, newArgs []string, newRedirects [3]string, rebuild bool) ([]api.DiscardedBreakpoint, error)
|
||||||
|
|
||||||
// GetState returns the current debugger state.
|
// GetState returns the current debugger state.
|
||||||
GetState() (*api.DebuggerState, error)
|
GetState() (*api.DebuggerState, error)
|
||||||
|
@ -117,6 +117,9 @@ type Config struct {
|
|||||||
|
|
||||||
// ExecuteKind contains the kind of the executed program.
|
// ExecuteKind contains the kind of the executed program.
|
||||||
ExecuteKind ExecuteKind
|
ExecuteKind ExecuteKind
|
||||||
|
|
||||||
|
// Redirects specifies redirect rules for stdin, stdout and stderr
|
||||||
|
Redirects [3]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new Debugger. ProcessArgs specify the commandline arguments for the
|
// New creates a new Debugger. ProcessArgs specify the commandline arguments for the
|
||||||
@ -221,16 +224,16 @@ func (d *Debugger) Launch(processArgs []string, wd string) (*proc.Target, error)
|
|||||||
}
|
}
|
||||||
switch d.config.Backend {
|
switch d.config.Backend {
|
||||||
case "native":
|
case "native":
|
||||||
return native.Launch(processArgs, wd, d.config.Foreground, d.config.DebugInfoDirectories, d.config.TTY)
|
return native.Launch(processArgs, wd, d.config.Foreground, d.config.DebugInfoDirectories, d.config.TTY, d.config.Redirects)
|
||||||
case "lldb":
|
case "lldb":
|
||||||
return betterGdbserialLaunchError(gdbserial.LLDBLaunch(processArgs, wd, d.config.Foreground, d.config.DebugInfoDirectories, d.config.TTY))
|
return betterGdbserialLaunchError(gdbserial.LLDBLaunch(processArgs, wd, d.config.Foreground, d.config.DebugInfoDirectories, d.config.TTY, d.config.Redirects))
|
||||||
case "rr":
|
case "rr":
|
||||||
if d.target != nil {
|
if d.target != nil {
|
||||||
// restart should not call us if the backend is 'rr'
|
// restart should not call us if the backend is 'rr'
|
||||||
panic("internal error: call to Launch with rr backend and target already exists")
|
panic("internal error: call to Launch with rr backend and target already exists")
|
||||||
}
|
}
|
||||||
|
|
||||||
run, stop, err := gdbserial.RecordAsync(processArgs, wd, false)
|
run, stop, err := gdbserial.RecordAsync(processArgs, wd, false, d.config.Redirects)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -265,9 +268,9 @@ func (d *Debugger) Launch(processArgs []string, wd string) (*proc.Target, error)
|
|||||||
|
|
||||||
case "default":
|
case "default":
|
||||||
if runtime.GOOS == "darwin" {
|
if runtime.GOOS == "darwin" {
|
||||||
return betterGdbserialLaunchError(gdbserial.LLDBLaunch(processArgs, wd, d.config.Foreground, d.config.DebugInfoDirectories, d.config.TTY))
|
return betterGdbserialLaunchError(gdbserial.LLDBLaunch(processArgs, wd, d.config.Foreground, d.config.DebugInfoDirectories, d.config.TTY, d.config.Redirects))
|
||||||
}
|
}
|
||||||
return native.Launch(processArgs, wd, d.config.Foreground, d.config.DebugInfoDirectories, d.config.TTY)
|
return native.Launch(processArgs, wd, d.config.Foreground, d.config.DebugInfoDirectories, d.config.TTY, d.config.Redirects)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown backend %q", d.config.Backend)
|
return nil, fmt.Errorf("unknown backend %q", d.config.Backend)
|
||||||
}
|
}
|
||||||
@ -409,7 +412,7 @@ func (d *Debugger) detach(kill bool) error {
|
|||||||
// If the target process is a recording it will restart it from the given
|
// If the target process is a recording it will restart it from the given
|
||||||
// position. If pos starts with 'c' it's a checkpoint ID, otherwise it's an
|
// position. If pos starts with 'c' it's a checkpoint ID, otherwise it's an
|
||||||
// event number. If resetArgs is true, newArgs will replace the process args.
|
// event number. If resetArgs is true, newArgs will replace the process args.
|
||||||
func (d *Debugger) Restart(rerecord bool, pos string, resetArgs bool, newArgs []string, rebuild bool) ([]api.DiscardedBreakpoint, error) {
|
func (d *Debugger) Restart(rerecord bool, pos string, resetArgs bool, newArgs []string, newRedirects [3]string, rebuild bool) ([]api.DiscardedBreakpoint, error) {
|
||||||
d.targetMutex.Lock()
|
d.targetMutex.Lock()
|
||||||
defer d.targetMutex.Unlock()
|
defer d.targetMutex.Unlock()
|
||||||
|
|
||||||
@ -437,6 +440,7 @@ func (d *Debugger) Restart(rerecord bool, pos string, resetArgs bool, newArgs []
|
|||||||
}
|
}
|
||||||
if resetArgs {
|
if resetArgs {
|
||||||
d.processArgs = append([]string{d.processArgs[0]}, newArgs...)
|
d.processArgs = append([]string{d.processArgs[0]}, newArgs...)
|
||||||
|
d.config.Redirects = newRedirects
|
||||||
}
|
}
|
||||||
var p *proc.Target
|
var p *proc.Target
|
||||||
var err error
|
var err error
|
||||||
@ -460,7 +464,7 @@ func (d *Debugger) Restart(rerecord bool, pos string, resetArgs bool, newArgs []
|
|||||||
}
|
}
|
||||||
|
|
||||||
if recorded {
|
if recorded {
|
||||||
run, stop, err2 := gdbserial.RecordAsync(d.processArgs, d.config.WorkingDir, false)
|
run, stop, err2 := gdbserial.RecordAsync(d.processArgs, d.config.WorkingDir, false, d.config.Redirects)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
return nil, err2
|
return nil, err2
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ func (s *RPCServer) Restart(arg1 interface{}, arg2 *int) error {
|
|||||||
if s.config.Debugger.AttachPid != 0 {
|
if s.config.Debugger.AttachPid != 0 {
|
||||||
return errors.New("cannot restart process Delve did not create")
|
return errors.New("cannot restart process Delve did not create")
|
||||||
}
|
}
|
||||||
_, err := s.debugger.Restart(false, "", false, nil, false)
|
_, err := s.debugger.Restart(false, "", false, nil, [3]string{}, false)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,13 +62,13 @@ func (c *RPCClient) Detach(kill bool) error {
|
|||||||
|
|
||||||
func (c *RPCClient) Restart(rebuild bool) ([]api.DiscardedBreakpoint, error) {
|
func (c *RPCClient) Restart(rebuild bool) ([]api.DiscardedBreakpoint, error) {
|
||||||
out := new(RestartOut)
|
out := new(RestartOut)
|
||||||
err := c.call("Restart", RestartIn{"", false, nil, false, rebuild}, out)
|
err := c.call("Restart", RestartIn{"", false, nil, false, rebuild, [3]string{}}, out)
|
||||||
return out.DiscardedBreakpoints, err
|
return out.DiscardedBreakpoints, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RPCClient) RestartFrom(rerecord bool, pos string, resetArgs bool, newArgs []string, rebuild bool) ([]api.DiscardedBreakpoint, error) {
|
func (c *RPCClient) RestartFrom(rerecord bool, pos string, resetArgs bool, newArgs []string, newRedirects [3]string, rebuild bool) ([]api.DiscardedBreakpoint, error) {
|
||||||
out := new(RestartOut)
|
out := new(RestartOut)
|
||||||
err := c.call("Restart", RestartIn{pos, resetArgs, newArgs, rerecord, rebuild}, out)
|
err := c.call("Restart", RestartIn{pos, resetArgs, newArgs, rerecord, rebuild, newRedirects}, out)
|
||||||
return out.DiscardedBreakpoints, err
|
return out.DiscardedBreakpoints, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ type RestartIn struct {
|
|||||||
// otherwise it's an event number. Only valid for recorded targets.
|
// otherwise it's an event number. Only valid for recorded targets.
|
||||||
Position string
|
Position string
|
||||||
|
|
||||||
// ResetArgs tell whether NewArgs should take effect.
|
// ResetArgs tell whether NewArgs and NewRedirects should take effect.
|
||||||
ResetArgs bool
|
ResetArgs bool
|
||||||
// NewArgs are arguments to launch a new process. They replace only the
|
// NewArgs are arguments to launch a new process. They replace only the
|
||||||
// argv[1] and later. Argv[0] cannot be changed.
|
// argv[1] and later. Argv[0] cannot be changed.
|
||||||
@ -79,6 +79,8 @@ type RestartIn struct {
|
|||||||
|
|
||||||
// When Rebuild is set the process will be build again
|
// When Rebuild is set the process will be build again
|
||||||
Rebuild bool
|
Rebuild bool
|
||||||
|
|
||||||
|
NewRedirects [3]string
|
||||||
}
|
}
|
||||||
|
|
||||||
type RestartOut struct {
|
type RestartOut struct {
|
||||||
@ -93,7 +95,7 @@ func (s *RPCServer) Restart(arg RestartIn, cb service.RPCCallback) {
|
|||||||
}
|
}
|
||||||
var out RestartOut
|
var out RestartOut
|
||||||
var err error
|
var err error
|
||||||
out.DiscardedBreakpoints, err = s.debugger.Restart(arg.Rerecord, arg.Position, arg.ResetArgs, arg.NewArgs, arg.Rebuild)
|
out.DiscardedBreakpoints, err = s.debugger.Restart(arg.Rerecord, arg.Position, arg.ResetArgs, arg.NewArgs, arg.NewRedirects, arg.Rebuild)
|
||||||
cb.Return(out, err)
|
cb.Return(out, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,12 +54,12 @@ func TestMain(m *testing.M) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func withTestClient2(name string, t *testing.T, fn func(c service.Client)) {
|
func withTestClient2(name string, t *testing.T, fn func(c service.Client)) {
|
||||||
withTestClient2Extended(name, t, 0, func(c service.Client, fixture protest.Fixture) {
|
withTestClient2Extended(name, t, 0, [3]string{}, func(c service.Client, fixture protest.Fixture) {
|
||||||
fn(c)
|
fn(c)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func startServer(name string, buildFlags protest.BuildFlags, t *testing.T) (clientConn net.Conn, fixture protest.Fixture) {
|
func startServer(name string, buildFlags protest.BuildFlags, t *testing.T, redirects [3]string) (clientConn net.Conn, fixture protest.Fixture) {
|
||||||
if testBackend == "rr" {
|
if testBackend == "rr" {
|
||||||
protest.MustHaveRecordingAllowed(t)
|
protest.MustHaveRecordingAllowed(t)
|
||||||
}
|
}
|
||||||
@ -69,6 +69,11 @@ func startServer(name string, buildFlags protest.BuildFlags, t *testing.T) (clie
|
|||||||
buildFlags |= protest.BuildModePIE
|
buildFlags |= protest.BuildModePIE
|
||||||
}
|
}
|
||||||
fixture = protest.BuildFixture(name, buildFlags)
|
fixture = protest.BuildFixture(name, buildFlags)
|
||||||
|
for i := range redirects {
|
||||||
|
if redirects[i] != "" {
|
||||||
|
redirects[i] = filepath.Join(fixture.BuildDir, redirects[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
server := rpccommon.NewServer(&service.Config{
|
server := rpccommon.NewServer(&service.Config{
|
||||||
Listener: listener,
|
Listener: listener,
|
||||||
ProcessArgs: []string{fixture.Path},
|
ProcessArgs: []string{fixture.Path},
|
||||||
@ -78,6 +83,7 @@ func startServer(name string, buildFlags protest.BuildFlags, t *testing.T) (clie
|
|||||||
Packages: []string{fixture.Source},
|
Packages: []string{fixture.Source},
|
||||||
BuildFlags: "", // build flags can be an empty string here because the only test that uses it, does not set special flags.
|
BuildFlags: "", // build flags can be an empty string here because the only test that uses it, does not set special flags.
|
||||||
ExecuteKind: debugger.ExecutingGeneratedFile,
|
ExecuteKind: debugger.ExecutingGeneratedFile,
|
||||||
|
Redirects: redirects,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err := server.Run(); err != nil {
|
if err := server.Run(); err != nil {
|
||||||
@ -86,8 +92,8 @@ func startServer(name string, buildFlags protest.BuildFlags, t *testing.T) (clie
|
|||||||
return clientConn, fixture
|
return clientConn, fixture
|
||||||
}
|
}
|
||||||
|
|
||||||
func withTestClient2Extended(name string, t *testing.T, buildFlags protest.BuildFlags, fn func(c service.Client, fixture protest.Fixture)) {
|
func withTestClient2Extended(name string, t *testing.T, buildFlags protest.BuildFlags, redirects [3]string, fn func(c service.Client, fixture protest.Fixture)) {
|
||||||
clientConn, fixture := startServer(name, buildFlags, t)
|
clientConn, fixture := startServer(name, buildFlags, t, redirects)
|
||||||
client := rpc2.NewClientFromConn(clientConn)
|
client := rpc2.NewClientFromConn(clientConn)
|
||||||
defer func() {
|
defer func() {
|
||||||
client.Detach(true)
|
client.Detach(true)
|
||||||
@ -223,7 +229,7 @@ func TestRestart_rebuild(t *testing.T) {
|
|||||||
// In the original fixture file the env var tested for is SOMEVAR.
|
// In the original fixture file the env var tested for is SOMEVAR.
|
||||||
os.Setenv("SOMEVAR", "bah")
|
os.Setenv("SOMEVAR", "bah")
|
||||||
|
|
||||||
withTestClient2Extended("testenv", t, 0, func(c service.Client, f protest.Fixture) {
|
withTestClient2Extended("testenv", t, 0, [3]string{}, func(c service.Client, f protest.Fixture) {
|
||||||
<-c.Continue()
|
<-c.Continue()
|
||||||
|
|
||||||
var1, err := c.EvalVariable(api.EvalScope{GoroutineID: -1}, "x", normalLoadConfig)
|
var1, err := c.EvalVariable(api.EvalScope{GoroutineID: -1}, "x", normalLoadConfig)
|
||||||
@ -772,7 +778,7 @@ func TestClientServer_FindLocations(t *testing.T) {
|
|||||||
findLocationHelper(t, c, "main.stacktraceme", false, 1, stacktracemeAddr)
|
findLocationHelper(t, c, "main.stacktraceme", false, 1, stacktracemeAddr)
|
||||||
})
|
})
|
||||||
|
|
||||||
withTestClient2Extended("locationsUpperCase", t, 0, func(c service.Client, fixture protest.Fixture) {
|
withTestClient2Extended("locationsUpperCase", t, 0, [3]string{}, func(c service.Client, fixture protest.Fixture) {
|
||||||
// Upper case
|
// Upper case
|
||||||
findLocationHelper(t, c, "locationsUpperCase.go:6", false, 1, 0)
|
findLocationHelper(t, c, "locationsUpperCase.go:6", false, 1, 0)
|
||||||
|
|
||||||
@ -1895,7 +1901,7 @@ func (c *brokenRPCClient) call(method string, args, reply interface{}) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestUnknownMethodCall(t *testing.T) {
|
func TestUnknownMethodCall(t *testing.T) {
|
||||||
clientConn, _ := startServer("continuetestprog", 0, t)
|
clientConn, _ := startServer("continuetestprog", 0, t, [3]string{})
|
||||||
client := &brokenRPCClient{jsonrpc.NewClient(clientConn)}
|
client := &brokenRPCClient{jsonrpc.NewClient(clientConn)}
|
||||||
client.call("SetApiVersion", api.SetAPIVersionIn{APIVersion: 2}, &api.SetAPIVersionOut{})
|
client.call("SetApiVersion", api.SetAPIVersionIn{APIVersion: 2}, &api.SetAPIVersionOut{})
|
||||||
defer client.Detach(true)
|
defer client.Detach(true)
|
||||||
@ -1950,7 +1956,7 @@ func TestRerecord(t *testing.T) {
|
|||||||
|
|
||||||
t0 := gett()
|
t0 := gett()
|
||||||
|
|
||||||
_, err = c.RestartFrom(false, "", false, nil, false)
|
_, err = c.RestartFrom(false, "", false, nil, [3]string{}, false)
|
||||||
assertNoError(err, t, "First restart")
|
assertNoError(err, t, "First restart")
|
||||||
t1 := gett()
|
t1 := gett()
|
||||||
|
|
||||||
@ -1960,7 +1966,7 @@ func TestRerecord(t *testing.T) {
|
|||||||
|
|
||||||
time.Sleep(2 * time.Second) // make sure that we're not running inside the same second
|
time.Sleep(2 * time.Second) // make sure that we're not running inside the same second
|
||||||
|
|
||||||
_, err = c.RestartFrom(true, "", false, nil, false)
|
_, err = c.RestartFrom(true, "", false, nil, [3]string{}, false)
|
||||||
assertNoError(err, t, "Second restart")
|
assertNoError(err, t, "Second restart")
|
||||||
t2 := gett()
|
t2 := gett()
|
||||||
|
|
||||||
@ -2025,7 +2031,7 @@ func TestStopRecording(t *testing.T) {
|
|||||||
|
|
||||||
// try rerecording
|
// try rerecording
|
||||||
go func() {
|
go func() {
|
||||||
c.RestartFrom(true, "", false, nil, false)
|
c.RestartFrom(true, "", false, nil, [3]string{}, false)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
time.Sleep(time.Second) // hopefully the re-recording started...
|
time.Sleep(time.Second) // hopefully the re-recording started...
|
||||||
@ -2039,7 +2045,7 @@ func TestClearLogicalBreakpoint(t *testing.T) {
|
|||||||
// Clearing a logical breakpoint should clear all associated physical
|
// Clearing a logical breakpoint should clear all associated physical
|
||||||
// breakpoints.
|
// breakpoints.
|
||||||
// Issue #1955.
|
// Issue #1955.
|
||||||
withTestClient2Extended("testinline", t, protest.EnableInlining, func(c service.Client, fixture protest.Fixture) {
|
withTestClient2Extended("testinline", t, protest.EnableInlining, [3]string{}, func(c service.Client, fixture protest.Fixture) {
|
||||||
bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.inlineThis"})
|
bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.inlineThis"})
|
||||||
assertNoError(err, t, "CreateBreakpoint()")
|
assertNoError(err, t, "CreateBreakpoint()")
|
||||||
t.Logf("breakpoint set at %#v", bp.Addrs)
|
t.Logf("breakpoint set at %#v", bp.Addrs)
|
||||||
@ -2058,3 +2064,37 @@ func TestClearLogicalBreakpoint(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRedirects(t *testing.T) {
|
||||||
|
const (
|
||||||
|
infile = "redirect-input.txt"
|
||||||
|
outfile = "redirect-output.txt"
|
||||||
|
)
|
||||||
|
protest.AllowRecording(t)
|
||||||
|
withTestClient2Extended("redirect", t, 0, [3]string{infile, outfile, ""}, func(c service.Client, fixture protest.Fixture) {
|
||||||
|
outpath := filepath.Join(fixture.BuildDir, outfile)
|
||||||
|
<-c.Continue()
|
||||||
|
buf, err := ioutil.ReadFile(outpath)
|
||||||
|
assertNoError(err, t, "Reading output file")
|
||||||
|
t.Logf("output %q", buf)
|
||||||
|
if !strings.HasPrefix(string(buf), "Redirect test") {
|
||||||
|
t.Fatalf("Wrong output %q", string(buf))
|
||||||
|
}
|
||||||
|
os.Remove(outpath)
|
||||||
|
if testBackend != "rr" {
|
||||||
|
_, err = c.Restart(false)
|
||||||
|
assertNoError(err, t, "Restart")
|
||||||
|
<-c.Continue()
|
||||||
|
buf2, err := ioutil.ReadFile(outpath)
|
||||||
|
t.Logf("output %q", buf2)
|
||||||
|
assertNoError(err, t, "Reading output file (second time)")
|
||||||
|
if !strings.HasPrefix(string(buf2), "Redirect test") {
|
||||||
|
t.Fatalf("Wrong output %q", string(buf2))
|
||||||
|
}
|
||||||
|
if string(buf2) == string(buf) {
|
||||||
|
t.Fatalf("Expected output change got %q and %q", string(buf), string(buf2))
|
||||||
|
}
|
||||||
|
os.Remove(outpath)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -131,13 +131,13 @@ func withTestProcessArgs(name string, t *testing.T, wd string, args []string, bu
|
|||||||
var tracedir string
|
var tracedir string
|
||||||
switch testBackend {
|
switch testBackend {
|
||||||
case "native":
|
case "native":
|
||||||
p, err = native.Launch(append([]string{fixture.Path}, args...), wd, false, []string{}, "")
|
p, err = native.Launch(append([]string{fixture.Path}, args...), wd, false, []string{}, "", [3]string{})
|
||||||
case "lldb":
|
case "lldb":
|
||||||
p, err = gdbserial.LLDBLaunch(append([]string{fixture.Path}, args...), wd, false, []string{}, "")
|
p, err = gdbserial.LLDBLaunch(append([]string{fixture.Path}, args...), wd, false, []string{}, "", [3]string{})
|
||||||
case "rr":
|
case "rr":
|
||||||
protest.MustHaveRecordingAllowed(t)
|
protest.MustHaveRecordingAllowed(t)
|
||||||
t.Log("recording")
|
t.Log("recording")
|
||||||
p, tracedir, err = gdbserial.RecordAndReplay(append([]string{fixture.Path}, args...), wd, true, []string{})
|
p, tracedir, err = gdbserial.RecordAndReplay(append([]string{fixture.Path}, args...), wd, true, []string{}, [3]string{})
|
||||||
t.Logf("replaying %q", tracedir)
|
t.Logf("replaying %q", tracedir)
|
||||||
default:
|
default:
|
||||||
t.Fatalf("unknown backend %q", testBackend)
|
t.Fatalf("unknown backend %q", testBackend)
|
||||||
|
Loading…
Reference in New Issue
Block a user