blob: 068e234db4b95744dbe5a2cc48029fa3416533fa [file] [log] [blame]
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package server
import (
"fmt"
"os"
"runtime"
"syscall"
"time"
)
// ptraceRun runs all the closures from fc on a dedicated OS thread. Errors
// are returned on ec. Both channels must be unbuffered, to ensure that the
// resultant error is sent back to the same goroutine that sent the closure.
func ptraceRun(fc chan func() error, ec chan error) {
if cap(fc) != 0 || cap(ec) != 0 {
panic("ptraceRun was given buffered channels")
}
runtime.LockOSThread()
for f := range fc {
ec <- f()
}
}
func (s *Server) startProcess(name string, argv []string, attr *os.ProcAttr) (proc *os.Process, err error) {
s.fc <- func() error {
var err1 error
proc, err1 = os.StartProcess(name, argv, attr)
return err1
}
err = <-s.ec
return
}
func (s *Server) ptraceCont(pid int, signal int) (err error) {
s.fc <- func() error {
return syscall.PtraceCont(pid, signal)
}
return <-s.ec
}
func (s *Server) ptraceGetRegs(pid int, regsout *syscall.PtraceRegs) (err error) {
s.fc <- func() error {
return syscall.PtraceGetRegs(pid, regsout)
}
return <-s.ec
}
func (s *Server) ptracePeek(pid int, addr uintptr, out []byte) (err error) {
s.fc <- func() error {
n, err := syscall.PtracePeekText(pid, addr, out)
if err != nil {
return err
}
if n != len(out) {
return fmt.Errorf("ptracePeek: peeked %d bytes, want %d", n, len(out))
}
return nil
}
return <-s.ec
}
func (s *Server) ptracePoke(pid int, addr uintptr, data []byte) (err error) {
s.fc <- func() error {
n, err := syscall.PtracePokeText(pid, addr, data)
if err != nil {
return err
}
if n != len(data) {
return fmt.Errorf("ptracePoke: poked %d bytes, want %d", n, len(data))
}
return nil
}
return <-s.ec
}
func (s *Server) ptraceSetOptions(pid int, options int) (err error) {
s.fc <- func() error {
return syscall.PtraceSetOptions(pid, options)
}
return <-s.ec
}
func (s *Server) ptraceSetRegs(pid int, regs *syscall.PtraceRegs) (err error) {
s.fc <- func() error {
return syscall.PtraceSetRegs(pid, regs)
}
return <-s.ec
}
func (s *Server) ptraceSingleStep(pid int) (err error) {
s.fc <- func() error {
return syscall.PtraceSingleStep(pid)
}
return <-s.ec
}
type breakpointsChangedError struct {
call call
}
func (*breakpointsChangedError) Error() string {
return "breakpoints changed"
}
func (s *Server) wait(pid int, allowBreakpointsChange bool) (wpid int, status syscall.WaitStatus, err error) {
// We poll syscall.Wait4 with WNOHANG, sleeping in between, as a poor man's
// waitpid-with-timeout. This allows adding and removing breakpoints
// concurrently with waiting to hit an existing breakpoint.
f := func() error {
var err1 error
wpid, err1 = syscall.Wait4(pid, &status, syscall.WALL|syscall.WNOHANG, nil)
return err1
}
const (
minSleep = 1 * time.Microsecond
maxSleep = 100 * time.Millisecond
)
for sleep := minSleep; ; {
s.fc <- f
err = <-s.ec
// wpid == 0 means that wait found nothing (and returned due to WNOHANG).
if wpid != 0 {
return
}
if allowBreakpointsChange {
select {
case c := <-s.breakpointc:
return 0, 0, &breakpointsChangedError{c}
default:
}
}
time.Sleep(sleep)
if sleep < maxSleep {
sleep *= 10
}
}
}