mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
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
This commit is contained in:
parent
495b3db88b
commit
ffe83e582e
16 changed files with 100 additions and 1945 deletions
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
include $(GOROOT)/src/Make.$(GOARCH)
|
||||
|
||||
TARG=ptrace
|
||||
TARG=debug/proc
|
||||
GOFILES=\
|
||||
proc.go\
|
||||
proc_$(GOOS).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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) });
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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.
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue