Commit ffe83e58 authored by Austin Clements's avatar Austin Clements

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
parent 495b3db8
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
include $(GOROOT)/src/Make.$(GOARCH) include $(GOROOT)/src/Make.$(GOARCH)
TARG=ptrace TARG=debug/proc
GOFILES=\ GOFILES=\
proc.go\ proc.go\
proc_$(GOOS).go\ proc_$(GOOS).go\
......
...@@ -5,17 +5,17 @@ ...@@ -5,17 +5,17 @@
package ogle package ogle
import ( import (
"debug/proc";
"math"; "math";
"ptrace";
) )
type Arch interface { type Arch interface {
// ToWord converts an array of up to 8 bytes in memory order // ToWord converts an array of up to 8 bytes in memory order
// to a word. // 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 // FromWord converts a word to an array of up to 8 bytes in
// memory order. // 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 // ToFloat32 converts a word to a float. The order of this
// word will be the order returned by ToWord on the memory // word will be the order returned by ToWord on the memory
// representation of a float, and thus may require reversing. // representation of a float, and thus may require reversing.
...@@ -40,7 +40,7 @@ type Arch interface { ...@@ -40,7 +40,7 @@ type Arch interface {
Align(offset, width int) int; Align(offset, width int) int;
// G returns the current G pointer. // 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 // ClosureSize returns the number of bytes expected by
// ParseClosure. // ParseClosure.
...@@ -53,15 +53,15 @@ type Arch interface { ...@@ -53,15 +53,15 @@ type Arch interface {
type ArchLSB struct {} type ArchLSB struct {}
func (ArchLSB) ToWord(data []byte) ptrace.Word { func (ArchLSB) ToWord(data []byte) proc.Word {
var v ptrace.Word; var v proc.Word;
for i, b := range data { for i, b := range data {
v |= ptrace.Word(b) << (uint(i)*8); v |= proc.Word(b) << (uint(i)*8);
} }
return v; return v;
} }
func (ArchLSB) FromWord(v ptrace.Word, out []byte) { func (ArchLSB) FromWord(v proc.Word, out []byte) {
for i := range out { for i := range out {
out[i] = byte(v); out[i] = byte(v);
v >>= 8; v >>= 8;
...@@ -110,7 +110,7 @@ func (a *amd64) FloatSize() int { ...@@ -110,7 +110,7 @@ func (a *amd64) FloatSize() int {
return 4; 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 // See src/pkg/runtime/mkasmh
if a.gReg == -1 { if a.gReg == -1 {
ns := regs.Names(); ns := regs.Names();
......
...@@ -6,12 +6,12 @@ package ogle ...@@ -6,12 +6,12 @@ package ogle
import ( import (
"bufio"; "bufio";
"debug/proc";
"eval"; "eval";
"fmt"; "fmt";
"go/scanner"; "go/scanner";
"go/token"; "go/token";
"os"; "os";
"ptrace";
"strconv"; "strconv";
"strings"; "strings";
"sym"; "sym";
...@@ -139,7 +139,7 @@ func cmdLoad(args []byte) os.Error { ...@@ -139,7 +139,7 @@ func cmdLoad(args []byte) os.Error {
// Parse argument and start or attach to process // Parse argument and start or attach to process
var fname string; var fname string;
var proc ptrace.Process; var tproc proc.Process;
if len(path) >= 4 && path[0:4] == "pid:" { if len(path) >= 4 && path[0:4] == "pid:" {
pid, err := strconv.Atoi(path[4:len(path)]); pid, err := strconv.Atoi(path[4:len(path)]);
if err != nil { if err != nil {
...@@ -149,7 +149,7 @@ func cmdLoad(args []byte) os.Error { ...@@ -149,7 +149,7 @@ func cmdLoad(args []byte) os.Error {
if err != nil { if err != nil {
return err; return err;
} }
proc, err = ptrace.Attach(pid); tproc, err = proc.Attach(pid);
if err != nil { if err != nil {
return err; return err;
} }
...@@ -161,30 +161,30 @@ func cmdLoad(args []byte) os.Error { ...@@ -161,30 +161,30 @@ func cmdLoad(args []byte) os.Error {
} else { } else {
fname = parts[0]; 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 { if err != nil {
return err; return err;
} }
println("Started", path); 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. // before detaching.
} }
// Get symbols // Get symbols
f, err := os.Open(fname, os.O_RDONLY, 0); f, err := os.Open(fname, os.O_RDONLY, 0);
if err != nil { if err != nil {
proc.Detach(); tproc.Detach();
return err; return err;
} }
defer f.Close(); defer f.Close();
elf, err := sym.NewElf(f); elf, err := sym.NewElf(f);
if err != nil { if err != nil {
proc.Detach(); tproc.Detach();
return err; return err;
} }
curProc, err = NewProcessElf(proc, elf); curProc, err = NewProcessElf(tproc, elf);
if err != nil { if err != nil {
proc.Detach(); tproc.Detach();
return err; return err;
} }
...@@ -194,7 +194,7 @@ func cmdLoad(args []byte) os.Error { ...@@ -194,7 +194,7 @@ func cmdLoad(args []byte) os.Error {
err = curProc.populateWorld(world); err = curProc.populateWorld(world);
if err != nil { if err != nil {
proc.Detach(); tproc.Detach();
return err; return err;
} }
...@@ -374,5 +374,5 @@ func fnBpSet(t *eval.Thread, args []eval.Value, res []eval.Value) { ...@@ -374,5 +374,5 @@ func fnBpSet(t *eval.Thread, args []eval.Value, res []eval.Value) {
if !ok { if !ok {
t.Abort(UsageError("symbol " + name + " is not a function")); 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);
} }
...@@ -5,9 +5,9 @@ ...@@ -5,9 +5,9 @@
package ogle package ogle
import ( import (
"debug/proc";
"fmt"; "fmt";
"os"; "os";
"ptrace";
) )
/* /*
...@@ -183,7 +183,7 @@ func EventStop(ev Event) (EventAction, os.Error) { ...@@ -183,7 +183,7 @@ func EventStop(ev Event) (EventAction, os.Error) {
type breakpointHook struct { type breakpointHook struct {
commonHook; commonHook;
p *Process; p *Process;
pc ptrace.Word; pc proc.Word;
} }
// A Breakpoint event occurs when a process reaches a particular // A Breakpoint event occurs when a process reaches a particular
...@@ -191,8 +191,8 @@ type breakpointHook struct { ...@@ -191,8 +191,8 @@ type breakpointHook struct {
// will be the goroutine that reached the program counter. // will be the goroutine that reached the program counter.
type Breakpoint struct { type Breakpoint struct {
commonEvent; commonEvent;
osThread ptrace.Thread; osThread proc.Thread;
pc ptrace.Word; pc proc.Word;
} }
func (h *breakpointHook) AddHandler(eh EventHandler) { func (h *breakpointHook) AddHandler(eh EventHandler) {
...@@ -229,7 +229,7 @@ func (h *breakpointHook) String() string { ...@@ -229,7 +229,7 @@ func (h *breakpointHook) String() string {
return fmt.Sprintf("breakpoint at %#x", h.pc); return fmt.Sprintf("breakpoint at %#x", h.pc);
} }
func (b *Breakpoint) PC() ptrace.Word { func (b *Breakpoint) PC() proc.Word {
return b.pc; return b.pc;
} }
......
...@@ -5,9 +5,9 @@ ...@@ -5,9 +5,9 @@
package ogle package ogle
import ( import (
"debug/proc";
"fmt"; "fmt";
"os"; "os";
"ptrace";
"sym"; "sym";
) )
...@@ -16,7 +16,7 @@ type Frame struct { ...@@ -16,7 +16,7 @@ type Frame struct {
// pc is the PC of the next instruction that will execute in // pc is the PC of the next instruction that will execute in
// this frame. For lower frames, this is the instruction // this frame. For lower frames, this is the instruction
// following the CALL instruction. // following the CALL instruction.
pc, sp, fp ptrace.Word; pc, sp, fp proc.Word;
// The runtime.Stktop of the active stack segment // The runtime.Stktop of the active stack segment
stk remoteStruct; stk remoteStruct;
// The function this stack frame is in // The function this stack frame is in
...@@ -39,7 +39,7 @@ func newFrame(g remoteStruct) (*Frame, os.Error) { ...@@ -39,7 +39,7 @@ func newFrame(g remoteStruct) (*Frame, os.Error) {
func aNewFrame(a aborter, g remoteStruct) *Frame { func aNewFrame(a aborter, g remoteStruct) *Frame {
p := g.r.p; p := g.r.p;
var pc, sp ptrace.Word; var pc, sp proc.Word;
// Is this G alive? // Is this G alive?
switch g.field(p.f.G.Status).(remoteInt).aGet(a) { switch g.field(p.f.G.Status).(remoteInt).aGet(a) {
...@@ -79,8 +79,8 @@ func aNewFrame(a aborter, g remoteStruct) *Frame { ...@@ -79,8 +79,8 @@ func aNewFrame(a aborter, g remoteStruct) *Frame {
// G is not mapped to an OS thread. Use the // G is not mapped to an OS thread. Use the
// scheduler's stored PC and SP. // scheduler's stored PC and SP.
sched := g.field(p.f.G.Sched).(remoteStruct); sched := g.field(p.f.G.Sched).(remoteStruct);
pc = ptrace.Word(sched.field(p.f.Gobuf.Pc).(remoteUint).aGet(a)); pc = proc.Word(sched.field(p.f.Gobuf.Pc).(remoteUint).aGet(a));
sp = ptrace.Word(sched.field(p.f.Gobuf.Sp).(remoteUint).aGet(a)); sp = proc.Word(sched.field(p.f.Gobuf.Sp).(remoteUint).aGet(a));
} }
// Get Stktop // Get Stktop
...@@ -92,7 +92,7 @@ func aNewFrame(a aborter, g remoteStruct) *Frame { ...@@ -92,7 +92,7 @@ func aNewFrame(a aborter, g remoteStruct) *Frame {
// prepareFrame creates a Frame from the PC and SP within that 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 // as well as the active stack segment. This function takes care of
// traversing stack breaks and unwinding closures. // 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 // Based on src/pkg/runtime/amd64/traceback.c:traceback
p := stk.r.p; p := stk.r.p;
top := inner == nil; top := inner == nil;
...@@ -104,11 +104,11 @@ func prepareFrame(a aborter, pc, sp ptrace.Word, stk remoteStruct, inner *Frame) ...@@ -104,11 +104,11 @@ func prepareFrame(a aborter, pc, sp ptrace.Word, stk remoteStruct, inner *Frame)
for i := 0; i < 100; i++ { for i := 0; i < 100; i++ {
// Traverse segmented stack breaks // 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 // 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 // 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 // Get stk->stackbase
stk = stk.field(p.f.Stktop.Stackbase).(remotePtr).aGet(a).(remoteStruct); stk = stk.field(p.f.Stktop.Stackbase).(remotePtr).aGet(a).(remoteStruct);
continue; continue;
...@@ -116,7 +116,7 @@ func prepareFrame(a aborter, pc, sp ptrace.Word, stk remoteStruct, inner *Frame) ...@@ -116,7 +116,7 @@ func prepareFrame(a aborter, pc, sp ptrace.Word, stk remoteStruct, inner *Frame)
// Get the PC of the call instruction // Get the PC of the call instruction
callpc := pc; 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--; callpc--;
} }
...@@ -133,8 +133,8 @@ func prepareFrame(a aborter, pc, sp ptrace.Word, stk remoteStruct, inner *Frame) ...@@ -133,8 +133,8 @@ func prepareFrame(a aborter, pc, sp ptrace.Word, stk remoteStruct, inner *Frame)
} }
spdelta, ok := p.ParseClosure(buf); spdelta, ok := p.ParseClosure(buf);
if ok { if ok {
sp += ptrace.Word(spdelta); sp += proc.Word(spdelta);
pc = p.peekUintptr(a, sp - ptrace.Word(p.PtrSize())); pc = p.peekUintptr(a, sp - proc.Word(p.PtrSize()));
} }
} }
if fn == nil { if fn == nil {
...@@ -142,11 +142,11 @@ func prepareFrame(a aborter, pc, sp ptrace.Word, stk remoteStruct, inner *Frame) ...@@ -142,11 +142,11 @@ func prepareFrame(a aborter, pc, sp ptrace.Word, stk remoteStruct, inner *Frame)
} }
// Compute frame pointer // Compute frame pointer
var fp ptrace.Word; var fp proc.Word;
if fn.FrameSize < p.PtrSize() { if fn.FrameSize < p.PtrSize() {
fp = sp + ptrace.Word(p.PtrSize()); fp = sp + proc.Word(p.PtrSize());
} else { } 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, // TODO(austin) To really figure out if we're in the prologue,
// we need to disassemble the function and look for the call // 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) ...@@ -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 // TODO(austin) What if we're in the call to morestack in the
// prologue? Then top == false. // 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 // We're in the function prologue, before SP
// has been adjusted for the frame. // 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}; return &Frame{pc, sp, fp, stk, fn, path, line, inner, nil};
...@@ -185,10 +185,10 @@ func (f *Frame) aOuter(a aborter) *Frame { ...@@ -185,10 +185,10 @@ func (f *Frame) aOuter(a aborter) *Frame {
// around calls to go and defer. Russ says this // around calls to go and defer. Russ says this
// should get fixed in the compiler, but we account // should get fixed in the compiler, but we account
// for it for now. // 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 { if pc < 0x1000 {
return nil; return nil;
} }
...@@ -207,8 +207,8 @@ func (f *Frame) Inner() *Frame { ...@@ -207,8 +207,8 @@ func (f *Frame) Inner() *Frame {
func (f *Frame) String() string { func (f *Frame) String() string {
res := f.fn.Name; res := f.fn.Name;
if f.pc > ptrace.Word(f.fn.Value) { if f.pc > proc.Word(f.fn.Value) {
res += fmt.Sprintf("+%#x", f.pc - ptrace.Word(f.fn.Entry())); res += fmt.Sprintf("+%#x", f.pc - proc.Word(f.fn.Entry()));
} }
return res + fmt.Sprintf(" %s:%d", f.path, f.line); return res + fmt.Sprintf(" %s:%d", f.path, f.line);
} }
...@@ -5,10 +5,10 @@ ...@@ -5,10 +5,10 @@
package ogle package ogle
import ( import (
"debug/proc";
"eval"; "eval";
"fmt"; "fmt";
"os"; "os";
"ptrace";
) )
// A Goroutine represents a goroutine in a remote process. // A Goroutine represents a goroutine in a remote process.
...@@ -68,7 +68,7 @@ func readylockedBP(ev Event) (EventAction, os.Error) { ...@@ -68,7 +68,7 @@ func readylockedBP(ev Event) (EventAction, os.Error) {
return EAStop, err; return EAStop, err;
} }
sp := regs.SP(); sp := regs.SP();
addr := sp + ptrace.Word(p.PtrSize()); addr := sp + proc.Word(p.PtrSize());
arg := remotePtr{remote{addr, p}, p.runtime.G}; arg := remotePtr{remote{addr, p}, p.runtime.G};
var gp eval.Value; var gp eval.Value;
err = try(func(a aborter) { gp = arg.aGet(a) }); err = try(func(a aborter) { gp = arg.aGet(a) });
......
...@@ -5,11 +5,11 @@ ...@@ -5,11 +5,11 @@
package ogle package ogle
import ( import (
"debug/proc";
"eval"; "eval";
"fmt"; "fmt";
"log"; "log";
"os"; "os";
"ptrace";
"reflect"; "reflect";
"sym"; "sym";
) )
...@@ -42,8 +42,8 @@ func (e ProcessNotStopped) String() string { ...@@ -42,8 +42,8 @@ func (e ProcessNotStopped) String() string {
// An UnknownGoroutine error is an internal error representing an // An UnknownGoroutine error is an internal error representing an
// unrecognized G structure pointer. // unrecognized G structure pointer.
type UnknownGoroutine struct { type UnknownGoroutine struct {
OSThread ptrace.Thread; OSThread proc.Thread;
Goroutine ptrace.Word; Goroutine proc.Word;
} }
func (e UnknownGoroutine) String() string { func (e UnknownGoroutine) String() string {
...@@ -62,16 +62,16 @@ func (e NoCurrentGoroutine) String() string { ...@@ -62,16 +62,16 @@ func (e NoCurrentGoroutine) String() string {
// A Process represents a remote attached process. // A Process represents a remote attached process.
type Process struct { type Process struct {
Arch; Arch;
proc ptrace.Process; proc proc.Process;
// The symbol table of this process // The symbol table of this process
syms *sym.GoSymTable; syms *sym.GoSymTable;
// A possibly-stopped OS thread, or nil // A possibly-stopped OS thread, or nil
threadCache ptrace.Thread; threadCache proc.Thread;
// Types parsed from the remote process // Types parsed from the remote process
types map[ptrace.Word] *remoteType; types map[proc.Word] *remoteType;
// Types and values from the remote runtime package // Types and values from the remote runtime package
runtime runtimeValues; runtime runtimeValues;
...@@ -92,7 +92,7 @@ type Process struct { ...@@ -92,7 +92,7 @@ type Process struct {
event Event; event Event;
// Event hooks // Event hooks
breakpointHooks map[ptrace.Word] *breakpointHook; breakpointHooks map[proc.Word] *breakpointHook;
goroutineCreateHook *goroutineCreateHook; goroutineCreateHook *goroutineCreateHook;
goroutineExitHook *goroutineExitHook; goroutineExitHook *goroutineExitHook;
...@@ -100,25 +100,25 @@ type Process struct { ...@@ -100,25 +100,25 @@ type Process struct {
curGoroutine *Goroutine; curGoroutine *Goroutine;
// Goroutines by the address of their G structure // Goroutines by the address of their G structure
goroutines map[ptrace.Word] *Goroutine; goroutines map[proc.Word] *Goroutine;
} }
/* /*
* Process creation * 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. // 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{ p := &Process{
Arch: arch, Arch: arch,
proc: proc, proc: tproc,
syms: syms, syms: syms,
types: make(map[ptrace.Word] *remoteType), types: make(map[proc.Word] *remoteType),
breakpointHooks: make(map[ptrace.Word] *breakpointHook), breakpointHooks: make(map[proc.Word] *breakpointHook),
goroutineCreateHook: new(goroutineCreateHook), goroutineCreateHook: new(goroutineCreateHook),
goroutineExitHook: new(goroutineExitHook), goroutineExitHook: new(goroutineExitHook),
goroutines: make(map[ptrace.Word] *Goroutine), goroutines: make(map[proc.Word] *Goroutine),
}; };
// Fill in remote runtime // Fill in remote runtime
...@@ -151,8 +151,8 @@ func NewProcess(proc ptrace.Process, arch Arch, syms *sym.GoSymTable) (*Process, ...@@ -151,8 +151,8 @@ func NewProcess(proc ptrace.Process, arch Arch, syms *sym.GoSymTable) (*Process,
} }
// Create internal breakpoints to catch new and exited goroutines // Create internal breakpoints to catch new and exited goroutines
p.OnBreakpoint(ptrace.Word(p.sys.newprocreadylocked.Entry())).(*breakpointHook).addHandler(readylockedBP, true); p.OnBreakpoint(proc.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.goexit.Entry())).(*breakpointHook).addHandler(goexitBP, true);
// Select current frames // Select current frames
for _, g := range p.goroutines { for _, g := range p.goroutines {
...@@ -164,9 +164,9 @@ func NewProcess(proc ptrace.Process, arch Arch, syms *sym.GoSymTable) (*Process, ...@@ -164,9 +164,9 @@ func NewProcess(proc ptrace.Process, arch Arch, syms *sym.GoSymTable) (*Process,
return p, nil; 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. // 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); syms, err := sym.ElfGoSyms(elf);
if err != nil { if err != nil {
return nil, err; return nil, err;
...@@ -181,7 +181,7 @@ func NewProcessElf(proc ptrace.Process, elf *sym.Elf) (*Process, os.Error) { ...@@ -181,7 +181,7 @@ func NewProcessElf(proc ptrace.Process, elf *sym.Elf) (*Process, os.Error) {
default: default:
return nil, UnknownArchitecture(elf.Machine); return nil, UnknownArchitecture(elf.Machine);
} }
return NewProcess(proc, arch, syms); return NewProcess(tproc, arch, syms);
} }
// bootstrap constructs the runtime structure of a remote process. // bootstrap constructs the runtime structure of a remote process.
...@@ -238,10 +238,10 @@ func (p *Process) bootstrap() { ...@@ -238,10 +238,10 @@ func (p *Process) bootstrap() {
p.sys.deferproc = globalFn("sys·deferproc"); p.sys.deferproc = globalFn("sys·deferproc");
p.sys.newprocreadylocked = globalFn("newprocreadylocked"); p.sys.newprocreadylocked = globalFn("newprocreadylocked");
if allg := p.syms.SymFromName("allg"); allg != nil { 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 { 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() { ...@@ -261,7 +261,7 @@ func (p *Process) selectSomeGoroutine() {
* Process memory * Process memory
*/ */
func (p *Process) someStoppedOSThread() ptrace.Thread { func (p *Process) someStoppedOSThread() proc.Thread {
if p.threadCache != nil { if p.threadCache != nil {
if _, err := p.threadCache.Stopped(); err == nil { if _, err := p.threadCache.Stopped(); err == nil {
return p.threadCache; return p.threadCache;
...@@ -277,7 +277,7 @@ func (p *Process) someStoppedOSThread() ptrace.Thread { ...@@ -277,7 +277,7 @@ func (p *Process) someStoppedOSThread() ptrace.Thread {
return nil; 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(); thr := p.someStoppedOSThread();
if thr == nil { if thr == nil {
return 0, ProcessNotStopped{}; return 0, ProcessNotStopped{};
...@@ -285,7 +285,7 @@ func (p *Process) Peek(addr ptrace.Word, out []byte) (int, os.Error) { ...@@ -285,7 +285,7 @@ func (p *Process) Peek(addr ptrace.Word, out []byte) (int, os.Error) {
return thr.Peek(addr, out); 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(); thr := p.someStoppedOSThread();
if thr == nil { if thr == nil {
return 0, ProcessNotStopped{}; return 0, ProcessNotStopped{};
...@@ -293,8 +293,8 @@ func (p *Process) Poke(addr ptrace.Word, b []byte) (int, os.Error) { ...@@ -293,8 +293,8 @@ func (p *Process) Poke(addr ptrace.Word, b []byte) (int, os.Error) {
return thr.Poke(addr, b); return thr.Poke(addr, b);
} }
func (p *Process) peekUintptr(a aborter, addr ptrace.Word) ptrace.Word { func (p *Process) peekUintptr(a aborter, addr proc.Word) proc.Word {
return ptrace.Word(mkUintptr(remote{addr, p}).(remoteUint).aGet(a)); 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 { ...@@ -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 // OnBreakpoint returns the hook that is run when the program reaches
// the given program counter. // 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 { if bp, ok := p.breakpointHooks[pc]; ok {
return bp; return bp;
} }
...@@ -322,7 +322,7 @@ func (p *Process) OnGoroutineExit() EventHook { ...@@ -322,7 +322,7 @@ func (p *Process) OnGoroutineExit() EventHook {
} }
// osThreadToGoroutine looks up the goroutine running on an OS thread. // 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(); regs, err := t.Regs();
if err != nil { if err != nil {
return nil, err; return nil, err;
...@@ -343,9 +343,9 @@ func (p *Process) causesToEvents() ([]Event, os.Error) { ...@@ -343,9 +343,9 @@ func (p *Process) causesToEvents() ([]Event, os.Error) {
for _, t := range p.proc.Threads() { for _, t := range p.proc.Threads() {
if c, err := t.Stopped(); err == nil { if c, err := t.Stopped(); err == nil {
switch c := c.(type) { switch c := c.(type) {
case ptrace.Breakpoint: case proc.Breakpoint:
nev++; nev++;
case ptrace.Signal: case proc.Signal:
// TODO(austin) // TODO(austin)
//nev++; //nev++;
} }
...@@ -358,14 +358,14 @@ func (p *Process) causesToEvents() ([]Event, os.Error) { ...@@ -358,14 +358,14 @@ func (p *Process) causesToEvents() ([]Event, os.Error) {
for _, t := range p.proc.Threads() { for _, t := range p.proc.Threads() {
if c, err := t.Stopped(); err == nil { if c, err := t.Stopped(); err == nil {
switch c := c.(type) { switch c := c.(type) {
case ptrace.Breakpoint: case proc.Breakpoint:
gt, err := p.osThreadToGoroutine(t); gt, err := p.osThreadToGoroutine(t);
if err != nil { if err != nil {
return nil, err; return nil, err;
} }
events[i] = &Breakpoint{commonEvent{p, gt}, t, ptrace.Word(c)}; events[i] = &Breakpoint{commonEvent{p, gt}, t, proc.Word(c)};
i++; i++;
case ptrace.Signal: case proc.Signal:
// TODO(austin) // TODO(austin)
} }
} }
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
package ogle package ogle
import ( import (
"debug/proc";
"eval"; "eval";
"ptrace";
"reflect"; "reflect";
) )
...@@ -227,7 +227,7 @@ type runtimeValues struct { ...@@ -227,7 +227,7 @@ type runtimeValues struct {
PFloat32Type, PFloat64Type, PFloatType, PFloat32Type, PFloat64Type, PFloatType,
PArrayType, PStringType, PStructType, PPtrType, PFuncType, PArrayType, PStringType, PStructType, PPtrType, PFuncType,
PInterfaceType, PSliceType, PMapType, PChanType, PInterfaceType, PSliceType, PMapType, PChanType,
PDotDotDotType, PUnsafePointerType ptrace.Word; PDotDotDotType, PUnsafePointerType proc.Word;
// G status values // G status values
runtimeGStatus; runtimeGStatus;
} }
......
...@@ -5,10 +5,10 @@ ...@@ -5,10 +5,10 @@
package ogle package ogle
import ( import (
"debug/proc";
"eval"; "eval";
"fmt"; "fmt";
"log"; "log";
"ptrace";
) )
const debugParseRemoteType = false const debugParseRemoteType = false
...@@ -156,7 +156,7 @@ func parseRemoteType(a aborter, rs remoteStruct) *remoteType { ...@@ -156,7 +156,7 @@ func parseRemoteType(a aborter, rs remoteStruct) *remoteType {
} }
// Get Type header // 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); typ := rs.field(p.f.Type.Ptr).(remotePtr).aGet(a).(remoteStruct);
// Is this a named type? // Is this a named type?
......
...@@ -5,9 +5,9 @@ ...@@ -5,9 +5,9 @@
package ogle package ogle
import ( import (
"debug/proc";
"eval"; "eval";
"fmt"; "fmt";
"ptrace";
) )
// A RemoteMismatchError occurs when an operation that requires two // A RemoteMismatchError occurs when an operation that requires two
...@@ -38,7 +38,7 @@ type remoteValue interface { ...@@ -38,7 +38,7 @@ type remoteValue interface {
// remote represents an address in a remote process. // remote represents an address in a remote process.
type remote struct { type remote struct {
base ptrace.Word; base proc.Word;
p *Process; p *Process;
} }
...@@ -71,14 +71,14 @@ func (v remote) Get(a aborter, size int) uint64 { ...@@ -71,14 +71,14 @@ func (v remote) Get(a aborter, size int) uint64 {
func (v remote) Set(a aborter, size int, x uint64) { func (v remote) Set(a aborter, size int, x uint64) {
var arr [8]byte; var arr [8]byte;
buf := arr[0:size]; 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); _, err := v.p.Poke(v.base, buf);
if err != nil { if err != nil {
a.Abort(err); 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}; return remote{v.base + x, v.p};
} }
...@@ -340,9 +340,9 @@ func (v remoteString) Get(t *eval.Thread) string { ...@@ -340,9 +340,9 @@ func (v remoteString) Get(t *eval.Thread) string {
func (v remoteString) aGet(a aborter) string { func (v remoteString) aGet(a aborter) string {
rs := v.r.p.runtime.String.mk(v.r).(remoteStruct); 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); len := rs.field(v.r.p.f.String.Len).(remoteInt).aGet(a);
bytes := make([]uint8, len); bytes := make([]uint8, len);
_, err := v.r.p.Peek(str, bytes); _, err := v.r.p.Peek(str, bytes);
if err != nil { if err != nil {
...@@ -404,11 +404,11 @@ func (v remoteArray) Elem(t *eval.Thread, i int64) eval.Value { ...@@ -404,11 +404,11 @@ func (v remoteArray) Elem(t *eval.Thread, i int64) eval.Value {
} }
func (v remoteArray) elem(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 { 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 { ...@@ -455,7 +455,7 @@ func (v remoteStruct) Field(t *eval.Thread, i int) eval.Value {
func (v remoteStruct) field(i int) eval.Value { func (v remoteStruct) field(i int) eval.Value {
f := &v.layout[i]; 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 { func (v remoteStruct) addr() remote {
...@@ -494,7 +494,7 @@ func (v remotePtr) Get(t *eval.Thread) eval.Value { ...@@ -494,7 +494,7 @@ func (v remotePtr) Get(t *eval.Thread) eval.Value {
} }
func (v remotePtr) aGet(a aborter) 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 { if addr == 0 {
return nil; return nil;
} }
...@@ -550,7 +550,7 @@ func (v remoteSlice) Get(t *eval.Thread) eval.Slice { ...@@ -550,7 +550,7 @@ func (v remoteSlice) Get(t *eval.Thread) eval.Slice {
func (v remoteSlice) aGet(a aborter) eval.Slice { func (v remoteSlice) aGet(a aborter) eval.Slice {
rs := v.r.p.runtime.Slice.mk(v.r).(remoteStruct); 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); nel := rs.field(v.r.p.f.Slice.Len).(remoteInt).aGet(a);
cap := rs.field(v.r.p.f.Slice.Cap).(remoteInt).aGet(a); cap := rs.field(v.r.p.f.Slice.Cap).(remoteInt).aGet(a);
if base == 0 { if base == 0 {
......
...@@ -5,10 +5,10 @@ ...@@ -5,10 +5,10 @@
package ogle package ogle
import ( import (
"debug/proc";
"eval"; "eval";
"log"; "log";
"os"; "os";
"ptrace";
"sym"; "sym";
) )
...@@ -160,7 +160,7 @@ func (p *Process) populateWorld(w *eval.World) os.Error { ...@@ -160,7 +160,7 @@ func (p *Process) populateWorld(w *eval.World) os.Error {
if rt == nil { if rt == nil {
continue; 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': case 'T', 't', 'L', 'l':
// Function // Function
...@@ -207,7 +207,7 @@ func (p *Process) typeOfSym(s *sym.CommonSym) (*remoteType, os.Error) { ...@@ -207,7 +207,7 @@ func (p *Process) typeOfSym(s *sym.CommonSym) (*remoteType, os.Error) {
if s.GoType == 0 { if s.GoType == 0 {
return nil, nil; return nil, nil;
} }
addr := ptrace.Word(s.GoType); addr := proc.Word(s.GoType);
var rt *remoteType; var rt *remoteType;
err := try(func(a aborter) { err := try(func(a aborter) {
rt = parseRemoteType(a, p.runtime.Type.mk(remote{addr, p}).(remoteStruct)); rt = parseRemoteType(a, p.runtime.Type.mk(remote{addr, p}).(remoteStruct));
......
# 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
// 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;
}
// 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.
This diff is collapsed.
// 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;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment