]> Cypherpunks repositories - gostls13.git/commitdiff
acid fixes etc. still not perfect.
authorRuss Cox <rsc@golang.org>
Tue, 16 Sep 2008 19:33:00 +0000 (12:33 -0700)
committerRuss Cox <rsc@golang.org>
Tue, 16 Sep 2008 19:33:00 +0000 (12:33 -0700)
R=r
DELTA=764  (694 added, 38 deleted, 32 changed)
OCL=15285
CL=15395

include/mach_amd64.h
src/libmach_amd64/darwin.c
src/libmach_amd64/linux.c

index 289fc597a1453ad4d64e8f2058dde2d9cce7f9da..1ffa44033c703f8d282237e9f523ce55c8696cb6 100644 (file)
@@ -415,5 +415,6 @@ void                detachproc(Map *m);
 int            procnotes(int pid, char ***pnotes);
 char*          proctextfile(int pid);
 int            procthreadpids(int pid, int **thread);
+char*  procstatus(int);
 
 Maprw  fdrw;
index 0703f9a217b65426c45fd73051d4cad96e483aae..45c1d9e9e199bacaa92d6c101990dc795a54a62e 100644 (file)
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
+#define __DARWIN_UNIX03 0
+
 #include <u.h>
+#include <sys/ptrace.h>
+#include <sys/signal.h>
+#include <mach/mach.h>
+#include <errno.h>
 #include <libc.h>
 #include <bio.h>
 #include <mach_amd64.h>
