diff --git a/src/pkg/Makefile b/src/pkg/Makefile index 8a99d790638..7542ae78010 100644 --- a/src/pkg/Makefile +++ b/src/pkg/Makefile @@ -68,7 +68,6 @@ DIRS=\ debug/elf\ debug/gosym\ debug/pe\ - debug/proc\ ebnf\ encoding/ascii85\ encoding/base32\ @@ -187,7 +186,6 @@ endif ifeq ($(GOOS),plan9) NOPLAN9BUILD=\ crypto/tls\ - debug/proc\ exp/gui/x11\ expvar\ http\ @@ -218,7 +216,6 @@ NOTEST+=\ crypto\ crypto/openpgp/error\ crypto/x509/pkix\ - debug/proc\ exp/gui\ exp/gui/x11\ go/ast\ diff --git a/src/pkg/debug/proc/Makefile b/src/pkg/debug/proc/Makefile deleted file mode 100644 index c6d879836b2..00000000000 --- a/src/pkg/debug/proc/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# 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. - -include ../../../Make.inc - -TARG=debug/proc -GOFILES=\ - proc.go\ - proc_$(GOOS).go\ - regs_$(GOOS)_$(GOARCH).go\ - -include ../../../Make.pkg diff --git a/src/pkg/debug/proc/proc.go b/src/pkg/debug/proc/proc.go deleted file mode 100644 index d89649cf887..00000000000 --- a/src/pkg/debug/proc/proc.go +++ /dev/null @@ -1,222 +0,0 @@ -// 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/src/pkg/debug/proc/proc_darwin.go b/src/pkg/debug/proc/proc_darwin.go deleted file mode 100644 index 49f0a5361fb..00000000000 --- a/src/pkg/debug/proc/proc_darwin.go +++ /dev/null @@ -1,17 +0,0 @@ -// 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 StartProcess(argv0 string, argv []string, attr *os.ProcAttr) (Process, os.Error) { - return Attach(0) -} diff --git a/src/pkg/debug/proc/proc_freebsd.go b/src/pkg/debug/proc/proc_freebsd.go deleted file mode 100644 index 4df07c365af..00000000000 --- a/src/pkg/debug/proc/proc_freebsd.go +++ /dev/null @@ -1,17 +0,0 @@ -// 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 StartProcess(argv0 string, argv []string, attr *os.ProcAttr) (Process, os.Error) { - return Attach(0) -} diff --git a/src/pkg/debug/proc/proc_linux.go b/src/pkg/debug/proc/proc_linux.go deleted file mode 100644 index 7ec797114c4..00000000000 --- a/src/pkg/debug/proc/proc_linux.go +++ /dev/null @@ -1,1324 +0,0 @@ -// 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 "" - } - 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 utilities - */ - -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(®s) - 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(®s) - 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(®s) - 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(®s) - }) - 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(®s, 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 "". -func sigName(signal int) string { - if signal < 0 || signal >= len(sigNames) { - return "" - } - 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(®s) - 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 repeatedly 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 zombie. - 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.SplitN(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, - Sys: &syscall.SysProcAttr{ - 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 -} diff --git a/src/pkg/debug/proc/proc_windows.go b/src/pkg/debug/proc/proc_windows.go deleted file mode 100644 index 661474b67aa..00000000000 --- a/src/pkg/debug/proc/proc_windows.go +++ /dev/null @@ -1,17 +0,0 @@ -// 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 StartProcess(argv0 string, argv []string, attr *os.ProcAttr) (Process, os.Error) { - return Attach(0) -} diff --git a/src/pkg/debug/proc/ptrace-nptl.txt b/src/pkg/debug/proc/ptrace-nptl.txt deleted file mode 100644 index 62cbf770035..00000000000 --- a/src/pkg/debug/proc/ptrace-nptl.txt +++ /dev/null @@ -1,132 +0,0 @@ -// 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/src/pkg/debug/proc/regs_darwin_386.go b/src/pkg/debug/proc/regs_darwin_386.go deleted file mode 100644 index 60c9ac719e9..00000000000 --- a/src/pkg/debug/proc/regs_darwin_386.go +++ /dev/null @@ -1,5 +0,0 @@ -// 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/src/pkg/debug/proc/regs_darwin_amd64.go b/src/pkg/debug/proc/regs_darwin_amd64.go deleted file mode 100644 index 60c9ac719e9..00000000000 --- a/src/pkg/debug/proc/regs_darwin_amd64.go +++ /dev/null @@ -1,5 +0,0 @@ -// 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/src/pkg/debug/proc/regs_freebsd_386.go b/src/pkg/debug/proc/regs_freebsd_386.go deleted file mode 100644 index 60c9ac719e9..00000000000 --- a/src/pkg/debug/proc/regs_freebsd_386.go +++ /dev/null @@ -1,5 +0,0 @@ -// 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/src/pkg/debug/proc/regs_freebsd_amd64.go b/src/pkg/debug/proc/regs_freebsd_amd64.go deleted file mode 100644 index 60c9ac719e9..00000000000 --- a/src/pkg/debug/proc/regs_freebsd_amd64.go +++ /dev/null @@ -1,5 +0,0 @@ -// 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/src/pkg/debug/proc/regs_linux_386.go b/src/pkg/debug/proc/regs_linux_386.go deleted file mode 100644 index b4a9769db55..00000000000 --- a/src/pkg/debug/proc/regs_linux_386.go +++ /dev/null @@ -1,143 +0,0 @@ -// 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/src/pkg/debug/proc/regs_linux_amd64.go b/src/pkg/debug/proc/regs_linux_amd64.go deleted file mode 100644 index 381be29b179..00000000000 --- a/src/pkg/debug/proc/regs_linux_amd64.go +++ /dev/null @@ -1,191 +0,0 @@ -// 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/src/pkg/debug/proc/regs_linux_arm.go b/src/pkg/debug/proc/regs_linux_arm.go deleted file mode 100644 index ec78cbcf259..00000000000 --- a/src/pkg/debug/proc/regs_linux_arm.go +++ /dev/null @@ -1,39 +0,0 @@ -// 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/src/pkg/debug/proc/regs_windows_386.go b/src/pkg/debug/proc/regs_windows_386.go deleted file mode 100644 index 60c9ac719e9..00000000000 --- a/src/pkg/debug/proc/regs_windows_386.go +++ /dev/null @@ -1,5 +0,0 @@ -// 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/src/pkg/debug/proc/regs_windows_amd64.go b/src/pkg/debug/proc/regs_windows_amd64.go deleted file mode 100644 index 60c9ac719e9..00000000000 --- a/src/pkg/debug/proc/regs_windows_amd64.go +++ /dev/null @@ -1,5 +0,0 @@ -// 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