Commit 8b795149 authored by Austin Clements's avatar Austin Clements

Implement forking debugged processes.

R=rsc
APPROVED=rsc
DELTA=81  (53 added, 3 deleted, 25 changed)
OCL=31651
CL=31675
parent 3d486d0d
...@@ -158,7 +158,7 @@ type thread struct { ...@@ -158,7 +158,7 @@ type thread struct {
exitStatus int; exitStatus int;
} }
func (p *process) newThread(tid int) (*thread, os.Error) func (p *process) newThread(tid int, signal int, cloned bool) (*thread, os.Error)
/* /*
* Errors * Errors
...@@ -186,6 +186,16 @@ func (e noBreakpointError) String() string { ...@@ -186,6 +186,16 @@ func (e noBreakpointError) String() string {
return fmt.Sprintf("no breakpoint at PC %#x", e); return fmt.Sprintf("no breakpoint at PC %#x", e);
} }
type newThreadError struct {
*os.Waitmsg;
wantPid int;
wantSig int;
}
func (e *newThreadError) String() string {
return fmt.Sprintf("newThread wait wanted pid %v and signal %v, got %v and %v", e.Pid, e.StopSignal(), e.wantPid, e.wantSig);
}
/* /*
* Ptrace wrappers * Ptrace wrappers
*/ */
...@@ -218,7 +228,6 @@ func (t *thread) ptraceSetOptions(options int) os.Error { ...@@ -218,7 +228,6 @@ func (t *thread) ptraceSetOptions(options int) os.Error {
func (t *thread) ptraceGetEventMsg() (uint, os.Error) { func (t *thread) ptraceGetEventMsg() (uint, os.Error) {
msg, err := syscall.PtraceGetEventMsg(t.tid); msg, err := syscall.PtraceGetEventMsg(t.tid);
return msg, os.NewSyscallError("ptrace(GETEVENTMSG)", err); return msg, os.NewSyscallError("ptrace(GETEVENTMSG)", err);
} }
func (t *thread) ptraceCont() os.Error { func (t *thread) ptraceCont() os.Error {
...@@ -464,6 +473,8 @@ func (p *process) stopAsync() os.Error { ...@@ -464,6 +473,8 @@ func (p *process) stopAsync() os.Error {
// doTrap handles SIGTRAP debug events with a cause of 0. These can // doTrap handles SIGTRAP debug events with a cause of 0. These can
// be caused either by an installed breakpoint, a breakpoint in the // be caused either by an installed breakpoint, a breakpoint in the
// program text, or by single stepping. // program text, or by single stepping.
//
// TODO(austin) I think we also get this on an execve syscall.
func (ev *debugEvent) doTrap() (threadState, os.Error) { func (ev *debugEvent) doTrap() (threadState, os.Error) {
t := ev.t; t := ev.t;
...@@ -512,7 +523,7 @@ func (ev *debugEvent) doPtraceClone() (threadState, os.Error) { ...@@ -512,7 +523,7 @@ func (ev *debugEvent) doPtraceClone() (threadState, os.Error) {
return stopped, err; return stopped, err;
} }
nt, err := t.proc.newThread(int(tid)); nt, err := t.proc.newThread(int(tid), syscall.SIGSTOP, true);
if err != nil { if err != nil {
return stopped, err; return stopped, err;
} }
...@@ -620,6 +631,9 @@ func (ev *debugEvent) process() os.Error { ...@@ -620,6 +631,9 @@ func (ev *debugEvent) process() os.Error {
t.ignoreNextSigstop = true; t.ignoreNextSigstop = true;
} }
// TODO(austin) If we're in state stopping and get a SIGSTOP,
// set state stopped instead of stoppedSignal.
t.setState(state); t.setState(state);
if t.proc.someRunningThread() == nil { if t.proc.someRunningThread() == nil {
...@@ -1036,6 +1050,8 @@ func (p *process) WaitStop() os.Error { ...@@ -1036,6 +1050,8 @@ func (p *process) WaitStop() os.Error {
h := &transitionHandler{}; h := &transitionHandler{};
h.handle = func (st *thread, old, new threadState) { h.handle = func (st *thread, old, new threadState) {
if !new.isRunning() { if !new.isRunning() {
// TODO(austin) This gets stuck on
// zombie threads.
if p.someRunningThread() == nil { if p.someRunningThread() == nil {
ready <- nil; ready <- nil;
return; return;
...@@ -1091,20 +1107,28 @@ func (p *process) Detach() os.Error { ...@@ -1091,20 +1107,28 @@ func (p *process) Detach() os.Error {
} }
// newThread creates a new thread object and waits for its initial // newThread creates a new thread object and waits for its initial
// SIGSTOP. // signal. If cloned is true, this thread was cloned from a thread we
// are already attached to.
// //
// Must be run from the monitor thread. // Must be run from the monitor thread.
func (p *process) newThread(tid int) (*thread, os.Error) { func (p *process) newThread(tid int, signal int, cloned bool) (*thread, os.Error) {
t := &thread{tid: tid, proc: p, state: stopped}; t := &thread{tid: tid, proc: p, state: stopped};
// Get the SIGSTOP from the thread // Get the signal from the thread
// TODO(austin) Thread might already be stopped // TODO(austin) Thread might already be stopped if we're attaching.
w, err := os.Wait(tid, syscall.WALL); w, err := os.Wait(tid, syscall.WALL);
if err != nil { if err != nil {
return nil, err; return nil, err;
} }
if w.Pid != tid || w.StopSignal() != syscall.SIGSTOP { if w.Pid != tid || w.StopSignal() != signal {
return nil, os.EINVAL; return nil, &newThreadError{w, tid, signal};
}
if !cloned {
err = t.ptraceSetOptions(syscall.PTRACE_O_TRACECLONE | syscall.PTRACE_O_TRACEEXIT);
if err != nil {
return nil, err;
}
} }
p.threads[tid] = t; p.threads[tid] = t;
...@@ -1125,7 +1149,7 @@ func (p *process) attachThread(tid int) (*thread, os.Error) { ...@@ -1125,7 +1149,7 @@ func (p *process) attachThread(tid int) (*thread, os.Error) {
} }
var err os.Error; var err os.Error;
thr, err = p.newThread(tid); thr, err = p.newThread(tid, syscall.SIGSTOP, false);
return err; return err;
}); });
return thr, err; return thr, err;
...@@ -1196,8 +1220,8 @@ func (p *process) attachAllThreads() os.Error { ...@@ -1196,8 +1220,8 @@ func (p *process) attachAllThreads() os.Error {
return nil; return nil;
} }
// Attach attaches to process pid and stops all of its threads. // newProcess creates a new process object and starts its monitor thread.
func Attach(pid int) (Process, os.Error) { func newProcess(pid int) *process {
p := &process{ p := &process{
pid: pid, pid: pid,
threads: make(map[int] *thread), threads: make(map[int] *thread),
...@@ -1208,10 +1232,15 @@ func Attach(pid int) (Process, os.Error) { ...@@ -1208,10 +1232,15 @@ func Attach(pid int) (Process, os.Error) {
transitionHandlers: vector.New(0) transitionHandlers: vector.New(0)
}; };
// All ptrace calls must be done from the same thread. Start
// the monitor thread now so we can attach from within it.
go p.monitor(); go p.monitor();
return p;
}
// Attach attaches to process pid and stops all of its threads.
func Attach(pid int) (Process, os.Error) {
p := newProcess(pid);
// Attach to all threads // Attach to all threads
err := p.attachAllThreads(); err := p.attachAllThreads();
if err != nil { if err != nil {
...@@ -1221,20 +1250,41 @@ func Attach(pid int) (Process, os.Error) { ...@@ -1221,20 +1250,41 @@ func Attach(pid int) (Process, os.Error) {
return nil, err; return nil, err;
} }
// Set ptrace options for all threads return p, nil;
err = p.do(func () os.Error { }
for _, t := range p.threads {
err := t.ptraceSetOptions(syscall.PTRACE_O_TRACECLONE | syscall.PTRACE_O_TRACEEXIT); // ForkExec forks the current process and execs argv0, stopping the
if err != nil { // new process after the exec syscall. See os.ForkExec for additional
return err; // details.
} func ForkExec(argv0 string, argv []string, envv []string, dir string, fd []*os.File)
(Process, os.Error)
{
p := newProcess(-1);
// Create array of integer (system) fds.
intfd := make([]int, len(fd));
for i, f := range fd {
if f == nil {
intfd[i] = -1;
} else {
intfd[i] = f.Fd();
} }
return nil; }
// Fork from the monitor thread so we get the right tracer pid.
err := p.do(func () os.Error {
pid, errno := syscall.PtraceForkExec(argv0, argv, envv, dir, intfd);
if errno != 0 {
return &os.PathError{"fork/exec", argv0, os.Errno(errno)};
}
p.pid = pid;
// The process will raise SIGTRAP when it reaches execve.
t, err := p.newThread(pid, syscall.SIGTRAP, false);
return err;
}); });
if err != nil { if err != nil {
p.Detach(); p.stopMonitor(err);
// TODO(austin)
//p.stopMonitor(err);
return nil, err; return nil, err;
} }
......
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