< prev index next >
src/jdk.hotspot.agent/macosx/native/libsaproc/MacosxDebuggerLocal.m
Print this page
*** 1,7 ****
/*
! * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
--- 1,7 ----
/*
! * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*** 31,40 ****
--- 31,41 ----
#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>
*** 67,76 ****
--- 68,134 ----
static jmethodID getJavaThreadsInfo_ID = 0;
// indicator if thread id (lwpid_t) was set
static bool _threads_filled = false;
+ // mach_exc_server defined in the generated mach_excServer.c
+ extern boolean_t mach_exc_server(mach_msg_header_t *input_msg_hdr,
+ mach_msg_header_t *output_msg_hdr);
+
+ 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);
+
+ 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);
+
+ 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 struct exception_saved_state {
+ exception_mask_t saved_masks[EXC_TYPES_COUNT];
+ mach_port_t saved_ports[EXC_TYPES_COUNT];
+ exception_behavior_t saved_behaviors[EXC_TYPES_COUNT];
+ thread_state_flavor_t saved_flavors[EXC_TYPES_COUNT];
+ mach_msg_type_number_t saved_exception_types_count;
+ } exception_saved_state;
+
+ static mach_port_t tgt_exception_port;
+
+ // Mirrors __Reply__mach_exception_raise_t generated in mach_excServer.c
+ static struct rep_msg {
+ mach_msg_header_t header;
+ NDR_record_t ndr;
+ kern_return_t ret_code;
+ } rep_msg;
+
+ // Mirrors __Request__mach_exception_raise_t generated in mach_excServer.c
+ // with a large trailing pad to avoid MACH_MSG_RCV_TOO_LARGE
+ static struct exc_msg {
+ mach_msg_header_t header;
+ // start of the kernel processed data
+ mach_msg_body_t msgh_body;
+ mach_msg_port_descriptor_t thread;
+ mach_msg_port_descriptor_t task;
+ // end of the kernel processed data
+ NDR_record_t ndr;
+ exception_type_t exception;
+ mach_msg_type_number_t code_cnt;
+ mach_exception_data_t code; // an array of int64_t
+ 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) {
*** 628,709 ****
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
! 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);
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;
}
return false;
}
- }
- }
! // attach to a process/thread specified by "pid"
! static bool ptrace_attach(pid_t pid) {
! int res;
! #ifdef __clang__
! #pragma clang diagnostic push
! #pragma clang diagnostic ignored "-Wdeprecated-declarations"
! #endif
! if ((res = ptrace(PT_ATTACH, pid, 0, 0)) < 0) {
! print_error("ptrace(PT_ATTACH, %d) failed with %d\n", pid, res);
! #ifdef __clang__
! #pragma clang diagnostic pop
! #endif
! return false;
! } else {
! return ptrace_waitpid(pid);
! }
}
/*
* Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal
* Method: attach0
--- 686,785 ----
print_debug("translateTID0: 0x%x -> 0x%x\n", foreign_tid, usable_tid);
return (jint) usable_tid;
}
+ // attach to a process/thread specified by "pid"
+ static bool ptrace_attach(pid_t pid) {
+ errno = 0;
+ ptrace(PT_ATTACHEXC, pid, 0, 0);
! if (errno != 0) {
! print_error("ptrace_attach: ptrace(PT_ATTACHEXC,...) failed: %s", strerror(errno));
return false;
}
return true;
}
! 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, num_codes = %d\n",
! exception_type, codes[0], codes[num_codes - 1], num_codes);
! 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() {
! 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;
}
!
! if (mach_exc_server(&exc_msg.header, &rep_msg.header) == false ||
! rep_msg.ret_code != KERN_SUCCESS) {
! print_error("attach: wait_for_exception: mach_exc_server failure\n");
! if (rep_msg.ret_code != KERN_SUCCESS) {
! print_error("catch_mach_exception_raise() failed '%s' (%d)\n",
! mach_error_string(rep_msg.ret_code), rep_msg.ret_code);
}
return false;
}
! 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
*** 717,738 ****
JNF_COCOA_ENTER(env);
kern_return_t result;
task_t gTask = 0;
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.");
}
putTask(env, this_obj, gTask);
// 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");
}
id symbolicator = nil;
id jrsSymbolicator = objc_lookUpClass("JRSSymbolicator");
if (jrsSymbolicator != nil) {
--- 793,893 ----
JNF_COCOA_ENTER(env);
kern_return_t result;
task_t gTask = 0;
+
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.");
}
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() for tgt_exception_port 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.");
+ }
+
+ // Enable the new exception port to send messages.
+ 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_port_insert_right() failed for port 0x%x: '%s' (%d)\n",
+ tgt_exception_port, mach_error_string(result), result);
+ THROW_NEW_DEBUGGER_EXCEPTION(
+ "Can't attach to the process. Couldn't add send privileges to the exception port.");
+ }
+
+ // Save the existing original exception ports registered with the target
+ // process (for later restoration while detaching from the process).
+ result = task_get_exception_ports(gTask,
+ EXC_MASK_ALL,
+ exception_saved_state.saved_masks,
+ &exception_saved_state.saved_exception_types_count,
+ exception_saved_state.saved_ports,
+ exception_saved_state.saved_behaviors,
+ exception_saved_state.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 to be used for all future exceptions 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 -- port 0x%x: '%s' (%d)\n",
+ tgt_exception_port, 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.");
+ }
+
// 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) {
+ print_error("attach: ptrace failure in attaching to %d\n", (int)jpid);
mach_port_deallocate(mach_task_self(), gTask);
! mach_port_deallocate(mach_task_self(), tgt_exception_port);
! THROW_NEW_DEBUGGER_EXCEPTION("Can't ptrace attach to the process");
! }
!
! if (wait_for_exception() != true) {
! 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. Issues with reception of the exception message.");
! }
!
! // 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 attach. Unable to suspend all the threads in the task.");
}
id symbolicator = nil;
id jrsSymbolicator = objc_lookUpClass("JRSSymbolicator");
if (jrsSymbolicator != nil) {
*** 743,752 ****
--- 898,909 ----
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);
}
*** 809,818 ****
--- 966,988 ----
(*env)->ReleaseStringUTFChars(env, execName, execName_cstr);
(*env)->ReleaseStringUTFChars(env, coreName, coreName_cstr);
fillLoadObjects(env, this_obj, ph);
}
+ static void detach_cleanup(task_t gTask, JNIEnv *env, jobject this_obj, bool throw_exception) {
+ 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);
+ }
+ if (throw_exception) {
+ THROW_NEW_DEBUGGER_EXCEPTION("Cannot detach.");
+ }
+ }
+
/*
* Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal
* Method: detach0
* Signature: ()V
*/
*** 825,852 ****
if (ph != NULL && ph->core != NULL) {
Prelease(ph);
return;
}
JNF_COCOA_ENTER(env);
task_t gTask = getTask(env, this_obj);
// detach from the ptraced process causing it to resume execution
int pid;
- kern_return_t k_res;
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);
! if (res < 0) {
! print_error("detach: ptrace(PT_DETACH, %d) failed (%d)\n", pid, res);
}
}
! mach_port_deallocate(mach_task_self(), gTask);
! id symbolicator = getSymbolicator(env, this_obj);
! if (symbolicator != nil) {
! CFRelease(symbolicator);
}
JNF_COCOA_EXIT(env);
}
--- 995,1051 ----
if (ph != NULL && ph->core != NULL) {
Prelease(ph);
return;
}
JNF_COCOA_ENTER(env);
+
task_t gTask = getTask(env, this_obj);
+ kern_return_t k_res = 0;
+
+ // Restore the pre-saved original exception ports registered with the target process
+ for (uint32_t i = 0; i < exception_saved_state.saved_exception_types_count; ++i) {
+ k_res = task_set_exception_ports(gTask,
+ exception_saved_state.saved_masks[i],
+ exception_saved_state.saved_ports[i],
+ exception_saved_state.saved_behaviors[i],
+ exception_saved_state.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);
+ detach_cleanup(gTask, env, this_obj, true);
+ }
+ }
// detach from the ptraced process causing it to resume execution
int pid;
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);
+ detach_cleanup(gTask, env, this_obj, true);
}
else {
! errno = 0;
! ptrace(PT_DETACH, pid, (caddr_t)1, 0);
! if (errno != 0) {
! print_error("detach: ptrace(PT_DETACH,...) failed: %s", strerror(errno));
! detach_cleanup(gTask, env, this_obj, true);
}
}
! // 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) {
! print_error("detach: mach_msg() for replying to pending exceptions failed: '%s' (%d)\n",
! mach_error_string(k_res), k_res);
! detach_cleanup(gTask, env, this_obj, true);
}
+
+ detach_cleanup(gTask, env, this_obj, false);
+
JNF_COCOA_EXIT(env);
}
< prev index next >