agent/src/os/bsd/MacosxDebuggerLocal.m

changeset 4603
5d5c577296fd
parent 4564
758935f7c23f
child 4688
40b7c6b800ab
     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) {

mercurial