+#include <ureg_amd64.h>
+typedef struct Ureg Ureg;
+
+// Mach-error wrapper.
+// Takes a mach return code and converts it into 0 / -1,
+// setting errstr when it returns -1.
+
+static struct {
+       int code;
+       char *name;
+} macherr[] = {
+       KERN_INVALID_ADDRESS,   "invalid address",
+       KERN_PROTECTION_FAILURE,        "protection failure",
+       KERN_NO_SPACE,  "no space",
+       KERN_INVALID_ARGUMENT,  "invalid argument",
+       KERN_FAILURE,   "failure",
+       KERN_RESOURCE_SHORTAGE, "resource shortage",
+       KERN_NOT_RECEIVER,      "not receiver",
+       KERN_NO_ACCESS, "no access",
+       KERN_MEMORY_FAILURE,    "memory failure",
+       KERN_MEMORY_ERROR,      "memory error",
+       KERN_ALREADY_IN_SET,    "already in set",
+       KERN_NOT_IN_SET,        "not in set",
+       KERN_NAME_EXISTS,       "name exists",
+       KERN_ABORTED,   "aborted",
+       KERN_INVALID_NAME,      "invalid name",
+       KERN_INVALID_TASK,      "invalid task",
+       KERN_INVALID_RIGHT,     "invalid right",
+       KERN_INVALID_VALUE,     "invalid value",
+       KERN_UREFS_OVERFLOW,    "urefs overflow",
+       KERN_INVALID_CAPABILITY,        "invalid capability",
+       KERN_RIGHT_EXISTS,      "right exists",
+       KERN_INVALID_HOST,      "invalid host",
+       KERN_MEMORY_PRESENT,    "memory present",
+       KERN_MEMORY_DATA_MOVED, "memory data moved",
+       KERN_MEMORY_RESTART_COPY,       "memory restart copy",
+       KERN_INVALID_PROCESSOR_SET,     "invalid processor set",
+       KERN_POLICY_LIMIT,      "policy limit",
+       KERN_INVALID_POLICY,    "invalid policy",
+       KERN_INVALID_OBJECT,    "invalid object",
+       KERN_ALREADY_WAITING,   "already waiting",
+       KERN_DEFAULT_SET,       "default set",
+       KERN_EXCEPTION_PROTECTED,       "exception protected",
+       KERN_INVALID_LEDGER,    "invalid ledger",
+       KERN_INVALID_MEMORY_CONTROL,    "invalid memory control",
+       KERN_INVALID_SECURITY,  "invalid security",
+       KERN_NOT_DEPRESSED,     "not depressed",
+       KERN_TERMINATED,        "terminated",
+       KERN_LOCK_SET_DESTROYED,        "lock set destroyed",
+       KERN_LOCK_UNSTABLE,     "lock unstable",
+       KERN_LOCK_OWNED,        "lock owned",
+       KERN_LOCK_OWNED_SELF,   "lock owned self",
+       KERN_SEMAPHORE_DESTROYED,       "semaphore destroyed",
+       KERN_RPC_SERVER_TERMINATED,     "rpc server terminated",
+       KERN_RPC_TERMINATE_ORPHAN,      "rpc terminate orphan",
+       KERN_RPC_CONTINUE_ORPHAN,       "rpc continue orphan",
+       KERN_NOT_SUPPORTED,     "not supported",
+       KERN_NODE_DOWN, "node down",
+       KERN_NOT_WAITING,       "not waiting",
+       KERN_OPERATION_TIMED_OUT,       "operation timed out",
+       KERN_RETURN_MAX,        "return max",
 
-Map*   
-attachproc(int pid, Fhdr *fp)
+       MACH_SEND_IN_PROGRESS,  "send in progress",
+       MACH_SEND_INVALID_DATA, "send invalid data",
+       MACH_SEND_INVALID_DEST, "send invalid dest",
+       MACH_SEND_TIMED_OUT,    "send timed out",
+       MACH_SEND_INTERRUPTED,  "send interrupted",
+       MACH_SEND_MSG_TOO_SMALL,        "send msg too small",
+       MACH_SEND_INVALID_REPLY,        "send invalid reply",
+       MACH_SEND_INVALID_RIGHT,        "send invalid right",
+       MACH_SEND_INVALID_NOTIFY,       "send invalid notify",
+       MACH_SEND_INVALID_MEMORY,       "send invalid memory",
+       MACH_SEND_NO_BUFFER,    "send no buffer",
+       MACH_SEND_TOO_LARGE,    "send too large",
+       MACH_SEND_INVALID_TYPE, "send invalid type",
+       MACH_SEND_INVALID_HEADER,       "send invalid header",
+       MACH_SEND_INVALID_TRAILER,      "send invalid trailer",
+       MACH_SEND_INVALID_RT_OOL_SIZE,  "send invalid rt ool size",
+       MACH_RCV_IN_PROGRESS,   "rcv in progress",
+       MACH_RCV_INVALID_NAME,  "rcv invalid name",
+       MACH_RCV_TIMED_OUT,     "rcv timed out",
+       MACH_RCV_TOO_LARGE,     "rcv too large",
+       MACH_RCV_INTERRUPTED,   "rcv interrupted",
+       MACH_RCV_PORT_CHANGED,  "rcv port changed",
+       MACH_RCV_INVALID_NOTIFY,        "rcv invalid notify",
+       MACH_RCV_INVALID_DATA,  "rcv invalid data",
+       MACH_RCV_PORT_DIED,     "rcv port died",
+       MACH_RCV_IN_SET,        "rcv in set",
+       MACH_RCV_HEADER_ERROR,  "rcv header error",
+       MACH_RCV_BODY_ERROR,    "rcv body error",
+       MACH_RCV_INVALID_TYPE,  "rcv invalid type",
+       MACH_RCV_SCATTER_SMALL, "rcv scatter small",
+       MACH_RCV_INVALID_TRAILER,       "rcv invalid trailer",
+       MACH_RCV_IN_PROGRESS_TIMED,     "rcv in progress timed",
+
+       MIG_TYPE_ERROR, "mig type error",
+       MIG_REPLY_MISMATCH,     "mig reply mismatch",
+       MIG_REMOTE_ERROR,       "mig remote error",
+       MIG_BAD_ID,     "mig bad id",
+       MIG_BAD_ARGUMENTS,      "mig bad arguments",
+       MIG_NO_REPLY,   "mig no reply",
+       MIG_EXCEPTION,  "mig exception",
+       MIG_ARRAY_TOO_LARGE,    "mig array too large",
+       MIG_SERVER_DIED,        "server died",
+       MIG_TRAILER_ERROR,      "trailer has an unknown format",
+};
+
+static int
+me(kern_return_t r)
 {
-       sysfatal("attachproc not implemented");
-       return nil;
+       int i;
+       
+       if(r == 0)
+               return 0;
+
+       for(i=0; i<nelem(macherr); i++){
+               if(r == macherr[i].code){
+                       werrstr("%s", macherr[i].name);
+                       return -1;
+               }
+       }
+       werrstr("mach error %#x", r);
+       return -1;
+}
+
+// Plan 9 and Linux do not distinguish between
+// process ids and thread ids, so the interface here doesn't either.
+// Unfortunately, Mach has three kinds of identifiers: process ids,
+// handles to tasks (processes), and handles to threads within a
+// process.  All of them are small integers.
+//
+// To accomodate Mach, we employ a clumsy hack: in this interface,
+// if you pass in a positive number, that's a process id.
+// If you pass in a negative number, that identifies a thread that
+// has been previously returned by procthreadpids (it indexes
+// into the Thread table below).
+
+// Table of threads we have handles for.
+typedef struct Thread Thread;
+struct Thread
+{
+       int pid;
+       mach_port_t task;
+       mach_port_t thread;
+       int stopped;
+       int exc;
+       int code[10];
+       Map *map;
+};
+static Thread thr[1000];
+static int nthr;
+static pthread_mutex_t mu;
+static pthread_cond_t cond;
+static void* excthread(void*);
+static mach_port_t excport;
+
+enum {
+       ExcMask = EXC_MASK_BAD_ACCESS |
+               EXC_MASK_BAD_INSTRUCTION |
+               EXC_MASK_ARITHMETIC |
+               EXC_MASK_BREAKPOINT |
+               EXC_MASK_SOFTWARE
+};
+
+// Add process pid to the thread table.
+// If it's already there, don't re-add it (unless force != 0).
+static Thread*
+addpid(int pid, int force)
+{
+       int i, j, r;
+       mach_port_t task;
+       mach_port_t *thread;
+       uint nthread;
+       Thread *ret;
+       static int first = 1;
+
+       if(first){
+               // Allocate a port for exception messages and
+               // send all thread exceptions to that port.
+               // The excthread reads that port and signals
+               // us if we are waiting on that thread.
+               pthread_t p;
+
+               excport = mach_reply_port();
+               pthread_create(&p, nil, excthread, nil);
+               pthread_mutex_init(&mu, nil);
+               pthread_cond_init(&cond, nil);
+               first = 0;
+       }
+       
+       if(!force){
+               for(i=0; i<nthr; i++)
+                       if(thr[i].pid == pid)
+                               return &thr[i];
+       }
+       if(me(task_for_pid(mach_task_self(), pid, &task)) < 0)
+               return nil;
+       if(me(task_threads(task, &thread, &nthread)) < 0)
+               return nil;
+       mach_port_insert_right(mach_task_self(), excport, excport, MACH_MSG_TYPE_MAKE_SEND);
+       if(me(task_set_exception_ports(task, ExcMask,
+                       excport, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE)) < 0){
+               fprint(2, "warning: cannot set excport: %r\n");
+       }
+       ret = nil;
+       for(j=0; j<nthread; j++){
+               if(force){
+                       // If we're forcing a refresh, don't re-add existing threads.
+                       for(i=0; i<nthr; i++)
+                               if(thr[i].pid == pid && thr[i].thread == thread[j]){
+                                       if(ret == nil)
+                                               ret = &thr[i];
+                                       goto skip;
+                               }
+               }
+               if(nthr >= nelem(thr))
+                       return nil;
+               // TODO: We probably should save the old thread exception
+               // ports for each bit and then put them back when we exit.
+               // Probably the BSD signal handlers have put stuff there.
+               mach_port_insert_right(mach_task_self(), excport, excport, MACH_MSG_TYPE_MAKE_SEND);
+               if(me(thread_set_exception_ports(thread[j], ExcMask,
+                               excport, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE)) < 0){
+                       fprint(2, "warning: cannot set excport: %r\n");
+               }
+               thr[nthr].pid = pid;
+               thr[nthr].task = task;
+               thr[nthr].thread = thread[j];
+               if(ret == nil)
+                       ret = &thr[nthr];
+               nthr++;
+       skip:;
+       }
+       return ret;
+}
+
+static Thread*
+idtotable(int id)
+{
+       if(id >= 0)
+               return addpid(id, 1);
+       
+       id = -(id+1);
+       if(id >= nthr)
+               return nil;
+       return &thr[id];
+}
+
+static int
+idtopid(int id)
+{
+       Thread *t;
+       
+       if((t = idtotable(id)) == nil)
+               return -1;
+       return t->pid;
 }
 
