/** * Returns the list of all Java threads. * * @param[out] threads resulting threads list * @param[out] count_ptr number of threads in the resulting list */ IDATA VMCALL jthread_get_all_threads(jthread ** threads, jint * count_ptr) { assert(threads); assert(count_ptr); hythread_group_t java_thread_group = get_java_thread_group(); assert(java_thread_group); hythread_iterator_t iterator = hythread_iterator_create(java_thread_group); IDATA count = hythread_iterator_size(iterator); IDATA java_thread_count = 0; for (IDATA i = 0; i < count; i++) { hythread_t native_thread = hythread_iterator_next(&iterator); vm_thread_t vm_thread = jthread_get_vm_thread(native_thread); if (vm_thread && vm_thread->java_thread) { java_thread_count++; } } jthread *java_threads = (jthread*)malloc(sizeof(jthread) * java_thread_count); if (!java_threads) { hythread_iterator_release(&iterator); return TM_ERROR_OUT_OF_MEMORY; } hythread_iterator_reset(&iterator); java_thread_count = 0; for (IDATA i = 0; i < count; i++) { hythread_t native_thread = hythread_iterator_next(&iterator); vm_thread_t vm_thread = jthread_get_vm_thread(native_thread); if (vm_thread && vm_thread->java_thread) { hythread_suspend_disable(); ObjectHandle thr = oh_allocate_local_handle(); assert(thr); thr->object = vm_thread->java_thread->object; assert(thr->object); hythread_suspend_enable(); java_threads[java_thread_count++] = thr; } } *threads = java_threads; *count_ptr = (jint)java_thread_count; IDATA status = hythread_iterator_release(&iterator); return status; } // jthread_get_all_threads
void ti_enumerate_roots(TIEnv *ti_env, hythread_iterator_t iterator) { TRACE2("ti.trace", "enumerating roots"); // FIXME: weird function table manipulations void (*save_gc_add_root_set_entry) (Managed_Object_Handle *ref, Boolean pinned); void (*save_gc_add_weak_root_set_entry) (Managed_Object_Handle *ref1, Boolean pinned, Boolean short_weak); void (*save_gc_add_root_set_entry_interior_pointer) (void **slot, int offset, Boolean pinned); void (*save_gc_add_compressed_root_set_entry) (U_32 *ref, Boolean pinned); // save away old values save_gc_add_root_set_entry = gc_add_root_set_entry; save_gc_add_weak_root_set_entry = gc_add_weak_root_set_entry; save_gc_add_root_set_entry_interior_pointer = gc_add_root_set_entry_interior_pointer; save_gc_add_compressed_root_set_entry = gc_add_compressed_root_set_entry; // hijack ti enumeration functions gc_add_root_set_entry = ti_add_root_set_entry; gc_add_weak_root_set_entry = ti_add_weak_root_set_entry; gc_add_root_set_entry_interior_pointer = ti_add_root_set_entry_interior_pointer; gc_add_compressed_root_set_entry = ti_add_compressed_root_set_entry; // Run through list of active threads and enumerate each one of them. hythread_t tm_thread = hythread_iterator_next(&iterator); while (tm_thread && !ti_env->iteration_state->abort) { vm_thread_t thread = jthread_get_vm_thread(tm_thread); if (thread) ti_enumerate_thread(ti_env, thread); tm_thread = hythread_iterator_next(&iterator); } // finally, process all the global refs ti_enumerate_globals(ti_env); // restore original enumeration functions gc_add_root_set_entry = save_gc_add_root_set_entry; gc_add_weak_root_set_entry = save_gc_add_weak_root_set_entry; gc_add_root_set_entry_interior_pointer = save_gc_add_root_set_entry_interior_pointer; gc_add_compressed_root_set_entry = save_gc_add_compressed_root_set_entry; TRACE2("ti.trace", "completed root enumeration"); }
/** * Returns the <code>thread</code>'s state according * to JVMTI specification. See <a href=http://java.sun.com/j2se/1.5.0/docs/guide/jvmti/jvmti.html#GetThreadState> * JVMTI Specification </a> for more details. * * @param[in] java_thread thread those state is to be queried * @param[out] state resulting thread state * */ IDATA VMCALL jthread_get_jvmti_state(jthread java_thread, jint * state) { assert(state); assert(java_thread); hythread_t native_thread = jthread_get_native_thread(java_thread); vm_thread_t vm_thread = jthread_get_vm_thread(native_thread); *state = 0; if (!native_thread) { // Not started yet return TM_ERROR_NONE; } if (hythread_is_alive(native_thread)) { *state |= JVMTI_THREAD_STATE_ALIVE; } if (hythread_is_runnable(native_thread)) { *state |= JVMTI_THREAD_STATE_RUNNABLE; } if (hythread_is_blocked_on_monitor_enter(native_thread)) { *state |= JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER; } if (hythread_is_waiting(native_thread)) { *state |= JVMTI_THREAD_STATE_WAITING; } if (hythread_is_waiting_indefinitely(native_thread)) { *state |= JVMTI_THREAD_STATE_WAITING_INDEFINITELY; } if (hythread_is_waiting_with_timeout(native_thread)) { *state |= JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT; } if (hythread_is_sleeping(native_thread)) { *state |= JVMTI_THREAD_STATE_SLEEPING; } if (hythread_is_in_monitor_wait(native_thread)) { *state |= JVMTI_THREAD_STATE_IN_OBJECT_WAIT; } if (hythread_is_parked(native_thread)) { *state |= JVMTI_THREAD_STATE_PARKED; } if (hythread_interrupted(native_thread)) { *state |= JVMTI_THREAD_STATE_INTERRUPTED; } if (hythread_is_in_native(native_thread)) { *state |= JVMTI_THREAD_STATE_IN_NATIVE; } if (hythread_is_terminated(native_thread)) { *state |= JVMTI_THREAD_STATE_TERMINATED; } if (vm_thread && vm_thread->suspend_flag) { *state |= JVMTI_THREAD_STATE_SUSPENDED; } return TM_ERROR_NONE; } // jthread_get_jvmti_state
static bool ncai_thread_is_alive(hythread_t hythread) { if (!hythread) return false; vm_thread_t vm_thread = jthread_get_vm_thread(hythread); if (!vm_thread || !vm_thread->java_thread) return false; return hythread_is_alive(hythread); }
ncaiError JNICALL ncaiGetThreadInfo(ncaiEnv *env, ncaiThread thread, ncaiThreadInfo *info_ptr) { TRACE2("ncai.thread", "ncaiGetThreadsInfo called"); SuspendEnabledChecker sec; if (env == NULL) return NCAI_ERROR_INVALID_ENVIRONMENT; if (info_ptr == NULL) return NCAI_ERROR_NULL_POINTER; if (thread == NULL) return NCAI_ERROR_INVALID_THREAD; hythread_t hythread = reinterpret_cast<hythread_t>(thread); if (!ncai_thread_is_alive(hythread)) return NCAI_ERROR_THREAD_NOT_ALIVE; jthread java_thread = jthread_get_java_thread(hythread); if (java_thread != NULL) { JNIEnv* jni_env = jthread_get_vm_thread(hythread)->jni_env; jclass cl = GetObjectClass(jni_env, java_thread); jmethodID id = jni_env->GetMethodID(cl, "getName","()Ljava/lang/String;"); jstring name = jni_env->CallObjectMethod(java_thread, id); info_ptr->name = (char *)jni_env->GetStringUTFChars(name, NULL); info_ptr->kind = NCAI_THREAD_JAVA; return NCAI_ERROR_NONE; } info_ptr->kind = NCAI_THREAD_VM_INTERNAL; const char* name_int = "native_0x"; size_t name_len = strlen(name_int) + 4 + 1; info_ptr->name = (char*)ncai_alloc(name_len); assert(info_ptr->name); sprintf(info_ptr->name, "%s%04X", name_int, hythread_get_id(hythread)); return NCAI_ERROR_NONE; }
/** * Returns the number of all Java threads. * * @param[out] count_ptr number of threads. */ IDATA VMCALL jthread_get_thread_count(jint * count_ptr) { assert(count_ptr); hythread_group_t java_thread_group = get_java_thread_group(); assert(java_thread_group); hythread_iterator_t iterator = hythread_iterator_create(java_thread_group); IDATA count = hythread_iterator_size(iterator); IDATA java_thread_count = 0; for (IDATA i = 0; i < count; i++) { hythread_t native_thread = hythread_iterator_next(&iterator); vm_thread_t vm_thread = jthread_get_vm_thread(native_thread); if (vm_thread) { java_thread_count++; } } *count_ptr = (jint)java_thread_count; IDATA status = hythread_iterator_release(&iterator); return status; } // jthread_get_thread_count
ncaiError JNICALL ncaiTerminateThread(ncaiEnv *env, ncaiThread thread) { TRACE2("ncai.thread", "TerminateThread called"); SuspendEnabledChecker sec; if (env == NULL) return NCAI_ERROR_INVALID_ENVIRONMENT; if (thread == NULL) return NCAI_ERROR_INVALID_THREAD; hythread_t hythread = reinterpret_cast<hythread_t>(thread); hythread_t self = hythread_self(); if (hythread == self) return NCAI_ERROR_INVALID_THREAD; assert(thread); // grab hythread global lock hythread_global_lock(); if (!ncai_thread_is_alive(hythread)) { hythread_global_unlock(); return NCAI_ERROR_THREAD_NOT_ALIVE; } IDATA UNUSED status = jthread_vm_detach(jthread_get_vm_thread(hythread)); assert(status == TM_ERROR_NONE); hythread_cancel(hythread); // release hythread global lock hythread_global_unlock(); return NCAI_ERROR_NONE; }
/** * Returns the owner of the lock associated with the given monitor. * * If the given monitor is not owned by any thread, NULL is returned. * * @param[in] monitor monitor those owner needs to be determined * @param[out] lock_owner thread which owns the monitor */ IDATA VMCALL jthread_get_lock_owner(jobject monitor, jthread * lock_owner) { assert(monitor); assert(lock_owner); *lock_owner = NULL; IDATA status = TM_ERROR_NONE; hythread_suspend_disable(); hythread_thin_monitor_t *lockword = vm_object_get_lockword_addr(monitor); hythread_t native_thread = hythread_thin_monitor_get_owner(lockword); if (native_thread) { vm_thread_t vm_thread = jthread_get_vm_thread(native_thread); if (vm_thread) { *lock_owner = vm_thread->java_thread; } else { status = TM_ERROR_ILLEGAL_STATE; } } hythread_suspend_enable(); return status; } // jthread_get_lock_owner
/** * Detaches current thread from VM. */ jint vm_detach(jobject java_thread) { assert(hythread_is_suspend_enabled()); hythread_t native_thread = jthread_get_native_thread(java_thread); assert(native_thread); vm_thread_t p_vm_thread = jthread_get_vm_thread(native_thread); assert(p_vm_thread); // Send Thread End event if(jvmti_should_report_event(JVMTI_EVENT_THREAD_END)) { jvmti_send_thread_start_end_event(p_vm_thread, 0); } // change java_status for native thread native_thread->java_status = TM_STATUS_ALLOCATED; if (native_thread == hythread_self()) { // Notify GC about thread detaching. // FIXME - GC notify detach thread works for current thread only gc_thread_kill(&p_vm_thread->_gc_private_information); } if (ti_is_enabled()) { apr_status_t UNREF status; status = port_vmem_free( p_vm_thread->jvmti_thread.jvmti_jit_breakpoints_handling_buffer, TM_JVMTI_MAX_BUFFER_SIZE); assert(status == APR_SUCCESS); } // Destroy current VM_thread pool and zero VM_thread structure jthread_deallocate_vm_thread_pool(p_vm_thread); return JNI_OK; }
vm_thread_t jthread_get_vm_thread_ptr_safe(jobject thread_obj) { hythread_t native = jthread_get_native_thread(thread_obj); return jthread_get_vm_thread(native); }
/* * Set Step Mode for given thread * If thread is NULL, set mode for all known threads */ ncaiError JNICALL ncaiSetStepMode(ncaiEnv* env, ncaiThread thread, ncaiStepMode mode) { TRACE2("ncai.event", "SetStepMode called," << "thread = " << thread << " mode = " << mode); SuspendEnabledChecker sec; if (mode < NCAI_STEP_OFF || mode > NCAI_STEP_OUT) return NCAI_ERROR_ILLEGAL_ARGUMENT; GlobalNCAI* ncai = VM_Global_State::loader_env->NCAI; hythread_t hythread = reinterpret_cast<hythread_t>(thread); VMBreakPoints* vm_brpt = ((NCAIEnv*)env)->ti_env->vm->vm_env->TI->vm_brpt; vm_thread_t vm_thread = jthread_get_vm_thread(hythread); assert(vm_thread); if (vm_thread == NULL) return NCAI_ERROR_INTERNAL; jvmti_thread_t jvmti_thread = &vm_thread->jvmti_thread; LMAutoUnlock lock(vm_brpt->get_lock()); if (thread) { ncai_check_alloc_ss_data(jvmti_thread); jvmti_thread->ncai_ss->use_local_mode = true; jvmti_thread->ncai_ss->step_mode = mode; jvmti_thread->ncai_ss->flag_out = false; return NCAI_ERROR_NONE; } ncaiThread* threads; jint thread_count; ncaiError err = ncaiGetAllThreads((ncaiEnv*)env, &thread_count, &threads); if (err != NCAI_ERROR_NONE) return err; assert(thread_count > 0); for (jint i = 0; i < thread_count; i++) { hythread = (hythread_t)threads[i]; vm_thread = jthread_get_vm_thread(hythread); if (!vm_thread) continue; ncai_check_alloc_ss_data(&vm_thread->jvmti_thread); vm_thread->jvmti_thread.ncai_ss->use_local_mode = false; vm_thread->jvmti_thread.ncai_ss->flag_out = false; } ncai->step_mode = mode; return NCAI_ERROR_NONE; }