< prev index next >

hotspot/src/jdk.hotspot.agent/macosx/native/libsaproc/MacosxDebuggerLocal.m

Print this page

        

@@ -31,10 +31,11 @@
 
 #import <mach/mach.h>
 #import <mach/mach_types.h>
 #import <sys/sysctl.h>
 #import <stdio.h>
+#import <string.h>
 #import <stdarg.h>
 #import <stdlib.h>
 #import <strings.h>
 #import <dlfcn.h>
 #import <limits.h>

@@ -55,10 +56,12 @@
 #error UNSUPPORTED_ARCH
 #endif
 
 static jfieldID symbolicatorID = 0; // set in _init0
 static jfieldID taskID = 0; // set in _init0
+static jfieldID tgtExceptionPortID = 0; // set in _init0
+static jfieldID savedExceptionCountID = 0; // set in _init0
 
 static jfieldID p_ps_prochandle_ID = 0;
 static jfieldID loadObjectList_ID = 0;
 static jmethodID listAdd_ID = 0;
 

@@ -67,10 +70,56 @@
 static jmethodID getJavaThreadsInfo_ID = 0;
 
 // indicator if thread id (lwpid_t) was set
 static bool _threads_filled = false;
 
+extern boolean_t mach_exc_server(mach_msg_header_t *input_msg_hdr,
+                                 mach_msg_header_t *output_msg_hdr);
+
+extern kern_return_t catch_mach_exception_raise(
+  mach_port_t exception_port, mach_port_t thread,
+  mach_port_t task, exception_type_t exception,
+  mach_exception_data_t code,
+  mach_msg_type_number_t code_cnt);
+
+extern kern_return_t catch_mach_exception_raise_state(
+  mach_port_t exception_port, exception_type_t exception,
+  const mach_exception_data_t code, mach_msg_type_number_t code_cnt,
+  int *flavor, const thread_state_t old_state,
+  mach_msg_type_number_t old_state_cnt, thread_state_t new_state,
+  mach_msg_type_number_t *new_state_cnt);
+
+extern kern_return_t catch_mach_exception_raise_state_identity(
+  mach_port_t exception_port, mach_port_t thread, mach_port_t task,
+  exception_type_t exception, mach_exception_data_t code,
+  mach_msg_type_number_t code_cnt, int *flavor, thread_state_t old_state,
+  mach_msg_type_number_t old_state_cnt, thread_state_t new_state,
+  mach_msg_type_number_t *new_state_cnt);
+
+static exception_mask_t       saved_masks[EXC_TYPES_COUNT];
+static mach_port_t            saved_ports[EXC_TYPES_COUNT];
+static exception_behavior_t   saved_behaviors[EXC_TYPES_COUNT];
+static thread_state_flavor_t  saved_flavors[EXC_TYPES_COUNT];
+
+static struct rep_msg {
+  mach_msg_header_t header;
+  NDR_record_t ndr;
+  kern_return_t ret_code;
+} rep_msg;
+
+static struct exc_msg {
+  mach_msg_header_t header;
+  mach_msg_body_t msgh_body;
+  mach_msg_port_descriptor_t thread;
+  mach_msg_port_descriptor_t task;
+  NDR_record_t ndr;
+  exception_type_t exception;
+  mach_msg_type_number_t code_cnt;
+  mach_exception_data_t code;
+  char pad[512];
+} exc_msg;
+
 static void putSymbolicator(JNIEnv *env, jobject this_obj, id symbolicator) {
   (*env)->SetLongField(env, this_obj, symbolicatorID, (jlong)(intptr_t)symbolicator);
 }
 
 static id getSymbolicator(JNIEnv *env, jobject this_obj) {

@@ -85,10 +134,31 @@
 static task_t getTask(JNIEnv *env, jobject this_obj) {
   jlong ptr = (*env)->GetLongField(env, this_obj, taskID);
   return (task_t)ptr;
 }
 
+static void putTargetExceptionPort(JNIEnv *env, jobject this_obj,
+                                   mach_port_t tgt_exception_port) {
+  (*env)->SetLongField(env, this_obj, tgtExceptionPortID, (jlong)tgt_exception_port);
+}
+
+static mach_port_t getTargetExceptionPort(JNIEnv *env, jobject this_obj) {
+  jlong ptr = (*env)->GetLongField(env, this_obj, tgtExceptionPortID);
+  return (mach_port_t)ptr;
+}
+
+static void putSavedExceptionTypesCount(JNIEnv *env, jobject this_obj,
+                                        mach_msg_type_number_t saved_exception_types_count) {
+  (*env)->SetLongField(env, this_obj, savedExceptionCountID,
+                       (jlong)saved_exception_types_count);
+}
+
+static mach_msg_type_number_t getSavedExceptionTypesCount(JNIEnv *env, jobject this_obj) {
+  jlong ptr = (*env)->GetLongField(env, this_obj, savedExceptionCountID);
+  return (mach_msg_type_number_t)ptr;
+}
+
 #define CHECK_EXCEPTION_(value) if ((*env)->ExceptionOccurred(env)) { return value; }
 #define CHECK_EXCEPTION if ((*env)->ExceptionOccurred(env)) { return;}
 #define THROW_NEW_DEBUGGER_EXCEPTION_(str, value) { throw_new_debugger_exception(env, str); return value; }
 #define THROW_NEW_DEBUGGER_EXCEPTION(str) { throw_new_debugger_exception(env, str); return;}
 #define CHECK_EXCEPTION_CLEAR if ((*env)->ExceptionOccurred(env)) { (*env)->ExceptionClear(env); } 

@@ -133,10 +203,15 @@
 Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_init0(JNIEnv *env, jclass cls) {
   symbolicatorID = (*env)->GetFieldID(env, cls, "symbolicator", "J");
   CHECK_EXCEPTION;
   taskID = (*env)->GetFieldID(env, cls, "task", "J");
   CHECK_EXCEPTION;
+  tgtExceptionPortID = (*env)->GetFieldID(env, cls, "tgt_exception_port", "J");
+  CHECK_EXCEPTION;
+  savedExceptionCountID = (*env)->GetFieldID(env, cls,
+                                             "saved_exception_types_count", "J");
+  CHECK_EXCEPTION;
 
   // for core file
   p_ps_prochandle_ID = (*env)->GetFieldID(env, cls, "p_ps_prochandle", "J");
   CHECK_EXCEPTION;
   loadObjectList_ID = (*env)->GetFieldID(env, cls, "loadObjectList", "Ljava/util/List;");

@@ -628,75 +703,93 @@
   print_debug("translateTID0: 0x%x -> 0x%x\n", foreign_tid, usable_tid);
     
   return (jint) usable_tid;
 }
 
-
-static bool ptrace_continue(pid_t pid, int signal) {
-  // pass the signal to the process so we don't swallow it
+// attach to a process/thread specified by "pid"
+static bool ptrace_attach(pid_t pid) {
   int res;
-  if ((res = ptrace(PT_CONTINUE, pid, (caddr_t)1, signal)) < 0) {
-    print_error("attach: ptrace(PT_CONTINUE, %d) failed with %d\n", pid, res);
+  if ((res = ptrace(PT_ATTACHEXC, pid, 0, 0)) < 0) {
+    perror("ptrace(PT_ATTACHEXC,...) failed");
     return false;
   }
   return true;
 }
 
-// waits until the ATTACH has stopped the process
-// by signal SIGSTOP
-static bool ptrace_waitpid(pid_t pid) {
-  int ret;
-  int status;
-  while (true) {
-    // Wait for debuggee to stop.
-    ret = waitpid(pid, &status, 0);
-    if (ret >= 0) {
-      if (WIFSTOPPED(status)) {
-        // Any signal will stop the thread, make sure it is SIGSTOP. Otherwise SIGSTOP
-        // will still be pending and delivered when the process is DETACHED and the process
-        // will go to sleep.
-        if (WSTOPSIG(status) == SIGSTOP) {
-          // Debuggee stopped by SIGSTOP.
-          return true;
-        }
-        if (!ptrace_continue(pid, WSTOPSIG(status))) {
-          print_error("attach: Failed to correctly attach to VM. VM might HANG! [PTRACE_CONT failed, stopped by %d]\n", WSTOPSIG(status));
-          return false;
-        }
-      } else {
-        print_error("attach: waitpid(): Child process exited/terminated (status = 0x%x)\n", status);
-        return false;
-      }
-    } else {
-      switch (errno) {
-        case EINTR:
-          continue;
-          break;
-        case ECHILD:
-          print_error("attach: waitpid() failed. Child process pid (%d) does not exist \n", pid);
-          break;
-        case EINVAL:
-          print_error("attach: waitpid() failed. Invalid options argument.\n");
-          break;
-        default:
-          print_error("attach: waitpid() failed. Unexpected error %d\n",errno);
-          break;
-      }
+kern_return_t catch_mach_exception_raise(
+  mach_port_t exception_port, mach_port_t thread_port, mach_port_t task_port,
+  exception_type_t exception_type, mach_exception_data_t codes,
+  mach_msg_type_number_t num_codes) {
+
+  print_debug("catch_mach_exception_raise: Exception port = %d thread_port = %d "
+              "task port %d exc type = %d num_codes %d\n",
+              exception_port, thread_port, task_port, exception_type, num_codes);
+
+  // This message should denote a Unix soft signal, with
+  // 1. the exception type = EXC_SOFTWARE
+  // 2. codes[0] (which is the code) = EXC_SOFT_SIGNAL
+  // 3. codes[1] (which is the sub-code) = SIGSTOP
+  if (!(exception_type == EXC_SOFTWARE &&
+        codes[0] == EXC_SOFT_SIGNAL    &&
+        codes[num_codes -1] == SIGSTOP)) {
+    print_error("catch_mach_exception_raise: Message doesn't denote a Unix "
+                "soft signal. exception_type = %d, codes[0] = %d, "
+                "codes[num_codes -1] = %d\n",
+                exception_type, codes[0], codes[num_codes - 1]);
+    return MACH_RCV_INVALID_TYPE;
+  }
+  return KERN_SUCCESS;
+}
+
+kern_return_t catch_mach_exception_raise_state(
+  mach_port_t exception_port, exception_type_t exception, const mach_exception_data_t code,
+  mach_msg_type_number_t code_cnt, int *flavor, const thread_state_t old_state,
+  mach_msg_type_number_t old_state_cnt, thread_state_t new_state,
+  mach_msg_type_number_t *new_state_cnt) {
+  return MACH_RCV_INVALID_TYPE;
+}
+
+
+kern_return_t catch_mach_exception_raise_state_identity(
+  mach_port_t exception_port, mach_port_t thread, mach_port_t task,
+  exception_type_t exception, mach_exception_data_t code,
+  mach_msg_type_number_t code_cnt, int *flavor,
+  thread_state_t old_state, mach_msg_type_number_t old_state_cnt,
+  thread_state_t new_state, mach_msg_type_number_t *new_state_cnt) {
+  return MACH_RCV_INVALID_TYPE;
+}
+
+// wait to receive an exception message
+static bool wait_for_exception(mach_port_t tgt_exception_port) {
+  kern_return_t result;
+
+  result = mach_msg(&exc_msg.header,
+                    MACH_RCV_MSG,
+                    0,
+                    sizeof(exc_msg),
+                    tgt_exception_port,
+                    MACH_MSG_TIMEOUT_NONE,
+                    MACH_PORT_NULL);
+
+  if (result != MACH_MSG_SUCCESS) {
+    print_error("attach: wait_for_exception: mach_msg() failed: '%s' (%d)\n",
+                mach_error_string(result), result);
       return false;
     }
-  }
-}
 
-// attach to a process/thread specified by "pid"
-static bool ptrace_attach(pid_t pid) {
-  int res;
-  if ((res = ptrace(PT_ATTACHEXC, pid, 0, 0)) < 0) {
-    print_error("ptrace(PT_ATTACHEXC, %d) failed with %d\n", pid, res);
+  if (mach_exc_server(&exc_msg.header, &rep_msg.header) == false) {
+    print_error("attach: wait_for_exception: mach_exc_server failure\n");
     return false;
-  } else {
-    return ptrace_waitpid(pid);
   }
+
+  print_debug("reply msg from mach_exc_server:  ( msg->{bits = %#x, size = %u, "
+              "remote_port = %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, )",
+              rep_msg.header.msgh_bits, rep_msg.header.msgh_size,
+              rep_msg.header.msgh_remote_port, rep_msg.header.msgh_local_port,
+              rep_msg.header.msgh_reserved, rep_msg.header.msgh_id);
+
+  return true;
 }
 
 /*
  * Class:     sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal
  * Method:    attach0

@@ -709,23 +802,102 @@
   print_debug("attach0 called for jpid=%d\n", (int)jpid);
 
 JNF_COCOA_ENTER(env);
 
   kern_return_t result;
+  mach_port_t tgt_exception_port;
   task_t gTask = 0;
+  mach_msg_type_number_t saved_exception_types_count;
+
   result = task_for_pid(mach_task_self(), jpid, &gTask);
   if (result != KERN_SUCCESS) {
     print_error("attach: task_for_pid(%d) failed: '%s' (%d)\n", (int)jpid, mach_error_string(result), result);
-    THROW_NEW_DEBUGGER_EXCEPTION("Can't attach to the process. Could be caused by an incorrect pid or lack of privileges.");
+    THROW_NEW_DEBUGGER_EXCEPTION(
+      "Can't attach to the process. Could be caused by an incorrect pid or lack of privileges.");
   }
   putTask(env, this_obj, gTask);
 
+  // Allocate an exception port.
+  result = mach_port_allocate(mach_task_self(),
+                              MACH_PORT_RIGHT_RECEIVE,
+                              &tgt_exception_port);
+  if (result != KERN_SUCCESS) {
+    print_error("attach: mach_port_allocate() failed: '%s' (%d)\n",
+                mach_error_string(result), result);
+    THROW_NEW_DEBUGGER_EXCEPTION(
+      "Can't attach to the process. Couldn't allocate an exception port.");
+  }
+
+  putTargetExceptionPort(env, this_obj, tgt_exception_port);
+
+  result = mach_port_insert_right (mach_task_self(),
+                                   tgt_exception_port,
+                                   tgt_exception_port,
+                                   MACH_MSG_TYPE_MAKE_SEND);
+  if (result != KERN_SUCCESS) {
+    print_error("attach: mach_post_insert_right() failed: '%s' (%d)\n",
+                mach_error_string(result), result);
+    THROW_NEW_DEBUGGER_EXCEPTION(
+      "Can't attach to the process. Couldn't insert the target exception port.");
+  }
+
+  result = task_get_exception_ports(gTask,
+                                    EXC_MASK_ALL,
+                                    saved_masks,
+                                    &saved_exception_types_count,
+                                    saved_ports,
+                                    saved_behaviors,
+                                    saved_flavors);
+
+  if (result != KERN_SUCCESS) {
+    print_error("attach: task_get_exception_ports() failed: '%s' (%d)\n",
+                mach_error_string(result), result);
+    THROW_NEW_DEBUGGER_EXCEPTION(
+      "Can't attach to the process. Could not get the target exception ports.");
+  }
+
+  // register the exception port with the target process
+  result = task_set_exception_ports(gTask,
+                         EXC_MASK_ALL,
+                         tgt_exception_port,
+                         EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
+                         THREAD_STATE_NONE);
+
+  if (result != KERN_SUCCESS) {
+    print_error("attach: task_set_exception_ports() failed: '%s' (%d)\n",
+                mach_error_string(result), result);
+    mach_port_deallocate(mach_task_self(), gTask);
+    mach_port_deallocate(mach_task_self(), tgt_exception_port);
+    THROW_NEW_DEBUGGER_EXCEPTION(
+      "Can't attach to the process. Could not register the exception port "
+      "with the target process.");
+  }
+
+  putSavedExceptionTypesCount(env, this_obj, saved_exception_types_count);
+
   // use ptrace to stop the process
   // on os x, ptrace only needs to be called on the process, not the individual threads
   if (ptrace_attach(jpid) != true) {
     mach_port_deallocate(mach_task_self(), gTask);
-    THROW_NEW_DEBUGGER_EXCEPTION("Can't attach to the process");
+    mach_port_deallocate(mach_task_self(), tgt_exception_port);
+    THROW_NEW_DEBUGGER_EXCEPTION("Can't ptrace attach to the process");
+  }
+
+  if (wait_for_exception(tgt_exception_port) != true) {
+    mach_port_deallocate(mach_task_self(), gTask);
+    mach_port_deallocate(mach_task_self(), tgt_exception_port);
+    THROW_NEW_DEBUGGER_EXCEPTION("Issues with exception wait.");
+  }
+
+  // suspend all the threads in the task
+  result = task_suspend(gTask);
+  if (result != KERN_SUCCESS) {
+    print_error("attach: task_suspend() failed: '%s' (%d)\n",
+                mach_error_string(result), result);
+    mach_port_deallocate(mach_task_self(), gTask);
+    mach_port_deallocate(mach_task_self(), tgt_exception_port);
+    THROW_NEW_DEBUGGER_EXCEPTION("Can't suspend all the threads in the task.");
   }
 
   id symbolicator = nil;
   id jrsSymbolicator = objc_lookUpClass("JRSSymbolicator");
   if (jrsSymbolicator != nil) {

@@ -736,10 +908,12 @@
     CFRetain(symbolicator); // pin symbolicator while in java heap
   }
 
   putSymbolicator(env, this_obj, symbolicator);
   if (symbolicator == nil) {
+    mach_port_deallocate(mach_task_self(), gTask);
+    mach_port_deallocate(mach_task_self(), tgt_exception_port);
     THROW_NEW_DEBUGGER_EXCEPTION("Can't attach symbolicator to the process");
   }
 
 JNF_COCOA_EXIT(env);
 }

@@ -818,27 +992,67 @@
   if (ph != NULL && ph->core != NULL) {
      Prelease(ph);
      return;
   }
 JNF_COCOA_ENTER(env);
+
   task_t gTask = getTask(env, this_obj);
+  mach_port_t tgt_exception_port = getTargetExceptionPort(env, this_obj);
+  mach_msg_type_number_t saved_exception_types_count = getSavedExceptionTypesCount(env, this_obj);
 
   // detach from the ptraced process causing it to resume execution
   int pid;
-  kern_return_t k_res;
+  kern_return_t k_res = 0;
   k_res = pid_for_task(gTask, &pid);
   if (k_res != KERN_SUCCESS) {
     print_error("detach: pid_for_task(%d) failed (%d)\n", pid, k_res);
   }
   else {
-    int res = ptrace(PT_DETACH, pid, 0, 0);
+    int res = ptrace(PT_DETACH, pid, (caddr_t)1, 0);
     if (res < 0) {
-      print_error("detach: ptrace(PT_DETACH, %d) failed (%d)\n", pid, res);
+      perror("ptrace(PT_DETACH,...) failed");
+      mach_port_deallocate(mach_task_self(), tgt_exception_port);
+      mach_port_deallocate(mach_task_self(), gTask);
+      THROW_NEW_DEBUGGER_EXCEPTION("Cannot detach.");
     }
   }
 
+  for (uint32_t i = 0; i < saved_exception_types_count; ++i) {
+    k_res = task_set_exception_ports(gTask,
+                                     saved_masks[i],
+                                     saved_ports[i],
+                                     saved_behaviors[i],
+                                     saved_flavors[i]);
+    if (k_res != KERN_SUCCESS) {
+      print_error("detach: task_set_exception_ports failed with %d while "
+                  "restoring the target exception ports.\n", k_res);
+      mach_port_deallocate(mach_task_self(), tgt_exception_port);
+      mach_port_deallocate(mach_task_self(), gTask);
+      THROW_NEW_DEBUGGER_EXCEPTION("Cannot detach.");
+    }
+  }
+
+  // reply to the previous exception message
+  k_res = mach_msg(&rep_msg.header,
+                   MACH_SEND_MSG| MACH_SEND_INTERRUPT,
+                   rep_msg.header.msgh_size,
+                   0,
+                   MACH_PORT_NULL,
+                   MACH_MSG_TIMEOUT_NONE,
+                   MACH_PORT_NULL);
+  if (k_res != MACH_MSG_SUCCESS) {
+    mach_port_deallocate(mach_task_self(), tgt_exception_port);
   mach_port_deallocate(mach_task_self(), gTask);
+    print_error("detach: mach_msg() for replying to pending exceptions failed: '%s' (%d)\n",
+                 mach_error_string(k_res), k_res);
+    THROW_NEW_DEBUGGER_EXCEPTION("Cannot detach.");
+    return;
+  }
+
+  mach_port_deallocate(mach_task_self(), tgt_exception_port);
+  mach_port_deallocate(mach_task_self(), gTask);
+
   id symbolicator = getSymbolicator(env, this_obj);
   if (symbolicator != nil) {
     CFRelease(symbolicator);
   }
 JNF_COCOA_EXIT(env);
< prev index next >