blob: 17c8fa529f53aad2cb85d62bb57c8819962a77c9 [file] [log] [blame]
// Copyright 2009 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 proc
// TODO(rsc): Imports here after to be in proc.go too in order
// for deps.bash to get the right answer.
import (
"container/vector"
"fmt"
"io/ioutil"
"os"
"runtime"
"strconv"
"strings"
"sync"
"syscall"
)
// This is an implementation of the process tracing interface using
// Linux's ptrace(2) interface. The implementation is multi-threaded.
// Each attached process has an associated monitor thread, and each
// running attached thread has an associated "wait" thread. The wait
// thread calls wait4 on the thread's TID and reports any wait events
// or errors via "debug events". The monitor thread consumes these
// wait events and updates the internally maintained state of each
// thread. All ptrace calls must run in the monitor thread, so the
// monitor executes closures received on the debugReq channel.
//
// As ptrace's documentation is somewhat light, this is heavily based
// on information gleaned from the implementation of ptrace found at
// http://lxr.linux.no/linux+v2.6.30/kernel/ptrace.c
// http://lxr.linux.no/linux+v2.6.30/arch/x86/kernel/ptrace.c#L854
// as well as experimentation and examination of gdb's behavior.
const (
trace = false
traceIP = false
traceMem = false
)
/*
* Thread state
*/
// Each thread can be in one of the following set of states.
// Each state satisfies
// isRunning() || isStopped() || isZombie() || isTerminal().
//
// Running threads can be sent signals and must be waited on, but they
// cannot be inspected using ptrace.
//
// Stopped threads can be inspected and continued, but cannot be
// meaningfully waited on. They can be sent signals, but the signals
// will be queued until they are running again.
//
// Zombie threads cannot be inspected, continued, or sent signals (and
// therefore they cannot be stopped), but they must be waited on.
//
// Terminal threads no longer exist in the OS and thus you can't do
// anything with them.
type threadState string
const (
running threadState = "Running"
singleStepping threadState = "SingleStepping" // Transient
stopping threadState = "Stopping" // Transient
stopped threadState = "Stopped"
stoppedBreakpoint threadState = "StoppedBreakpoint"
stoppedSignal threadState = "StoppedSignal"
stoppedThreadCreate threadState = "StoppedThreadCreate"
stoppedExiting threadState = "StoppedExiting"
exiting threadState = "Exiting" // Transient (except main thread)
exited threadState = "Exited"
detached threadState = "Detached"
)
func (ts threadState) isRunning() bool {
return ts == running || ts == singleStepping || ts == stopping
}
func (ts threadState) isStopped() bool {
return ts == stopped || ts == stoppedBreakpoint || ts == stoppedSignal || ts == stoppedThreadCreate || ts == stoppedExiting
}
func (ts threadState) isZombie() bool { return ts == exiting }
func (ts threadState) isTerminal() bool { return ts == exited || ts == detached }
func (ts threadState) String() string { return string(ts) }
/*
* Basic types
*/
// A breakpoint stores information about a single breakpoint,
// including its program counter, the overwritten text if the
// breakpoint is installed.
type breakpoint struct {
pc uintptr
olddata []byte
}
func (bp *breakpoint) String() string {
if bp == nil {
return "<nil>"
}
return fmt.Sprintf("%#x", bp.pc)
}
// bpinst386 is the breakpoint instruction used on 386 and amd64.
var bpinst386 = []byte{0xcc}
// A debugEvent represents a reason a thread stopped or a wait error.
type debugEvent struct {
*os.Waitmsg
t *thread
err os.Error
}
// A debugReq is a request to execute a closure in the monitor thread.
type debugReq struct {
f func() os.Error
res chan os.Error
}
// A transitionHandler specifies a function to be called when a thread
// changes state and a function to be called when an error occurs in
// the monitor. Both run in the monitor thread. Before the monitor
// invokes a handler, it removes the handler from the handler queue.
// The handler should re-add itself if needed.
type transitionHandler struct {
handle func(*thread, threadState, threadState)
onErr func(os.Error)
}
// A process is a Linux process, which consists of a set of threads.
// Each running process has one monitor thread, which processes
// messages from the debugEvents, debugReqs, and stopReq channels and
// calls transition handlers.
//
// To send a message to the monitor thread, first receive from the
// ready channel. If the ready channel returns true, the monitor is
// still running and will accept a message. If the ready channel
// returns false, the monitor is not running (the ready channel has
// been closed), and the reason it is not running will be stored in err.
type process struct {
pid int
threads map[int]*thread
breakpoints map[uintptr]*breakpoint
ready chan bool
debugEvents chan *debugEvent
debugReqs chan *debugReq
stopReq chan os.Error
transitionHandlers vector.Vector
err os.Error
}
// A thread represents a Linux thread in another process that is being
// debugged. Each running thread has an associated goroutine that
// waits for thread updates and sends them to the process monitor.
type thread struct {
tid int
proc *process
// Whether to ignore the next SIGSTOP received by wait.
ignoreNextSigstop bool
// Thread state. Only modified via setState.
state threadState
// If state == StoppedBreakpoint
breakpoint *breakpoint
// If state == StoppedSignal or state == Exited
signal int
// If state == StoppedThreadCreate
newThread *thread
// If state == Exited
exitStatus int
}
/*
* Errors
*/
type badState struct {
thread *thread
message string
state threadState
}
func (e *badState) String() string {
return fmt.Sprintf("Thread %d %s from state %v", e.thread.tid, e.message, e.state)
}
type breakpointExistsError Word
func (e breakpointExistsError) String() string {
return fmt.Sprintf("breakpoint already exists at PC %#x", e)
}
type noBreakpointError Word
func (e noBreakpointError) String() string { return fmt.Sprintf("no breakpoint at PC %#x", e) }
type newThreadError struct {
*os.Waitmsg
wantPid int
wantSig int
}
func (e *newThreadError) String() string {
return fmt.Sprintf("newThread wait wanted pid %v and signal %v, got %v and %v", e.Pid, e.StopSignal(), e.wantPid, e.wantSig)
}
type ProcessExited struct{}
func (p ProcessExited) String() string { return "process exited" }
/*
* Ptrace wrappers
*/
func (t *thread) ptracePeekText(addr uintptr, out []byte) (int, os.Error) {
c, err := syscall.PtracePeekText(t.tid, addr, out)
if traceMem {
fmt.Printf("peek(%#x) => %v, %v\n", addr, out, err)
}
return c, os.NewSyscallError("ptrace(PEEKTEXT)", err)
}
func (t *thread) ptracePokeText(addr uintptr, out []byte) (int, os.Error) {
c, err := syscall.PtracePokeText(t.tid, addr, out)
if traceMem {
fmt.Printf("poke(%#x, %v) => %v\n", addr, out, err)
}
return c, os.NewSyscallError("ptrace(POKETEXT)", err)
}
func (t *thread) ptraceGetRegs(regs *syscall.PtraceRegs) os.Error {
err := syscall.PtraceGetRegs(t.tid, regs)
return os.NewSyscallError("ptrace(GETREGS)", err)
}
func (t *thread) ptraceSetRegs(regs *syscall.PtraceRegs) os.Error {
err := syscall.PtraceSetRegs(t.tid, regs)
return os.NewSyscallError("ptrace(SETREGS)", err)
}
func (t *thread) ptraceSetOptions(options int) os.Error {
err := syscall.PtraceSetOptions(t.tid, options)
return os.NewSyscallError("ptrace(SETOPTIONS)", err)
}
func (t *thread) ptraceGetEventMsg() (uint, os.Error) {
msg, err := syscall.PtraceGetEventMsg(t.tid)
return msg, os.NewSyscallError("ptrace(GETEVENTMSG)", err)
}
func (t *thread) ptraceCont() os.Error {
err := syscall.PtraceCont(t.tid, 0)
return os.NewSyscallError("ptrace(CONT)", err)
}
func (t *thread) ptraceContWithSignal(sig int) os.Error {
err := syscall.PtraceCont(t.tid, sig)
return os.NewSyscallError("ptrace(CONT)", err)
}
func (t *thread) ptraceStep() os.Error {
err := syscall.PtraceSingleStep(t.tid)
return os.NewSyscallError("ptrace(SINGLESTEP)", err)
}
func (t *thread) ptraceDetach() os.Error {
err := syscall.PtraceDetach(t.tid)
return os.NewSyscallError("ptrace(DETACH)", err)
}
/*
* Logging utilties
*/
var logLock sync.Mutex
func (t *thread) logTrace(format string, args ...interface{}) {
if !trace {
return
}
logLock.Lock()
defer logLock.Unlock()
fmt.Fprintf(os.Stderr, "Thread %d", t.tid)
if traceIP {
var regs syscall.PtraceRegs
err := t.ptraceGetRegs(&regs)
if err == nil {
fmt.Fprintf(os.Stderr, "@%x", regs.PC())
}
}
fmt.Fprint(os.Stderr, ": ")
fmt.Fprintf(os.Stderr, format, args...)
fmt.Fprint(os.Stderr, "\n")
}
func (t *thread) warn(format string, args ...interface{}) {
logLock.Lock()
defer logLock.Unlock()
fmt.Fprintf(os.Stderr, "Thread %d: WARNING ", t.tid)
fmt.Fprintf(os.Stderr, format, args...)
fmt.Fprint(os.Stderr, "\n")
}
func (p *process) logTrace(format string, args ...interface{}) {
if !trace {
return
}
logLock.Lock()
defer logLock.Unlock()
fmt.Fprintf(os.Stderr, "Process %d: ", p.pid)
fmt.Fprintf(os.Stderr, format, args...)
fmt.Fprint(os.Stderr, "\n")
}
/*
* State utilities
*/
// someStoppedThread returns a stopped thread from the process.
// Returns nil if no threads are stopped.
//
// Must be called from the monitor thread.
func (p *process) someStoppedThread() *thread {
for _, t := range p.threads {
if t.state.isStopped() {
return t
}
}
return nil
}
// someRunningThread returns a running thread from the process.
// Returns nil if no threads are running.
//
// Must be called from the monitor thread.
func (p *process) someRunningThread() *thread {
for _, t := range p.threads {
if t.state.isRunning() {
return t
}
}
return nil
}
/*
* Breakpoint utilities
*/
// installBreakpoints adds breakpoints to the attached process.
//
// Must be called from the monitor thread.
func (p *process) installBreakpoints() os.Error {
n := 0
main := p.someStoppedThread()
for _, b := range p.breakpoints {
if b.olddata != nil {
continue
}
b.olddata = make([]byte, len(bpinst386))
_, err := main.ptracePeekText(uintptr(b.pc), b.olddata)
if err != nil {
b.olddata = nil
return err
}
_, err = main.ptracePokeText(uintptr(b.pc), bpinst386)
if err != nil {
b.olddata = nil
return err
}
n++
}
if n > 0 {
p.logTrace("installed %d/%d breakpoints", n, len(p.breakpoints))
}
return nil
}
// uninstallBreakpoints removes the installed breakpoints from p.
//
// Must be called from the monitor thread.
func (p *process) uninstallBreakpoints() os.Error {
if len(p.threads) == 0 {
return nil
}
n := 0
main := p.someStoppedThread()
for _, b := range p.breakpoints {
if b.olddata == nil {
continue
}
_, err := main.ptracePokeText(uintptr(b.pc), b.olddata)
if err != nil {
return err
}
b.olddata = nil
n++
}
if n > 0 {
p.logTrace("uninstalled %d/%d breakpoints", n, len(p.breakpoints))
}
return nil
}
/*
* Debug event handling
*/
// wait waits for a wait event from this thread and sends it on the
// debug events channel for this thread's process. This should be
// started in its own goroutine when the attached thread enters a
// running state. The goroutine will exit as soon as it sends a debug
// event.
func (t *thread) wait() {
for {
var ev debugEvent
ev.t = t
t.logTrace("beginning wait")
ev.Waitmsg, ev.err = os.Wait(t.tid, syscall.WALL)
if ev.err == nil && ev.Pid != t.tid {
panic(fmt.Sprint("Wait returned pid ", ev.Pid, " wanted ", t.tid))
}
if ev.StopSignal() == syscall.SIGSTOP && t.ignoreNextSigstop {
// Spurious SIGSTOP. See Thread.Stop().
t.ignoreNextSigstop = false
err := t.ptraceCont()
if err == nil {
continue
}
// If we failed to continue, just let
// the stop go through so we can
// update the thread's state.
}
if !<-t.proc.ready {
// The monitor exited
break
}
t.proc.debugEvents <- &ev
break
}
}
// setState sets this thread's state, starts a wait thread if
// necessary, and invokes state transition handlers.
//
// Must be called from the monitor thread.
func (t *thread) setState(newState threadState) {
oldState := t.state
t.state = newState
t.logTrace("state %v -> %v", oldState, newState)
if !oldState.isRunning() && (newState.isRunning() || newState.isZombie()) {
// Start waiting on this thread
go t.wait()
}
// Invoke state change handlers
handlers := t.proc.transitionHandlers
if handlers.Len() == 0 {
return
}
t.proc.transitionHandlers = nil
for _, h := range handlers {
h := h.(*transitionHandler)
h.handle(t, oldState, newState)
}
}
// sendSigstop sends a SIGSTOP to this thread.
func (t *thread) sendSigstop() os.Error {
t.logTrace("sending SIGSTOP")
err := syscall.Tgkill(t.proc.pid, t.tid, syscall.SIGSTOP)
return os.NewSyscallError("tgkill", err)
}
// stopAsync sends SIGSTOP to all threads in state 'running'.
//
// Must be called from the monitor thread.
func (p *process) stopAsync() os.Error {
for _, t := range p.threads {
if t.state == running {
err := t.sendSigstop()
if err != nil {
return err
}
t.setState(stopping)
}
}
return nil
}
// doTrap handles SIGTRAP debug events with a cause of 0. These can
// be caused either by an installed breakpoint, a breakpoint in the
// program text, or by single stepping.
//
// TODO(austin) I think we also get this on an execve syscall.
func (ev *debugEvent) doTrap() (threadState, os.Error) {
t := ev.t
if t.state == singleStepping {
return stopped, nil
}
// Hit a breakpoint. Linux leaves the program counter after
// the breakpoint. If this is an installed breakpoint, we
// need to back the PC up to the breakpoint PC.
var regs syscall.PtraceRegs
err := t.ptraceGetRegs(&regs)
if err != nil {
return stopped, err
}
b, ok := t.proc.breakpoints[uintptr(regs.PC())-uintptr(len(bpinst386))]
if !ok {
// We must have hit a breakpoint that was actually in
// the program. Leave the IP where it is so we don't
// re-execute the breakpoint instruction. Expose the
// fact that we stopped with a SIGTRAP.
return stoppedSignal, nil
}
t.breakpoint = b
t.logTrace("at breakpoint %v, backing up PC from %#x", b, regs.PC())
regs.SetPC(uint64(b.pc))
err = t.ptraceSetRegs(&regs)
if err != nil {
return stopped, err
}
return stoppedBreakpoint, nil
}
// doPtraceClone handles SIGTRAP debug events with a PTRACE_EVENT_CLONE
// cause. It initializes the new thread, adds it to the process, and
// returns the appropriate thread state for the existing thread.
func (ev *debugEvent) doPtraceClone() (threadState, os.Error) {
t := ev.t
// Get the TID of the new thread
tid, err := t.ptraceGetEventMsg()
if err != nil {
return stopped, err
}
nt, err := t.proc.newThread(int(tid), syscall.SIGSTOP, true)
if err != nil {
return stopped, err
}
// Remember the thread
t.newThread = nt
return stoppedThreadCreate, nil
}
// doPtraceExit handles SIGTRAP debug events with a PTRACE_EVENT_EXIT
// cause. It sets up the thread's state, but does not remove it from
// the process. A later WIFEXITED debug event will remove it from the
// process.
func (ev *debugEvent) doPtraceExit() (threadState, os.Error) {
t := ev.t
// Get exit status
exitStatus, err := t.ptraceGetEventMsg()
if err != nil {
return stopped, err
}
ws := syscall.WaitStatus(exitStatus)
t.logTrace("exited with %v", ws)
switch {
case ws.Exited():
t.exitStatus = ws.ExitStatus()
case ws.Signaled():
t.signal = ws.Signal()
}
// We still need to continue this thread and wait on this
// thread's WIFEXITED event. We'll delete it then.
return stoppedExiting, nil
}
// process handles a debug event. It modifies any thread or process
// state as necessary, uninstalls breakpoints if necessary, and stops
// any running threads.
func (ev *debugEvent) process() os.Error {
if ev.err != nil {
return ev.err
}
t := ev.t
t.exitStatus = -1
t.signal = -1
// Decode wait status.
var state threadState
switch {
case ev.Stopped():
state = stoppedSignal
t.signal = ev.StopSignal()
t.logTrace("stopped with %v", ev)
if ev.StopSignal() == syscall.SIGTRAP {
// What caused the debug trap?
var err os.Error
switch cause := ev.TrapCause(); cause {
case 0:
// Breakpoint or single stepping
state, err = ev.doTrap()
case syscall.PTRACE_EVENT_CLONE:
state, err = ev.doPtraceClone()
case syscall.PTRACE_EVENT_EXIT:
state, err = ev.doPtraceExit()
default:
t.warn("Unknown trap cause %d", cause)
}
if err != nil {
t.setState(stopped)
t.warn("failed to handle trap %v: %v", ev, err)
}
}
case ev.Exited():
state = exited
t.proc.threads[t.tid] = nil, false
t.logTrace("exited %v", ev)
// We should have gotten the exit status in
// PTRACE_EVENT_EXIT, but just in case.
t.exitStatus = ev.ExitStatus()
case ev.Signaled():
state = exited
t.proc.threads[t.tid] = nil, false
t.logTrace("signaled %v", ev)
// Again, this should be redundant.
t.signal = ev.Signal()
default:
panic(fmt.Sprintf("Unexpected wait status %v", ev.Waitmsg))
}
// If we sent a SIGSTOP to the thread (indicated by state
// Stopping), we might have raced with a different type of
// stop. If we didn't get the stop we expected, then the
// SIGSTOP we sent is now queued up, so we should ignore the
// next one we get.
if t.state == stopping && ev.StopSignal() != syscall.SIGSTOP {
t.ignoreNextSigstop = true
}
// TODO(austin) If we're in state stopping and get a SIGSTOP,
// set state stopped instead of stoppedSignal.
t.setState(state)
if t.proc.someRunningThread() == nil {
// Nothing is running, uninstall breakpoints
return t.proc.uninstallBreakpoints()
}
// Stop any other running threads
return t.proc.stopAsync()
}
// onStop adds a handler for state transitions from running to
// non-running states. The handler will be called from the monitor
// thread.
//
// Must be called from the monitor thread.
func (t *thread) onStop(handle func(), onErr func(os.Error)) {
// TODO(austin) This is rather inefficient for things like
// stepping all threads during a continue. Maybe move
// transitionHandlers to the thread, or have both per-thread
// and per-process transition handlers.
h := &transitionHandler{nil, onErr}
h.handle = func(st *thread, old, new threadState) {
if t == st && old.isRunning() && !new.isRunning() {
handle()
} else {
t.proc.transitionHandlers.Push(h)
}
}
t.proc.transitionHandlers.Push(h)
}
/*
* Event monitor
*/
// monitor handles debug events and debug requests for p, exiting when
// there are no threads left in p.
func (p *process) monitor() {
var err os.Error
// Linux requires that all ptrace calls come from the thread
// that originally attached. Prevent the Go scheduler from
// migrating us to other OS threads.
runtime.LockOSThread()
defer runtime.UnlockOSThread()
hadThreads := false
for err == nil {
p.ready <- true
select {
case event := <-p.debugEvents:
err = event.process()
case req := <-p.debugReqs:
req.res <- req.f()
case err = <-p.stopReq:
break
}
if len(p.threads) == 0 {
if err == nil && hadThreads {
p.logTrace("no more threads; monitor exiting")
err = ProcessExited{}
}
} else {
hadThreads = true
}
}
// Abort waiting handlers
// TODO(austin) How do I stop the wait threads?
for _, h := range p.transitionHandlers {
h := h.(*transitionHandler)
h.onErr(err)
}
// Indicate that the monitor cannot receive any more messages
p.err = err
close(p.ready)
}
// do executes f in the monitor thread (and, thus, atomically with
// respect to thread state changes). f must not block.
//
// Must NOT be called from the monitor thread.
func (p *process) do(f func() os.Error) os.Error {
if !<-p.ready {
return p.err
}
req := &debugReq{f, make(chan os.Error)}
p.debugReqs <- req
return <-req.res
}
// stopMonitor stops the monitor with the given error. If the monitor
// is already stopped, does nothing.
func (p *process) stopMonitor(err os.Error) {
if err == nil {
panic("cannot stop the monitor with no error")
}
if <-p.ready {
p.stopReq <- err
}
}
/*
* Public thread interface
*/
func (t *thread) Regs() (Regs, os.Error) {
var regs syscall.PtraceRegs
err := t.proc.do(func() os.Error {
if !t.state.isStopped() {
return &badState{t, "cannot get registers", t.state}
}
return t.ptraceGetRegs(&regs)
})
if err != nil {
return nil, err
}
setter := func(r *syscall.PtraceRegs) os.Error {
return t.proc.do(func() os.Error {
if !t.state.isStopped() {
return &badState{t, "cannot get registers", t.state}
}
return t.ptraceSetRegs(r)
})
}
return newRegs(&regs, setter), nil
}
func (t *thread) Peek(addr Word, out []byte) (int, os.Error) {
var c int
err := t.proc.do(func() os.Error {
if !t.state.isStopped() {
return &badState{t, "cannot peek text", t.state}
}
var err os.Error
c, err = t.ptracePeekText(uintptr(addr), out)
return err
})
return c, err
}
func (t *thread) Poke(addr Word, out []byte) (int, os.Error) {
var c int
err := t.proc.do(func() os.Error {
if !t.state.isStopped() {
return &badState{t, "cannot poke text", t.state}
}
var err os.Error
c, err = t.ptracePokeText(uintptr(addr), out)
return err
})
return c, err
}
// stepAsync starts this thread single stepping. When the single step
// is complete, it will send nil on the given channel. If an error
// occurs while setting up the single step, it returns that error. If
// an error occurs while waiting for the single step to complete, it
// sends that error on the channel.
func (t *thread) stepAsync(ready chan os.Error) os.Error {
if err := t.ptraceStep(); err != nil {
return err
}
t.setState(singleStepping)
t.onStop(func() { ready <- nil },
func(err os.Error) { ready <- err })
return nil
}
func (t *thread) Step() os.Error {
t.logTrace("Step {")
defer t.logTrace("}")
ready := make(chan os.Error)
err := t.proc.do(func() os.Error {
if !t.state.isStopped() {
return &badState{t, "cannot single step", t.state}
}
return t.stepAsync(ready)
})
if err != nil {
return err
}
err = <-ready
return err
}
// TODO(austin) We should probably get this via C's strsignal.
var sigNames = [...]string{
"SIGEXIT", "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL",
"SIGTRAP", "SIGABRT", "SIGBUS", "SIGFPE", "SIGKILL",
"SIGUSR1", "SIGSEGV", "SIGUSR2", "SIGPIPE", "SIGALRM",
"SIGTERM", "SIGSTKFLT", "SIGCHLD", "SIGCONT", "SIGSTOP",
"SIGTSTP", "SIGTTIN", "SIGTTOU", "SIGURG", "SIGXCPU",
"SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGWINCH", "SIGPOLL",
"SIGPWR", "SIGSYS",
}
// sigName returns the symbolic name for the given signal number. If
// the signal number is invalid, returns "<invalid>".
func sigName(signal int) string {
if signal < 0 || signal >= len(sigNames) {
return "<invalid>"
}
return sigNames[signal]
}
func (t *thread) Stopped() (Cause, os.Error) {
var c Cause
err := t.proc.do(func() os.Error {
switch t.state {
case stopped:
c = Stopped{}
case stoppedBreakpoint:
c = Breakpoint(t.breakpoint.pc)
case stoppedSignal:
c = Signal(sigName(t.signal))
case stoppedThreadCreate:
c = &ThreadCreate{t.newThread}
case stoppedExiting, exiting, exited:
if t.signal == -1 {
c = &ThreadExit{t.exitStatus, ""}
} else {
c = &ThreadExit{t.exitStatus, sigName(t.signal)}
}
default:
return &badState{t, "cannot get stop cause", t.state}
}
return nil
})
if err != nil {
return nil, err
}
return c, nil
}
func (p *process) Threads() []Thread {
var res []Thread
p.do(func() os.Error {
res = make([]Thread, len(p.threads))
i := 0
for _, t := range p.threads {
// Exclude zombie threads.
st := t.state
if st == exiting || st == exited || st == detached {
continue
}
res[i] = t
i++
}
res = res[0:i]
return nil
})
return res
}
func (p *process) AddBreakpoint(pc Word) os.Error {
return p.do(func() os.Error {
if t := p.someRunningThread(); t != nil {
return &badState{t, "cannot add breakpoint", t.state}
}
if _, ok := p.breakpoints[uintptr(pc)]; ok {
return breakpointExistsError(pc)
}
p.breakpoints[uintptr(pc)] = &breakpoint{pc: uintptr(pc)}
return nil
})
}
func (p *process) RemoveBreakpoint(pc Word) os.Error {
return p.do(func() os.Error {
if t := p.someRunningThread(); t != nil {
return &badState{t, "cannot remove breakpoint", t.state}
}
if _, ok := p.breakpoints[uintptr(pc)]; !ok {
return noBreakpointError(pc)
}
p.breakpoints[uintptr(pc)] = nil, false
return nil
})
}
func (p *process) Continue() os.Error {
// Single step any threads that are stopped at breakpoints so
// we can reinstall breakpoints.
var ready chan os.Error
count := 0
err := p.do(func() os.Error {
// We make the ready channel big enough to hold all
// ready message so we don't jam up the monitor if we
// stop listening (e.g., if there's an error).
ready = make(chan os.Error, len(p.threads))
for _, t := range p.threads {
if !t.state.isStopped() {
continue
}
// We use the breakpoint map directly here
// instead of checking the stop cause because
// it could have been stopped at a breakpoint
// for some other reason, or the breakpoint
// could have been added since it was stopped.
var regs syscall.PtraceRegs
err := t.ptraceGetRegs(&regs)
if err != nil {
return err
}
if b, ok := p.breakpoints[uintptr(regs.PC())]; ok {
t.logTrace("stepping over breakpoint %v", b)
if err := t.stepAsync(ready); err != nil {
return err
}
count++
}
}
return nil
})
if err != nil {
p.stopMonitor(err)
return err
}
// Wait for single stepping threads
for count > 0 {
err = <-ready
if err != nil {
p.stopMonitor(err)
return err
}
count--
}
// Continue all threads
err = p.do(func() os.Error {
if err := p.installBreakpoints(); err != nil {
return err
}
for _, t := range p.threads {
var err os.Error
switch {
case !t.state.isStopped():
continue
case t.state == stoppedSignal && t.signal != syscall.SIGSTOP && t.signal != syscall.SIGTRAP:
t.logTrace("continuing with signal %d", t.signal)
err = t.ptraceContWithSignal(t.signal)
default:
t.logTrace("continuing")
err = t.ptraceCont()
}
if err != nil {
return err
}
if t.state == stoppedExiting {
t.setState(exiting)
} else {
t.setState(running)
}
}
return nil
})
if err != nil {
// TODO(austin) Do we need to stop the monitor with
// this error atomically with the do-routine above?
p.stopMonitor(err)
return err
}
return nil
}
func (p *process) WaitStop() os.Error {
// We need a non-blocking ready channel for the case where all
// threads are already stopped.
ready := make(chan os.Error, 1)
err := p.do(func() os.Error {
// Are all of the threads already stopped?
if p.someRunningThread() == nil {
ready <- nil
return nil
}
// Monitor state transitions
h := &transitionHandler{}
h.handle = func(st *thread, old, new threadState) {
if !new.isRunning() {
if p.someRunningThread() == nil {
ready <- nil
return
}
}
p.transitionHandlers.Push(h)
}
h.onErr = func(err os.Error) { ready <- err }
p.transitionHandlers.Push(h)
return nil
})
if err != nil {
return err
}
return <-ready
}
func (p *process) Stop() os.Error {
err := p.do(func() os.Error { return p.stopAsync() })
if err != nil {
return err
}
return p.WaitStop()
}
func (p *process) Detach() os.Error {
if err := p.Stop(); err != nil {
return err
}
err := p.do(func() os.Error {
if err := p.uninstallBreakpoints(); err != nil {
return err
}
for pid, t := range p.threads {
if t.state.isStopped() {
// We can't detach from zombies.
if err := t.ptraceDetach(); err != nil {
return err
}
}
t.setState(detached)
p.threads[pid] = nil, false
}
return nil
})
// TODO(austin) Wait for monitor thread to exit?
return err
}
// newThread creates a new thread object and waits for its initial
// signal. If cloned is true, this thread was cloned from a thread we
// are already attached to.
//
// Must be run from the monitor thread.
func (p *process) newThread(tid int, signal int, cloned bool) (*thread, os.Error) {
t := &thread{tid: tid, proc: p, state: stopped}
// Get the signal from the thread
// TODO(austin) Thread might already be stopped if we're attaching.
w, err := os.Wait(tid, syscall.WALL)
if err != nil {
return nil, err
}
if w.Pid != tid || w.StopSignal() != signal {
return nil, &newThreadError{w, tid, signal}
}
if !cloned {
err = t.ptraceSetOptions(syscall.PTRACE_O_TRACECLONE | syscall.PTRACE_O_TRACEEXIT)
if err != nil {
return nil, err
}
}
p.threads[tid] = t
return t, nil
}
// attachThread attaches a running thread to the process.
//
// Must NOT be run from the monitor thread.
func (p *process) attachThread(tid int) (*thread, os.Error) {
p.logTrace("attaching to thread %d", tid)
var thr *thread
err := p.do(func() os.Error {
errno := syscall.PtraceAttach(tid)
if errno != 0 {
return os.NewSyscallError("ptrace(ATTACH)", errno)
}
var err os.Error
thr, err = p.newThread(tid, syscall.SIGSTOP, false)
return err
})
return thr, err
}
// attachAllThreads attaches to all threads in a process.
func (p *process) attachAllThreads() os.Error {
taskPath := "/proc/" + strconv.Itoa(p.pid) + "/task"
taskDir, err := os.Open(taskPath)
if err != nil {
return err
}
defer taskDir.Close()
// We stop threads as we attach to them; however, because new
// threads can appear while we're looping over all of them, we
// have to repeatly scan until we know we're attached to all
// of them.
for again := true; again; {
again = false
tids, err := taskDir.Readdirnames(-1)
if err != nil {
return err
}
for _, tidStr := range tids {
tid, err := strconv.Atoi(tidStr)
if err != nil {
return err
}
if _, ok := p.threads[tid]; ok {
continue
}
_, err = p.attachThread(tid)
if err != nil {
// There could have been a race, or
// this process could be a zobmie.
statFile, err2 := ioutil.ReadFile(taskPath + "/" + tidStr + "/stat")
if err2 != nil {
switch err2 := err2.(type) {
case *os.PathError:
if err2.Error == os.ENOENT {
// Raced with thread exit
p.logTrace("raced with thread %d exit", tid)
continue
}
}
// Return the original error
return err
}
statParts := strings.Split(string(statFile), " ", 4)
if len(statParts) > 2 && statParts[2] == "Z" {
// tid is a zombie
p.logTrace("thread %d is a zombie", tid)
continue
}
// Return the original error
return err
}
again = true
}
}
return nil
}
// newProcess creates a new process object and starts its monitor thread.
func newProcess(pid int) *process {
p := &process{
pid: pid,
threads: make(map[int]*thread),
breakpoints: make(map[uintptr]*breakpoint),
ready: make(chan bool, 1),
debugEvents: make(chan *debugEvent),
debugReqs: make(chan *debugReq),
stopReq: make(chan os.Error),
}
go p.monitor()
return p
}
// Attach attaches to process pid and stops all of its threads.
func Attach(pid int) (Process, os.Error) {
p := newProcess(pid)
// Attach to all threads
err := p.attachAllThreads()
if err != nil {
p.Detach()
// TODO(austin) Detach stopped the monitor already
//p.stopMonitor(err);
return nil, err
}
return p, nil
}
// StartProcess forks the current process and execs argv0, stopping the
// new process after the exec syscall. See os.StartProcess for additional
// details.
func StartProcess(argv0 string, argv []string, attr *os.ProcAttr) (Process, os.Error) {
sysattr := &syscall.ProcAttr{
Dir: attr.Dir,
Env: attr.Env,
Ptrace: true,
}
p := newProcess(-1)
// Create array of integer (system) fds.
intfd := make([]int, len(attr.Files))
for i, f := range attr.Files {
if f == nil {
intfd[i] = -1
} else {
intfd[i] = f.Fd()
}
}
sysattr.Files = intfd
// Fork from the monitor thread so we get the right tracer pid.
err := p.do(func() os.Error {
pid, _, errno := syscall.StartProcess(argv0, argv, sysattr)
if errno != 0 {
return &os.PathError{"fork/exec", argv0, os.Errno(errno)}
}
p.pid = pid
// The process will raise SIGTRAP when it reaches execve.
_, err := p.newThread(pid, syscall.SIGTRAP, false)
return err
})
if err != nil {
p.stopMonitor(err)
return nil, err
}
return p, nil
}