+static mach_port_t
+idtotask(int id)
+{
+       Thread *t;
+       
+       if((t = idtotable(id)) == nil)
+               return -1;
+       return t->task;
+}
+
+static mach_port_t
+idtothread(int id)
+{
+       Thread *t;
+       
+       if((t = idtotable(id)) == nil)
+               return -1;
+       return t->thread;
+}
+
+static int machsegrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr);
+static int machregrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr);
+
+Map*
+attachproc(int id, Fhdr *fp)
+{
+       Thread *t;
+       Map *map;
+
+       if((t = idtotable(id)) == nil)
+               return nil;
+       if(t->map)
+               return t->map;
+       map = newmap(0, 4);
+       if(!map)
+               return nil;
+       map->pid = -((t-thr) + 1);
+       if(mach->regsize)
+               setmap(map, -1, 0, mach->regsize, 0, "regs", machregrw);
+       setmap(map, -1, fp->txtaddr, fp->txtaddr+fp->txtsz, fp->txtaddr, "*text", machsegrw);
+       setmap(map, -1, fp->dataddr, mach->utop, fp->dataddr, "*data", machsegrw);
+       t->map = map;
+       return map;
+}
+
+// Return list of ids for threads in id.
 int
-ctlproc(int pid, char *msg)
+procthreadpids(int id, int **thread)
 {
-       sysfatal("ctlproc not implemented");
-       return -1;
+       Thread *t;
+       int i, n, pid;
+       int *out;
+       
+       t = idtotable(id);
+       if(t == nil)
+               return -1;
+       pid = t->pid;
+       n = 0;
+       for(i=0; i<nthr; i++)
+               if(thr[i].pid == pid)
+                       n++;
+       out = malloc(n*sizeof out[0]);
+       if(out == nil)
+               return -1;
+       n = 0;
+       for(i=0; i<nthr; i++)
+               if(thr[i].pid == pid)
+                       out[n++] = -(i+1);
+       *thread = out;
+       return n;
 }
 
