summaryrefslogtreecommitdiff
path: root/libgo/go/debug/proc
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/debug/proc')
-rw-r--r--libgo/go/debug/proc/proc.go222
-rw-r--r--libgo/go/debug/proc/proc_darwin.go17
-rw-r--r--libgo/go/debug/proc/proc_freebsd.go17
-rw-r--r--libgo/go/debug/proc/proc_linux.go1316
-rw-r--r--libgo/go/debug/proc/proc_rtems.go17
-rw-r--r--libgo/go/debug/proc/proc_solaris.go17
-rw-r--r--libgo/go/debug/proc/proc_windows.go17
-rw-r--r--libgo/go/debug/proc/ptrace-nptl.txt132
-rw-r--r--libgo/go/debug/proc/regs_darwin_386.go5
-rw-r--r--libgo/go/debug/proc/regs_darwin_amd64.go5
-rw-r--r--libgo/go/debug/proc/regs_freebsd_386.go5
-rw-r--r--libgo/go/debug/proc/regs_freebsd_amd64.go5
-rw-r--r--libgo/go/debug/proc/regs_linux_386.go143
-rw-r--r--libgo/go/debug/proc/regs_linux_amd64.go191
-rw-r--r--libgo/go/debug/proc/regs_linux_arm.go39
-rw-r--r--libgo/go/debug/proc/regs_windows_386.go5
-rw-r--r--libgo/go/debug/proc/regs_windows_amd64.go5
17 files changed, 2158 insertions, 0 deletions
diff --git a/libgo/go/debug/proc/proc.go b/libgo/go/debug/proc/proc.go
new file mode 100644
index 000000000..d89649cf8
--- /dev/null
+++ b/libgo/go/debug/proc/proc.go
@@ -0,0 +1,222 @@
+// 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 provides a platform-independent interface for
+// tracing and controlling running processes. It supports
+// multi-threaded processes and provides typical low-level debugging
+// controls such as breakpoints, single stepping, and manipulating
+// memory and registers.
+package proc
+
+// TODO(rsc): Have to import everything that proc_linux.go
+// and proc_darwin.go do, because deps.bash only looks at
+// this file.
+import (
+ _ "container/vector"
+ _ "fmt"
+ _ "io"
+ "os"
+ _ "runtime"
+ "strconv"
+ _ "strings"
+ _ "sync"
+ _ "syscall"
+)
+
+type Word uint64
+
+// A Cause explains why a thread is stopped.
+type Cause interface {
+ String() string
+}
+
+// Regs is a set of named machine registers, including a program
+// counter, link register, and stack pointer.
+//
+// TODO(austin) There's quite a proliferation of methods here. We
+// could make a Reg interface with Get and Set and make this just PC,
+// Link, SP, Names, and Reg. We could also put Index in Reg and that
+// makes it easy to get the index of things like the PC (currently
+// there's just no way to know that). This would also let us include
+// other per-register information like how to print it.
+type Regs interface {
+ // PC returns the value of the program counter.
+ PC() Word
+
+ // SetPC sets the program counter to val.
+ SetPC(val Word) os.Error
+
+ // Link returns the link register, if any.
+ Link() Word
+
+ // SetLink sets the link register to val.
+ SetLink(val Word) os.Error
+
+ // SP returns the value of the stack pointer.
+ SP() Word
+
+ // SetSP sets the stack pointer register to val.
+ SetSP(val Word) os.Error
+
+ // Names returns the names of all of the registers.
+ Names() []string
+
+ // Get returns the value of a register, where i corresponds to
+ // the index of the register's name in the array returned by
+ // Names.
+ Get(i int) Word
+
+ // Set sets the value of a register.
+ Set(i int, val Word) os.Error
+}
+
+// Thread is a thread in the process being traced.
+type Thread interface {
+ // Step steps this thread by a single instruction. The thread
+ // must be stopped. If the thread is currently stopped on a
+ // breakpoint, this will step over the breakpoint.
+ //
+ // XXX What if it's stopped because of a signal?
+ Step() os.Error
+
+ // Stopped returns the reason that this thread is stopped. It
+ // is an error is the thread not stopped.
+ Stopped() (Cause, os.Error)
+
+ // Regs retrieves the current register values from this
+ // thread. The thread must be stopped.
+ Regs() (Regs, os.Error)
+
+ // Peek reads len(out) bytes from the address addr in this
+ // thread into out. The thread must be stopped. It returns
+ // the number of bytes successfully read. If an error occurs,
+ // such as attempting to read unmapped memory, this count
+ // could be short and an error will be returned. If this does
+ // encounter unmapped memory, it will read up to the byte
+ // preceding the unmapped area.
+ Peek(addr Word, out []byte) (int, os.Error)
+
+ // Poke writes b to the address addr in this thread. The
+ // thread must be stopped. It returns the number of bytes
+ // successfully written. If an error occurs, such as
+ // attempting to write to unmapped memory, this count could be
+ // short and an error will be returned. If this does
+ // encounter unmapped memory, it will write up to the byte
+ // preceding the unmapped area.
+ Poke(addr Word, b []byte) (int, os.Error)
+}
+
+// Process is a process being traced. It consists of a set of
+// threads. A process can be running, stopped, or terminated. The
+// process's state extends to all of its threads.
+type Process interface {
+ // Threads returns an array of all threads in this process.
+ Threads() []Thread
+
+ // AddBreakpoint creates a new breakpoint at program counter
+ // pc. Breakpoints can only be created when the process is
+ // stopped. It is an error if a breakpoint already exists at
+ // pc.
+ AddBreakpoint(pc Word) os.Error
+
+ // RemoveBreakpoint removes the breakpoint at the program
+ // counter pc. It is an error if no breakpoint exists at pc.
+ RemoveBreakpoint(pc Word) os.Error
+
+ // Stop stops all running threads in this process before
+ // returning.
+ Stop() os.Error
+
+ // Continue resumes execution of all threads in this process.
+ // Any thread that is stopped on a breakpoint will be stepped
+ // over that breakpoint. Any thread that is stopped because
+ // of a signal (other than SIGSTOP or SIGTRAP) will receive
+ // the pending signal.
+ Continue() os.Error
+
+ // WaitStop waits until all threads in process p are stopped
+ // as a result of some thread hitting a breakpoint, receiving
+ // a signal, creating a new thread, or exiting.
+ WaitStop() os.Error
+
+ // Detach detaches from this process. All stopped threads
+ // will be resumed.
+ Detach() os.Error
+}
+
+// Stopped is a stop cause used for threads that are stopped either by
+// user request (e.g., from the Stop method or after single stepping),
+// or that are stopped because some other thread caused the program to
+// stop.
+type Stopped struct{}
+
+func (c Stopped) String() string { return "stopped" }
+
+// Breakpoint is a stop cause resulting from a thread reaching a set
+// breakpoint.
+type Breakpoint Word
+
+// PC returns the program counter that the program is stopped at.
+func (c Breakpoint) PC() Word { return Word(c) }
+
+func (c Breakpoint) String() string {
+ return "breakpoint at 0x" + strconv.Uitob64(uint64(c.PC()), 16)
+}
+
+// Signal is a stop cause resulting from a thread receiving a signal.
+// When the process is continued, the signal will be delivered.
+type Signal string
+
+// Signal returns the signal being delivered to the thread.
+func (c Signal) Name() string { return string(c) }
+
+func (c Signal) String() string { return c.Name() }
+
+// ThreadCreate is a stop cause returned from an existing thread when
+// it creates a new thread. The new thread exists in a primordial
+// form at this point and will begin executing in earnest when the
+// process is continued.
+type ThreadCreate struct {
+ thread Thread
+}
+
+func (c *ThreadCreate) NewThread() Thread { return c.thread }
+
+func (c *ThreadCreate) String() string { return "thread create" }
+
+// ThreadExit is a stop cause resulting from a thread exiting. When
+// this cause first arises, the thread will still be in the list of
+// process threads and its registers and memory will still be
+// accessible.
+type ThreadExit struct {
+ exitStatus int
+ signal string
+}
+
+// Exited returns true if the thread exited normally.
+func (c *ThreadExit) Exited() bool { return c.exitStatus != -1 }
+
+// ExitStatus returns the exit status of the thread if it exited
+// normally or -1 otherwise.
+func (c *ThreadExit) ExitStatus() int { return c.exitStatus }
+
+// Signaled returns true if the thread was terminated by a signal.
+func (c *ThreadExit) Signaled() bool { return c.exitStatus == -1 }
+
+// StopSignal returns the signal that terminated the thread, or "" if
+// it was not terminated by a signal.
+func (c *ThreadExit) StopSignal() string { return c.signal }
+
+func (c *ThreadExit) String() string {
+ res := "thread exited "
+ switch {
+ case c.Exited():
+ res += "with status " + strconv.Itoa(c.ExitStatus())
+ case c.Signaled():
+ res += "from signal " + c.StopSignal()
+ default:
+ res += "from unknown cause"
+ }
+ return res
+}
diff --git a/libgo/go/debug/proc/proc_darwin.go b/libgo/go/debug/proc/proc_darwin.go
new file mode 100644
index 000000000..7caf3a21a
--- /dev/null
+++ b/libgo/go/debug/proc/proc_darwin.go
@@ -0,0 +1,17 @@
+// 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
+
+import "os"
+
+// Process tracing is not supported on OS X yet.
+
+func Attach(pid int) (Process, os.Error) {
+ return nil, os.NewError("debug/proc not implemented on OS X")
+}
+
+func ForkExec(argv0 string, argv []string, envv []string, dir string, fd []*os.File) (Process, os.Error) {
+ return Attach(0)
+}
diff --git a/libgo/go/debug/proc/proc_freebsd.go b/libgo/go/debug/proc/proc_freebsd.go
new file mode 100644
index 000000000..f6474ce80
--- /dev/null
+++ b/libgo/go/debug/proc/proc_freebsd.go
@@ -0,0 +1,17 @@
+// 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
+
+import "os"
+
+// Process tracing is not supported on FreeBSD yet.
+
+func Attach(pid int) (Process, os.Error) {
+ return nil, os.NewError("debug/proc not implemented on FreeBSD")
+}
+
+func ForkExec(argv0 string, argv []string, envv []string, dir string, fd []*os.File) (Process, os.Error) {
+ return Attach(0)
+}
diff --git a/libgo/go/debug/proc/proc_linux.go b/libgo/go/debug/proc/proc_linux.go
new file mode 100644
index 000000000..f0cc43a10
--- /dev/null
+++ b/libgo/go/debug/proc/proc_linux.go
@@ -0,0 +1,1316 @@
+// 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, os.O_RDONLY, 0)
+ 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
+}
+
+// ForkExec forks the current process and execs argv0, stopping the
+// new process after the exec syscall. See os.ForkExec for additional
+// details.
+func ForkExec(argv0 string, argv []string, envv []string, dir string, fd []*os.File) (Process, os.Error) {
+ p := newProcess(-1)
+
+ // Create array of integer (system) fds.
+ intfd := make([]int, len(fd))
+ for i, f := range fd {
+ if f == nil {
+ intfd[i] = -1
+ } else {
+ intfd[i] = f.Fd()
+ }
+ }
+
+ // Fork from the monitor thread so we get the right tracer pid.
+ err := p.do(func() os.Error {
+ pid, errno := syscall.PtraceForkExec(argv0, argv, envv, dir, intfd)
+ 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
+}
diff --git a/libgo/go/debug/proc/proc_rtems.go b/libgo/go/debug/proc/proc_rtems.go
new file mode 100644
index 000000000..5311a63ba
--- /dev/null
+++ b/libgo/go/debug/proc/proc_rtems.go
@@ -0,0 +1,17 @@
+// Copyright 2011 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
+
+import "os"
+
+// Process tracing is not supported on RTEMS yet.
+
+func Attach(pid int) (Process, os.Error) {
+ return nil, os.NewError("debug/proc not implemented on RTEMS")
+}
+
+func ForkExec(argv0 string, argv []string, envv []string, dir string, fd []*os.File) (Process, os.Error) {
+ return Attach(0)
+}
diff --git a/libgo/go/debug/proc/proc_solaris.go b/libgo/go/debug/proc/proc_solaris.go
new file mode 100644
index 000000000..a72c59237
--- /dev/null
+++ b/libgo/go/debug/proc/proc_solaris.go
@@ -0,0 +1,17 @@
+// Copyright 2011 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
+
+import "os"
+
+// Process tracing is not supported on Solaris yet.
+
+func Attach(pid int) (Process, os.Error) {
+ return nil, os.NewError("debug/proc not implemented on Solaris")
+}
+
+func ForkExec(argv0 string, argv []string, envv []string, dir string, fd []*os.File) (Process, os.Error) {
+ return Attach(0)
+}
diff --git a/libgo/go/debug/proc/proc_windows.go b/libgo/go/debug/proc/proc_windows.go
new file mode 100644
index 000000000..dc22faef8
--- /dev/null
+++ b/libgo/go/debug/proc/proc_windows.go
@@ -0,0 +1,17 @@
+// 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
+
+import "os"
+
+// Process tracing is not supported on windows yet.
+
+func Attach(pid int) (Process, os.Error) {
+ return nil, os.NewError("debug/proc not implemented on windows")
+}
+
+func ForkExec(argv0 string, argv []string, envv []string, dir string, fd []*os.File) (Process, os.Error) {
+ return Attach(0)
+}
diff --git a/libgo/go/debug/proc/ptrace-nptl.txt b/libgo/go/debug/proc/ptrace-nptl.txt
new file mode 100644
index 000000000..62cbf7700
--- /dev/null
+++ b/libgo/go/debug/proc/ptrace-nptl.txt
@@ -0,0 +1,132 @@
+// 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.
+
+ptrace and NTPL, the missing manpage
+
+== Signals ==
+
+A signal sent to a ptrace'd process or thread causes only the thread
+that receives it to stop and report to the attached process.
+
+Use tgkill to target a signal (for example, SIGSTOP) at a particular
+thread. If you use kill, the signal could be delivered to another
+thread in the same process.
+
+Note that SIGSTOP differs from its usual behavior when a process is
+being traced. Usually, a SIGSTOP sent to any thread in a thread group
+will stop all threads in the thread group. When a thread is traced,
+however, a SIGSTOP affects only the receiving thread (and any other
+threads in the thread group that are not traced).
+
+SIGKILL behaves like it does for non-traced processes. It affects all
+threads in the process and terminates them without the WSTOPSIG event
+generated by other signals. However, if PTRACE_O_TRACEEXIT is set,
+the attached process will still receive PTRACE_EVENT_EXIT events
+before receiving WIFSIGNALED events.
+
+See "Following thread death" for a caveat regarding signal delivery to
+zombie threads.
+
+== Waiting on threads ==
+
+Cloned threads in ptrace'd processes are treated similarly to cloned
+threads in your own process. Thus, you must use the __WALL option in
+order to receive notifications from threads created by the child
+process. Similarly, the __WCLONE option will wait only on
+notifications from threads created by the child process and *not* on
+notifications from the initial child thread.
+
+Even when waiting on a specific thread's PID using waitpid or similar,
+__WALL or __WCLONE is necessary or waitpid will return ECHILD.
+
+== Attaching to existing threads ==
+
+libthread_db (which gdb uses), attaches to existing threads by pulling
+the pthread data structures out of the traced process. The much
+easier way is to traverse the /proc/PID/task directory, though it's
+unclear how the semantics of these two approaches differ.
+
+Unfortunately, if the main thread has exited (but the overall process
+has not), it sticks around as a zombie process. This zombie will
+appear in the /proc/PID/task directory, but trying to attach to it
+will yield EPERM. In this case, the third field of the
+/proc/PID/task/PID/stat file will be "Z". Attempting to open the stat
+file is also a convenient way to detect races between listing the task
+directory and the thread exiting. Coincidentally, gdb will simply
+fail to attach to a process whose main thread is a zombie.
+
+Because new threads may be created while the debugger is in the
+process of attaching to existing threads, the debugger must repeatedly
+re-list the task directory until it has attached to (and thus stopped)
+every thread listed.
+
+In order to follow new threads created by existing threads,
+PTRACE_O_TRACECLONE must be set on each thread attached to.
+
+== Following new threads ==
+
+With the child process stopped, use PTRACE_SETOPTIONS to set the
+PTRACE_O_TRACECLONE option. This option is per-thread, and thus must
+be set on each existing thread individually. When an existing thread
+with PTRACE_O_TRACECLONE set spawns a new thread, the existing thread
+will stop with (SIGTRAP | PTRACE_EVENT_CLONE << 8) and the PID of the
+new thread can be retrieved with PTRACE_GETEVENTMSG on the creating
+thread. At this time, the new thread will exist, but will initially
+be stopped with a SIGSTOP. The new thread will automatically be
+traced and will inherit the PTRACE_O_TRACECLONE option from its
+parent. The attached process should wait on the new thread to receive
+the SIGSTOP notification.
+
+When using waitpid(-1, ...), don't rely on the parent thread reporting
+a SIGTRAP before receiving the SIGSTOP from the new child thread.
+
+Without PTRACE_O_TRACECLONE, newly cloned threads will not be
+ptrace'd. As a result, signals received by new threads will be
+handled in the usual way, which may affect the parent and in turn
+appear to the attached process, but attributed to the parent (possibly
+in unexpected ways).
+
+== Following thread death ==
+
+If any thread with the PTRACE_O_TRACEEXIT option set exits (either by
+returning or pthread_exit'ing), the tracing process will receive an
+immediate PTRACE_EVENT_EXIT. At this point, the thread will still
+exist. The exit status, encoded as for wait, can be queried using
+PTRACE_GETEVENTMSG on the exiting thread's PID. The thread should be
+continued so it can actually exit, after which its wait behavior is
+the same as for a thread without the PTRACE_O_TRACEEXIT option.
+
+If a non-main thread exits (either by returning or pthread_exit'ing),
+its corresponding process will also exit, producing a WIFEXITED event
+(after the process is continued from a possible PTRACE_EVENT_EXIT
+event). It is *not* necessary for another thread to ptrace_join for
+this to happen.
+
+If the main thread exits by returning, then all threads will exit,
+first generating a PTRACE_EVENT_EXIT event for each thread if
+appropriate, then producing a WIFEXITED event for each thread.
+
+If the main thread exits using pthread_exit, then it enters a
+non-waitable zombie state. It will still produce an immediate
+PTRACE_O_TRACEEXIT event, but the WIFEXITED event will be delayed
+until the entire process exits. This state exists so that shells
+don't think the process is done until all of the threads have exited.
+Unfortunately, signals cannot be delivered to non-waitable zombies.
+Most notably, SIGSTOP cannot be delivered; as a result, when you
+broadcast SIGSTOP to all of the threads, you must not wait for
+non-waitable zombies to stop. Furthermore, any ptrace command on a
+non-waitable zombie, including PTRACE_DETACH, will return ESRCH.
+
+== Multi-threaded debuggers ==
+
+If the debugger itself is multi-threaded, ptrace calls must come from
+the same thread that originally attached to the remote thread. The
+kernel simply compares the PID of the caller of ptrace against the
+tracer PID of the process passed to ptrace. Because each debugger
+thread has a different PID, calling ptrace from a different thread
+might as well be calling it from a different process and the kernel
+will return ESRCH.
+
+wait, on the other hand, does not have this restriction. Any debugger
+thread can wait on any thread in the attached process.
diff --git a/libgo/go/debug/proc/regs_darwin_386.go b/libgo/go/debug/proc/regs_darwin_386.go
new file mode 100644
index 000000000..60c9ac719
--- /dev/null
+++ b/libgo/go/debug/proc/regs_darwin_386.go
@@ -0,0 +1,5 @@
+// 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
diff --git a/libgo/go/debug/proc/regs_darwin_amd64.go b/libgo/go/debug/proc/regs_darwin_amd64.go
new file mode 100644
index 000000000..60c9ac719
--- /dev/null
+++ b/libgo/go/debug/proc/regs_darwin_amd64.go
@@ -0,0 +1,5 @@
+// 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
diff --git a/libgo/go/debug/proc/regs_freebsd_386.go b/libgo/go/debug/proc/regs_freebsd_386.go
new file mode 100644
index 000000000..60c9ac719
--- /dev/null
+++ b/libgo/go/debug/proc/regs_freebsd_386.go
@@ -0,0 +1,5 @@
+// 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
diff --git a/libgo/go/debug/proc/regs_freebsd_amd64.go b/libgo/go/debug/proc/regs_freebsd_amd64.go
new file mode 100644
index 000000000..60c9ac719
--- /dev/null
+++ b/libgo/go/debug/proc/regs_freebsd_amd64.go
@@ -0,0 +1,5 @@
+// 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
diff --git a/libgo/go/debug/proc/regs_linux_386.go b/libgo/go/debug/proc/regs_linux_386.go
new file mode 100644
index 000000000..b4a9769db
--- /dev/null
+++ b/libgo/go/debug/proc/regs_linux_386.go
@@ -0,0 +1,143 @@
+// 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
+
+import (
+ "os"
+ "strconv"
+ "syscall"
+)
+
+type _386Regs struct {
+ syscall.PtraceRegs
+ setter func(*syscall.PtraceRegs) os.Error
+}
+
+var names = []string{
+ "eax",
+ "ebx",
+ "ecx",
+ "edx",
+ "esi",
+ "edi",
+ "ebp",
+ "esp",
+ "eip",
+ "eflags",
+ "cs",
+ "ss",
+ "ds",
+ "es",
+ "fs",
+ "gs",
+}
+
+func (r *_386Regs) PC() Word { return Word(r.Eip) }
+
+func (r *_386Regs) SetPC(val Word) os.Error {
+ r.Eip = int32(val)
+ return r.setter(&r.PtraceRegs)
+}
+
+func (r *_386Regs) Link() Word {
+ // TODO(austin)
+ panic("No link register")
+}
+
+func (r *_386Regs) SetLink(val Word) os.Error { panic("No link register") }
+
+func (r *_386Regs) SP() Word { return Word(r.Esp) }
+
+func (r *_386Regs) SetSP(val Word) os.Error {
+ r.Esp = int32(val)
+ return r.setter(&r.PtraceRegs)
+}
+
+func (r *_386Regs) Names() []string { return names }
+
+func (r *_386Regs) Get(i int) Word {
+ switch i {
+ case 0:
+ return Word(uint32(r.Eax))
+ case 1:
+ return Word(uint32(r.Ebx))
+ case 2:
+ return Word(uint32(r.Ecx))
+ case 3:
+ return Word(uint32(r.Edx))
+ case 4:
+ return Word(uint32(r.Esi))
+ case 5:
+ return Word(uint32(r.Edi))
+ case 6:
+ return Word(uint32(r.Ebp))
+ case 7:
+ return Word(uint32(r.Esp))
+ case 8:
+ return Word(uint32(r.Eip))
+ case 9:
+ return Word(uint32(r.Eflags))
+ case 10:
+ return Word(r.Xcs)
+ case 11:
+ return Word(r.Xss)
+ case 12:
+ return Word(r.Xds)
+ case 13:
+ return Word(r.Xes)
+ case 14:
+ return Word(r.Xfs)
+ case 15:
+ return Word(r.Xgs)
+ }
+ panic("invalid register index " + strconv.Itoa(i))
+}
+
+func (r *_386Regs) Set(i int, val Word) os.Error {
+ switch i {
+ case 0:
+ r.Eax = int32(val)
+ case 1:
+ r.Ebx = int32(val)
+ case 2:
+ r.Ecx = int32(val)
+ case 3:
+ r.Edx = int32(val)
+ case 4:
+ r.Esi = int32(val)
+ case 5:
+ r.Edi = int32(val)
+ case 6:
+ r.Ebp = int32(val)
+ case 7:
+ r.Esp = int32(val)
+ case 8:
+ r.Eip = int32(val)
+ case 9:
+ r.Eflags = int32(val)
+ case 10:
+ r.Xcs = int32(val)
+ case 11:
+ r.Xss = int32(val)
+ case 12:
+ r.Xds = int32(val)
+ case 13:
+ r.Xes = int32(val)
+ case 14:
+ r.Xfs = int32(val)
+ case 15:
+ r.Xgs = int32(val)
+ default:
+ panic("invalid register index " + strconv.Itoa(i))
+ }
+ return r.setter(&r.PtraceRegs)
+}
+
+func newRegs(regs *syscall.PtraceRegs, setter func(*syscall.PtraceRegs) os.Error) Regs {
+ res := _386Regs{}
+ res.PtraceRegs = *regs
+ res.setter = setter
+ return &res
+}
diff --git a/libgo/go/debug/proc/regs_linux_amd64.go b/libgo/go/debug/proc/regs_linux_amd64.go
new file mode 100644
index 000000000..381be29b1
--- /dev/null
+++ b/libgo/go/debug/proc/regs_linux_amd64.go
@@ -0,0 +1,191 @@
+// 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
+
+import (
+ "os"
+ "strconv"
+ "syscall"
+)
+
+type amd64Regs struct {
+ syscall.PtraceRegs
+ setter func(*syscall.PtraceRegs) os.Error
+}
+
+var names = [...]string{
+ "rax",
+ "rbx",
+ "rcx",
+ "rdx",
+ "rsi",
+ "rdi",
+ "rbp",
+ "rsp",
+ "r8",
+ "r9",
+ "r10",
+ "r11",
+ "r12",
+ "r13",
+ "r14",
+ "r15",
+ "rip",
+ "eflags",
+ "cs",
+ "ss",
+ "ds",
+ "es",
+ "fs",
+ "gs",
+
+ // PtraceRegs contains these registers, but I don't think
+ // they're actually meaningful.
+ //"orig_rax",
+ //"fs_base",
+ //"gs_base",
+}
+
+func (r *amd64Regs) PC() Word { return Word(r.Rip) }
+
+func (r *amd64Regs) SetPC(val Word) os.Error {
+ r.Rip = uint64(val)
+ return r.setter(&r.PtraceRegs)
+}
+
+func (r *amd64Regs) Link() Word {
+ // TODO(austin)
+ panic("No link register")
+}
+
+func (r *amd64Regs) SetLink(val Word) os.Error {
+ panic("No link register")
+}
+
+func (r *amd64Regs) SP() Word { return Word(r.Rsp) }
+
+func (r *amd64Regs) SetSP(val Word) os.Error {
+ r.Rsp = uint64(val)
+ return r.setter(&r.PtraceRegs)
+}
+
+func (r *amd64Regs) Names() []string { return names[0:] }
+
+func (r *amd64Regs) Get(i int) Word {
+ switch i {
+ case 0:
+ return Word(r.Rax)
+ case 1:
+ return Word(r.Rbx)
+ case 2:
+ return Word(r.Rcx)
+ case 3:
+ return Word(r.Rdx)
+ case 4:
+ return Word(r.Rsi)
+ case 5:
+ return Word(r.Rdi)
+ case 6:
+ return Word(r.Rbp)
+ case 7:
+ return Word(r.Rsp)
+ case 8:
+ return Word(r.R8)
+ case 9:
+ return Word(r.R9)
+ case 10:
+ return Word(r.R10)
+ case 11:
+ return Word(r.R11)
+ case 12:
+ return Word(r.R12)
+ case 13:
+ return Word(r.R13)
+ case 14:
+ return Word(r.R14)
+ case 15:
+ return Word(r.R15)
+ case 16:
+ return Word(r.Rip)
+ case 17:
+ return Word(r.Eflags)
+ case 18:
+ return Word(r.Cs)
+ case 19:
+ return Word(r.Ss)
+ case 20:
+ return Word(r.Ds)
+ case 21:
+ return Word(r.Es)
+ case 22:
+ return Word(r.Fs)
+ case 23:
+ return Word(r.Gs)
+ }
+ panic("invalid register index " + strconv.Itoa(i))
+}
+
+func (r *amd64Regs) Set(i int, val Word) os.Error {
+ switch i {
+ case 0:
+ r.Rax = uint64(val)
+ case 1:
+ r.Rbx = uint64(val)
+ case 2:
+ r.Rcx = uint64(val)
+ case 3:
+ r.Rdx = uint64(val)
+ case 4:
+ r.Rsi = uint64(val)
+ case 5:
+ r.Rdi = uint64(val)
+ case 6:
+ r.Rbp = uint64(val)
+ case 7:
+ r.Rsp = uint64(val)
+ case 8:
+ r.R8 = uint64(val)
+ case 9:
+ r.R9 = uint64(val)
+ case 10:
+ r.R10 = uint64(val)
+ case 11:
+ r.R11 = uint64(val)
+ case 12:
+ r.R12 = uint64(val)
+ case 13:
+ r.R13 = uint64(val)
+ case 14:
+ r.R14 = uint64(val)
+ case 15:
+ r.R15 = uint64(val)
+ case 16:
+ r.Rip = uint64(val)
+ case 17:
+ r.Eflags = uint64(val)
+ case 18:
+ r.Cs = uint64(val)
+ case 19:
+ r.Ss = uint64(val)
+ case 20:
+ r.Ds = uint64(val)
+ case 21:
+ r.Es = uint64(val)
+ case 22:
+ r.Fs = uint64(val)
+ case 23:
+ r.Gs = uint64(val)
+ default:
+ panic("invalid register index " + strconv.Itoa(i))
+ }
+ return r.setter(&r.PtraceRegs)
+}
+
+func newRegs(regs *syscall.PtraceRegs, setter func(*syscall.PtraceRegs) os.Error) Regs {
+ res := amd64Regs{}
+ res.PtraceRegs = *regs
+ res.setter = setter
+ return &res
+}
diff --git a/libgo/go/debug/proc/regs_linux_arm.go b/libgo/go/debug/proc/regs_linux_arm.go
new file mode 100644
index 000000000..ec78cbcf2
--- /dev/null
+++ b/libgo/go/debug/proc/regs_linux_arm.go
@@ -0,0 +1,39 @@
+// 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
+
+import (
+ "os"
+ "syscall"
+)
+
+// TODO(kaib): add support
+
+type armRegs struct{}
+
+func (r *armRegs) PC() Word { return Word(0) }
+
+func (r *armRegs) SetPC(val Word) os.Error { return nil }
+
+func (r *armRegs) Link() Word { return Word(0) }
+
+func (r *armRegs) SetLink(val Word) os.Error { return nil }
+
+func (r *armRegs) SP() Word { return Word(0) }
+
+func (r *armRegs) SetSP(val Word) os.Error { return nil }
+
+func (r *armRegs) Names() []string { return nil }
+
+func (r *armRegs) Get(i int) Word { return Word(0) }
+
+func (r *armRegs) Set(i int, val Word) os.Error {
+ return nil
+}
+
+func newRegs(regs *syscall.PtraceRegs, setter func(*syscall.PtraceRegs) os.Error) Regs {
+ res := armRegs{}
+ return &res
+}
diff --git a/libgo/go/debug/proc/regs_windows_386.go b/libgo/go/debug/proc/regs_windows_386.go
new file mode 100644
index 000000000..60c9ac719
--- /dev/null
+++ b/libgo/go/debug/proc/regs_windows_386.go
@@ -0,0 +1,5 @@
+// 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
diff --git a/libgo/go/debug/proc/regs_windows_amd64.go b/libgo/go/debug/proc/regs_windows_amd64.go
new file mode 100644
index 000000000..60c9ac719
--- /dev/null
+++ b/libgo/go/debug/proc/regs_windows_amd64.go
@@ -0,0 +1,5 @@
+// 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