summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2008-09-16 12:33:00 -0700
committerRuss Cox <rsc@golang.org>2008-09-16 12:33:00 -0700
commita69f1e80d8ed354e8a0541e90c9c6044a85aab84 (patch)
treef467c6cab25c298d4833e3cbf655f6c8578f6052
parent4407ea24eb919877fa3c0ee7111c0fbb4ba316f5 (diff)
downloadgo-a69f1e80d8ed354e8a0541e90c9c6044a85aab84.tar.gz
acid fixes etc. still not perfect.
R=r DELTA=764 (694 added, 38 deleted, 32 changed) OCL=15285 CL=15395
-rw-r--r--include/mach_amd64.h1
-rw-r--r--src/libmach_amd64/darwin.c720
-rw-r--r--src/libmach_amd64/linux.c34
3 files changed, 716 insertions, 39 deletions
diff --git a/include/mach_amd64.h b/include/mach_amd64.h
index 289fc597a..1ffa44033 100644
--- a/include/mach_amd64.h
+++ b/include/mach_amd64.h
@@ -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;
diff --git a/src/libmach_amd64/darwin.c b/src/libmach_amd64/darwin.c
index 0703f9a21..45c1d9e9e 100644
--- a/src/libmach_amd64/darwin.c
+++ b/src/libmach_amd64/darwin.c
@@ -18,49 +18,741 @@
// 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";
+}
+
diff --git a/src/libmach_amd64/linux.c b/src/libmach_amd64/linux.c
index 3f66d2f1b..ee4c8a217 100644
--- a/src/libmach_amd64/linux.c
+++ b/src/libmach_amd64/linux.c
@@ -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);