/* * Copyright (c) 2003, 2022, 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. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. *
*/
/////////////////////////////////////////////////////////////// // // JvmtiEventTransition // // TO DO -- // more handle purging
// Use this for JavaThreads and state is _thread_in_vm. class JvmtiJavaThreadEventTransition : StackObj { private:
ResourceMark _rm;
ThreadToNativeFromVM _transition;
HandleMark _hm;
// For JavaThreads which are not in _thread_in_vm state // and other system threads use this. class JvmtiThreadEventTransition : StackObj { private:
ResourceMark _rm;
HandleMark _hm;
JavaThreadState _saved_state;
JavaThread *_jthread;
class JvmtiEventMark : public StackObj { private:
JavaThread *_thread;
JNIEnv* _jni_env;
JvmtiThreadState::ExceptionState _saved_exception_state;
public:
JvmtiEventMark(JavaThread *thread) : _thread(thread),
_jni_env(thread->jni_environment()),
_saved_exception_state(JvmtiThreadState::ES_CLEARED) {
JvmtiThreadState *state = thread->jvmti_thread_state(); // we are before an event. // Save current jvmti thread exception state. if (state != NULL) {
_saved_exception_state = state->get_exception_state();
}
thread->push_jni_handle_block();
assert(thread == JavaThread::current(), "thread must be current!");
thread->frame_anchor()->make_walkable();
};
JvmtiThreadState* state = _thread->jvmti_thread_state(); // we are continuing after an event. if (state != NULL) { // Restore the jvmti thread exception state.
state->restore_exception_state(_saved_exception_state);
}
}
// interpreter generator needs the address of the counter
address JvmtiExport::get_field_access_count_addr() { // We don't grab a lock because we don't want to // serialize field access between all threads. This means that a // thread on another processor can see the wrong count value and // may either miss making a needed call into post_field_access() // or will make an unneeded call into post_field_access(). We pay // this price to avoid slowing down the VM when we aren't watching // field accesses. // Other access/mutation safe by virtue of being in VM state. return (address)(&_field_access_count);
}
// // field modification management //
// interpreter generator needs the address of the counter
address JvmtiExport::get_field_modification_count_addr() { // We don't grab a lock because we don't // want to serialize field modification between all threads. This // means that a thread on another processor can see the wrong // count value and may either miss making a needed call into // post_field_modification() or will make an unneeded call into // post_field_modification(). We pay this price to avoid slowing // down the VM when we aren't watching field modifications. // Other access/mutation safe by virtue of being in VM state. return (address)(&_field_modification_count);
}
/////////////////////////////////////////////////////////////// // Functions needed by java.lang.instrument for starting up javaagent. ///////////////////////////////////////////////////////////////
jint
JvmtiExport::get_jvmti_interface(JavaVM *jvm, void **penv, jint version) { // The JVMTI_VERSION_INTERFACE_JVMTI part of the version number // has already been validated in JNI GetEnv(). int major, minor, micro;
// micro version doesn't matter here (yet?)
decode_version_values(version, &major, &minor, µ); switch (major) { case 1: switch (minor) { case 0: // version 1.0.<micro> is recognized case 1: // version 1.1.<micro> is recognized case 2: // version 1.2.<micro> is recognized break;
default: return JNI_EVERSION; // unsupported minor version number
} break; case 9: switch (minor) { case 0: // version 9.0.<micro> is recognized break; default: return JNI_EVERSION; // unsupported minor version number
} break; case 11: switch (minor) { case 0: // version 11.0.<micro> is recognized break; default: return JNI_EVERSION; // unsupported minor version number
} break; default: // Starting from 13 we do not care about minor version anymore if (major < 13 || major > VM_Version::vm_major_version()) { return JNI_EVERSION; // unsupported major version number
}
} if (Continuations::enabled()) { // Virtual threads support. There is a performance impact when VTMS transitions are enabled.
java_lang_VirtualThread::set_notify_jvmti_events(true); if (JvmtiEnv::get_phase() == JVMTI_PHASE_LIVE) {
ThreadInVMfromNative __tiv(JavaThread::current());
java_lang_VirtualThread::init_static_notify_jvmti_events();
}
}
if (JvmtiEnv::get_phase() == JVMTI_PHASE_LIVE) {
JavaThread* current_thread = JavaThread::current(); // transition code: native to VM
ThreadInVMfromNative __tiv(current_thread);
VM_ENTRY_BASE(jvmtiEnv*, JvmtiExport::get_jvmti_interface, current_thread)
debug_only(VMNativeEntryWrapper __vew;)
JvmtiEnv *jvmti_env = JvmtiEnv::create_a_jvmti(version);
*penv = jvmti_env->jvmti_external(); // actual type is jvmtiEnv* -- not to be confused with JvmtiEnv* return JNI_OK;
} elseif (JvmtiEnv::get_phase() == JVMTI_PHASE_ONLOAD) { // not live, no thread to transition
JvmtiEnv *jvmti_env = JvmtiEnv::create_a_jvmti(version);
*penv = jvmti_env->jvmti_external(); // actual type is jvmtiEnv* -- not to be confused with JvmtiEnv* return JNI_OK;
} else { // Called at the wrong time
*penv = NULL; return JNI_EDETACHED;
}
}
void
JvmtiExport::add_default_read_edges(Handle h_module, TRAPS) { if (!Universe::is_module_initialized()) { return; // extra safety
}
assert(!h_module.is_null(), "module should always be set");
jvmtiError
JvmtiExport::add_module_reads(Handle module, Handle to_module, TRAPS) { if (!Universe::is_module_initialized()) { return JVMTI_ERROR_NONE; // extra safety
}
assert(!module.is_null(), "module should always be set");
assert(!to_module.is_null(), "to_module should always be set");
jvmtiError
JvmtiExport::add_module_exports(Handle module, Handle pkg_name, Handle to_module, TRAPS) { if (!Universe::is_module_initialized()) { return JVMTI_ERROR_NONE; // extra safety
}
assert(!module.is_null(), "module should always be set");
assert(!to_module.is_null(), "to_module should always be set");
assert(!pkg_name.is_null(), "pkg_name should always be set");
jvmtiError
JvmtiExport::add_module_opens(Handle module, Handle pkg_name, Handle to_module, TRAPS) { if (!Universe::is_module_initialized()) { return JVMTI_ERROR_NONE; // extra safety
}
assert(!module.is_null(), "module should always be set");
assert(!to_module.is_null(), "to_module should always be set");
assert(!pkg_name.is_null(), "pkg_name should always be set");
jvmtiError
JvmtiExport::add_module_uses(Handle module, Handle service, TRAPS) { if (!Universe::is_module_initialized()) { return JVMTI_ERROR_NONE; // extra safety
}
assert(!module.is_null(), "module should always be set");
assert(!service.is_null(), "service should always be set");
jvmtiError
JvmtiExport::add_module_provides(Handle module, Handle service, Handle impl_class, TRAPS) { if (!Universe::is_module_initialized()) { return JVMTI_ERROR_NONE; // extra safety
}
assert(!module.is_null(), "module should always be set");
assert(!service.is_null(), "service should always be set");
assert(!impl_class.is_null(), "impl_class should always be set");
// // JVMTI events that the VM posts to the debugger and also startup agent // and call the agent's premain() for java.lang.instrument. //
void JvmtiExport::post_early_vm_start() {
EVT_TRIG_TRACE(JVMTI_EVENT_VM_START, ("Trg Early VM start event triggered" ));
// can now enable some events
JvmtiEventController::vm_start();
JvmtiEnvIterator it; for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { // Only early vmstart envs post early VMStart event if (env->early_vmstart_env() && env->is_enabled(JVMTI_EVENT_VM_START)) {
EVT_TRACE(JVMTI_EVENT_VM_START, ("Evt Early VM start event sent" ));
JavaThread *thread = JavaThread::current();
JvmtiThreadEventMark jem(thread);
JvmtiJavaThreadEventTransition jet(thread);
jvmtiEventVMStart callback = env->callbacks()->VMStart; if (callback != NULL) {
(*callback)(env->jvmti_external(), jem.jni_env());
}
}
}
}
void JvmtiExport::post_vm_start() {
EVT_TRIG_TRACE(JVMTI_EVENT_VM_START, ("Trg VM start event triggered" ));
// can now enable some events
JvmtiEventController::vm_start();
JvmtiEnvIterator it; for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { // Early vmstart envs do not post normal VMStart event if (!env->early_vmstart_env() && env->is_enabled(JVMTI_EVENT_VM_START)) {
EVT_TRACE(JVMTI_EVENT_VM_START, ("Evt VM start event sent" ));
void JvmtiExport::initialize_oop_storage() { // OopStorage needs to be created early in startup and unconditionally // because of OopStorageSet static array indices.
_jvmti_oop_storage = OopStorageSet::create_strong("JVMTI OopStorage", mtServiceability);
_weak_tag_storage = OopStorageSet::create_weak("JVMTI Tag Weak OopStorage", mtServiceability);
_weak_tag_storage->register_num_dead_callback(&JvmtiTagMap::gc_notification);
}
void JvmtiExport::post_vm_initialized() {
EVT_TRIG_TRACE(JVMTI_EVENT_VM_INIT, ("Trg VM init event triggered" ));
// can now enable events
JvmtiEventController::vm_init();
JvmtiEnvIterator it; for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { if (env->is_enabled(JVMTI_EVENT_VM_INIT)) {
EVT_TRACE(JVMTI_EVENT_VM_INIT, ("Evt VM init event sent" ));
char**
JvmtiExport::get_all_native_method_prefixes(int* count_ptr) { // Have to grab JVMTI thread state lock to be sure environment doesn't // go away while we iterate them. No locks during VM bring-up. if (Threads::number_of_threads() == 0 || SafepointSynchronize::is_at_safepoint()) { return JvmtiEnvBase::get_all_native_method_prefixes(count_ptr);
} else {
MutexLocker mu(JvmtiThreadState_lock); return JvmtiEnvBase::get_all_native_method_prefixes(count_ptr);
}
}
// Convert an external thread reference to a JavaThread found on the // specified ThreadsList. The ThreadsListHandle in the caller "protects" // the returned JavaThread *. // // If thread_oop_p is not NULL, then the caller wants to use the oop // after this call so the oop is returned. On success, *jt_pp is set // to the converted JavaThread * and JVMTI_ERROR_NONE is returned. // On error, returns various JVMTI_ERROR_* values. //
jvmtiError
JvmtiExport::cv_external_thread_to_JavaThread(ThreadsList * t_list,
jthread thread,
JavaThread ** jt_pp,
oop * thread_oop_p) {
assert(t_list != NULL, "must have a ThreadsList");
assert(jt_pp != NULL, "must have a return JavaThread pointer"); // thread_oop_p is optional so no assert()
if (thread_oop_p != NULL) {
*thread_oop_p = NULL;
}
oop thread_oop = JNIHandles::resolve_external_guard(thread); if (thread_oop == NULL) { // NULL jthread, GC'ed jthread or a bad JNI handle. return JVMTI_ERROR_INVALID_THREAD;
} // Looks like an oop at this point.
if (!thread_oop->is_a(vmClasses::Thread_klass())) { // The oop is not a java.lang.Thread. return JVMTI_ERROR_INVALID_THREAD;
} // Looks like a java.lang.Thread oop at this point.
if (thread_oop_p != NULL) { // Return the oop to the caller; the caller may still want // the oop even if this function returns an error.
*thread_oop_p = thread_oop;
}
JavaThread * java_thread = java_lang_Thread::thread(thread_oop); if (java_thread == NULL) { if (java_lang_VirtualThread::is_instance(thread_oop)) { return JVMTI_ERROR_INVALID_THREAD;
} // The java.lang.Thread does not contain a JavaThread * so it has // not yet run or it has died. return JVMTI_ERROR_THREAD_NOT_ALIVE;
} // Looks like a live JavaThread at this point.
// We do not check the EnableThreadSMRExtraValidityChecks option // for this includes() call because JVM/TI's spec is tighter. if (!t_list->includes(java_thread)) { // Not on the JavaThreads list so it is not alive. return JVMTI_ERROR_THREAD_NOT_ALIVE;
}
// Return a live JavaThread that is "protected" by the // ThreadsListHandle in the caller.
*jt_pp = java_thread;
return JVMTI_ERROR_NONE;
}
// Convert an oop to a JavaThread found on the specified ThreadsList. // The ThreadsListHandle in the caller "protects" the returned // JavaThread *. // // On success, *jt_pp is set to the converted JavaThread * and // JVMTI_ERROR_NONE is returned. On error, returns various // JVMTI_ERROR_* values. //
jvmtiError
JvmtiExport::cv_oop_to_JavaThread(ThreadsList * t_list, oop thread_oop,
JavaThread ** jt_pp) {
assert(t_list != NULL, "must have a ThreadsList");
assert(thread_oop != NULL, "must have an oop");
assert(jt_pp != NULL, "must have a return JavaThread pointer");
if (!thread_oop->is_a(vmClasses::Thread_klass())) { // The oop is not a java.lang.Thread. return JVMTI_ERROR_INVALID_THREAD;
} // Looks like a java.lang.Thread oop at this point.
JavaThread * java_thread = java_lang_Thread::thread(thread_oop); if (java_thread == NULL) { // The java.lang.Thread does not contain a JavaThread * so it has // not yet run or it has died. return JVMTI_ERROR_THREAD_NOT_ALIVE;
} // Looks like a live JavaThread at this point.
// We do not check the EnableThreadSMRExtraValidityChecks option // for this includes() call because JVM/TI's spec is tighter. if (!t_list->includes(java_thread)) { // Not on the JavaThreads list so it is not alive. return JVMTI_ERROR_THREAD_NOT_ALIVE;
}
// Return a live JavaThread that is "protected" by the // ThreadsListHandle in the caller.
*jt_pp = java_thread;
assert(!_thread->is_in_any_VTMS_transition(), "CFLH events are not allowed in any VTMS transition");
_state = _thread->jvmti_thread_state(); if (_state != NULL) {
_class_being_redefined = _state->get_class_being_redefined();
_load_kind = _state->get_class_load_kind();
Klass* klass = (_class_being_redefined == NULL) ? NULL : _class_being_redefined; if (_load_kind != jvmti_class_load_kind_load && klass != NULL) {
ModuleEntry* module_entry = InstanceKlass::cast(klass)->module();
assert(module_entry != NULL, "module_entry should always be set"); if (module_entry->is_named() &&
module_entry->module() != NULL &&
!module_entry->has_default_read_edges()) { if (!module_entry->set_has_default_read_edges()) { // We won a potential race. // Add read edges to the unnamed modules of the bootstrap and app class loaders
Handle class_module(_thread, module_entry->module()); // Obtain j.l.r.Module
JvmtiExport::add_default_read_edges(class_module, _thread);
}
}
} // Clear class_being_redefined flag here. The action // from agent handler could generate a new class file load // hook event and if it is not cleared the new event generated // from regular class file load could have this stale redefined // class handle info.
_state->clear_class_being_redefined();
} else { // redefine and retransform will always set the thread state
_class_being_redefined = NULL;
_load_kind = jvmti_class_load_kind_load;
}
}
private: void post_all_envs() { if (_load_kind != jvmti_class_load_kind_retransform) { // for class load and redefine, // call the non-retransformable agents
JvmtiEnvIterator it; for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { if (!env->is_retransformable() && env->is_enabled(JVMTI_EVENT_CLASS_FILE_LOAD_HOOK)) { // non-retransformable agents cannot retransform back, // so no need to cache the original class file bytes
post_to_env(env, false);
}
}
}
JvmtiEnvIterator it; for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { // retransformable agents get all events if (env->is_retransformable() && env->is_enabled(JVMTI_EVENT_CLASS_FILE_LOAD_HOOK)) { // retransformable agents need to cache the original class file // bytes if changes are made via the ClassFileLoadHook
post_to_env(env, true);
}
}
}
void post_to_env(JvmtiEnv* env, bool caching_needed) { if (env->phase() == JVMTI_PHASE_PRIMORDIAL && !env->early_class_hook_env()) { return;
} unsignedchar *new_data = NULL;
jint new_len = 0;
JvmtiClassFileLoadEventMark jem(_thread, _h_name, _class_loader,
_h_protection_domain,
_class_being_redefined);
JvmtiJavaThreadEventTransition jet(_thread);
jvmtiEventClassFileLoadHook callback = env->callbacks()->ClassFileLoadHook; if (callback != NULL) {
(*callback)(env->jvmti_external(), jem.jni_env(),
jem.class_being_redefined(),
jem.jloader(), jem.class_name(),
jem.protection_domain(),
_curr_len, _curr_data,
&new_len, &new_data);
} if (new_data != NULL) { // this agent has modified class data.
_has_been_modified = true; if (caching_needed && *_cached_class_file_ptr == NULL) { // data has been changed by the new retransformable agent // and it hasn't already been cached, cache it
JvmtiCachedClassFileData *p;
p = (JvmtiCachedClassFileData *)os::malloc(
offset_of(JvmtiCachedClassFileData, data) + _curr_len, mtInternal); if (p == NULL) {
vm_exit_out_of_memory(offset_of(JvmtiCachedClassFileData, data) + _curr_len,
OOM_MALLOC_ERROR, "unable to allocate cached copy of original class bytes");
}
p->length = _curr_len;
memcpy(p->data, _curr_data, _curr_len);
*_cached_class_file_ptr = p;
}
if (_curr_data != *_data_ptr) { // curr_data is previous agent modified class data. // And this has been changed by the new agent so // we can delete it now.
_curr_env->Deallocate(_curr_data);
}
// Class file data has changed by the current agent.
_curr_data = new_data;
_curr_len = new_len; // Save the current agent env we need this to deallocate the // memory allocated by this agent.
_curr_env = env;
}
}
void copy_modified_data() { // if one of the agent has modified class file data. // Copy modified class data to new resources array. if (_curr_data != *_data_ptr) {
*_data_ptr = NEW_RESOURCE_ARRAY(u1, _curr_len);
memcpy(*_data_ptr, _curr_data, _curr_len);
*_end_ptr = *_data_ptr + _curr_len;
_curr_env->Deallocate(_curr_data);
}
}
};
// this entry is for class file load hook on class load, redefine and retransform bool JvmtiExport::post_class_file_load_hook(Symbol* h_name,
Handle class_loader,
Handle h_protection_domain, unsignedchar **data_ptr, unsignedchar **end_ptr,
JvmtiCachedClassFileData **cache_ptr) { if (JvmtiEnv::get_phase() < JVMTI_PHASE_PRIMORDIAL) { returnfalse;
} if (JavaThread::current()->is_in_tmp_VTMS_transition()) { returnfalse; // skip CFLH events in tmp VTMS transition
}
void JvmtiExport::report_unsupported(bool on) { // If any JVMTI service is turned on, we need to exit before native code // tries to access nonexistent services. if (on) {
vm_exit_during_initialization("Java Kernel does not support JVMTI.");
}
}
staticinline Klass* oop_to_klass(oop obj) {
Klass* k = obj->klass();
// if the object is a java.lang.Class then return the java mirror if (k == vmClasses::Class_klass()) { if (!java_lang_Class::is_primitive(obj)) {
k = java_lang_Class::as_Klass(obj);
assert(k != NULL, "class for non-primitive mirror must exist");
}
} return k;
}
// post the event for each environment that has this event enabled.
JvmtiEnvIterator it; for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { if (env->is_enabled(JVMTI_EVENT_COMPILED_METHOD_UNLOAD)) { if (env->phase() == JVMTI_PHASE_PRIMORDIAL) { continue;
}
EVT_TRACE(JVMTI_EVENT_COMPILED_METHOD_UNLOAD,
("[%s] class compile method unload event sent jmethodID " PTR_FORMAT,
JvmtiTrace::safe_get_thread_name(thread), p2i(method)));
JvmtiThreadState *state = thread->jvmti_thread_state(); if (state == NULL) { return;
} if (thread->is_in_any_VTMS_transition()) { return; // no events should be posted if thread is in any VTMS transition
}
// update information about current location and post a step event
JvmtiThreadState *state = thread->jvmti_thread_state(); if (state == NULL) { return;
}
EVT_TRIG_TRACE(JVMTI_EVENT_SINGLE_STEP, ("[%s] Trg Single Step triggered",
JvmtiTrace::safe_get_thread_name(thread))); if (!state->hide_single_stepping()) { if (state->is_pending_step_for_popframe()) {
state->process_pending_step_for_popframe();
} if (state->is_pending_step_for_earlyret()) {
state->process_pending_step_for_earlyret();
}
JvmtiExport::post_single_step(thread, mh(), location);
}
}
JvmtiThreadState* state = thread->jvmti_thread_state(); if (state == NULL) { return;
} if (thread->is_in_tmp_VTMS_transition()) { return; // skip ClassLoad events in tmp VTMS transition
}
assert(!thread->is_in_any_VTMS_transition(), "class load events are not allowed in any VTMS transition");
EVT_TRIG_TRACE(JVMTI_EVENT_CLASS_LOAD, ("[%s] Trg Class Load triggered",
JvmtiTrace::safe_get_thread_name(thread)));
JvmtiEnvThreadStateIterator it(state); for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { if (ets->is_enabled(JVMTI_EVENT_CLASS_LOAD)) {
JvmtiEnv *env = ets->get_env(); if (env->phase() == JVMTI_PHASE_PRIMORDIAL) { continue;
}
EVT_TRACE(JVMTI_EVENT_CLASS_LOAD, ("[%s] Evt Class Load sent %s",
JvmtiTrace::safe_get_thread_name(thread),
klass==NULL? "NULL" : klass->external_name() ));
JvmtiClassEventMark jem(thread, klass);
JvmtiJavaThreadEventTransition jet(thread);
jvmtiEventClassLoad callback = env->callbacks()->ClassLoad; if (callback != NULL) {
(*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), jem.jni_class());
}
}
}
}
JvmtiThreadState* state = thread->jvmti_thread_state(); if (state == NULL) { return;
} if (thread->is_in_tmp_VTMS_transition()) { return; // skip ClassPrepare events in tmp VTMS transition
}
assert(!thread->is_in_any_VTMS_transition(), "class prepare events are not allowed in any VTMS transition");
EVT_TRIG_TRACE(JVMTI_EVENT_CLASS_PREPARE, ("[%s] Trg Class Prepare triggered",
JvmtiTrace::safe_get_thread_name(thread)));
JvmtiEnvThreadStateIterator it(state); for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { if (ets->is_enabled(JVMTI_EVENT_CLASS_PREPARE)) {
JvmtiEnv *env = ets->get_env(); if (env->phase() == JVMTI_PHASE_PRIMORDIAL) { continue;
}
EVT_TRACE(JVMTI_EVENT_CLASS_PREPARE, ("[%s] Evt Class Prepare sent %s",
JvmtiTrace::safe_get_thread_name(thread),
klass==NULL? "NULL" : klass->external_name() ));
JvmtiClassEventMark jem(thread, klass);
JvmtiJavaThreadEventTransition jet(thread);
jvmtiEventClassPrepare callback = env->callbacks()->ClassPrepare; if (callback != NULL) {
(*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), jem.jni_class());
}
}
}
}
// postings to the service thread so that it can perform them in a safe // context and in-order.
ResourceMark rm; // JvmtiDeferredEvent copies the string.
JvmtiDeferredEvent event = JvmtiDeferredEvent::class_unload_event(klass->name()->as_C_string());
ServiceThread::enqueue_deferred_event(&event);
}
void JvmtiExport::post_class_unload_internal(constchar* name) { if (JvmtiEnv::get_phase() < JVMTI_PHASE_PRIMORDIAL) { return;
}
assert(Thread::current()->is_service_thread(), "must be called from ServiceThread");
JavaThread *thread = JavaThread::current();
HandleMark hm(thread);
EVT_TRIG_TRACE(EXT_EVENT_CLASS_UNLOAD, ("[?] Trg Class Unload triggered" )); if (JvmtiEventController::is_enabled((jvmtiEvent)EXT_EVENT_CLASS_UNLOAD)) {
JvmtiEnvIterator it; for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { if (env->phase() == JVMTI_PHASE_PRIMORDIAL) { continue;
} if (env->is_enabled((jvmtiEvent)EXT_EVENT_CLASS_UNLOAD)) {
EVT_TRACE(EXT_EVENT_CLASS_UNLOAD, ("[?] Evt Class Unload sent %s", name));
void JvmtiExport::post_thread_start(JavaThread *thread) { if (JvmtiEnv::get_phase() < JVMTI_PHASE_PRIMORDIAL) { return;
}
assert(thread->thread_state() == _thread_in_vm, "must be in vm state");
EVT_TRIG_TRACE(JVMTI_EVENT_THREAD_START, ("[%s] Trg Thread Start event triggered",
JvmtiTrace::safe_get_thread_name(thread)));
// do JVMTI thread initialization (if needed)
JvmtiEventController::thread_started(thread);
// Do not post thread start event for hidden java thread. if (JvmtiEventController::is_enabled(JVMTI_EVENT_THREAD_START) &&
!thread->is_hidden_from_external_view()) {
JvmtiEnvIterator it; for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { if (env->phase() == JVMTI_PHASE_PRIMORDIAL) { continue;
} if (env->is_enabled(JVMTI_EVENT_THREAD_START)) {
EVT_TRACE(JVMTI_EVENT_THREAD_START, ("[%s] Evt Thread Start event sent",
JvmtiTrace::safe_get_thread_name(thread) ));
// Clear frame_pop requests in frames popped by yield if (can_post_frame_pop()) {
JvmtiEnvThreadStateIterator it(state); int top_frame_num = state->cur_stack_depth() + continuation_frame_count;
for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { if (!ets->has_frame_pops()) { continue;
} for (int frame_idx = 0; frame_idx < continuation_frame_count; frame_idx++) { int frame_num = top_frame_num - frame_idx;
if (!state->is_virtual() && ets->is_frame_pop(frame_num)) { // remove the frame's entry
MutexLocker mu(JvmtiThreadState_lock);
ets->clear_frame_pop(frame_num);
}
}
}
}
}
JavaThread *javaThread = JavaThread::current(); if (javaThread->is_in_any_VTMS_transition()) { return; // no events should be posted if thread is in any VTMS transition
} if (!env->is_enabled(JVMTI_EVENT_OBJECT_FREE)) { return; // the event type has been already disabled
}
// JDK-8213834: handlers of ResourceExhausted may attempt some analysis // which often requires running java. // This will cause problems on threads not able to run java, e.g. compiler // threads. To forestall these problems, we therefore suppress sending this // event from threads which are not able to run java. if (!thread->can_call_java()) { return;
}
JvmtiThreadState* state = thread->jvmti_thread_state(); if (state == NULL || !state->is_interp_only_mode()) { // for any thread that actually wants method entry, interp_only_mode is set return;
} if (mh->jvmti_mount_transition() || thread->is_in_any_VTMS_transition()) { return; // no events should be posted if thread is in any VTMS transition
}
EVT_TRIG_TRACE(JVMTI_EVENT_METHOD_ENTRY, ("[%s] Trg Method Entry triggered %s.%s",
JvmtiTrace::safe_get_thread_name(thread),
(mh() == NULL) ? "NULL" : mh()->klass_name()->as_C_string(),
(mh() == NULL) ? "NULL" : mh()->name()->as_C_string() ));
if (state == NULL || !state->is_interp_only_mode()) { // for any thread that actually wants method exit, interp_only_mode is set return;
}
// return a flag when a method terminates by throwing an exception // i.e. if an exception is thrown and it's not caught by the current method bool exception_exit = state->is_exception_detected() && !state->is_exception_caught();
Handle result;
jvalue value;
value.j = 0L;
if (state->is_enabled(JVMTI_EVENT_METHOD_EXIT)) { // if the method hasn't been popped because of an exception then we populate // the return_value parameter for the callback. At this point we only have // the address of a "raw result" and we just call into the interpreter to // convert this into a jvalue. if (!exception_exit) {
oop oop_result;
BasicType type = current_frame.interpreter_frame_result(&oop_result, &value); if (is_reference_type(type)) {
result = Handle(thread, oop_result);
value.l = JNIHandles::make_local(thread, result());
}
}
}
// Deferred transition to VM, so we can stash away the return oop before GC // Note that this transition is not needed when throwing an exception, because // there is no oop to retain.
JavaThread* current = thread; // for JRT_BLOCK
JRT_BLOCK
post_method_exit_inner(thread, mh, state, exception_exit, current_frame, value);
JRT_BLOCK_END
if (result.not_null() && !mh->is_native()) { // We have to restore the oop on the stack for interpreter frames
*(oop*)current_frame.interpreter_frame_tos_address() = result();
}
}
void JvmtiExport::post_method_exit_inner(JavaThread* thread,
methodHandle& mh,
JvmtiThreadState *state, bool exception_exit,
frame current_frame,
jvalue& value) { if (mh->jvmti_mount_transition() || thread->is_in_any_VTMS_transition()) { return; // no events should be posted if thread is in any VTMS transition
}
JvmtiEnvThreadStateIterator it(state); for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { if (ets->has_frame_pops()) { int cur_frame_number = state->cur_stack_depth();
if (ets->is_frame_pop(cur_frame_number)) { // we have a NotifyFramePop entry for this frame. // now check that this env/thread wants this event if (ets->is_enabled(JVMTI_EVENT_FRAME_POP)) {
EVT_TRACE(JVMTI_EVENT_FRAME_POP, ("[%s] Evt Frame Pop sent %s.%s",
JvmtiTrace::safe_get_thread_name(thread),
(mh() == NULL) ? "NULL" : mh()->klass_name()->as_C_string(),
(mh() == NULL) ? "NULL" : mh()->name()->as_C_string() ));
// we also need to issue a frame pop event for this frame
JvmtiEnv *env = ets->get_env();
JvmtiMethodEventMark jem(thread, mh);
JvmtiJavaThreadEventTransition jet(thread);
jvmtiEventFramePop callback = env->callbacks()->FramePop; if (callback != NULL) {
(*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(),
jem.jni_methodID(), exception_exit);
}
} // remove the frame's entry
{
MutexLocker mu(JvmtiThreadState_lock);
ets->clear_frame_pop(cur_frame_number);
}
}
}
}
state->decr_cur_stack_depth();
}
// Todo: inline this for optimization void JvmtiExport::post_single_step(JavaThread *thread, Method* method, address location) {
HandleMark hm(thread);
methodHandle mh(thread, method);
JvmtiThreadState *state = thread->jvmti_thread_state(); if (state == NULL) { return;
} if (mh->jvmti_mount_transition() || thread->is_in_any_VTMS_transition()) { return; // no events should be posted if thread is in any VTMS transition
}
void JvmtiExport::post_exception_throw(JavaThread *thread, Method* method, address location, oop exception) {
HandleMark hm(thread);
methodHandle mh(thread, method);
Handle exception_handle(thread, exception); // The KeepStackGCProcessedMark below keeps the target thread and its stack fully // GC processed across this scope. This is needed because there is a stack walk // below with safepoint polls inside of it. After such safepoints, we have to // ensure the stack is sufficiently processed.
KeepStackGCProcessedMark ksgcpm(thread);
JvmtiThreadState *state = thread->jvmti_thread_state(); if (state == NULL) { return;
} if (thread->is_in_any_VTMS_transition()) { return; // no events should be posted if thread is in any VTMS transition
}
EVT_TRIG_TRACE(JVMTI_EVENT_EXCEPTION, ("[%s] Trg Exception thrown triggered",
JvmtiTrace::safe_get_thread_name(thread))); if (!state->is_exception_detected()) {
state->set_exception_detected();
JvmtiEnvThreadStateIterator it(state); for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { if (ets->is_enabled(JVMTI_EVENT_EXCEPTION) && (exception != NULL)) {
// It's okay to clear these exceptions here because we duplicate // this lookup in InterpreterRuntime::exception_handler_for_exception.
EXCEPTION_MARK;
bool should_repeat;
vframeStream st(thread);
assert(!st.at_end(), "cannot be at end");
Method* current_method = NULL; // A GC may occur during the Method::fast_exception_handler_bci_for() // call below if it needs to load the constraint class. Using a // methodHandle to keep the 'current_method' from being deallocated // if GC happens.
methodHandle current_mh = methodHandle(thread, current_method); int current_bci = -1; do {
current_method = st.method();
current_mh = methodHandle(thread, current_method);
current_bci = st.bci(); do {
should_repeat = false;
Klass* eh_klass = exception_handle()->klass();
current_bci = Method::fast_exception_handler_bci_for(
current_mh, eh_klass, current_bci, THREAD); if (HAS_PENDING_EXCEPTION) {
exception_handle = Handle(thread, PENDING_EXCEPTION);
CLEAR_PENDING_EXCEPTION;
should_repeat = true;
}
} while (should_repeat && (current_bci != -1));
st.next();
} while ((current_bci < 0) && (!st.at_end()));
state->invalidate_cur_stack_depth(); if (!in_handler_frame) { // Not in exception handler. if(state->is_interp_only_mode()) { // method exit and frame pop events are posted only in interp mode. // When these events are enabled code should be in running in interp mode.
jvalue no_value;
no_value.j = 0L;
JvmtiExport::post_method_exit_inner(thread, mh, state, true, thread->last_frame(), no_value); // The cached cur_stack_depth might have changed from the // operations of frame pop or method exit. We are not 100% sure // the cached cur_stack_depth is still valid depth so invalidate // it.
state->invalidate_cur_stack_depth();
}
} else { // In exception handler frame. Report exception catch.
assert(location != NULL, "must be a known location"); // Update cur_stack_depth - the frames above the current frame // have been unwound due to this exception:
assert(!state->is_exception_caught(), "exception must not be caught yet.");
state->set_exception_caught();
if (mh->jvmti_mount_transition() || thread->is_in_any_VTMS_transition()) { return; // no events should be posted if thread is in any VTMS transition
}
JvmtiEnvThreadStateIterator it(state); for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { if (ets->is_enabled(JVMTI_EVENT_EXCEPTION_CATCH) && (exception_handle() != NULL)) {
EVT_TRACE(JVMTI_EVENT_EXCEPTION_CATCH,
("[%s] Evt ExceptionCatch sent %s.%s @ " INTX_FORMAT,
JvmtiTrace::safe_get_thread_name(thread),
(mh() == NULL) ? "NULL" : mh()->klass_name()->as_C_string(),
(mh() == NULL) ? "NULL" : mh()->name()->as_C_string(),
location - mh()->code_base() ));
oop JvmtiExport::jni_GetField_probe(JavaThread *thread, jobject jobj, oop obj,
Klass* klass, jfieldID fieldID, bool is_static) { if (*((int *)get_field_access_count_addr()) > 0 && thread->has_last_Java_frame()) { // At least one field access watch is set so we have more work to do.
post_field_access_by_jni(thread, obj, klass, fieldID, is_static); // event posting can block so refetch oop if we were passed a jobj if (jobj != NULL) return JNIHandles::resolve_non_null(jobj);
} return obj;
}
void JvmtiExport::post_field_access_by_jni(JavaThread *thread, oop obj,
Klass* klass, jfieldID fieldID, bool is_static) { // We must be called with a Java context in order to provide reasonable // values for the klazz, method, and location fields. The callers of this // function don't make the call unless there is a Java context.
assert(thread->has_last_Java_frame(), "must be called with a Java context");
if (thread->is_in_any_VTMS_transition()) { return; // no events should be posted if thread is in any VTMS transition
}
ResourceMark rm;
fieldDescriptor fd; // if get_field_descriptor finds fieldID to be invalid, then we just bail bool valid_fieldID = JvmtiEnv::get_field_descriptor(klass, fieldID, &fd);
assert(valid_fieldID == true,"post_field_access_by_jni called with invalid fieldID"); if (!valid_fieldID) return; // field accesses are not watched so bail if (!fd.is_field_access_watched()) return;
HandleMark hm(thread);
Handle h_obj; if (!is_static) { // non-static field accessors have an object, but we need a handle
assert(obj != NULL, "non-static needs an object");
h_obj = Handle(thread, obj);
}
post_field_access(thread,
thread->last_frame().interpreter_frame_method(),
thread->last_frame().interpreter_frame_bcp(),
klass, h_obj, fieldID);
}
JvmtiThreadState *state = thread->jvmti_thread_state(); if (state == NULL) { return;
} if (thread->is_in_any_VTMS_transition()) { return; // no events should be posted if thread is in any VTMS transition
}
EVT_TRIG_TRACE(JVMTI_EVENT_FIELD_ACCESS, ("[%s] Trg Field Access event triggered",
JvmtiTrace::safe_get_thread_name(thread)));
JvmtiEnvThreadStateIterator it(state); for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { if (ets->is_enabled(JVMTI_EVENT_FIELD_ACCESS)) {
EVT_TRACE(JVMTI_EVENT_FIELD_ACCESS, ("[%s] Evt Field Access event sent %s.%s @ " INTX_FORMAT,
JvmtiTrace::safe_get_thread_name(thread),
(mh() == NULL) ? "NULL" : mh()->klass_name()->as_C_string(),
(mh() == NULL) ? "NULL" : mh()->name()->as_C_string(),
location - mh()->code_base() ));
oop JvmtiExport::jni_SetField_probe(JavaThread *thread, jobject jobj, oop obj,
Klass* klass, jfieldID fieldID, bool is_static, char sig_type, jvalue *value) { if (*((int *)get_field_modification_count_addr()) > 0 && thread->has_last_Java_frame()) { // At least one field modification watch is set so we have more work to do.
post_field_modification_by_jni(thread, obj, klass, fieldID, is_static, sig_type, value); // event posting can block so refetch oop if we were passed a jobj if (jobj != NULL) return JNIHandles::resolve_non_null(jobj);
} return obj;
}
void JvmtiExport::post_field_modification_by_jni(JavaThread *thread, oop obj,
Klass* klass, jfieldID fieldID, bool is_static, char sig_type, jvalue *value) { // We must be called with a Java context in order to provide reasonable // values for the klazz, method, and location fields. The callers of this // function don't make the call unless there is a Java context.
assert(thread->has_last_Java_frame(), "must be called with Java context");
if (thread->is_in_any_VTMS_transition()) { return; // no events should be posted if thread is in any VTMS transition
}
ResourceMark rm;
fieldDescriptor fd; // if get_field_descriptor finds fieldID to be invalid, then we just bail bool valid_fieldID = JvmtiEnv::get_field_descriptor(klass, fieldID, &fd);
assert(valid_fieldID == true,"post_field_modification_by_jni called with invalid fieldID"); if (!valid_fieldID) return; // field modifications are not watched so bail if (!fd.is_field_modification_watched()) return;
HandleMark hm(thread);
Handle h_obj; if (!is_static) { // non-static field accessors have an object, but we need a handle
assert(obj != NULL, "non-static needs an object");
h_obj = Handle(thread, obj);
}
post_field_modification(thread,
thread->last_frame().interpreter_frame_method(),
thread->last_frame().interpreter_frame_bcp(),
klass, h_obj, fieldID, sig_type, value);
}
if (thread->is_in_any_VTMS_transition()) { return; // no events should be posted if thread is in any VTMS transition
}
if (sig_type == JVM_SIGNATURE_INT || sig_type == JVM_SIGNATURE_BOOLEAN ||
sig_type == JVM_SIGNATURE_BYTE || sig_type == JVM_SIGNATURE_CHAR ||
sig_type == JVM_SIGNATURE_SHORT) { // 'I' instructions are used for byte, char, short and int. // determine which it really is, and convert
fieldDescriptor fd; bool found = JvmtiEnv::get_field_descriptor(field_klass, field, &fd); // should be found (if not, leave as is) if (found) {
jint ival = value->i; // convert value from int to appropriate type switch (fd.field_type()) { case T_BOOLEAN:
sig_type = JVM_SIGNATURE_BOOLEAN;
value->i = 0; // clear it
value->z = (jboolean)ival; break; case T_BYTE:
sig_type = JVM_SIGNATURE_BYTE;
value->i = 0; // clear it
value->b = (jbyte)ival; break; case T_CHAR:
sig_type = JVM_SIGNATURE_CHAR;
value->i = 0; // clear it
value->c = (jchar)ival; break; case T_SHORT:
sig_type = JVM_SIGNATURE_SHORT;
value->i = 0; // clear it
value->s = (jshort)ival; break; case T_INT: // nothing to do break; default: // this is an integer instruction, should be one of above
ShouldNotReachHere(); break;
}
}
}
assert(sig_type != JVM_SIGNATURE_ARRAY, "array should have sig_type == 'L'"); bool handle_created = false;
// convert oop to JNI handle. if (sig_type == JVM_SIGNATURE_CLASS) {
handle_created = true;
value->l = (jobject)JNIHandles::make_local(thread, cast_to_oop(value->l));
}
JvmtiThreadState *state = thread->jvmti_thread_state(); if (state == NULL) { return;
} if (thread->is_in_any_VTMS_transition()) { return; // no events should be posted if thread is in any VTMS transition
}
EVT_TRIG_TRACE(JVMTI_EVENT_FIELD_MODIFICATION,
("[%s] Trg Field Modification event triggered",
JvmtiTrace::safe_get_thread_name(thread)));
JvmtiEnvThreadStateIterator it(state); for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { if (ets->is_enabled(JVMTI_EVENT_FIELD_MODIFICATION)) {
EVT_TRACE(JVMTI_EVENT_FIELD_MODIFICATION,
("[%s] Evt Field Modification event sent %s.%s @ " INTX_FORMAT,
JvmtiTrace::safe_get_thread_name(thread),
(mh() == NULL) ? "NULL" : mh()->klass_name()->as_C_string(),
(mh() == NULL) ? "NULL" : mh()->name()->as_C_string(),
location - mh()->code_base() ));
if (thread->is_in_any_VTMS_transition()) { return; // no events should be posted if thread is in any VTMS transition
}
EVT_TRIG_TRACE(JVMTI_EVENT_NATIVE_METHOD_BIND, ("[%s] Trg Native Method Bind event triggered",
JvmtiTrace::safe_get_thread_name(thread)));
if (JvmtiEventController::is_enabled(JVMTI_EVENT_NATIVE_METHOD_BIND)) {
JvmtiEnvIterator it; for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { if (env->is_enabled(JVMTI_EVENT_NATIVE_METHOD_BIND)) {
EVT_TRACE(JVMTI_EVENT_NATIVE_METHOD_BIND, ("[%s] Evt Native Method Bind event sent",
JvmtiTrace::safe_get_thread_name(thread) ));
assert(!thread->is_in_any_VTMS_transition(), "dynamic code generated events are not allowed in any VTMS transition");
// In theory everyone coming thru here is in_vm but we need to be certain // because a callee will do a vm->native transition
ThreadInVMfromUnknown __tiv;
void JvmtiExport::post_dynamic_code_generated(constchar *name, constvoid *code_begin, constvoid *code_end) {
jvmtiPhase phase = JvmtiEnv::get_phase(); if (phase == JVMTI_PHASE_PRIMORDIAL || phase == JVMTI_PHASE_START) {
post_dynamic_code_generated_internal(name, code_begin, code_end);
} else { // It may not be safe to post the event from this thread. Defer all // postings to the service thread so that it can perform them in a safe // context and in-order.
JvmtiDeferredEvent event = JvmtiDeferredEvent::dynamic_code_generated_event(
name, code_begin, code_end);
ServiceThread::enqueue_deferred_event(&event);
}
}
// post a DYNAMIC_CODE_GENERATED event for a given environment // used by GenerateEvents void JvmtiExport::post_dynamic_code_generated(JvmtiEnv* env, constchar *name, constvoid *code_begin, constvoid *code_end)
{
JavaThread* thread = JavaThread::current();
assert(!thread->is_in_any_VTMS_transition(), "dynamic code generated events are not allowed in any VTMS transition");
// post a DynamicCodeGenerated event while holding locks in the VM. void JvmtiExport::post_dynamic_code_generated_while_holding_locks(constchar* name,
address code_begin, address code_end)
{
JavaThread* thread = JavaThread::current();
assert(!thread->is_in_any_VTMS_transition(), "dynamic code generated events are not allowed in any VTMS transition");
// register the stub with the current dynamic code event collector // Cannot take safepoint here so do not use state_for to get // jvmti thread state. // The collector and/or state might be NULL if JvmtiDynamicCodeEventCollector // has been initialized while JVMTI_EVENT_DYNAMIC_CODE_GENERATED was disabled.
JvmtiThreadState* state = thread->jvmti_thread_state(); if (state != NULL) {
JvmtiDynamicCodeEventCollector *collector = state->get_dynamic_code_event_collector(); if (collector != NULL) {
collector->register_stub(name, code_begin, code_end);
}
}
}
// Collect all the vm internally allocated objects which are visible to java world void JvmtiExport::record_vm_internal_object_allocation(oop obj) {
Thread* thread = Thread::current_or_null(); if (thread != NULL && thread->is_Java_thread()) { // Can not take safepoint here.
NoSafepointVerifier no_sfpt; // Cannot take safepoint here so do not use state_for to get // jvmti thread state.
JvmtiThreadState *state = JavaThread::cast(thread)->jvmti_thread_state(); if (state != NULL) { // state is non NULL when VMObjectAllocEventCollector is enabled.
JvmtiVMObjectAllocEventCollector *collector;
collector = state->get_vm_object_alloc_event_collector(); if (collector != NULL && collector->is_enabled()) { // Don't record classes as these will be notified via the ClassLoad // event. if (obj->klass() != vmClasses::Class_klass()) {
collector->record_allocation(obj);
}
}
}
}
}
// Collect all the sampled allocated objects. void JvmtiExport::record_sampled_internal_object_allocation(oop obj) {
Thread* thread = Thread::current_or_null(); if (thread != NULL && thread->is_Java_thread()) { // Can not take safepoint here.
NoSafepointVerifier no_sfpt; // Cannot take safepoint here so do not use state_for to get // jvmti thread state.
JvmtiThreadState *state = JavaThread::cast(thread)->jvmti_thread_state(); if (state != NULL) { // state is non NULL when SampledObjectAllocEventCollector is enabled.
JvmtiSampledObjectAllocEventCollector *collector;
collector = state->get_sampled_object_alloc_event_collector();
void JvmtiExport::post_garbage_collection_finish() {
Thread *thread = Thread::current(); // this event is posted from VM-Thread.
EVT_TRIG_TRACE(JVMTI_EVENT_GARBAGE_COLLECTION_FINISH,
("[%s] garbage collection finish event triggered",
JvmtiTrace::safe_get_thread_name(thread)));
JvmtiEnvIterator it; for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { if (env->is_enabled(JVMTI_EVENT_GARBAGE_COLLECTION_FINISH)) {
EVT_TRACE(JVMTI_EVENT_GARBAGE_COLLECTION_FINISH,
("[%s] garbage collection finish event sent",
JvmtiTrace::safe_get_thread_name(thread)));
JvmtiThreadEventTransition jet(thread); // JNIEnv is NULL here because this event is posted from VM Thread
jvmtiEventGarbageCollectionFinish callback = env->callbacks()->GarbageCollectionFinish; if (callback != NULL) {
(*callback)(env->jvmti_external());
}
}
}
}
void JvmtiExport::post_garbage_collection_start() {
Thread* thread = Thread::current(); // this event is posted from vm-thread.
EVT_TRIG_TRACE(JVMTI_EVENT_GARBAGE_COLLECTION_START,
("[%s] garbage collection start event triggered",
JvmtiTrace::safe_get_thread_name(thread)));
JvmtiEnvIterator it; for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { if (env->is_enabled(JVMTI_EVENT_GARBAGE_COLLECTION_START)) {
EVT_TRACE(JVMTI_EVENT_GARBAGE_COLLECTION_START,
("[%s] garbage collection start event sent",
JvmtiTrace::safe_get_thread_name(thread)));
JvmtiThreadEventTransition jet(thread); // JNIEnv is NULL here because this event is posted from VM Thread
jvmtiEventGarbageCollectionStart callback = env->callbacks()->GarbageCollectionStart; if (callback != NULL) {
(*callback)(env->jvmti_external());
}
}
}
}
void JvmtiExport::post_data_dump() {
Thread *thread = Thread::current();
EVT_TRIG_TRACE(JVMTI_EVENT_DATA_DUMP_REQUEST,
("[%s] data dump request event triggered",
JvmtiTrace::safe_get_thread_name(thread)));
JvmtiEnvIterator it; for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { if (env->is_enabled(JVMTI_EVENT_DATA_DUMP_REQUEST)) {
EVT_TRACE(JVMTI_EVENT_DATA_DUMP_REQUEST,
("[%s] data dump request event sent",
JvmtiTrace::safe_get_thread_name(thread)));
JvmtiThreadEventTransition jet(thread); // JNIEnv is NULL here because this event is posted from VM Thread
jvmtiEventDataDumpRequest callback = env->callbacks()->DataDumpRequest; if (callback != NULL) {
(*callback)(env->jvmti_external());
}
}
}
}
void JvmtiExport::post_monitor_contended_enter(JavaThread *thread, ObjectMonitor *obj_mntr) {
oop object = obj_mntr->object();
JvmtiThreadState *state = thread->jvmti_thread_state(); if (state == NULL) { return;
} if (thread->is_in_any_VTMS_transition()) { return; // no events should be posted if thread is in any VTMS transition
}
void JvmtiExport::post_monitor_contended_entered(JavaThread *thread, ObjectMonitor *obj_mntr) {
oop object = obj_mntr->object();
JvmtiThreadState *state = thread->jvmti_thread_state(); if (state == NULL) { return;
} if (thread->is_in_any_VTMS_transition()) { return; // no events should be posted if thread is in any VTMS transition
}
void JvmtiExport::post_monitor_wait(JavaThread *thread, oop object,
jlong timeout) {
JvmtiThreadState *state = thread->jvmti_thread_state(); if (state == NULL) { return;
} if (thread->is_in_any_VTMS_transition()) { return; // no events should be posted if thread is in any VTMS transition
}
void JvmtiExport::post_monitor_waited(JavaThread *thread, ObjectMonitor *obj_mntr, jboolean timed_out) {
oop object = obj_mntr->object();
JvmtiThreadState *state = thread->jvmti_thread_state(); if (state == NULL) { return;
} if (thread->is_in_any_VTMS_transition()) { return; // no events should be posted if thread is in any VTMS transition
}
void JvmtiExport::post_vm_object_alloc(JavaThread *thread, oop object) { if (object == NULL) { return;
} if (thread->is_in_any_VTMS_transition()) { return; // no events should be posted if thread is in any VTMS transition
}
HandleMark hm(thread);
Handle h(thread, object);
EVT_TRIG_TRACE(JVMTI_EVENT_VM_OBJECT_ALLOC, ("[%s] Trg vm object alloc triggered",
JvmtiTrace::safe_get_thread_name(thread)));
JvmtiEnvIterator it; for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { if (env->is_enabled(JVMTI_EVENT_VM_OBJECT_ALLOC)) {
EVT_TRACE(JVMTI_EVENT_VM_OBJECT_ALLOC, ("[%s] Evt vmobject alloc sent %s",
JvmtiTrace::safe_get_thread_name(thread),
object==NULL? "NULL" : object->klass()->external_name()));
void JvmtiExport::post_sampled_object_alloc(JavaThread *thread, oop object) {
JvmtiThreadState *state = thread->jvmti_thread_state(); if (state == NULL) { return;
} if (object == NULL) { return;
} if (thread->is_in_any_VTMS_transition()) { return; // no events should be posted if thread is in any VTMS transition
}
HandleMark hm(thread);
Handle h(thread, object);
void JvmtiExport::cleanup_thread(JavaThread* thread) {
assert(JavaThread::current() == thread, "thread is not current");
MutexLocker mu(thread, JvmtiThreadState_lock);
if (thread->jvmti_thread_state() != NULL) { // This has to happen after the thread state is removed, which is // why it is not in post_thread_end_event like its complement // Maybe both these functions should be rolled into the posts?
JvmtiEventController::thread_ended(thread);
}
}
void JvmtiExport::clear_detected_exception(JavaThread* thread) {
assert(JavaThread::current() == thread, "thread is not current");
JvmtiThreadState* state = thread->jvmti_thread_state(); if (state != NULL) {
state->clear_exception_state();
}
}
// Onload raw monitor transition. void JvmtiExport::transition_pending_onload_raw_monitors() {
JvmtiPendingMonitors::transition_raw_monitors();
}
//////////////////////////////////////////////////////////////////////////////////////////////// #if INCLUDE_SERVICES // Attach is disabled if SERVICES is not included
// type for the Agent_OnAttach entry point extern"C" { typedef jint (JNICALL *OnAttachEntry_t)(JavaVM*, char *, void *);
}
// The abs parameter should be "true" or "false" bool is_absolute_path = (absParam != NULL) && (strcmp(absParam,"true")==0);
// Initially marked as invalid. It will be set to valid if we can find the agent
AgentLibrary *agent_lib = new AgentLibrary(agent, options, is_absolute_path, NULL);
// Check for statically linked in agent. If not found then if the path is // absolute we attempt to load the library. Otherwise we try to load it // from the standard dll directory.
if (!os::find_builtin_agent(agent_lib, on_attach_symbols, num_symbol_entries)) { if (is_absolute_path) {
library = os::dll_load(agent, ebuf, sizeof ebuf);
} else { // Try to load the agent from the standard dll directory if (os::dll_locate_lib(buffer, sizeof(buffer), Arguments::get_dll_dir(),
agent)) {
library = os::dll_load(buffer, ebuf, sizeof ebuf);
} if (library == NULL) { // not found - try OS default library path if (os::dll_build_name(buffer, sizeof(buffer), agent)) {
library = os::dll_load(buffer, ebuf, sizeof ebuf);
}
}
} if (library != NULL) {
agent_lib->set_os_lib(library);
agent_lib->set_valid();
}
} // If the library was loaded then we attempt to invoke the Agent_OnAttach // function if (agent_lib->valid()) { // Lookup the Agent_OnAttach function
OnAttachEntry_t on_attach_entry = NULL;
on_attach_entry = CAST_TO_FN_PTR(OnAttachEntry_t,
os::find_agent_function(agent_lib, false, on_attach_symbols, num_symbol_entries)); if (on_attach_entry == NULL) { // Agent_OnAttach missing - unload library if (!agent_lib->is_static_lib()) {
os::dll_unload(library);
}
st->print_cr("%s is not available in %s",
on_attach_symbols[0], agent_lib->name()); delete agent_lib;
} else { // Invoke the Agent_OnAttach function
JavaThread* THREAD = JavaThread::current(); // For exception macros.
{ externstruct JavaVM_ main_vm;
JvmtiThreadEventMark jem(THREAD);
JvmtiJavaThreadEventTransition jet(THREAD);
result = (*on_attach_entry)(&main_vm, (char*)options, NULL);
// Agent_OnAttach may have used JNI if (THREAD->is_pending_jni_exception_check()) {
THREAD->clear_pending_jni_exception_check();
}
}
// Agent_OnAttach may have used JNI if (HAS_PENDING_EXCEPTION) {
CLEAR_PENDING_EXCEPTION;
}
// If OnAttach returns JNI_OK then we add it to the list of // agent libraries so that we can call Agent_OnUnload later. if (result == JNI_OK) {
Arguments::add_loaded_agent(agent_lib);
} else { if (!agent_lib->is_static_lib()) {
os::dll_unload(library);
} delete agent_lib;
}
// Agent_OnAttach executed so completion status is JNI_OK
st->print_cr("return code: %d", result);
result = JNI_OK;
}
} else {
st->print_cr("%s was not loaded.", agent); if (*ebuf != '\0') {
st->print_cr("%s", ebuf);
}
} return result;
}
// Setup current current thread for event collection. void JvmtiEventCollector::setup_jvmti_thread_state() { // set this event collector to be the current one.
JvmtiThreadState* state = JvmtiThreadState::state_for(JavaThread::current()); // state can only be NULL if the current thread is exiting which // should not happen since we're trying to configure for event collection
guarantee(state != NULL, "exiting thread called setup_jvmti_thread_state"); if (is_vm_object_alloc_event()) {
JvmtiVMObjectAllocEventCollector *prev = state->get_vm_object_alloc_event_collector();
// If we have a previous collector and it is disabled, it means this allocation came from a // callback induced VM Object allocation, do not register this collector then. if (prev && !prev->is_enabled()) { return;
}
_prev = prev;
state->set_vm_object_alloc_event_collector((JvmtiVMObjectAllocEventCollector *)this);
} elseif (is_dynamic_code_event()) {
_prev = state->get_dynamic_code_event_collector();
state->set_dynamic_code_event_collector((JvmtiDynamicCodeEventCollector *)this);
} elseif (is_sampled_object_alloc_event()) {
JvmtiSampledObjectAllocEventCollector *prev = state->get_sampled_object_alloc_event_collector();
if (prev) { // JvmtiSampledObjectAllocEventCollector wants only one active collector // enabled. This allows to have a collector detect a user code requiring // a sample in the callback. return;
}
state->set_sampled_object_alloc_event_collector((JvmtiSampledObjectAllocEventCollector*) this);
}
_unset_jvmti_thread_state = true;
}
// Unset current event collection in this thread and reset it with previous // collector. void JvmtiEventCollector::unset_jvmti_thread_state() { if (!_unset_jvmti_thread_state) { return;
}
JvmtiThreadState* state = JavaThread::current()->jvmti_thread_state(); if (state != NULL) { // restore the previous event collector (if any) if (is_vm_object_alloc_event()) { if (state->get_vm_object_alloc_event_collector() == this) {
state->set_vm_object_alloc_event_collector((JvmtiVMObjectAllocEventCollector *)_prev);
} else { // this thread's jvmti state was created during the scope of // the event collector.
}
} elseif (is_dynamic_code_event()) { if (state->get_dynamic_code_event_collector() == this) {
state->set_dynamic_code_event_collector((JvmtiDynamicCodeEventCollector *)_prev);
} else { // this thread's jvmti state was created during the scope of // the event collector.
}
} elseif (is_sampled_object_alloc_event()) { if (state->get_sampled_object_alloc_event_collector() == this) {
state->set_sampled_object_alloc_event_collector((JvmtiSampledObjectAllocEventCollector*)_prev);
} else { // this thread's jvmti state was created during the scope of // the event collector.
}
}
}
}
// create the dynamic code event collector
JvmtiDynamicCodeEventCollector::JvmtiDynamicCodeEventCollector() : _code_blobs(NULL) { if (JvmtiExport::should_post_dynamic_code_generated()) {
setup_jvmti_thread_state();
}
}
// iterate over any code blob descriptors collected and post a // DYNAMIC_CODE_GENERATED event to the profiler.
JvmtiDynamicCodeEventCollector::~JvmtiDynamicCodeEventCollector() {
assert(!JavaThread::current()->owns_locks(), "all locks must be released to post deferred events"); // iterate over any code blob descriptors that we collected if (_code_blobs != NULL) { for (int i=0; i<_code_blobs->length(); i++) {
JvmtiCodeBlobDesc* blob = _code_blobs->at(i);
JvmtiExport::post_dynamic_code_generated(blob->name(), blob->code_begin(), blob->code_end());
FreeHeap(blob);
} delete _code_blobs;
}
unset_jvmti_thread_state();
}
// register a stub void JvmtiDynamicCodeEventCollector::register_stub(constchar* name, address start, address end) { if (_code_blobs == NULL) {
_code_blobs = new (mtServiceability) GrowableArray<JvmtiCodeBlobDesc*>(1, mtServiceability);
}
_code_blobs->append(new JvmtiCodeBlobDesc(name, start, end));
}
// Setup current thread to record vm allocated objects.
JvmtiObjectAllocEventCollector::JvmtiObjectAllocEventCollector() :
_allocated(NULL), _enable(false), _post_callback(NULL) {
}
// Post vm_object_alloc event for vm allocated objects visible to java // world. void JvmtiObjectAllocEventCollector::generate_call_for_allocated() { if (_allocated) {
set_enabled(false); for (int i = 0; i < _allocated->length(); i++) {
oop obj = _allocated->at(i).resolve();
_post_callback(JavaThread::current(), obj); // Release OopHandle
_allocated->at(i).release(JvmtiExport::jvmti_oop_storage());
} delete _allocated, _allocated = NULL;
}
}
void JvmtiObjectAllocEventCollector::record_allocation(oop obj) {
assert(is_enabled(), "Object alloc event collector is not enabled"); if (_allocated == NULL) {
_allocated = new (mtServiceability) GrowableArray<OopHandle>(1, mtServiceability);
}
_allocated->push(OopHandle(JvmtiExport::jvmti_oop_storage(), obj));
}
// Disable collection of VMObjectAlloc events
NoJvmtiVMObjectAllocMark::NoJvmtiVMObjectAllocMark() : _collector(NULL) { // a no-op if VMObjectAlloc event is not enabled if (!JvmtiExport::should_post_vm_object_alloc()) { return;
}
Thread* thread = Thread::current_or_null(); if (thread != NULL && thread->is_Java_thread()) {
JavaThread* current_thread = JavaThread::cast(thread);
JvmtiThreadState *state = current_thread->jvmti_thread_state(); if (state != NULL) {
JvmtiVMObjectAllocEventCollector *collector;
collector = state->get_vm_object_alloc_event_collector(); if (collector != NULL && collector->is_enabled()) {
_collector = collector;
_collector->set_enabled(false);
}
}
}
}
// Re-Enable collection of VMObjectAlloc events (if previously enabled)
NoJvmtiVMObjectAllocMark::~NoJvmtiVMObjectAllocMark() { if (was_enabled()) {
_collector->set_enabled(true);
}
};
// Setup current thread to record vm allocated objects.
JvmtiVMObjectAllocEventCollector::JvmtiVMObjectAllocEventCollector() { if (JvmtiExport::should_post_vm_object_alloc()) {
_enable = true;
setup_jvmti_thread_state();
_post_callback = JvmtiExport::post_vm_object_alloc;
}
}
JvmtiVMObjectAllocEventCollector::~JvmtiVMObjectAllocEventCollector() { if (_enable) {
generate_call_for_allocated();
}
unset_jvmti_thread_state();
}
bool JvmtiSampledObjectAllocEventCollector::object_alloc_is_safe_to_sample() {
Thread* thread = Thread::current(); // Really only sample allocations if this is a JavaThread and not the compiler // thread. if (!thread->is_Java_thread() || thread->is_Compiler_thread()) { returnfalse;
}
if (MultiArray_lock->owner() == thread) { returnfalse;
} returntrue;
}
// Setup current thread to record sampled allocated objects. void JvmtiSampledObjectAllocEventCollector::start() { if (JvmtiExport::should_post_sampled_object_alloc()) { if (!object_alloc_is_safe_to_sample()) { return;
}
// Unset the sampling collector as present in assertion mode only.
assert(Thread::current()->is_Java_thread(), "Should always be in a Java thread");
}
JvmtiGCMarker::JvmtiGCMarker() { // if there aren't any JVMTI environments then nothing to do if (!JvmtiEnv::environments_might_exist()) { return;
}
if (JvmtiExport::should_post_garbage_collection_start()) {
JvmtiExport::post_garbage_collection_start();
}
if (SafepointSynchronize::is_at_safepoint()) { // Do clean up tasks that need to be done at a safepoint
JvmtiEnvBase::check_for_periodic_clean_up();
}
}
JvmtiGCMarker::~JvmtiGCMarker() { // if there aren't any JVMTI environments then nothing to do if (!JvmtiEnv::environments_might_exist()) { return;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.