From 3e60ae202b7d7c67fe7ea5fffafc92c7e39e4b67 Mon Sep 17 00:00:00 2001 From: Derek Parker Date: Sat, 4 Apr 2020 15:19:35 -0700 Subject: [PATCH] *: Add --tty flag for debug / exec This flag allows users on UNIX systems to set the tty for the program being debugged by Delve. This is useful for debugging command line applications which need access to their own TTY, and also for controlling the output of the debugged programs so that IDEs may open a dedicated terminal to show the output for the process. --- .travis.yml | 6 ++- Documentation/faq.md | 14 +++++++ Documentation/usage/dlv_debug.md | 1 + Documentation/usage/dlv_exec.md | 3 +- cmd/dlv/cmds/commands.go | 10 ++++- go.mod | 2 +- go.sum | 2 + pkg/proc/gdbserial/gdbserver.go | 5 ++- pkg/proc/native/nonative_darwin.go | 4 +- pkg/proc/native/proc.go | 8 ++++ pkg/proc/native/proc_darwin.go | 2 +- pkg/proc/native/proc_freebsd.go | 8 +++- pkg/proc/native/proc_linux.go | 14 ++++++- pkg/proc/native/proc_unix.go | 30 +++++++++++++++ pkg/proc/native/proc_windows.go | 2 +- pkg/proc/proc_linux_test.go | 2 +- pkg/proc/proc_test.go | 8 ++-- service/config.go | 4 ++ service/dap/server.go | 1 + service/debugger/debugger.go | 12 ++++-- service/debugger/debugger_unix_test.go | 52 +++++++++++++++++++++++++- service/rpccommon/server.go | 1 + service/test/variables_test.go | 4 +- 23 files changed, 170 insertions(+), 25 deletions(-) create mode 100644 pkg/proc/native/proc_unix.go diff --git a/.travis.yml b/.travis.yml index 21b4f8eb..03119174 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,7 +58,11 @@ jobs: script: >- if [ $TRAVIS_OS_NAME = "linux" ] && [ $go_32_version ]; then docker pull i386/centos:7; - docker run -v $(pwd):/delve --privileged i386/centos:7 /bin/bash -c "set -x && \ + docker run \ + -v $(pwd):/delve \ + --env TRAVIS=true \ + --privileged i386/centos:7 \ + /bin/bash -c "set -x && \ cd delve && \ yum -y update && yum -y upgrade && \ yum -y install wget make git gcc && \ diff --git a/Documentation/faq.md b/Documentation/faq.md index 9b8874eb..54f10957 100644 --- a/Documentation/faq.md +++ b/Documentation/faq.md @@ -35,3 +35,17 @@ dlv exec --headless --continue --listen :4040 --accept-multiclient /path/to/exec ``` Note that the connection to Delve is unauthenticated and will allow arbitrary remote code execution: *do not do this in production*. + +#### How can I use Delve to debug a CLI application? + +There are three good ways to go about this + +1. Run your CLI application in a separate terminal and then attach to it via `dlv attach`. + +1. Run Delve in headless mode via `dlv debug --headless` and then connect to it from +another terminal. This will place the process in the foreground and allow it to access +the terminal TTY. + +1. Assign the process its own TTY. This can be done on UNIX systems via the `--tty` flag for the +`dlv debug` and `dlv exec` commands. For the best experience, you should create your own PTY and +assign it as the TTY. This can be done via [ptyme](https://github.com/derekparker/ptyme). diff --git a/Documentation/usage/dlv_debug.md b/Documentation/usage/dlv_debug.md index d3242475..d00dee1b 100644 --- a/Documentation/usage/dlv_debug.md +++ b/Documentation/usage/dlv_debug.md @@ -21,6 +21,7 @@ dlv debug [package] ``` --continue Continue the debugged process on start. --output string Output path for the binary. (default "./__debug_bin") + --tty string TTY to use for the target program ``` ### Options inherited from parent commands diff --git a/Documentation/usage/dlv_exec.md b/Documentation/usage/dlv_exec.md index e9711b98..2b3d123e 100644 --- a/Documentation/usage/dlv_exec.md +++ b/Documentation/usage/dlv_exec.md @@ -20,7 +20,8 @@ dlv exec ### Options ``` - --continue Continue the debugged process on start. + --continue Continue the debugged process on start. + --tty string TTY to use for the target program ``` ### Options inherited from parent commands diff --git a/cmd/dlv/cmds/commands.go b/cmd/dlv/cmds/commands.go index 3fade4f3..ca6ac1ed 100644 --- a/cmd/dlv/cmds/commands.go +++ b/cmd/dlv/cmds/commands.go @@ -52,6 +52,8 @@ var ( // checkLocalConnUser is true if the debugger should check that local // connections come from the same user that started the headless server checkLocalConnUser bool + // tty is used to provide an alternate TTY for the program you wish to debug. + tty string // backend selection backend string @@ -184,6 +186,7 @@ session.`, } debugCommand.Flags().String("output", "./__debug_bin", "Output path for the binary.") debugCommand.Flags().BoolVar(&continueOnStart, "continue", false, "Continue the debugged process on start.") + debugCommand.Flags().StringVar(&tty, "tty", "", "TTY to use for the target program") rootCommand.AddCommand(debugCommand) // 'exec' subcommand. @@ -207,6 +210,7 @@ or later, -gcflags="-N -l" on earlier versions of Go.`, os.Exit(execute(0, args, conf, "", executingExistingFile)) }, } + execCommand.Flags().StringVar(&tty, "tty", "", "TTY to use for the target program") execCommand.Flags().BoolVar(&continueOnStart, "continue", false, "Continue the debugged process on start.") rootCommand.AddCommand(execCommand) @@ -398,10 +402,11 @@ func dapCmd(cmd *cobra.Command, args []string) { server := dap.NewServer(&service.Config{ Listener: listener, Backend: backend, - Foreground: true, // always headless + Foreground: (headless && tty == ""), DebugInfoDirectories: conf.DebugInfoDirectories, CheckGoVersion: checkGoVersion, DisconnectChan: disconnectChan, + TTY: tty, }) defer server.Stop() @@ -741,11 +746,12 @@ func execute(attachPid int, processArgs []string, conf *config.Config, coreFile WorkingDir: workingDir, Backend: backend, CoreFile: coreFile, - Foreground: headless, + Foreground: (headless && tty == ""), DebugInfoDirectories: conf.DebugInfoDirectories, CheckGoVersion: checkGoVersion, CheckLocalConnUser: checkLocalConnUser, DisconnectChan: disconnectChan, + TTY: tty, }) default: fmt.Printf("Unknown API version: %d\n", apiVersion) diff --git a/go.mod b/go.mod index 7ded24d4..76275bc9 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,8 @@ go 1.11 require ( github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5 github.com/cpuguy83/go-md2man v1.0.10 // indirect + github.com/creack/pty v1.1.9 github.com/google/go-dap v0.2.0 - github.com/cpuguy83/go-md2man v1.0.8 // indirect github.com/hashicorp/golang-lru v0.5.4 github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/mattn/go-colorable v0.0.0-20170327083344-ded68f7a9561 diff --git a/go.sum b/go.sum index fde29856..51983b18 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,8 @@ github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5/go.mod h1:p/NrK5tF6IC github.com/cpuguy83/go-md2man v1.0.8/go.mod h1:N6JayAiVKtlHSnuTCeuLSQVs75hb8q+dYQLjr7cDsKY= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= diff --git a/pkg/proc/gdbserial/gdbserver.go b/pkg/proc/gdbserial/gdbserver.go index 225eeef0..642b7580 100644 --- a/pkg/proc/gdbserial/gdbserver.go +++ b/pkg/proc/gdbserial/gdbserver.go @@ -345,7 +345,7 @@ func getLdEnvVars() []string { // LLDBLaunch starts an instance of lldb-server and connects to it, asking // it to launch the specified target program with the specified arguments // (cmd) on the specified directory wd. -func LLDBLaunch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (*proc.Target, error) { +func LLDBLaunch(cmd []string, wd string, foreground bool, debugInfoDirs []string, tty string) (*proc.Target, error) { if runtime.GOOS == "windows" { return nil, ErrUnsupportedOS } @@ -374,6 +374,9 @@ func LLDBLaunch(cmd []string, wd string, foreground bool, debugInfoDirs []string if foreground { args = append(args, "--stdio-path", "/dev/tty") } + if tty != "" { + args = append(args, "--stdio-path", tty) + } if logflags.LLDBServerOutput() { args = append(args, "-g", "-l", "stdout") } diff --git a/pkg/proc/native/nonative_darwin.go b/pkg/proc/native/nonative_darwin.go index 2f006f3b..5ea4dc8a 100644 --- a/pkg/proc/native/nonative_darwin.go +++ b/pkg/proc/native/nonative_darwin.go @@ -12,12 +12,12 @@ import ( var ErrNativeBackendDisabled = errors.New("native backend disabled during compilation") // Launch returns ErrNativeBackendDisabled. -func Launch(cmd []string, wd string, foreground bool, _ []string) (*proc.Target, error) { +func Launch(_ []string, _ string, _ bool, _ []string, _ string) (*proc.Target, error) { return nil, ErrNativeBackendDisabled } // Attach returns ErrNativeBackendDisabled. -func Attach(pid int, _ []string) (*proc.Target, error) { +func Attach(_ int, _ []string) (*proc.Target, error) { return nil, ErrNativeBackendDisabled } diff --git a/pkg/proc/native/proc.go b/pkg/proc/native/proc.go index 1fe3454a..18869198 100644 --- a/pkg/proc/native/proc.go +++ b/pkg/proc/native/proc.go @@ -2,6 +2,7 @@ package native import ( "go/ast" + "os" "runtime" "sync" @@ -34,6 +35,10 @@ type nativeProcess struct { childProcess bool // this process was launched, not attached to manualStopRequested bool + // Controlling terminal file descriptor for + // this process. + ctty *os.File + exited, detached bool } @@ -343,6 +348,9 @@ func (dbp *nativeProcess) postExit() { close(dbp.ptraceChan) close(dbp.ptraceDoneChan) dbp.bi.Close() + if dbp.ctty != nil { + dbp.ctty.Close() + } } func (dbp *nativeProcess) writeSoftwareBreakpoint(thread *nativeThread, addr uint64) error { diff --git a/pkg/proc/native/proc_darwin.go b/pkg/proc/native/proc_darwin.go index aa0b7655..95636462 100644 --- a/pkg/proc/native/proc_darwin.go +++ b/pkg/proc/native/proc_darwin.go @@ -37,7 +37,7 @@ type osProcessDetails struct { // custom fork/exec process in order to take advantage of // PT_SIGEXC on Darwin which will turn Unix signals into // Mach exceptions. -func Launch(cmd []string, wd string, foreground bool, _ []string) (*proc.Target, error) { +func Launch(cmd []string, wd string, foreground bool, _ []string, _ string) (*proc.Target, error) { argv0Go, err := filepath.Abs(cmd[0]) if err != nil { return nil, err diff --git a/pkg/proc/native/proc_freebsd.go b/pkg/proc/native/proc_freebsd.go index b491b8f9..c925cc81 100644 --- a/pkg/proc/native/proc_freebsd.go +++ b/pkg/proc/native/proc_freebsd.go @@ -43,7 +43,7 @@ type osProcessDetails struct { // 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 // for external debug files in the directories passed in. -func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (*proc.Target, error) { +func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string, tty string) (*proc.Target, error) { var ( process *exec.Cmd err error @@ -66,6 +66,12 @@ func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (* signal.Ignore(syscall.SIGTTOU, syscall.SIGTTIN) process.Stdin = os.Stdin } + if tty != "" { + dbp.ctty, err = attachProcessToTTY(process, tty) + if err != nil { + return + } + } if wd != "" { process.Dir = wd } diff --git a/pkg/proc/native/proc_linux.go b/pkg/proc/native/proc_linux.go index 3aa0c56a..c1b53cc0 100644 --- a/pkg/proc/native/proc_linux.go +++ b/pkg/proc/native/proc_linux.go @@ -49,7 +49,7 @@ type osProcessDetails struct { // 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 // for external debug files in the directories passed in. -func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (*proc.Target, error) { +func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string, tty string) (*proc.Target, error) { var ( process *exec.Cmd err error @@ -67,11 +67,21 @@ func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (* process.Args = cmd process.Stdout = os.Stdout process.Stderr = os.Stderr - process.SysProcAttr = &syscall.SysProcAttr{Ptrace: true, Setpgid: true, Foreground: foreground} + process.SysProcAttr = &syscall.SysProcAttr{ + Ptrace: true, + Setpgid: true, + Foreground: foreground, + } if foreground { signal.Ignore(syscall.SIGTTOU, syscall.SIGTTIN) process.Stdin = os.Stdin } + if tty != "" { + dbp.ctty, err = attachProcessToTTY(process, tty) + if err != nil { + return + } + } if wd != "" { process.Dir = wd } diff --git a/pkg/proc/native/proc_unix.go b/pkg/proc/native/proc_unix.go new file mode 100644 index 00000000..2ca69f3a --- /dev/null +++ b/pkg/proc/native/proc_unix.go @@ -0,0 +1,30 @@ +// +build !windows + +package native + +import ( + "fmt" + "os" + "os/exec" + + isatty "github.com/mattn/go-isatty" +) + +func attachProcessToTTY(process *exec.Cmd, tty string) (*os.File, error) { + f, err := os.OpenFile(tty, os.O_RDWR, 0) + if err != nil { + return nil, err + } + if !isatty.IsTerminal(f.Fd()) { + f.Close() + return nil, fmt.Errorf("%s is not a terminal", f.Name()) + } + process.Stdin = f + process.Stdout = f + process.Stderr = f + process.SysProcAttr.Setpgid = false + process.SysProcAttr.Setsid = true + process.SysProcAttr.Setctty = true + + return f, nil +} diff --git a/pkg/proc/native/proc_windows.go b/pkg/proc/native/proc_windows.go index 4585eb62..1e04e0d2 100644 --- a/pkg/proc/native/proc_windows.go +++ b/pkg/proc/native/proc_windows.go @@ -20,7 +20,7 @@ type osProcessDetails struct { } // Launch creates and begins debugging a new process. -func Launch(cmd []string, wd string, foreground bool, _ []string) (*proc.Target, error) { +func Launch(cmd []string, wd string, foreground bool, _ []string, _ string) (*proc.Target, error) { argv0Go, err := filepath.Abs(cmd[0]) if err != nil { return nil, err diff --git a/pkg/proc/proc_linux_test.go b/pkg/proc/proc_linux_test.go index 530bcadb..6ea02e0f 100644 --- a/pkg/proc/proc_linux_test.go +++ b/pkg/proc/proc_linux_test.go @@ -14,7 +14,7 @@ func TestLoadingExternalDebugInfo(t *testing.T) { fixture := protest.BuildFixture("locationsprog", 0) defer os.Remove(fixture.Path) 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)}, "") if err != nil { t.Fatal(err) } diff --git a/pkg/proc/proc_test.go b/pkg/proc/proc_test.go index b8c374bb..09082737 100644 --- a/pkg/proc/proc_test.go +++ b/pkg/proc/proc_test.go @@ -67,9 +67,9 @@ func withTestProcessArgs(name string, t testing.TB, wd string, args []string, bu switch testBackend { 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{}, "") 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{}, "") case "rr": protest.MustHaveRecordingAllowed(t) t.Log("recording") @@ -2065,9 +2065,9 @@ func TestUnsupportedArch(t *testing.T) { switch testBackend { case "native": - p, err = native.Launch([]string{outfile}, ".", false, []string{}) + p, err = native.Launch([]string{outfile}, ".", false, []string{}, "") case "lldb": - p, err = gdbserial.LLDBLaunch([]string{outfile}, ".", false, []string{}) + p, err = gdbserial.LLDBLaunch([]string{outfile}, ".", false, []string{}, "") default: t.Skip("test not valid for this backend") } diff --git a/service/config.go b/service/config.go index 11fc31a0..7ff4a5a3 100644 --- a/service/config.go +++ b/service/config.go @@ -50,4 +50,8 @@ type Config struct { // DisconnectChan will be closed by the server when the client disconnects DisconnectChan chan<- struct{} + + // TTY is passed along to the target process on creation. Used to specify a + // TTY for that process. + TTY string } diff --git a/service/dap/server.go b/service/dap/server.go index a7262a8e..fadbee78 100644 --- a/service/dap/server.go +++ b/service/dap/server.go @@ -368,6 +368,7 @@ func (s *Server) onLaunchRequest(request *dap.LaunchRequest) { Foreground: s.config.Foreground, DebugInfoDirectories: s.config.DebugInfoDirectories, CheckGoVersion: s.config.CheckGoVersion, + TTY: s.config.TTY, } var err error if s.debugger, err = debugger.New(config, s.config.ProcessArgs); err != nil { diff --git a/service/debugger/debugger.go b/service/debugger/debugger.go index a12e4f82..cec918a4 100644 --- a/service/debugger/debugger.go +++ b/service/debugger/debugger.go @@ -91,6 +91,10 @@ type Config struct { // used to compile the executable and refuse to work on incompatible // versions. CheckGoVersion bool + + // TTY is passed along to the target process on creation. Used to specify a + // TTY for that process. + TTY string } // New creates a new Debugger. ProcessArgs specify the commandline arguments for the @@ -195,9 +199,9 @@ func (d *Debugger) Launch(processArgs []string, wd string) (*proc.Target, error) } switch d.config.Backend { case "native": - return native.Launch(processArgs, wd, d.config.Foreground, d.config.DebugInfoDirectories) + return native.Launch(processArgs, wd, d.config.Foreground, d.config.DebugInfoDirectories, d.config.TTY) case "lldb": - return betterGdbserialLaunchError(gdbserial.LLDBLaunch(processArgs, wd, d.config.Foreground, d.config.DebugInfoDirectories)) + return betterGdbserialLaunchError(gdbserial.LLDBLaunch(processArgs, wd, d.config.Foreground, d.config.DebugInfoDirectories, d.config.TTY)) case "rr": if d.target != nil { // restart should not call us if the backend is 'rr' @@ -239,9 +243,9 @@ func (d *Debugger) Launch(processArgs []string, wd string) (*proc.Target, error) case "default": if runtime.GOOS == "darwin" { - return betterGdbserialLaunchError(gdbserial.LLDBLaunch(processArgs, wd, d.config.Foreground, d.config.DebugInfoDirectories)) + return betterGdbserialLaunchError(gdbserial.LLDBLaunch(processArgs, wd, d.config.Foreground, d.config.DebugInfoDirectories, d.config.TTY)) } - return native.Launch(processArgs, wd, d.config.Foreground, d.config.DebugInfoDirectories) + return native.Launch(processArgs, wd, d.config.Foreground, d.config.DebugInfoDirectories, d.config.TTY) default: return nil, fmt.Errorf("unknown backend %q", d.config.Backend) } diff --git a/service/debugger/debugger_unix_test.go b/service/debugger/debugger_unix_test.go index 2bea8f8f..caa12c96 100644 --- a/service/debugger/debugger_unix_test.go +++ b/service/debugger/debugger_unix_test.go @@ -3,18 +3,25 @@ package debugger import ( + "bytes" "fmt" "os" + "os/exec" "path/filepath" "runtime" "testing" + "github.com/creack/pty" "github.com/go-delve/delve/pkg/gobuild" protest "github.com/go-delve/delve/pkg/proc/test" "github.com/go-delve/delve/service/api" ) func TestDebugger_LaunchNoExecutablePerm(t *testing.T) { + defer func() { + os.Setenv("GOOS", runtime.GOOS) + os.Setenv("GOARCH", runtime.GOARCH) + }() fixturesDir := protest.FindFixturesDir() buildtestdir := filepath.Join(fixturesDir, "buildtest") debugname := "debug" @@ -29,10 +36,10 @@ func TestDebugger_LaunchNoExecutablePerm(t *testing.T) { } os.Setenv("GOOS", switchOS[runtime.GOOS]) exepath := filepath.Join(buildtestdir, debugname) + defer os.Remove(exepath) if err := gobuild.GoBuild(debugname, []string{buildtestdir}, fmt.Sprintf("-o %s", exepath)); err != nil { t.Fatalf("go build error %v", err) } - defer os.Remove(exepath) if err := os.Chmod(exepath, 0644); err != nil { t.Fatal(err) } @@ -45,3 +52,46 @@ func TestDebugger_LaunchNoExecutablePerm(t *testing.T) { t.Fatalf("expected error \"%s\" got \"%v\"", api.ErrNotExecutable, err) } } + +func TestDebugger_LaunchWithTTY(t *testing.T) { + if os.Getenv("TRAVIS") == "true" { + if _, err := exec.LookPath("lsof"); err != nil { + t.Skip("skipping test in CI, system does not contain lsof") + } + } + // Ensure no env meddling is leftover from previous tests. + os.Setenv("GOOS", runtime.GOOS) + os.Setenv("GOARCH", runtime.GOARCH) + + p, tty, err := pty.Open() + if err != nil { + t.Fatal(err) + } + defer p.Close() + defer tty.Close() + + fixturesDir := protest.FindFixturesDir() + buildtestdir := filepath.Join(fixturesDir, "buildtest") + debugname := "debugtty" + exepath := filepath.Join(buildtestdir, debugname) + if err := gobuild.GoBuild(debugname, []string{buildtestdir}, fmt.Sprintf("-o %s", exepath)); err != nil { + t.Fatalf("go build error %v", err) + } + defer os.Remove(exepath) + var backend string + protest.DefaultTestBackend(&backend) + conf := &Config{TTY: tty.Name(), Backend: backend} + pArgs := []string{exepath} + d, err := New(conf, pArgs) + if err != nil { + t.Fatal(err) + } + cmd := exec.Command("lsof", "-p", fmt.Sprintf("%d", d.ProcessPid())) + result, err := cmd.CombinedOutput() + if err != nil { + t.Fatal(err) + } + if !bytes.Contains(result, []byte(tty.Name())) { + t.Fatal("process open file list does not contain expected tty") + } +} diff --git a/service/rpccommon/server.go b/service/rpccommon/server.go index bde3bfc9..b07ece3e 100644 --- a/service/rpccommon/server.go +++ b/service/rpccommon/server.go @@ -116,6 +116,7 @@ func (s *ServerImpl) Run() error { Foreground: s.config.Foreground, DebugInfoDirectories: s.config.DebugInfoDirectories, CheckGoVersion: s.config.CheckGoVersion, + TTY: s.config.TTY, }, s.config.ProcessArgs); err != nil { return err diff --git a/service/test/variables_test.go b/service/test/variables_test.go index 4e1943b5..5d000e4a 100644 --- a/service/test/variables_test.go +++ b/service/test/variables_test.go @@ -131,9 +131,9 @@ func withTestProcessArgs(name string, t *testing.T, wd string, args []string, bu var tracedir string switch testBackend { 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{}, "") 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{}, "") case "rr": protest.MustHaveRecordingAllowed(t) t.Log("recording")