proc/native,proc/gdbserial: let target access terminal
Change the linux verison of proc/native and proc/gdbserial (with debugserver) so that they let the target process use the terminal when delve is launched in headless mode. Windows already worked, proc/gdbserial (with rr) already worked. I couldn't find a way to make proc/gdbserial (with lldb-server) work. No tests are added because I can't think of a way to test for foregroundness of a process. Fixes #65
This commit is contained in:
parent
c7cde8b151
commit
cc86bde549
@ -505,6 +505,7 @@ func execute(attachPid int, processArgs []string, conf *config.Config, coreFile
|
||||
WorkingDir: WorkingDir,
|
||||
Backend: Backend,
|
||||
CoreFile: coreFile,
|
||||
Foreground: Headless,
|
||||
|
||||
DisconnectChan: disconnectChan,
|
||||
}, logflags.Debugger())
|
||||
|
@ -191,7 +191,7 @@ func New(process *os.Process) *Process {
|
||||
}
|
||||
|
||||
// Listen waits for a connection from the stub.
|
||||
func (p *Process) Listen(listener net.Listener, path string, pid int) error {
|
||||
func (p *Process) Listen(listener net.Listener, path string, pid int, foreground bool) error {
|
||||
acceptChan := make(chan net.Conn)
|
||||
|
||||
go func() {
|
||||
@ -205,7 +205,7 @@ func (p *Process) Listen(listener net.Listener, path string, pid int) error {
|
||||
if conn == nil {
|
||||
return errors.New("could not connect")
|
||||
}
|
||||
return p.Connect(conn, path, pid)
|
||||
return p.Connect(conn, path, pid, foreground)
|
||||
case status := <-p.waitChan:
|
||||
listener.Close()
|
||||
return fmt.Errorf("stub exited while waiting for connection: %v", status)
|
||||
@ -217,7 +217,7 @@ func (p *Process) Dial(addr string, path string, pid int) error {
|
||||
for {
|
||||
conn, err := net.Dial("tcp", addr)
|
||||
if err == nil {
|
||||
return p.Connect(conn, path, pid)
|
||||
return p.Connect(conn, path, pid, false)
|
||||
}
|
||||
select {
|
||||
case status := <-p.waitChan:
|
||||
@ -234,7 +234,7 @@ func (p *Process) Dial(addr string, path string, pid int) error {
|
||||
// program and the PID of the target process, both are optional, however
|
||||
// some stubs do not provide ways to determine path and pid automatically
|
||||
// and Connect will be unable to function without knowing them.
|
||||
func (p *Process) Connect(conn net.Conn, path string, pid int) error {
|
||||
func (p *Process) Connect(conn net.Conn, path string, pid int, foreground bool) error {
|
||||
p.conn.conn = conn
|
||||
|
||||
p.conn.pid = pid
|
||||
@ -341,6 +341,14 @@ func (p *Process) Connect(conn net.Conn, path string, pid int) error {
|
||||
}
|
||||
}
|
||||
|
||||
if foreground {
|
||||
// Moves process group of the target to foreground. Here we use the PID of
|
||||
// the debugserver instance because we already asked Go to put it into a
|
||||
// new process group (Setpgid) and debugserver does not spawn the target
|
||||
// into a different process group.
|
||||
moveToForeground(p.process.Pid)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -380,7 +388,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) (*Process, error) {
|
||||
func LLDBLaunch(cmd []string, wd string, foreground bool) (*Process, error) {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
return nil, ErrUnsupportedOS
|
||||
@ -404,6 +412,9 @@ func LLDBLaunch(cmd []string, wd string) (*Process, error) {
|
||||
ldEnvVars := getLdEnvVars()
|
||||
args := make([]string, 0, len(cmd)+4+len(ldEnvVars))
|
||||
args = append(args, ldEnvVars...)
|
||||
if foreground {
|
||||
args = append(args, "--stdio-path", "/dev/tty")
|
||||
}
|
||||
args = append(args, "-F", "-R", fmt.Sprintf("127.0.0.1:%d", listener.Addr().(*net.TCPAddr).Port), "--")
|
||||
args = append(args, cmd...)
|
||||
|
||||
@ -423,10 +434,13 @@ func LLDBLaunch(cmd []string, wd string) (*Process, error) {
|
||||
proc = exec.Command("lldb-server", args...)
|
||||
}
|
||||
|
||||
if logflags.LLDBServerOutput() || logflags.GdbWire() {
|
||||
if logflags.LLDBServerOutput() || logflags.GdbWire() || foreground {
|
||||
proc.Stdout = os.Stdout
|
||||
proc.Stderr = os.Stderr
|
||||
}
|
||||
if foreground {
|
||||
proc.Stdin = os.Stdin
|
||||
}
|
||||
if wd != "" {
|
||||
proc.Dir = wd
|
||||
}
|
||||
@ -442,7 +456,7 @@ func LLDBLaunch(cmd []string, wd string) (*Process, error) {
|
||||
p.conn.isDebugserver = isDebugserver
|
||||
|
||||
if listener != nil {
|
||||
err = p.Listen(listener, cmd[0], 0)
|
||||
err = p.Listen(listener, cmd[0], 0, p.conn.isDebugserver && foreground)
|
||||
} else {
|
||||
err = p.Dial(port, cmd[0], 0)
|
||||
}
|
||||
@ -495,7 +509,7 @@ func LLDBAttach(pid int, path string) (*Process, error) {
|
||||
p.conn.isDebugserver = isDebugserver
|
||||
|
||||
if listener != nil {
|
||||
err = p.Listen(listener, path, pid)
|
||||
err = p.Listen(listener, path, pid, false)
|
||||
} else {
|
||||
err = p.Dial(port, path, pid)
|
||||
}
|
||||
|
@ -2,8 +2,15 @@
|
||||
|
||||
package gdbserial
|
||||
|
||||
import "syscall"
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func backgroundSysProcAttr() *syscall.SysProcAttr {
|
||||
return &syscall.SysProcAttr{Setpgid: true, Pgid: 0, Foreground: false}
|
||||
}
|
||||
|
||||
func moveToForeground(pid int) {
|
||||
syscall.Syscall(syscall.SYS_IOCTL, uintptr(0), uintptr(syscall.TIOCSPGRP), uintptr(unsafe.Pointer(&pid)))
|
||||
}
|
||||
|
@ -5,3 +5,7 @@ import "syscall"
|
||||
func backgroundSysProcAttr() *syscall.SysProcAttr {
|
||||
return nil
|
||||
}
|
||||
|
||||
func moveToForeground(pid int) {
|
||||
panic("lldb backend not supported on windows")
|
||||
}
|
||||
|
@ -36,7 +36,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) (*Process, error) {
|
||||
func Launch(cmd []string, wd string, foreground bool) (*Process, error) {
|
||||
// check that the argument to Launch is an executable file
|
||||
if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
|
||||
return nil, proc.NotExecutableErr
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
|
||||
@ -43,7 +44,7 @@ type OSProcessDetails struct {
|
||||
// Launch creates and begins debugging a new process. First entry in
|
||||
// `cmd` is the program to run, and then rest are the arguments
|
||||
// to be supplied to that process. `wd` is working directory of the program.
|
||||
func Launch(cmd []string, wd string) (*Process, error) {
|
||||
func Launch(cmd []string, wd string, foreground bool) (*Process, error) {
|
||||
var (
|
||||
process *exec.Cmd
|
||||
err error
|
||||
@ -59,6 +60,9 @@ func Launch(cmd []string, wd string) (*Process, error) {
|
||||
process.Stdout = os.Stdout
|
||||
process.Stderr = os.Stderr
|
||||
process.SysProcAttr = &syscall.SysProcAttr{Ptrace: true, Setpgid: true}
|
||||
if foreground {
|
||||
process.Stdin = os.Stdin
|
||||
}
|
||||
if wd != "" {
|
||||
process.Dir = wd
|
||||
}
|
||||
@ -73,6 +77,10 @@ func Launch(cmd []string, wd string) (*Process, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("waiting for target execve failed: %s", err)
|
||||
}
|
||||
if foreground {
|
||||
// Sets target process as the controlling process for our tty, equivalent to tcsetpgrp
|
||||
syscall.Syscall(syscall.SYS_IOCTL, uintptr(0), uintptr(syscall.TIOCSPGRP), uintptr(unsafe.Pointer(&dbp.pid)))
|
||||
}
|
||||
return initializeDebugProcess(dbp, process.Path)
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ func openExecutablePathPE(path string) (*pe.File, io.Closer, error) {
|
||||
}
|
||||
|
||||
// Launch creates and begins debugging a new process.
|
||||
func Launch(cmd []string, wd string) (*Process, error) {
|
||||
func Launch(cmd []string, wd string, foreground bool) (*Process, error) {
|
||||
argv0Go, err := filepath.Abs(cmd[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -59,9 +59,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)
|
||||
p, err = native.Launch(append([]string{fixture.Path}, args...), wd, false)
|
||||
case "lldb":
|
||||
p, err = gdbserial.LLDBLaunch(append([]string{fixture.Path}, args...), wd)
|
||||
p, err = gdbserial.LLDBLaunch(append([]string{fixture.Path}, args...), wd, false)
|
||||
case "rr":
|
||||
protest.MustHaveRecordingAllowed(t)
|
||||
t.Log("recording")
|
||||
@ -2036,7 +2036,7 @@ func TestIssue509(t *testing.T) {
|
||||
cmd.Dir = nomaindir
|
||||
assertNoError(cmd.Run(), t, "go build")
|
||||
exepath := filepath.Join(nomaindir, "debug")
|
||||
_, err := native.Launch([]string{exepath}, ".")
|
||||
_, err := native.Launch([]string{exepath}, ".", false)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error but none was generated")
|
||||
}
|
||||
@ -2070,7 +2070,7 @@ func TestUnsupportedArch(t *testing.T) {
|
||||
}
|
||||
defer os.Remove(outfile)
|
||||
|
||||
p, err := native.Launch([]string{outfile}, ".")
|
||||
p, err := native.Launch([]string{outfile}, ".", false)
|
||||
switch err {
|
||||
case proc.UnsupportedLinuxArchErr, proc.UnsupportedWindowsArchErr, proc.UnsupportedDarwinArchErr:
|
||||
// all good
|
||||
|
@ -32,6 +32,9 @@ type Config struct {
|
||||
// Selects server backend.
|
||||
Backend string
|
||||
|
||||
// Foreground lets target process access stdin.
|
||||
Foreground bool
|
||||
|
||||
// DisconnectChan will be closed by the server when the client disconnects
|
||||
DisconnectChan chan<- struct{}
|
||||
}
|
||||
|
@ -54,6 +54,9 @@ type Config struct {
|
||||
CoreFile string
|
||||
// Backend specifies the debugger backend.
|
||||
Backend string
|
||||
|
||||
// Foreground lets target process access stdin.
|
||||
Foreground bool
|
||||
}
|
||||
|
||||
// New creates a new Debugger. ProcessArgs specify the commandline arguments for the
|
||||
@ -111,17 +114,17 @@ func New(config *Config, processArgs []string) (*Debugger, error) {
|
||||
func (d *Debugger) Launch(processArgs []string, wd string) (proc.Process, error) {
|
||||
switch d.config.Backend {
|
||||
case "native":
|
||||
return native.Launch(processArgs, wd)
|
||||
return native.Launch(processArgs, wd, d.config.Foreground)
|
||||
case "lldb":
|
||||
return betterGdbserialLaunchError(gdbserial.LLDBLaunch(processArgs, wd))
|
||||
return betterGdbserialLaunchError(gdbserial.LLDBLaunch(processArgs, wd, d.config.Foreground))
|
||||
case "rr":
|
||||
p, _, err := gdbserial.RecordAndReplay(processArgs, wd, false)
|
||||
return p, err
|
||||
case "default":
|
||||
if runtime.GOOS == "darwin" {
|
||||
return betterGdbserialLaunchError(gdbserial.LLDBLaunch(processArgs, wd))
|
||||
return betterGdbserialLaunchError(gdbserial.LLDBLaunch(processArgs, wd, d.config.Foreground))
|
||||
}
|
||||
return native.Launch(processArgs, wd)
|
||||
return native.Launch(processArgs, wd, d.config.Foreground)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown backend %q", d.config.Backend)
|
||||
}
|
||||
|
@ -115,6 +115,7 @@ func (s *ServerImpl) Run() error {
|
||||
WorkingDir: s.config.WorkingDir,
|
||||
CoreFile: s.config.CoreFile,
|
||||
Backend: s.config.Backend,
|
||||
Foreground: s.config.Foreground,
|
||||
},
|
||||
s.config.ProcessArgs); err != nil {
|
||||
return err
|
||||
|
@ -112,9 +112,9 @@ func withTestProcess(name string, t *testing.T, fn func(p proc.Process, fixture
|
||||
var tracedir string
|
||||
switch testBackend {
|
||||
case "native":
|
||||
p, err = native.Launch([]string{fixture.Path}, ".")
|
||||
p, err = native.Launch([]string{fixture.Path}, ".", false)
|
||||
case "lldb":
|
||||
p, err = gdbserial.LLDBLaunch([]string{fixture.Path}, ".")
|
||||
p, err = gdbserial.LLDBLaunch([]string{fixture.Path}, ".", false)
|
||||
case "rr":
|
||||
protest.MustHaveRecordingAllowed(t)
|
||||
t.Log("recording")
|
||||
|
Loading…
Reference in New Issue
Block a user