+// Detach from proc.
+// TODO(rsc): Perhaps should unsuspend any threads and clean-up the table.
 void
 detachproc(Map *m)
 {
-       sysfatal("detachproc not implemented");
+       free(m);
 }
 
+// Should return array of pending signals (notes)
+// but don't know how to do that on OS X.
 int
 procnotes(int pid, char ***pnotes)
 {
-       sysfatal("procnotes not implemented");
-       return -1;
+       *pnotes = 0;
+       return 0;
 }
 
+// There must be a way to do this.  Gdb can do it.
+// But I don't see, in the Apple gdb sources, how.
 char*
 proctextfile(int pid)
 {
-       sysfatal("proctextfile not implemented");
        return nil;
 }
 
-int    
-procthreadpids(int pid, int **thread)
+// Read/write from a Mach data segment.
+static int
+machsegrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr)
+{
+       uintptr nn;
+       mach_port_t task;
+       int r;
+       
+       task = idtotask(map->pid);
+       if(task == -1)
+               return -1;
+
+       if(isr){
+               nn = n;
+               if(me(vm_read_overwrite(task, addr, n, (uintptr)v, &nn)) < 0)
+                       return -1;
+               return nn;
+       }else{
+               r = vm_write(task, addr, (uintptr)v, n);
+               if(r == KERN_INVALID_ADDRESS){
+                       // Happens when writing to text segment.
+                       // Change protections.
+                       if(me(vm_protect(task, addr, n, 0, VM_PROT_WRITE|VM_PROT_READ|VM_PROT_EXECUTE)) < 0){
+                               fprint(2, "vm_protect: %s\n", r);
+                               return -1;
+                       }
+                       r = vm_write(task, addr, (uintptr)v, n);
+               }
+               if(r != 0){
+                       me(r);
+                       return -1;
+               }
+               return n;
+       }
+}
+
+// Convert Ureg offset to x86_thread_state64_t offset.
+static int
+go2darwin(uvlong addr)
+{
+       switch(addr){
+       case offsetof(Ureg, ax):
+               return offsetof(x86_thread_state64_t, rax);
+       case offsetof(Ureg, bx):
+               return offsetof(x86_thread_state64_t, rbx);
+       case offsetof(Ureg, cx):
+               return offsetof(x86_thread_state64_t, rcx);
+       case offsetof(Ureg, dx):
+               return offsetof(x86_thread_state64_t, rdx);
+       case offsetof(Ureg, si):
+               return offsetof(x86_thread_state64_t, rsi);
+       case offsetof(Ureg, di):
+               return offsetof(x86_thread_state64_t, rdi);
+       case offsetof(Ureg, bp):
+               return offsetof(x86_thread_state64_t, rbp);
+       case offsetof(Ureg, r8):
+               return offsetof(x86_thread_state64_t, r8);
+       case offsetof(Ureg, r9):
+               return offsetof(x86_thread_state64_t, r9);
+       case offsetof(Ureg, r10):
+               return offsetof(x86_thread_state64_t, r10);
+       case offsetof(Ureg, r11):
+               return offsetof(x86_thread_state64_t, r11);
+       case offsetof(Ureg, r12):
+               return offsetof(x86_thread_state64_t, r12);
+       case offsetof(Ureg, r13):
+               return offsetof(x86_thread_state64_t, r13);
+       case offsetof(Ureg, r14):
+               return offsetof(x86_thread_state64_t, r14);
+       case offsetof(Ureg, r15):
+               return offsetof(x86_thread_state64_t, r15);
+       case offsetof(Ureg, fs):
+               return offsetof(x86_thread_state64_t, fs);
+       case offsetof(Ureg, gs):
+               return offsetof(x86_thread_state64_t, gs);
+       case offsetof(Ureg, ip):
+               return offsetof(x86_thread_state64_t, rip);
+       case offsetof(Ureg, cs):
+               return offsetof(x86_thread_state64_t, cs);
+       case offsetof(Ureg, flags):
+               return offsetof(x86_thread_state64_t, rflags);
+       case offsetof(Ureg, sp):
+               return offsetof(x86_thread_state64_t, rsp);
+       }
+       return -1;
+}
+
+// Read/write from fake register segment.
+static int
+machregrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr)
+{
+       uint nn;
+       mach_port_t thread;
+       int reg, r;
+       union {
+               x86_thread_state64_t regs;
+               uchar p[1];
+       } u;
+       uchar *p;
+
+       if(n > 8){
+               werrstr("asked for %d-byte register", n);
+               return -1;
+       }
+
+       thread = idtothread(map->pid);
+       if(thread == -1){
+               werrstr("no such id");
+               return -1;
+       }
+       
+       if((reg = go2darwin(addr)) < 0 || reg+n > sizeof u){
+               if(isr){
+                       memset(v, 0, n);
+                       return 0;
+               }
+               werrstr("register %llud not available", addr);
+               return -1;
+       }
+
+       if(!isr && me(thread_suspend(thread)) < 0){
+               werrstr("thread suspend %#x: %r", thread);
+               return -1;
+       }
+       nn = x86_THREAD_STATE64_COUNT;
+       if(me(thread_get_state(thread, x86_THREAD_STATE64, (thread_state_t)&u.regs, &nn)) < 0){
+               if(!isr)
+                       thread_resume(thread);
+               werrstr("thread_get_state: %r");
+               return -1;
+       }
+
+       p = u.p+reg;
+       if(isr)
+               memmove(v, p, n);
+       else{
+               memmove(p, v, n);
+               nn = x86_THREAD_STATE64_COUNT;
+               if(me(thread_set_state(thread, x86_THREAD_STATE64, (thread_state_t)&u.regs, nn)) < 0){
+                       thread_resume(thread);
+                       werrstr("thread_set_state: %r");
+                       return -1;
+               }
+               
+               if(me(thread_resume(thread)) < 0){
+                       werrstr("thread_resume: %r");
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+enum
+{
+       RFLAGS_TF = 0x100               // x86 single-step processor flag
+};
+
+// Is thread t suspended?
+static int
+threadstopped(Thread *t)
+{
+       struct thread_basic_info info;
+       uint size;
+       int r;
+
+       size = sizeof info;
+       if(me(thread_info(t->thread, THREAD_BASIC_INFO, (thread_info_t)&info, &size)) <  0){
+               fprint(2, "threadstopped thread_info %#x: %r\n");
+               return 1;
+       }
+       return info.suspend_count > 0;
+}
+
+// If thread t is suspended, start it up again.
+// If singlestep is set, only let it execute one instruction.
+static int
+threadstart(Thread *t, int singlestep)
+{
+       int i;
+       uint n;
+       struct thread_basic_info info;
+       x86_thread_state64_t regs;
+
+       if(!threadstopped(t))
+               return;
+
+       // Set or clear the processor single-step flag, as appropriate.
+       n = x86_THREAD_STATE64_COUNT;
+       if(me(thread_get_state(t->thread, x86_THREAD_STATE64,
+                       (thread_state_t)&regs,
+                       &n)) < 0)
+               return -1;
+       if(singlestep)
+               regs.rflags |= RFLAGS_TF;
+       else
+               regs.rflags &= ~RFLAGS_TF;
+       if(me(thread_set_state(t->thread, x86_THREAD_STATE64,
+                       (thread_state_t)&regs,
+                       x86_THREAD_STATE64_COUNT)) < 0)
+               return -1;
+       
+       // Run.
+       n = sizeof info;
+       if(me(thread_info(t->thread, THREAD_BASIC_INFO, (thread_info_t)&info, &n)) < 0)
+               return -1;
+       for(i=0; i<info.suspend_count; i++)
+               if(me(thread_resume(t->thread)) < 0)
+                       return -1;
+       return 0;
+}
+
+// Stop thread t.
+static int
+threadstop(Thread *t)
+{
+       if(threadstopped(t))
+               return 0;
+       if(me(thread_suspend(t->thread)) < 0)
+               return -1;
+       return 0;
+}
+
+// Callback for exc_server below.  Called when a thread we are
+// watching has an exception like hitting a breakpoint.
+kern_return_t
+catch_exception_raise(mach_port_t eport, mach_port_t thread,
+       mach_port_t task, exception_type_t exception,
+       exception_data_t code, mach_msg_type_number_t ncode)
+{
+       Thread *t;
+       int i;
+
+       t = nil;
+       for(i=0; i<nthr; i++){
+               if(thr[i].thread == thread){
+                       t = &thr[i];
+                       goto havet;
+               }
+       }
+       fprint(2, "did not find thread in catch_exception_raise\n");
+       return KERN_SUCCESS;    // let thread continue
+
+havet:
+       t->exc = exception;
+       if(ncode > nelem(t->code))
+               ncode = nelem(t->code);
+       memmove(t->code, code, ncode*sizeof t->code[0]);
+       
+       // Synchronize with waitstop below.
+       pthread_mutex_lock(&mu);
+       pthread_cond_broadcast(&cond);
+       pthread_mutex_unlock(&mu);
+       
+       // Suspend thread, so that we can look at it & restart it later.
+       if(me(thread_suspend(thread)) < 0)
+               fprint(2, "catch_exception_raise thread_suspend: %r\n");
+       return KERN_SUCCESS;
+}
+
+// Exception watching thread, started in addpid above.
+static void*
+excthread(void *v)
 {
-       sysfatal("procthreadpids not implemented");
+       extern boolean_t exc_server();
+       mach_msg_server(exc_server, 2048, excport, 0);
+       return 0;
+}
+
+// Wait for thread t to stop.
+static int
+waitstop(Thread *t)
+{
+       pthread_mutex_lock(&mu);
+       while(!threadstopped(t))
+               pthread_cond_wait(&cond, &mu);
+       pthread_mutex_unlock(&mu);
+       return 0;
+}
+
+int
+ctlproc(int id, char *msg)
+{
+       Thread *t;
+       int status, r;
+
+       // Hang/attached dance is for debugging newly exec'ed programs.
+       // After fork, the child does ctlproc("hang") before exec,
+       // and the parent does ctlproc("attached") and then waitstop.
+       // Using these requires the BSD ptrace interface, unlike everything
+       // else we do, which uses only the Mach interface.  Our goal here
+       // is to do as little as possible using ptrace and then flip over to Mach.
+
+       if(strcmp(msg, "hang") == 0)
+               return ptrace(PT_TRACE_ME, 0, 0, 0);
+
+       if(strcmp(msg, "attached") == 0){
+               // The pid "id" has done a ctlproc "hang" and then
+               // exec, so we should find it stoppped just before exec
+               // of the new program.
+               #undef waitpid
+               if(waitpid(id, &status, WUNTRACED) < 0){
+                       fprint(2, "ctlproc attached waitpid: %r\n");
+                       return -1;
+               }
+               if(WIFEXITED(status) || !WIFSTOPPED(status)){
+                       fprint(2, "ctlproc attached: bad process state\n");
+                       return -1;
+               }
+
+               // Find Mach thread for pid and suspend it.
+               t = addpid(id, 1);
+               if(t == nil)
+                       return -1;
+               if(me(thread_suspend(t->thread)) < 0){
+                       fprint(2, "ctlproc attached: thread_suspend: %r\n");
+                       return -1;
+               }
+
+               // Let ptrace tell the process to keep going:
+               // then ptrace is out of the way and we're back in Mach land.
+               return ptrace(PT_CONTINUE, id, (caddr_t)1, 0);
+       }
+
+       // All the other control messages require a Thread structure.
+       if((t = idtotable(id)) == nil){
+               werrstr("no such thread");
+               return -1;
+       }
+
+       if(strcmp(msg, "kill") == 0)
+               return ptrace(PT_KILL, t->pid, 0, 0);
+
+       if(strcmp(msg, "start") == 0)
+               return threadstart(t, 0);
+
+       if(strcmp(msg, "stop") == 0)
+               return threadstop(t);
+
+       if(strcmp(msg, "startstop") == 0){
+               if(threadstart(t, 0) < 0)
+                       return -1;
+               return waitstop(t);
+       }
+
+       if(strcmp(msg, "step") == 0){
+               if(threadstart(t, 1) < 0)
+                       return -1;
+               return waitstop(t);
+       }
+       
+       if(strcmp(msg, "waitstop") == 0)
+               return waitstop(t);
+       
+       // sysstop not available on OS X
+
+       werrstr("unknown control message");
        return -1;
 }
 
+char*
+procstatus(int id)
+{
+       Thread *t;
+       
+       if((t = idtotable(id)) == nil)
+               return "gone!";
+       
+       if(threadstopped(t))
+               return "Stopped";
+       
+       return "Running";
+}
+
index 3f66d2f1b36d46887202c9cf8d591cfbfeccf98a..ee4c8a2177fe44ff179339f6f82c24f5cb9dc799 100644 (file)
@@ -49,8 +49,8 @@ struct user_regs_struct {
        unsigned long rip,cs,eflags;
        unsigned long rsp,ss;
        unsigned long fs_base, gs_base;
-       unsigned long ds,es,fs,gs; 
-}; 
+       unsigned long ds,es,fs,gs;
+};
 
 static int
 isstopped(int pid)
@@ -160,7 +160,7 @@ detachproc(Map *m)
        free(m);
 }
 
-/* /proc/pid/stat contains 
+/* /proc/pid/stat contains
        pid
        command in parens
        0. state
@@ -289,41 +289,25 @@ ctlproc(int pid, char *msg)
        if(strcmp(msg, "startstop") == 0){
                if(ptrace(PTRACE_CONT, pid, 0, 0) < 0)
                        return -1;
-               goto waitstop;
+               return waitstop(pid);
        }
        if(strcmp(msg, "sysstop") == 0){
                if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
                        return -1;
-               goto waitstop;
+               return waitstop(pid);
        }
        if(strcmp(msg, "stop") == 0){
                if(kill(pid, SIGSTOP) < 0)
                        return -1;
-               goto waitstop;
+               return waitstop(pid);
        }
        if(strcmp(msg, "step") == 0){
                if(ptrace(PTRACE_SINGLESTEP, pid, 0, 0) < 0)
                        return -1;
-               goto waitstop;
-       }
-       if(strcmp(msg, "waitstop") == 0){
-       waitstop:
-               if(isstopped(pid))
-                       return 0;
-               for(;;){
-                       p = waitpid(pid, &status, WUNTRACED|__WALL);
-                       if(p <= 0){
-                               if(errno == ECHILD){
-                                       if(isstopped(pid))
-                                               return 0;
-                               }
-                               return -1;
-                       }
-/*fprint(2, "got pid %d status %x\n", pid, status); */
-                       if(WIFEXITED(status) || WIFSTOPPED(status))
-                               return 0;
-               }
+               return waitstop(pid);
        }
+       if(strcmp(msg, "waitstop") == 0)
+               return waitstop(pid);
        if(strcmp(msg, "start") == 0)
                return ptrace(PTRACE_CONT, pid, 0, 0);
        werrstr("unknown control message '%s'", msg);