1.1 --- a/agent/src/os/bsd/MacosxDebuggerLocal.m Thu Feb 14 14:33:35 2013 -0500 1.2 +++ b/agent/src/os/bsd/MacosxDebuggerLocal.m Fri Feb 15 08:54:12 2013 +0100 1.3 @@ -38,6 +38,8 @@ 1.4 #import <dlfcn.h> 1.5 #import <limits.h> 1.6 #import <errno.h> 1.7 +#import <sys/types.h> 1.8 +#import <sys/ptrace.h> 1.9 1.10 jboolean debug = JNI_FALSE; 1.11 1.12 @@ -430,6 +432,73 @@ 1.13 return (jint) usable_tid; 1.14 } 1.15 1.16 + 1.17 +static bool ptrace_continue(pid_t pid, int signal) { 1.18 + // pass the signal to the process so we don't swallow it 1.19 + int res; 1.20 + if ((res = ptrace(PT_CONTINUE, pid, (caddr_t)1, signal)) < 0) { 1.21 + fprintf(stderr, "attach: ptrace(PT_CONTINUE, %d) failed with %d\n", pid, res); 1.22 + return false; 1.23 + } 1.24 + return true; 1.25 +} 1.26 + 1.27 +// waits until the ATTACH has stopped the process 1.28 +// by signal SIGSTOP 1.29 +static bool ptrace_waitpid(pid_t pid) { 1.30 + int ret; 1.31 + int status; 1.32 + while (true) { 1.33 + // Wait for debuggee to stop. 1.34 + ret = waitpid(pid, &status, 0); 1.35 + if (ret >= 0) { 1.36 + if (WIFSTOPPED(status)) { 1.37 + // Any signal will stop the thread, make sure it is SIGSTOP. Otherwise SIGSTOP 1.38 + // will still be pending and delivered when the process is DETACHED and the process 1.39 + // will go to sleep. 1.40 + if (WSTOPSIG(status) == SIGSTOP) { 1.41 + // Debuggee stopped by SIGSTOP. 1.42 + return true; 1.43 + } 1.44 + if (!ptrace_continue(pid, WSTOPSIG(status))) { 1.45 + fprintf(stderr, "attach: Failed to correctly attach to VM. VM might HANG! [PTRACE_CONT failed, stopped by %d]\n", WSTOPSIG(status)); 1.46 + return false; 1.47 + } 1.48 + } else { 1.49 + fprintf(stderr, "attach: waitpid(): Child process exited/terminated (status = 0x%x)\n", status); 1.50 + return false; 1.51 + } 1.52 + } else { 1.53 + switch (errno) { 1.54 + case EINTR: 1.55 + continue; 1.56 + break; 1.57 + case ECHILD: 1.58 + fprintf(stderr, "attach: waitpid() failed. Child process pid (%d) does not exist \n", pid); 1.59 + break; 1.60 + case EINVAL: 1.61 + fprintf(stderr, "attach: waitpid() failed. Invalid options argument.\n"); 1.62 + break; 1.63 + default: 1.64 + fprintf(stderr, "attach: waitpid() failed. Unexpected error %d\n",errno); 1.65 + break; 1.66 + } 1.67 + return false; 1.68 + } 1.69 + } 1.70 +} 1.71 + 1.72 +// attach to a process/thread specified by "pid" 1.73 +static bool ptrace_attach(pid_t pid) { 1.74 + int res; 1.75 + if ((res = ptrace(PT_ATTACH, pid, 0, 0)) < 0) { 1.76 + fprintf(stderr, "ptrace(PT_ATTACH, %d) failed with %d\n", pid, res); 1.77 + return false; 1.78 + } else { 1.79 + return ptrace_waitpid(pid); 1.80 + } 1.81 +} 1.82 + 1.83 /* 1.84 * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal 1.85 * Method: attach0 1.86 @@ -445,7 +514,8 @@ 1.87 else 1.88 debug = JNI_FALSE; 1.89 if (debug) printf("attach0 called for jpid=%d\n", (int)jpid); 1.90 - 1.91 + 1.92 + // get the task from the pid 1.93 kern_return_t result; 1.94 task_t gTask = 0; 1.95 result = task_for_pid(mach_task_self(), jpid, &gTask); 1.96 @@ -455,6 +525,13 @@ 1.97 } 1.98 putTask(env, this_obj, gTask); 1.99 1.100 + // use ptrace to stop the process 1.101 + // on os x, ptrace only needs to be called on the process, not the individual threads 1.102 + if (ptrace_attach(jpid) != true) { 1.103 + mach_port_deallocate(mach_task_self(), gTask); 1.104 + THROW_NEW_DEBUGGER_EXCEPTION("Can't attach to the process"); 1.105 + } 1.106 + 1.107 id symbolicator = nil; 1.108 id jrsSymbolicator = objc_lookUpClass("JRSSymbolicator"); 1.109 if (jrsSymbolicator != nil) { 1.110 @@ -486,6 +563,21 @@ 1.111 if (debug) printf("detach0 called\n"); 1.112 1.113 task_t gTask = getTask(env, this_obj); 1.114 + 1.115 + // detach from the ptraced process causing it to resume execution 1.116 + int pid; 1.117 + kern_return_t k_res; 1.118 + k_res = pid_for_task(gTask, &pid); 1.119 + if (k_res != KERN_SUCCESS) { 1.120 + fprintf(stderr, "detach: pid_for_task(%d) failed (%d)\n", pid, k_res); 1.121 + } 1.122 + else { 1.123 + int res = ptrace(PT_DETACH, pid, 0, 0); 1.124 + if (res < 0) { 1.125 + fprintf(stderr, "detach: ptrace(PT_DETACH, %d) failed (%d)\n", pid, res); 1.126 + } 1.127 + } 1.128 + 1.129 mach_port_deallocate(mach_task_self(), gTask); 1.130 id symbolicator = getSymbolicator(env, this_obj); 1.131 if (symbolicator != nil) {