提交 cc86bde5 编写于 作者: A aarzilli 提交者: Derek Parker

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
上级 c7cde8b1
......@@ -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")
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册