From: Austin Clements Date: Thu, 24 Sep 2009 16:07:47 +0000 (-0700) Subject: Switch ogle over to the in-tree debug/proc package. Fix X-Git-Tag: weekly.2009-11-06~495 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=ffe83e582ea3a01450d23c17eefa036b52bc7788;p=gostls13.git Switch ogle over to the in-tree debug/proc package. Fix debug/proc to install to the right place. Delete the old ptrace package. The diff looks huge, but it's mostly s/ptrace/proc/. R=rsc APPROVED=rsc DELTA=1940 (10 added, 1835 deleted, 95 changed) OCL=34966 CL=34968 --- diff --git a/src/pkg/debug/proc/Makefile b/src/pkg/debug/proc/Makefile index d7eb34855e..1c6dc9089f 100644 --- a/src/pkg/debug/proc/Makefile +++ b/src/pkg/debug/proc/Makefile @@ -4,7 +4,7 @@ include $(GOROOT)/src/Make.$(GOARCH) -TARG=ptrace +TARG=debug/proc GOFILES=\ proc.go\ proc_$(GOOS).go\ diff --git a/usr/austin/ogle/arch.go b/usr/austin/ogle/arch.go index 5c23c4ea6f..30a2bcf58b 100644 --- a/usr/austin/ogle/arch.go +++ b/usr/austin/ogle/arch.go @@ -5,17 +5,17 @@ package ogle import ( + "debug/proc"; "math"; - "ptrace"; ) type Arch interface { // ToWord converts an array of up to 8 bytes in memory order // to a word. - ToWord(data []byte) ptrace.Word; + ToWord(data []byte) proc.Word; // FromWord converts a word to an array of up to 8 bytes in // memory order. - FromWord(v ptrace.Word, out []byte); + FromWord(v proc.Word, out []byte); // ToFloat32 converts a word to a float. The order of this // word will be the order returned by ToWord on the memory // representation of a float, and thus may require reversing. @@ -40,7 +40,7 @@ type Arch interface { Align(offset, width int) int; // G returns the current G pointer. - G(regs ptrace.Regs) ptrace.Word; + G(regs proc.Regs) proc.Word; // ClosureSize returns the number of bytes expected by // ParseClosure. @@ -53,15 +53,15 @@ type Arch interface { type ArchLSB struct {} -func (ArchLSB) ToWord(data []byte) ptrace.Word { - var v ptrace.Word; +func (ArchLSB) ToWord(data []byte) proc.Word { + var v proc.Word; for i, b := range data { - v |= ptrace.Word(b) << (uint(i)*8); + v |= proc.Word(b) << (uint(i)*8); } return v; } -func (ArchLSB) FromWord(v ptrace.Word, out []byte) { +func (ArchLSB) FromWord(v proc.Word, out []byte) { for i := range out { out[i] = byte(v); v >>= 8; @@ -110,7 +110,7 @@ func (a *amd64) FloatSize() int { return 4; } -func (a *amd64) G(regs ptrace.Regs) ptrace.Word { +func (a *amd64) G(regs proc.Regs) proc.Word { // See src/pkg/runtime/mkasmh if a.gReg == -1 { ns := regs.Names(); diff --git a/usr/austin/ogle/cmd.go b/usr/austin/ogle/cmd.go index 88a675711f..150b5a5a3e 100644 --- a/usr/austin/ogle/cmd.go +++ b/usr/austin/ogle/cmd.go @@ -6,12 +6,12 @@ package ogle import ( "bufio"; + "debug/proc"; "eval"; "fmt"; "go/scanner"; "go/token"; "os"; - "ptrace"; "strconv"; "strings"; "sym"; @@ -139,7 +139,7 @@ func cmdLoad(args []byte) os.Error { // Parse argument and start or attach to process var fname string; - var proc ptrace.Process; + var tproc proc.Process; if len(path) >= 4 && path[0:4] == "pid:" { pid, err := strconv.Atoi(path[4:len(path)]); if err != nil { @@ -149,7 +149,7 @@ func cmdLoad(args []byte) os.Error { if err != nil { return err; } - proc, err = ptrace.Attach(pid); + tproc, err = proc.Attach(pid); if err != nil { return err; } @@ -161,30 +161,30 @@ func cmdLoad(args []byte) os.Error { } else { fname = parts[0]; } - proc, err = ptrace.ForkExec(fname, parts, os.Environ(), "", []*os.File{os.Stdin, os.Stdout, os.Stderr}); + tproc, err = proc.ForkExec(fname, parts, os.Environ(), "", []*os.File{os.Stdin, os.Stdout, os.Stderr}); if err != nil { return err; } println("Started", path); - // TODO(austin) If we fail after this point, kill proc + // TODO(austin) If we fail after this point, kill tproc // before detaching. } // Get symbols f, err := os.Open(fname, os.O_RDONLY, 0); if err != nil { - proc.Detach(); + tproc.Detach(); return err; } defer f.Close(); elf, err := sym.NewElf(f); if err != nil { - proc.Detach(); + tproc.Detach(); return err; } - curProc, err = NewProcessElf(proc, elf); + curProc, err = NewProcessElf(tproc, elf); if err != nil { - proc.Detach(); + tproc.Detach(); return err; } @@ -194,7 +194,7 @@ func cmdLoad(args []byte) os.Error { err = curProc.populateWorld(world); if err != nil { - proc.Detach(); + tproc.Detach(); return err; } @@ -374,5 +374,5 @@ func fnBpSet(t *eval.Thread, args []eval.Value, res []eval.Value) { if !ok { t.Abort(UsageError("symbol " + name + " is not a function")); } - curProc.OnBreakpoint(ptrace.Word(fn.Entry())).AddHandler(EventStop); + curProc.OnBreakpoint(proc.Word(fn.Entry())).AddHandler(EventStop); } diff --git a/usr/austin/ogle/event.go b/usr/austin/ogle/event.go index 86892e691f..9dc7a8445f 100644 --- a/usr/austin/ogle/event.go +++ b/usr/austin/ogle/event.go @@ -5,9 +5,9 @@ package ogle import ( + "debug/proc"; "fmt"; "os"; - "ptrace"; ) /* @@ -183,7 +183,7 @@ func EventStop(ev Event) (EventAction, os.Error) { type breakpointHook struct { commonHook; p *Process; - pc ptrace.Word; + pc proc.Word; } // A Breakpoint event occurs when a process reaches a particular @@ -191,8 +191,8 @@ type breakpointHook struct { // will be the goroutine that reached the program counter. type Breakpoint struct { commonEvent; - osThread ptrace.Thread; - pc ptrace.Word; + osThread proc.Thread; + pc proc.Word; } func (h *breakpointHook) AddHandler(eh EventHandler) { @@ -229,7 +229,7 @@ func (h *breakpointHook) String() string { return fmt.Sprintf("breakpoint at %#x", h.pc); } -func (b *Breakpoint) PC() ptrace.Word { +func (b *Breakpoint) PC() proc.Word { return b.pc; } diff --git a/usr/austin/ogle/frame.go b/usr/austin/ogle/frame.go index d36f9aa1c8..8e9dc3e106 100644 --- a/usr/austin/ogle/frame.go +++ b/usr/austin/ogle/frame.go @@ -5,9 +5,9 @@ package ogle import ( + "debug/proc"; "fmt"; "os"; - "ptrace"; "sym"; ) @@ -16,7 +16,7 @@ type Frame struct { // pc is the PC of the next instruction that will execute in // this frame. For lower frames, this is the instruction // following the CALL instruction. - pc, sp, fp ptrace.Word; + pc, sp, fp proc.Word; // The runtime.Stktop of the active stack segment stk remoteStruct; // The function this stack frame is in @@ -39,7 +39,7 @@ func newFrame(g remoteStruct) (*Frame, os.Error) { func aNewFrame(a aborter, g remoteStruct) *Frame { p := g.r.p; - var pc, sp ptrace.Word; + var pc, sp proc.Word; // Is this G alive? switch g.field(p.f.G.Status).(remoteInt).aGet(a) { @@ -79,8 +79,8 @@ func aNewFrame(a aborter, g remoteStruct) *Frame { // G is not mapped to an OS thread. Use the // scheduler's stored PC and SP. sched := g.field(p.f.G.Sched).(remoteStruct); - pc = ptrace.Word(sched.field(p.f.Gobuf.Pc).(remoteUint).aGet(a)); - sp = ptrace.Word(sched.field(p.f.Gobuf.Sp).(remoteUint).aGet(a)); + pc = proc.Word(sched.field(p.f.Gobuf.Pc).(remoteUint).aGet(a)); + sp = proc.Word(sched.field(p.f.Gobuf.Sp).(remoteUint).aGet(a)); } // Get Stktop @@ -92,7 +92,7 @@ func aNewFrame(a aborter, g remoteStruct) *Frame { // prepareFrame creates a Frame from the PC and SP within that frame, // as well as the active stack segment. This function takes care of // traversing stack breaks and unwinding closures. -func prepareFrame(a aborter, pc, sp ptrace.Word, stk remoteStruct, inner *Frame) *Frame { +func prepareFrame(a aborter, pc, sp proc.Word, stk remoteStruct, inner *Frame) *Frame { // Based on src/pkg/runtime/amd64/traceback.c:traceback p := stk.r.p; top := inner == nil; @@ -104,11 +104,11 @@ func prepareFrame(a aborter, pc, sp ptrace.Word, stk remoteStruct, inner *Frame) for i := 0; i < 100; i++ { // Traverse segmented stack breaks - if p.sys.lessstack != nil && pc == ptrace.Word(p.sys.lessstack.Value) { + if p.sys.lessstack != nil && pc == proc.Word(p.sys.lessstack.Value) { // Get stk->gobuf.pc - pc = ptrace.Word(stk.field(p.f.Stktop.Gobuf).(remoteStruct).field(p.f.Gobuf.Pc).(remoteUint).aGet(a)); + pc = proc.Word(stk.field(p.f.Stktop.Gobuf).(remoteStruct).field(p.f.Gobuf.Pc).(remoteUint).aGet(a)); // Get stk->gobuf.sp - sp = ptrace.Word(stk.field(p.f.Stktop.Gobuf).(remoteStruct).field(p.f.Gobuf.Sp).(remoteUint).aGet(a)); + sp = proc.Word(stk.field(p.f.Stktop.Gobuf).(remoteStruct).field(p.f.Gobuf.Sp).(remoteUint).aGet(a)); // Get stk->stackbase stk = stk.field(p.f.Stktop.Stackbase).(remotePtr).aGet(a).(remoteStruct); continue; @@ -116,7 +116,7 @@ func prepareFrame(a aborter, pc, sp ptrace.Word, stk remoteStruct, inner *Frame) // Get the PC of the call instruction callpc := pc; - if !top && (p.sys.goexit == nil || pc != ptrace.Word(p.sys.goexit.Value)) { + if !top && (p.sys.goexit == nil || pc != proc.Word(p.sys.goexit.Value)) { callpc--; } @@ -133,8 +133,8 @@ func prepareFrame(a aborter, pc, sp ptrace.Word, stk remoteStruct, inner *Frame) } spdelta, ok := p.ParseClosure(buf); if ok { - sp += ptrace.Word(spdelta); - pc = p.peekUintptr(a, sp - ptrace.Word(p.PtrSize())); + sp += proc.Word(spdelta); + pc = p.peekUintptr(a, sp - proc.Word(p.PtrSize())); } } if fn == nil { @@ -142,11 +142,11 @@ func prepareFrame(a aborter, pc, sp ptrace.Word, stk remoteStruct, inner *Frame) } // Compute frame pointer - var fp ptrace.Word; + var fp proc.Word; if fn.FrameSize < p.PtrSize() { - fp = sp + ptrace.Word(p.PtrSize()); + fp = sp + proc.Word(p.PtrSize()); } else { - fp = sp + ptrace.Word(fn.FrameSize); + fp = sp + proc.Word(fn.FrameSize); } // TODO(austin) To really figure out if we're in the prologue, // we need to disassemble the function and look for the call @@ -154,10 +154,10 @@ func prepareFrame(a aborter, pc, sp ptrace.Word, stk remoteStruct, inner *Frame) // // TODO(austin) What if we're in the call to morestack in the // prologue? Then top == false. - if top && pc == ptrace.Word(fn.Entry()) { + if top && pc == proc.Word(fn.Entry()) { // We're in the function prologue, before SP // has been adjusted for the frame. - fp -= ptrace.Word(fn.FrameSize - p.PtrSize()); + fp -= proc.Word(fn.FrameSize - p.PtrSize()); } return &Frame{pc, sp, fp, stk, fn, path, line, inner, nil}; @@ -185,10 +185,10 @@ func (f *Frame) aOuter(a aborter) *Frame { // around calls to go and defer. Russ says this // should get fixed in the compiler, but we account // for it for now. - sp += ptrace.Word(2 * p.PtrSize()); + sp += proc.Word(2 * p.PtrSize()); } - pc := p.peekUintptr(a, f.fp - ptrace.Word(p.PtrSize())); + pc := p.peekUintptr(a, f.fp - proc.Word(p.PtrSize())); if pc < 0x1000 { return nil; } @@ -207,8 +207,8 @@ func (f *Frame) Inner() *Frame { func (f *Frame) String() string { res := f.fn.Name; - if f.pc > ptrace.Word(f.fn.Value) { - res += fmt.Sprintf("+%#x", f.pc - ptrace.Word(f.fn.Entry())); + if f.pc > proc.Word(f.fn.Value) { + res += fmt.Sprintf("+%#x", f.pc - proc.Word(f.fn.Entry())); } return res + fmt.Sprintf(" %s:%d", f.path, f.line); } diff --git a/usr/austin/ogle/goroutine.go b/usr/austin/ogle/goroutine.go index 2dc3d7ec7b..de80c604ec 100644 --- a/usr/austin/ogle/goroutine.go +++ b/usr/austin/ogle/goroutine.go @@ -5,10 +5,10 @@ package ogle import ( + "debug/proc"; "eval"; "fmt"; "os"; - "ptrace"; ) // A Goroutine represents a goroutine in a remote process. @@ -68,7 +68,7 @@ func readylockedBP(ev Event) (EventAction, os.Error) { return EAStop, err; } sp := regs.SP(); - addr := sp + ptrace.Word(p.PtrSize()); + addr := sp + proc.Word(p.PtrSize()); arg := remotePtr{remote{addr, p}, p.runtime.G}; var gp eval.Value; err = try(func(a aborter) { gp = arg.aGet(a) }); diff --git a/usr/austin/ogle/process.go b/usr/austin/ogle/process.go index e51fb15281..ffd4eb6723 100644 --- a/usr/austin/ogle/process.go +++ b/usr/austin/ogle/process.go @@ -5,11 +5,11 @@ package ogle import ( + "debug/proc"; "eval"; "fmt"; "log"; "os"; - "ptrace"; "reflect"; "sym"; ) @@ -42,8 +42,8 @@ func (e ProcessNotStopped) String() string { // An UnknownGoroutine error is an internal error representing an // unrecognized G structure pointer. type UnknownGoroutine struct { - OSThread ptrace.Thread; - Goroutine ptrace.Word; + OSThread proc.Thread; + Goroutine proc.Word; } func (e UnknownGoroutine) String() string { @@ -62,16 +62,16 @@ func (e NoCurrentGoroutine) String() string { // A Process represents a remote attached process. type Process struct { Arch; - proc ptrace.Process; + proc proc.Process; // The symbol table of this process syms *sym.GoSymTable; // A possibly-stopped OS thread, or nil - threadCache ptrace.Thread; + threadCache proc.Thread; // Types parsed from the remote process - types map[ptrace.Word] *remoteType; + types map[proc.Word] *remoteType; // Types and values from the remote runtime package runtime runtimeValues; @@ -92,7 +92,7 @@ type Process struct { event Event; // Event hooks - breakpointHooks map[ptrace.Word] *breakpointHook; + breakpointHooks map[proc.Word] *breakpointHook; goroutineCreateHook *goroutineCreateHook; goroutineExitHook *goroutineExitHook; @@ -100,25 +100,25 @@ type Process struct { curGoroutine *Goroutine; // Goroutines by the address of their G structure - goroutines map[ptrace.Word] *Goroutine; + goroutines map[proc.Word] *Goroutine; } /* * Process creation */ -// NewProcess constructs a new remote process around a ptrace'd +// NewProcess constructs a new remote process around a traced // process, an architecture, and a symbol table. -func NewProcess(proc ptrace.Process, arch Arch, syms *sym.GoSymTable) (*Process, os.Error) { +func NewProcess(tproc proc.Process, arch Arch, syms *sym.GoSymTable) (*Process, os.Error) { p := &Process{ Arch: arch, - proc: proc, + proc: tproc, syms: syms, - types: make(map[ptrace.Word] *remoteType), - breakpointHooks: make(map[ptrace.Word] *breakpointHook), + types: make(map[proc.Word] *remoteType), + breakpointHooks: make(map[proc.Word] *breakpointHook), goroutineCreateHook: new(goroutineCreateHook), goroutineExitHook: new(goroutineExitHook), - goroutines: make(map[ptrace.Word] *Goroutine), + goroutines: make(map[proc.Word] *Goroutine), }; // Fill in remote runtime @@ -151,8 +151,8 @@ func NewProcess(proc ptrace.Process, arch Arch, syms *sym.GoSymTable) (*Process, } // Create internal breakpoints to catch new and exited goroutines - p.OnBreakpoint(ptrace.Word(p.sys.newprocreadylocked.Entry())).(*breakpointHook).addHandler(readylockedBP, true); - p.OnBreakpoint(ptrace.Word(p.sys.goexit.Entry())).(*breakpointHook).addHandler(goexitBP, true); + p.OnBreakpoint(proc.Word(p.sys.newprocreadylocked.Entry())).(*breakpointHook).addHandler(readylockedBP, true); + p.OnBreakpoint(proc.Word(p.sys.goexit.Entry())).(*breakpointHook).addHandler(goexitBP, true); // Select current frames for _, g := range p.goroutines { @@ -164,9 +164,9 @@ func NewProcess(proc ptrace.Process, arch Arch, syms *sym.GoSymTable) (*Process, return p, nil; } -// NewProcessElf constructs a new remote process around a ptrace'd +// NewProcessElf constructs a new remote process around a traced // process and the process' ELF object. -func NewProcessElf(proc ptrace.Process, elf *sym.Elf) (*Process, os.Error) { +func NewProcessElf(tproc proc.Process, elf *sym.Elf) (*Process, os.Error) { syms, err := sym.ElfGoSyms(elf); if err != nil { return nil, err; @@ -181,7 +181,7 @@ func NewProcessElf(proc ptrace.Process, elf *sym.Elf) (*Process, os.Error) { default: return nil, UnknownArchitecture(elf.Machine); } - return NewProcess(proc, arch, syms); + return NewProcess(tproc, arch, syms); } // bootstrap constructs the runtime structure of a remote process. @@ -238,10 +238,10 @@ func (p *Process) bootstrap() { p.sys.deferproc = globalFn("sys·deferproc"); p.sys.newprocreadylocked = globalFn("newprocreadylocked"); if allg := p.syms.SymFromName("allg"); allg != nil { - p.sys.allg = remotePtr{remote{ptrace.Word(allg.Common().Value), p}, p.runtime.G}; + p.sys.allg = remotePtr{remote{proc.Word(allg.Common().Value), p}, p.runtime.G}; } if g0 := p.syms.SymFromName("g0"); g0 != nil { - p.sys.g0 = p.runtime.G.mk(remote{ptrace.Word(g0.Common().Value), p}).(remoteStruct); + p.sys.g0 = p.runtime.G.mk(remote{proc.Word(g0.Common().Value), p}).(remoteStruct); } } @@ -261,7 +261,7 @@ func (p *Process) selectSomeGoroutine() { * Process memory */ -func (p *Process) someStoppedOSThread() ptrace.Thread { +func (p *Process) someStoppedOSThread() proc.Thread { if p.threadCache != nil { if _, err := p.threadCache.Stopped(); err == nil { return p.threadCache; @@ -277,7 +277,7 @@ func (p *Process) someStoppedOSThread() ptrace.Thread { return nil; } -func (p *Process) Peek(addr ptrace.Word, out []byte) (int, os.Error) { +func (p *Process) Peek(addr proc.Word, out []byte) (int, os.Error) { thr := p.someStoppedOSThread(); if thr == nil { return 0, ProcessNotStopped{}; @@ -285,7 +285,7 @@ func (p *Process) Peek(addr ptrace.Word, out []byte) (int, os.Error) { return thr.Peek(addr, out); } -func (p *Process) Poke(addr ptrace.Word, b []byte) (int, os.Error) { +func (p *Process) Poke(addr proc.Word, b []byte) (int, os.Error) { thr := p.someStoppedOSThread(); if thr == nil { return 0, ProcessNotStopped{}; @@ -293,8 +293,8 @@ func (p *Process) Poke(addr ptrace.Word, b []byte) (int, os.Error) { return thr.Poke(addr, b); } -func (p *Process) peekUintptr(a aborter, addr ptrace.Word) ptrace.Word { - return ptrace.Word(mkUintptr(remote{addr, p}).(remoteUint).aGet(a)); +func (p *Process) peekUintptr(a aborter, addr proc.Word) proc.Word { + return proc.Word(mkUintptr(remote{addr, p}).(remoteUint).aGet(a)); } /* @@ -303,7 +303,7 @@ func (p *Process) peekUintptr(a aborter, addr ptrace.Word) ptrace.Word { // OnBreakpoint returns the hook that is run when the program reaches // the given program counter. -func (p *Process) OnBreakpoint(pc ptrace.Word) EventHook { +func (p *Process) OnBreakpoint(pc proc.Word) EventHook { if bp, ok := p.breakpointHooks[pc]; ok { return bp; } @@ -322,7 +322,7 @@ func (p *Process) OnGoroutineExit() EventHook { } // osThreadToGoroutine looks up the goroutine running on an OS thread. -func (p *Process) osThreadToGoroutine(t ptrace.Thread) (*Goroutine, os.Error) { +func (p *Process) osThreadToGoroutine(t proc.Thread) (*Goroutine, os.Error) { regs, err := t.Regs(); if err != nil { return nil, err; @@ -343,9 +343,9 @@ func (p *Process) causesToEvents() ([]Event, os.Error) { for _, t := range p.proc.Threads() { if c, err := t.Stopped(); err == nil { switch c := c.(type) { - case ptrace.Breakpoint: + case proc.Breakpoint: nev++; - case ptrace.Signal: + case proc.Signal: // TODO(austin) //nev++; } @@ -358,14 +358,14 @@ func (p *Process) causesToEvents() ([]Event, os.Error) { for _, t := range p.proc.Threads() { if c, err := t.Stopped(); err == nil { switch c := c.(type) { - case ptrace.Breakpoint: + case proc.Breakpoint: gt, err := p.osThreadToGoroutine(t); if err != nil { return nil, err; } - events[i] = &Breakpoint{commonEvent{p, gt}, t, ptrace.Word(c)}; + events[i] = &Breakpoint{commonEvent{p, gt}, t, proc.Word(c)}; i++; - case ptrace.Signal: + case proc.Signal: // TODO(austin) } } diff --git a/usr/austin/ogle/rruntime.go b/usr/austin/ogle/rruntime.go index 758f1c7084..03d1c79803 100644 --- a/usr/austin/ogle/rruntime.go +++ b/usr/austin/ogle/rruntime.go @@ -5,8 +5,8 @@ package ogle import ( + "debug/proc"; "eval"; - "ptrace"; "reflect"; ) @@ -227,7 +227,7 @@ type runtimeValues struct { PFloat32Type, PFloat64Type, PFloatType, PArrayType, PStringType, PStructType, PPtrType, PFuncType, PInterfaceType, PSliceType, PMapType, PChanType, - PDotDotDotType, PUnsafePointerType ptrace.Word; + PDotDotDotType, PUnsafePointerType proc.Word; // G status values runtimeGStatus; } diff --git a/usr/austin/ogle/rtype.go b/usr/austin/ogle/rtype.go index a71a70a4af..ee7b7fe759 100644 --- a/usr/austin/ogle/rtype.go +++ b/usr/austin/ogle/rtype.go @@ -5,10 +5,10 @@ package ogle import ( + "debug/proc"; "eval"; "fmt"; "log"; - "ptrace"; ) const debugParseRemoteType = false @@ -156,7 +156,7 @@ func parseRemoteType(a aborter, rs remoteStruct) *remoteType { } // Get Type header - itype := ptrace.Word(rs.field(p.f.Type.Typ).(remoteUint).aGet(a)); + itype := proc.Word(rs.field(p.f.Type.Typ).(remoteUint).aGet(a)); typ := rs.field(p.f.Type.Ptr).(remotePtr).aGet(a).(remoteStruct); // Is this a named type? diff --git a/usr/austin/ogle/rvalue.go b/usr/austin/ogle/rvalue.go index b22f531acb..47a54a9343 100644 --- a/usr/austin/ogle/rvalue.go +++ b/usr/austin/ogle/rvalue.go @@ -5,9 +5,9 @@ package ogle import ( + "debug/proc"; "eval"; "fmt"; - "ptrace"; ) // A RemoteMismatchError occurs when an operation that requires two @@ -38,7 +38,7 @@ type remoteValue interface { // remote represents an address in a remote process. type remote struct { - base ptrace.Word; + base proc.Word; p *Process; } @@ -71,14 +71,14 @@ func (v remote) Get(a aborter, size int) uint64 { func (v remote) Set(a aborter, size int, x uint64) { var arr [8]byte; buf := arr[0:size]; - v.p.FromWord(ptrace.Word(x), buf); + v.p.FromWord(proc.Word(x), buf); _, err := v.p.Poke(v.base, buf); if err != nil { a.Abort(err); } } -func (v remote) plus(x ptrace.Word) remote { +func (v remote) plus(x proc.Word) remote { return remote{v.base + x, v.p}; } @@ -340,9 +340,9 @@ func (v remoteString) Get(t *eval.Thread) string { func (v remoteString) aGet(a aborter) string { rs := v.r.p.runtime.String.mk(v.r).(remoteStruct); - str := ptrace.Word(rs.field(v.r.p.f.String.Str).(remoteUint).aGet(a)); + str := proc.Word(rs.field(v.r.p.f.String.Str).(remoteUint).aGet(a)); len := rs.field(v.r.p.f.String.Len).(remoteInt).aGet(a); - + bytes := make([]uint8, len); _, err := v.r.p.Peek(str, bytes); if err != nil { @@ -404,11 +404,11 @@ func (v remoteArray) Elem(t *eval.Thread, i int64) eval.Value { } func (v remoteArray) elem(i int64) eval.Value { - return v.elemType.mk(v.r.plus(ptrace.Word(int64(v.elemType.size) * i))); + return v.elemType.mk(v.r.plus(proc.Word(int64(v.elemType.size) * i))); } func (v remoteArray) Sub(i int64, len int64) eval.ArrayValue { - return remoteArray{v.r.plus(ptrace.Word(int64(v.elemType.size) * i)), len, v.elemType}; + return remoteArray{v.r.plus(proc.Word(int64(v.elemType.size) * i)), len, v.elemType}; } /* @@ -455,7 +455,7 @@ func (v remoteStruct) Field(t *eval.Thread, i int) eval.Value { func (v remoteStruct) field(i int) eval.Value { f := &v.layout[i]; - return f.fieldType.mk(v.r.plus(ptrace.Word(f.offset))); + return f.fieldType.mk(v.r.plus(proc.Word(f.offset))); } func (v remoteStruct) addr() remote { @@ -494,7 +494,7 @@ func (v remotePtr) Get(t *eval.Thread) eval.Value { } func (v remotePtr) aGet(a aborter) eval.Value { - addr := ptrace.Word(v.r.Get(a, v.r.p.PtrSize())); + addr := proc.Word(v.r.Get(a, v.r.p.PtrSize())); if addr == 0 { return nil; } @@ -550,7 +550,7 @@ func (v remoteSlice) Get(t *eval.Thread) eval.Slice { func (v remoteSlice) aGet(a aborter) eval.Slice { rs := v.r.p.runtime.Slice.mk(v.r).(remoteStruct); - base := ptrace.Word(rs.field(v.r.p.f.Slice.Array).(remoteUint).aGet(a)); + base := proc.Word(rs.field(v.r.p.f.Slice.Array).(remoteUint).aGet(a)); nel := rs.field(v.r.p.f.Slice.Len).(remoteInt).aGet(a); cap := rs.field(v.r.p.f.Slice.Cap).(remoteInt).aGet(a); if base == 0 { diff --git a/usr/austin/ogle/vars.go b/usr/austin/ogle/vars.go index eb96b60ce8..6c1bd5f6f9 100644 --- a/usr/austin/ogle/vars.go +++ b/usr/austin/ogle/vars.go @@ -5,10 +5,10 @@ package ogle import ( + "debug/proc"; "eval"; "log"; "os"; - "ptrace"; "sym"; ) @@ -160,7 +160,7 @@ func (p *Process) populateWorld(w *eval.World) os.Error { if rt == nil { continue; } - pkg[name] = def{rt.Type, rt.mk(remote{ptrace.Word(sc.Value), p})}; + pkg[name] = def{rt.Type, rt.mk(remote{proc.Word(sc.Value), p})}; case 'T', 't', 'L', 'l': // Function @@ -207,7 +207,7 @@ func (p *Process) typeOfSym(s *sym.CommonSym) (*remoteType, os.Error) { if s.GoType == 0 { return nil, nil; } - addr := ptrace.Word(s.GoType); + addr := proc.Word(s.GoType); var rt *remoteType; err := try(func(a aborter) { rt = parseRemoteType(a, p.runtime.Type.mk(remote{addr, p}).(remoteStruct)); diff --git a/usr/austin/ptrace/Makefile b/usr/austin/ptrace/Makefile deleted file mode 100644 index 2158abc93c..0000000000 --- a/usr/austin/ptrace/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 $(GOROOT)/src/Make.$(GOARCH) - -TARG=ptrace -GOFILES=\ - process.go\ - ptrace_linux.go\ - regs_$(GOOS)_$(GOARCH).go\ - -include $(GOROOT)/src/Make.pkg diff --git a/usr/austin/ptrace/process.go b/usr/austin/ptrace/process.go deleted file mode 100644 index d88bcf7e97..0000000000 --- a/usr/austin/ptrace/process.go +++ /dev/null @@ -1,232 +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 ptrace 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 ptrace - -import ( - "os"; - "strconv"; -) - -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/usr/austin/ptrace/ptrace-nptl.txt b/usr/austin/ptrace/ptrace-nptl.txt deleted file mode 100644 index 62cbf77003..0000000000 --- a/usr/austin/ptrace/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/usr/austin/ptrace/ptrace_linux.go b/usr/austin/ptrace/ptrace_linux.go deleted file mode 100644 index 83faa667fc..0000000000 --- a/usr/austin/ptrace/ptrace_linux.go +++ /dev/null @@ -1,1319 +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 ptrace - -import ( - "container/vector"; - "fmt"; - "io"; - "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. -type process struct { - pid int; - threads map[int] *thread; - breakpoints map[uintptr] *breakpoint; - debugEvents chan *debugEvent; - debugReqs chan *debugReq; - stopReq chan os.Error; - transitionHandlers *vector.Vector; -} - -// 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); -} - -/* - * 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 ...) { - 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.Rip); - } - } - fmt.Fprint(os.Stderr, ": "); - fmt.Fprintf(os.Stderr, format, args); - fmt.Fprint(os.Stderr, "\n"); -} - -func (t *thread) warn(format string, args ...) { - 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 ...) { - 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("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. - } - 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(new threadState) { - old := t.state; - t.state = new; - t.logTrace("state %v -> %v", old, new); - - if !old.isRunning() && (new.isRunning() || new.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 = vector.New(0); - for _, h := range handlers.Data() { - h := h.(*transitionHandler); - h.handle(t, old, new); - } -} - -// 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.Rip)-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.Rip); - - regs.Rip = 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. -// -// TODO(austin) When an unrecoverable error occurs, abort the monitor -// and record this error so all future calls to do will return it -// immediately. -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 { - select { - case event := <-p.debugEvents: - err = event.process(); - if err != nil { - break; - } - - case req := <-p.debugReqs: - req.res <- req.f(); - - case err = <-p.stopReq: - break; - } - - if len(p.threads) == 0 { - if hadThreads { - p.logTrace("no more threads; monitor exiting"); - // TODO(austin) Use a real error do - // future operations will fail - err = nil; - break; - } - } else { - hadThreads = true; - } - } - - // Abort waiting handlers - for _, h := range p.transitionHandlers.Data() { - h := h.(*transitionHandler); - h.onErr(err); - } - - // TODO(austin) How do I stop the wait threads? - if err != nil { - panic(err.String()); - } -} - -// 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 { - // TODO(austin) If monitor is stopped, return error. - 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) { - _ = p.stopReq <- err; - // TODO(austin) Wait until monitor has exited? -} - -/* - * 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.Rip)]; 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 := io.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), - debugEvents: make(chan *debugEvent), - debugReqs: make(chan *debugReq), - stopReq: make(chan os.Error), - transitionHandlers: vector.New(0) - }; - - 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/usr/austin/ptrace/regs_linux_amd64.go b/usr/austin/ptrace/regs_linux_amd64.go deleted file mode 100644 index 3b2a058d17..0000000000 --- a/usr/austin/ptrace/regs_linux_amd64.go +++ /dev/null @@ -1,149 +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 ptrace - -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; -} - -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; -}