/*
* 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.
*
*/
#include "precompiled.hpp"
#include "classfile/javaClasses.inline.hpp"
#include "classfile/moduleEntry.hpp"
#include "classfile/vmClasses.hpp"
#include "classfile/vmSymbols.hpp"
#include "code/nmethod.hpp"
#include "code/pcDesc.hpp"
#include "code/scopeDesc.hpp"
#include "gc/shared/oopStorageSet.hpp"
#include "interpreter/interpreter.hpp"
#include "jvmtifiles/jvmtiEnv.hpp"
#include "logging/log.hpp"
#include "logging/logStream.hpp"
#include "memory/allocation.inline.hpp"
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
#include "oops/klass.inline.hpp"
#include "oops/objArrayKlass.hpp"
#include "oops/objArrayOop.hpp"
#include "oops/oop.inline.hpp"
#include "oops/oopHandle.inline.hpp"
#include "prims/jvmtiCodeBlobEvents.hpp"
#include "prims/jvmtiEventController.hpp"
#include "prims/jvmtiEventController.inline.hpp"
#include "prims/jvmtiExport.hpp"
#include "prims/jvmtiImpl.hpp"
#include "prims/jvmtiManageCapabilities.hpp"
#include "prims/jvmtiRawMonitor.hpp"
#include "prims/jvmtiRedefineClasses.hpp"
#include "prims/jvmtiTagMap.hpp"
#include "prims/jvmtiThreadState.inline.hpp"
#include "runtime/arguments.hpp"
#include "runtime/fieldDescriptor.inline.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/javaCalls.hpp"
#include "runtime/javaThread.hpp"
#include "runtime/jniHandles.inline.hpp"
#include "runtime/keepStackGCProcessed.hpp"
#include "runtime/objectMonitor.hpp"
#include "runtime/objectMonitor.inline.hpp"
#include "runtime/os.hpp"
#include "runtime/osThread.hpp"
#include "runtime/safepointVerifiers.hpp"
#include "runtime/serviceThread.hpp"
#include "runtime/threads.hpp"
#include "runtime/threadSMR.hpp"
#include "runtime/vframe.inline.hpp"
#include "runtime/vm_version.hpp"
#include "utilities/macros.hpp"
#ifdef JVMTI_TRACE
#define EVT_TRACE(evt,out) if ((JvmtiTrace::event_trace_flags(evt) & JvmtiTrace::SHOW_EVENT_SENT) != 0) { SafeResourceMark rm; log_trace(jvmti) out; }
#define EVT_TRIG_TRACE(evt,out) if ((JvmtiTrace::event_trace_flags(evt) & JvmtiTrace::SHOW_EVENT_TRIGGER) != 0) { SafeResourceMark rm; log_trace(jvmti) out; }
#else
#define EVT_TRIG_TRACE(evt,out)
#define EVT_TRACE(evt,out)
#endif
///////////////////////////////////////////////////////////////
//
// 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;
public:
JvmtiJavaThreadEventTransition(JavaThread *thread) :
_rm(),
_transition(thread),
_hm(thread) {};
};
// 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;
public:
JvmtiThreadEventTransition(Thread *thread) : _rm(), _hm(thread) {
if (thread->is_Java_thread()) {
_jthread = JavaThread::cast(thread);
_saved_state = _jthread->thread_state();
if (_saved_state == _thread_in_Java) {
ThreadStateTransition::transition_from_java(_jthread, _thread_in_native);
} else {
ThreadStateTransition::transition_from_vm(_jthread, _thread_in_native);
}
} else {
_jthread = NULL;
}
}
~JvmtiThreadEventTransition() {
if (_jthread != NULL)
ThreadStateTransition::transition_from_native(_jthread, _saved_state);
}
};
///////////////////////////////////////////////////////////////
//
// JvmtiEventMark
//
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();
};
~JvmtiEventMark() {
_thread->pop_jni_handle_block();
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);
}
}
jobject to_jobject(oop obj) { return JNIHandles::make_local(_thread,obj); }
jclass to_jclass(Klass* klass) { return (klass == NULL ? NULL : (jclass)to_jobject(klass->java_mirror())); }
jmethodID to_jmethodID(const methodHandle& method) { return method->jmethod_id(); }
JNIEnv* jni_env() { return _jni_env; }
};
class JvmtiThreadEventMark : public JvmtiEventMark {
private:
jobject _jthread;
public:
JvmtiThreadEventMark(JavaThread *thread) :
JvmtiEventMark(thread) {
_jthread = to_jobject(thread->threadObj());
};
jthread jni_thread() { return (jthread)_jthread; }
};
class JvmtiVirtualThreadEventMark : public JvmtiEventMark {
private:
jobject _jthread;
public:
JvmtiVirtualThreadEventMark(JavaThread *thread) :
JvmtiEventMark(thread) {
assert(thread->vthread() != NULL || thread->threadObj() == NULL, "sanity check");
_jthread = to_jobject(thread->vthread());
};
jthread jni_thread() { return (jthread)_jthread; }
};
class JvmtiClassEventMark : public JvmtiVirtualThreadEventMark {
private:
jclass _jc;
public:
JvmtiClassEventMark(JavaThread *thread, Klass* klass) :
JvmtiVirtualThreadEventMark(thread) {
_jc = to_jclass(klass);
};
jclass jni_class() { return _jc; }
};
class JvmtiMethodEventMark : public JvmtiVirtualThreadEventMark {
private:
jmethodID _mid;
public:
JvmtiMethodEventMark(JavaThread *thread, const methodHandle& method) :
JvmtiVirtualThreadEventMark(thread),
_mid(to_jmethodID(method)) {};
jmethodID jni_methodID() { return _mid; }
};
class JvmtiLocationEventMark : public JvmtiMethodEventMark {
private:
jlocation _loc;
public:
JvmtiLocationEventMark(JavaThread *thread, const methodHandle& method, address location) :
JvmtiMethodEventMark(thread, method),
_loc(location - method->code_base()) {};
jlocation location() { return _loc; }
};
class JvmtiExceptionEventMark : public JvmtiLocationEventMark {
private:
jobject _exc;
public:
JvmtiExceptionEventMark(JavaThread *thread, const methodHandle& method, address location, Handle exception) :
JvmtiLocationEventMark(thread, method, location),
_exc(to_jobject(exception())) {};
jobject exception() { return _exc; }
};
class JvmtiClassFileLoadEventMark : public JvmtiThreadEventMark {
private:
const char *_class_name;
jobject _jloader;
jobject _protection_domain;
jclass _class_being_redefined;
public:
JvmtiClassFileLoadEventMark(JavaThread *thread, Symbol* name,
Handle class_loader, Handle prot_domain, Klass* class_being_redefined) : JvmtiThreadEventMark(thread) {
_class_name = name != NULL? name->as_utf8() : NULL;
_jloader = (jobject)to_jobject(class_loader());
_protection_domain = (jobject)to_jobject(prot_domain());
if (class_being_redefined == NULL) {
_class_being_redefined = NULL;
} else {
_class_being_redefined = (jclass)to_jclass(class_being_redefined);
}
};
const char *class_name() {
return _class_name;
}
jobject jloader() {
return _jloader;
}
jobject protection_domain() {
return _protection_domain;
}
jclass class_being_redefined() {
return _class_being_redefined;
}
};
//////////////////////////////////////////////////////////////////////////////
int JvmtiExport::_field_access_count = 0;
int JvmtiExport::_field_modification_count = 0;
bool JvmtiExport::_can_access_local_variables = false;
bool JvmtiExport::_can_hotswap_or_post_breakpoint = false;
bool JvmtiExport::_can_modify_any_class = false;
bool JvmtiExport::_can_walk_any_space = false;
uint64_t JvmtiExport::_redefinition_count = 0;
bool JvmtiExport::_all_dependencies_are_recorded = false;
//
// field access management
//
// 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;
} else if (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");
// Invoke the transformedByAgent method
JavaValue result(T_VOID);
JavaCalls::call_static(&result,
vmClasses::module_Modules_klass(),
vmSymbols::transformedByAgent_name(),
vmSymbols::transformedByAgent_signature(),
h_module,
THREAD);
if (HAS_PENDING_EXCEPTION) {
LogTarget(Trace, jvmti) log;
LogStream log_stream(log);
java_lang_Throwable::print(PENDING_EXCEPTION, &log_stream);
log_stream.cr();
CLEAR_PENDING_EXCEPTION;
return;
}
}
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");
// Invoke the addReads method
JavaValue result(T_VOID);
JavaCalls::call_static(&result,
vmClasses::module_Modules_klass(),
vmSymbols::addReads_name(),
vmSymbols::addReads_signature(),
module,
to_module,
THREAD);
if (HAS_PENDING_EXCEPTION) {
LogTarget(Trace, jvmti) log;
LogStream log_stream(log);
java_lang_Throwable::print(PENDING_EXCEPTION, &log_stream);
log_stream.cr();
CLEAR_PENDING_EXCEPTION;
return JVMTI_ERROR_INTERNAL;
}
return JVMTI_ERROR_NONE;
}
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");
// Invoke the addExports method
JavaValue result(T_VOID);
JavaCalls::call_static(&result,
vmClasses::module_Modules_klass(),
vmSymbols::addExports_name(),
vmSymbols::addExports_signature(),
module,
pkg_name,
to_module,
THREAD);
if (HAS_PENDING_EXCEPTION) {
Symbol* ex_name = PENDING_EXCEPTION->klass()->name();
LogTarget(Trace, jvmti) log;
LogStream log_stream(log);
java_lang_Throwable::print(PENDING_EXCEPTION, &log_stream);
log_stream.cr();
CLEAR_PENDING_EXCEPTION;
if (ex_name == vmSymbols::java_lang_IllegalArgumentException()) {
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
}
return JVMTI_ERROR_INTERNAL;
}
return JVMTI_ERROR_NONE;
}
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");
// Invoke the addOpens method
JavaValue result(T_VOID);
JavaCalls::call_static(&result,
vmClasses::module_Modules_klass(),
vmSymbols::addOpens_name(),
vmSymbols::addExports_signature(),
module,
pkg_name,
to_module,
THREAD);
if (HAS_PENDING_EXCEPTION) {
Symbol* ex_name = PENDING_EXCEPTION->klass()->name();
LogTarget(Trace, jvmti) log;
LogStream log_stream(log);
java_lang_Throwable::print(PENDING_EXCEPTION, &log_stream);
log_stream.cr();
CLEAR_PENDING_EXCEPTION;
if (ex_name == vmSymbols::java_lang_IllegalArgumentException()) {
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
}
return JVMTI_ERROR_INTERNAL;
}
return JVMTI_ERROR_NONE;
}
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");
// Invoke the addUses method
JavaValue result(T_VOID);
JavaCalls::call_static(&result,
vmClasses::module_Modules_klass(),
vmSymbols::addUses_name(),
vmSymbols::addUses_signature(),
module,
service,
THREAD);
if (HAS_PENDING_EXCEPTION) {
LogTarget(Trace, jvmti) log;
LogStream log_stream(log);
java_lang_Throwable::print(PENDING_EXCEPTION, &log_stream);
log_stream.cr();
CLEAR_PENDING_EXCEPTION;
return JVMTI_ERROR_INTERNAL;
}
return JVMTI_ERROR_NONE;
}
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");
// Invoke the addProvides method
JavaValue result(T_VOID);
JavaCalls::call_static(&result,
vmClasses::module_Modules_klass(),
vmSymbols::addProvides_name(),
vmSymbols::addProvides_signature(),
module,
service,
impl_class,
THREAD);
if (HAS_PENDING_EXCEPTION) {
LogTarget(Trace, jvmti) log;
LogStream log_stream(log);
java_lang_Throwable::print(PENDING_EXCEPTION, &log_stream);
log_stream.cr();
CLEAR_PENDING_EXCEPTION;
return JVMTI_ERROR_INTERNAL;
}
return JVMTI_ERROR_NONE;
}
void
JvmtiExport::decode_version_values(jint version, int * major, int * minor,
int * micro) {
*major = (version & JVMTI_VERSION_MASK_MAJOR) >> JVMTI_VERSION_SHIFT_MAJOR;
*minor = (version & JVMTI_VERSION_MASK_MINOR) >> JVMTI_VERSION_SHIFT_MINOR;
*micro = (version & JVMTI_VERSION_MASK_MICRO) >> JVMTI_VERSION_SHIFT_MICRO;
}
void JvmtiExport::enter_primordial_phase() {
JvmtiEnvBase::set_phase(JVMTI_PHASE_PRIMORDIAL);
}
void JvmtiExport::enter_early_start_phase() {
set_early_vmstart_recorded(true);
}
void JvmtiExport::enter_start_phase() {
JvmtiEnvBase::set_phase(JVMTI_PHASE_START);
}
void JvmtiExport::enter_onload_phase() {
JvmtiEnvBase::set_phase(JVMTI_PHASE_ONLOAD);
}
void JvmtiExport::enter_live_phase() {
JvmtiEnvBase::set_phase(JVMTI_PHASE_LIVE);
}
//
// 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" ));
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());
}
}
}
}
static OopStorage* _jvmti_oop_storage = NULL;
static OopStorage* _weak_tag_storage = NULL;
OopStorage* JvmtiExport::jvmti_oop_storage() {
assert(_jvmti_oop_storage != NULL, "not yet initialized");
return _jvmti_oop_storage;
}
OopStorage* JvmtiExport::weak_tag_storage() {
assert(_weak_tag_storage != NULL, "not yet initialized");
return _weak_tag_storage;
}
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" ));
JavaThread *thread = JavaThread::current();
JvmtiThreadEventMark jem(thread);
JvmtiJavaThreadEventTransition jet(thread);
jvmtiEventVMInit callback = env->callbacks()->VMInit;
if (callback != NULL) {
(*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread());
}
}
}
}
void JvmtiExport::post_vm_death() {
EVT_TRIG_TRACE(JVMTI_EVENT_VM_DEATH, ("Trg VM death event triggered" ));
JvmtiTagMap::flush_all_object_free_events();
JvmtiEnvIterator it;
for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) {
if (env->is_enabled(JVMTI_EVENT_VM_DEATH)) {
EVT_TRACE(JVMTI_EVENT_VM_DEATH, ("Evt VM death event sent" ));
JavaThread *thread = JavaThread::current();
JvmtiEventMark jem(thread);
JvmtiJavaThreadEventTransition jet(thread);
jvmtiEventVMDeath callback = env->callbacks()->VMDeath;
if (callback != NULL) {
(*callback)(env->jvmti_external(), jem.jni_env());
}
}
}
JvmtiEnvBase::set_phase(JVMTI_PHASE_DEAD);
JvmtiEventController::vm_death();
}
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;
return JVMTI_ERROR_NONE;
}
class JvmtiClassFileLoadHookPoster : public StackObj {
private:
Symbol* _h_name;
Handle _class_loader;
Handle _h_protection_domain;
unsigned char ** _data_ptr;
unsigned char ** _end_ptr;
JavaThread * _thread;
jint _curr_len;
unsigned char * _curr_data;
JvmtiEnv * _curr_env;
JvmtiCachedClassFileData ** _cached_class_file_ptr;
JvmtiThreadState * _state;
Klass* _class_being_redefined;
JvmtiClassLoadKind _load_kind;
bool _has_been_modified;
public:
inline JvmtiClassFileLoadHookPoster(Symbol* h_name, Handle class_loader,
Handle h_protection_domain,
unsigned char **data_ptr, unsigned char **end_ptr,
JvmtiCachedClassFileData **cache_ptr) {
_h_name = h_name;
_class_loader = class_loader;
_h_protection_domain = h_protection_domain;
_data_ptr = data_ptr;
_end_ptr = end_ptr;
_thread = JavaThread::current();
_curr_len = *end_ptr - *data_ptr;
_curr_data = *data_ptr;
_curr_env = NULL;
_cached_class_file_ptr = cache_ptr;
_has_been_modified = false;
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;
}
}
void post() {
post_all_envs();
copy_modified_data();
}
bool has_been_modified() { return _has_been_modified; }
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;
}
unsigned char *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);
}
}
};
bool JvmtiExport::is_early_phase() {
return JvmtiEnvBase::get_phase() <= JVMTI_PHASE_PRIMORDIAL;
}
bool JvmtiExport::has_early_class_hook_env() {
JvmtiEnvIterator it;
for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) {
if (env->early_class_hook_env()) {
return true;
}
}
return false;
}
bool JvmtiExport::_should_post_class_file_load_hook = false;
// 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,
unsigned char **data_ptr,
unsigned char **end_ptr,
JvmtiCachedClassFileData **cache_ptr) {
if (JvmtiEnv::get_phase() < JVMTI_PHASE_PRIMORDIAL) {
return false;
}
if (JavaThread::current()->is_in_tmp_VTMS_transition()) {
return false; // skip CFLH events in tmp VTMS transition
}
JvmtiClassFileLoadHookPoster poster(h_name, class_loader,
h_protection_domain,
data_ptr, end_ptr,
cache_ptr);
poster.post();
return poster.has_been_modified();
}
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.");
}
}
static inline 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;
}
class JvmtiObjectAllocEventMark : public JvmtiClassEventMark {
private:
jobject _jobj;
jlong _size;
public:
JvmtiObjectAllocEventMark(JavaThread *thread, oop obj) : JvmtiClassEventMark(thread, oop_to_klass(obj)) {
_jobj = (jobject)to_jobject(obj);
_size = obj->size() * wordSize;
};
jobject jni_jobject() { return _jobj; }
jlong size() { return _size; }
};
class JvmtiCompiledMethodLoadEventMark : public JvmtiMethodEventMark {
private:
jint _code_size;
const void *_code_data;
jint _map_length;
jvmtiAddrLocationMap *_map;
const void *_compile_info;
public:
JvmtiCompiledMethodLoadEventMark(JavaThread *thread, nmethod *nm, void* compile_info_ptr = NULL)
: JvmtiMethodEventMark(thread,methodHandle(thread, nm->method())) {
_code_data = nm->code_begin();
_code_size = nm->code_size();
_compile_info = compile_info_ptr; // Set void pointer of compiledMethodLoad Event. Default value is NULL.
JvmtiCodeBlobEvents::build_jvmti_addr_location_map(nm, &_map, &_map_length);
}
~JvmtiCompiledMethodLoadEventMark() {
FREE_C_HEAP_ARRAY(jvmtiAddrLocationMap, _map);
}
jint code_size() { return _code_size; }
const void *code_data() { return _code_data; }
jint map_length() { return _map_length; }
const jvmtiAddrLocationMap* map() { return _map; }
const void *compile_info() { return _compile_info; }
};
class JvmtiMonitorEventMark : public JvmtiVirtualThreadEventMark {
private:
jobject _jobj;
public:
JvmtiMonitorEventMark(JavaThread *thread, oop object)
: JvmtiVirtualThreadEventMark(thread){
_jobj = to_jobject(object);
}
jobject jni_object() { return _jobj; }
};
///////////////////////////////////////////////////////////////
//
// pending CompiledMethodUnload support
//
void JvmtiExport::post_compiled_method_unload(
jmethodID method, const void *code_begin) {
if (JvmtiEnv::get_phase() < JVMTI_PHASE_PRIMORDIAL) {
return;
}
JavaThread* thread = JavaThread::current();
EVT_TRIG_TRACE(JVMTI_EVENT_COMPILED_METHOD_UNLOAD,
("[%s] method compile unload event triggered",
JvmtiTrace::safe_get_thread_name(thread)));
// 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)));
ResourceMark rm(thread);
JvmtiEventMark jem(thread);
JvmtiJavaThreadEventTransition jet(thread);
jvmtiEventCompiledMethodUnload callback = env->callbacks()->CompiledMethodUnload;
if (callback != NULL) {
(*callback)(env->jvmti_external(), method, code_begin);
}
}
}
}
///////////////////////////////////////////////////////////////
//
// JvmtiExport
//
void JvmtiExport::post_raw_breakpoint(JavaThread *thread, Method* method, address location) {
HandleMark hm(thread);
methodHandle mh(thread, 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
}
EVT_TRIG_TRACE(JVMTI_EVENT_BREAKPOINT, ("[%s] Trg Breakpoint triggered",
JvmtiTrace::safe_get_thread_name(thread)));
JvmtiEnvThreadStateIterator it(state);
for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) {
ets->compare_and_set_current_location(mh(), location, JVMTI_EVENT_BREAKPOINT);
if (!ets->breakpoint_posted() && ets->is_enabled(JVMTI_EVENT_BREAKPOINT)) {
ThreadState old_os_state = thread->osthread()->get_state();
thread->osthread()->set_state(BREAKPOINTED);
EVT_TRACE(JVMTI_EVENT_BREAKPOINT, ("[%s] Evt Breakpoint 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() ));
JvmtiEnv *env = ets->get_env();
JvmtiLocationEventMark jem(thread, mh, location);
JvmtiJavaThreadEventTransition jet(thread);
jvmtiEventBreakpoint callback = env->callbacks()->Breakpoint;
if (callback != NULL) {
(*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(),
jem.jni_methodID(), jem.location());
}
ets->set_breakpoint_posted();
thread->osthread()->set_state(old_os_state);
}
}
}
//////////////////////////////////////////////////////////////////////////////
bool JvmtiExport::_can_get_source_debug_extension = false;
bool JvmtiExport::_can_maintain_original_method_order = false;
bool JvmtiExport::_can_post_interpreter_events = false;
bool JvmtiExport::_can_post_on_exceptions = false;
bool JvmtiExport::_can_post_breakpoint = false;
bool JvmtiExport::_can_post_field_access = false;
bool JvmtiExport::_can_post_field_modification = false;
bool JvmtiExport::_can_post_method_entry = false;
bool JvmtiExport::_can_post_method_exit = false;
bool JvmtiExport::_can_post_frame_pop = false;
bool JvmtiExport::_can_pop_frame = false;
bool JvmtiExport::_can_force_early_return = false;
bool JvmtiExport::_can_support_virtual_threads = false;
bool JvmtiExport::_can_get_owned_monitor_info = false;
bool JvmtiExport::_early_vmstart_recorded = false;
bool JvmtiExport::_should_post_single_step = false;
bool JvmtiExport::_should_post_field_access = false;
bool JvmtiExport::_should_post_field_modification = false;
bool JvmtiExport::_should_post_class_load = false;
bool JvmtiExport::_should_post_class_prepare = false;
bool JvmtiExport::_should_post_class_unload = false;
bool JvmtiExport::_should_post_thread_life = false;
bool JvmtiExport::_should_clean_up_heap_objects = false;
bool JvmtiExport::_should_post_native_method_bind = false;
bool JvmtiExport::_should_post_dynamic_code_generated = false;
bool JvmtiExport::_should_post_data_dump = false;
bool JvmtiExport::_should_post_compiled_method_load = false;
bool JvmtiExport::_should_post_compiled_method_unload = false;
bool JvmtiExport::_should_post_monitor_contended_enter = false;
bool JvmtiExport::_should_post_monitor_contended_entered = false;
bool JvmtiExport::_should_post_monitor_wait = false;
bool JvmtiExport::_should_post_monitor_waited = false;
bool JvmtiExport::_should_post_garbage_collection_start = false;
bool JvmtiExport::_should_post_garbage_collection_finish = false;
bool JvmtiExport::_should_post_object_free = false;
bool JvmtiExport::_should_post_resource_exhausted = false;
bool JvmtiExport::_should_post_vm_object_alloc = false;
bool JvmtiExport::_should_post_sampled_object_alloc = false;
bool JvmtiExport::_should_post_on_exceptions = false;
bool JvmtiExport::_should_post_vthread_start = false;
bool JvmtiExport::_should_post_vthread_end = false;
bool JvmtiExport::_should_post_vthread_mount = false;
bool JvmtiExport::_should_post_vthread_unmount = false;
////////////////////////////////////////////////////////////////////////////////////////////////
void JvmtiExport::check_vthread_and_suspend_at_safepoint(JavaThread *thread) {
oop vt = thread->jvmti_vthread();
if (vt != NULL && java_lang_VirtualThread::is_instance(vt)) {
int64_t id = java_lang_Thread::thread_id(vt);
ThreadBlockInVM tbivm(thread);
MonitorLocker ml(JvmtiVTMSTransition_lock, Mutex::_no_safepoint_check_flag);
// block while vthread is externally suspended
while (JvmtiVTSuspender::is_vthread_suspended(id)) {
ml.wait();
}
}
}
//
// JVMTI single step management
//
void JvmtiExport::at_single_stepping_point(JavaThread *thread, Method* method, address location) {
assert(JvmtiExport::should_post_single_step(), "must be single stepping");
HandleMark hm(thread);
methodHandle mh(thread, method);
// 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);
}
}
void JvmtiExport::expose_single_stepping(JavaThread *thread) {
JvmtiThreadState *state = thread->jvmti_thread_state();
if (state != NULL) {
state->clear_hide_single_stepping();
}
}
bool JvmtiExport::hide_single_stepping(JavaThread *thread) {
JvmtiThreadState *state = thread->jvmti_thread_state();
if (state != NULL && state->is_enabled(JVMTI_EVENT_SINGLE_STEP)) {
state->set_hide_single_stepping();
return true;
} else {
return false;
}
}
void JvmtiExport::post_class_load(JavaThread *thread, Klass* klass) {
if (JvmtiEnv::get_phase() < JVMTI_PHASE_PRIMORDIAL) {
return;
}
HandleMark hm(thread);
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());
}
}
}
}
void JvmtiExport::post_class_prepare(JavaThread *thread, Klass* klass) {
if (JvmtiEnv::get_phase() < JVMTI_PHASE_PRIMORDIAL) {
return;
}
HandleMark hm(thread);
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());
}
}
}
}
void JvmtiExport::post_class_unload(Klass* klass) {
if (JvmtiEnv::get_phase() < JVMTI_PHASE_PRIMORDIAL) {
return;
}
// 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(const char* 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));
JvmtiEventMark jem(thread);
JvmtiJavaThreadEventTransition jet(thread);
jvmtiExtensionEvent callback = env->ext_callbacks()->ClassUnload;
if (callback != NULL) {
(*callback)(env->jvmti_external(), jem.jni_env(), 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) ));
JvmtiVirtualThreadEventMark jem(thread);
JvmtiJavaThreadEventTransition jet(thread);
jvmtiEventThreadStart callback = env->callbacks()->ThreadStart;
if (callback != NULL) {
(*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread());
}
}
}
}
}
void JvmtiExport::post_thread_end(JavaThread *thread) {
if (JvmtiEnv::get_phase() < JVMTI_PHASE_PRIMORDIAL) {
return;
}
EVT_TRIG_TRACE(JVMTI_EVENT_THREAD_END, ("[%s] Trg Thread End event triggered",
JvmtiTrace::safe_get_thread_name(thread)));
JvmtiThreadState *state = thread->jvmti_thread_state();
if (state == NULL) {
return;
}
// Do not post thread end event for hidden java thread.
if (state->is_enabled(JVMTI_EVENT_THREAD_END) &&
!thread->is_hidden_from_external_view()) {
JvmtiEnvThreadStateIterator it(state);
for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) {
if (ets->is_enabled(JVMTI_EVENT_THREAD_END)) {
JvmtiEnv *env = ets->get_env();
if (env->phase() == JVMTI_PHASE_PRIMORDIAL) {
continue;
}
EVT_TRACE(JVMTI_EVENT_THREAD_END, ("[%s] Evt Thread End event sent",
JvmtiTrace::safe_get_thread_name(thread) ));
JvmtiVirtualThreadEventMark jem(thread);
JvmtiJavaThreadEventTransition jet(thread);
jvmtiEventThreadEnd callback = env->callbacks()->ThreadEnd;
if (callback != NULL) {
(*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread());
}
}
}
}
}
void JvmtiExport::post_vthread_start(jobject vthread) {
if (JvmtiEnv::get_phase() < JVMTI_PHASE_PRIMORDIAL) {
return;
}
EVT_TRIG_TRACE(JVMTI_EVENT_VIRTUAL_THREAD_START, ("[%p] Trg Virtual Thread Start event triggered", vthread));
JavaThread *cur_thread = JavaThread::current();
JvmtiThreadState *state = cur_thread->jvmti_thread_state();
if (state == NULL) {
return;
}
if (state->is_enabled(JVMTI_EVENT_VIRTUAL_THREAD_START)) {
JvmtiEnvThreadStateIterator it(state);
for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) {
JvmtiEnv *env = ets->get_env();
if (env->phase() == JVMTI_PHASE_PRIMORDIAL) {
continue;
}
if (ets->is_enabled(JVMTI_EVENT_VIRTUAL_THREAD_START)) {
EVT_TRACE(JVMTI_EVENT_VIRTUAL_THREAD_START, ("[%p] Evt Virtual Thread Start event sent", vthread));
JvmtiVirtualThreadEventMark jem(cur_thread);
JvmtiJavaThreadEventTransition jet(cur_thread);
jvmtiEventVirtualThreadStart callback = env->callbacks()->VirtualThreadStart;
if (callback != NULL) {
(*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread());
}
}
}
}
}
void JvmtiExport::post_vthread_end(jobject vthread) {
if (JvmtiEnv::get_phase() < JVMTI_PHASE_PRIMORDIAL) {
return;
}
EVT_TRIG_TRACE(JVMTI_EVENT_VIRTUAL_THREAD_END, ("[%p] Trg Virtual Thread End event triggered", vthread));
JavaThread *cur_thread = JavaThread::current();
JvmtiThreadState *state = cur_thread->jvmti_thread_state();
if (state == NULL) {
return;
}
if (state->is_enabled(JVMTI_EVENT_VIRTUAL_THREAD_END)) {
JvmtiEnvThreadStateIterator it(state);
for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) {
JvmtiEnv *env = ets->get_env();
if (env->phase() == JVMTI_PHASE_PRIMORDIAL) {
continue;
}
if (ets->is_enabled(JVMTI_EVENT_VIRTUAL_THREAD_END)) {
EVT_TRACE(JVMTI_EVENT_VIRTUAL_THREAD_END, ("[%p] Evt Virtual Thread End event sent", vthread));
JvmtiVirtualThreadEventMark jem(cur_thread);
JvmtiJavaThreadEventTransition jet(cur_thread);
jvmtiEventVirtualThreadEnd callback = env->callbacks()->VirtualThreadEnd;
if (callback != NULL) {
(*callback)(env->jvmti_external(), jem.jni_env(), vthread);
}
}
}
}
}
void JvmtiExport::post_vthread_mount(jobject vthread) {
if (JvmtiEnv::get_phase() < JVMTI_PHASE_PRIMORDIAL) {
return;
}
JavaThread *thread = JavaThread::current();
HandleMark hm(thread);
EVT_TRIG_TRACE(EXT_EVENT_VIRTUAL_THREAD_MOUNT, ("[%p] Trg Virtual Thread Mount event triggered", vthread));
JvmtiThreadState *state = thread->jvmti_thread_state();
if (state == NULL) {
return;
}
if (state->is_enabled((jvmtiEvent)EXT_EVENT_VIRTUAL_THREAD_MOUNT)) {
JvmtiEnvThreadStateIterator it(state);
for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) {
JvmtiEnv *env = ets->get_env();
if (env->phase() == JVMTI_PHASE_PRIMORDIAL) {
continue;
}
if (ets->is_enabled((jvmtiEvent)EXT_EVENT_VIRTUAL_THREAD_MOUNT)) {
EVT_TRACE(EXT_EVENT_VIRTUAL_THREAD_MOUNT, ("[%p] Evt Virtual Thread Mount event sent", vthread));
JvmtiVirtualThreadEventMark jem(thread);
JvmtiJavaThreadEventTransition jet(thread);
jvmtiExtensionEvent callback = env->ext_callbacks()->VirtualThreadMount;
if (callback != NULL) {
(*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread());
}
}
}
}
}
void JvmtiExport::post_vthread_unmount(jobject vthread) {
if (JvmtiEnv::get_phase() < JVMTI_PHASE_PRIMORDIAL) {
return;
}
JavaThread *thread = JavaThread::current();
HandleMark hm(thread);
EVT_TRIG_TRACE(EXT_EVENT_VIRTUAL_THREAD_UNMOUNT, ("[%p] Trg Virtual Thread Unmount event triggered", vthread));
JvmtiThreadState *state = thread->jvmti_thread_state();
if (state == NULL) {
return;
}
if (state->is_enabled((jvmtiEvent)EXT_EVENT_VIRTUAL_THREAD_UNMOUNT)) {
JvmtiEnvThreadStateIterator it(state);
for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) {
JvmtiEnv *env = ets->get_env();
if (env->phase() == JVMTI_PHASE_PRIMORDIAL) {
continue;
}
if (ets->is_enabled((jvmtiEvent)EXT_EVENT_VIRTUAL_THREAD_UNMOUNT)) {
EVT_TRACE(EXT_EVENT_VIRTUAL_THREAD_UNMOUNT, ("[%p] Evt Virtual Thread Unmount event sent", vthread));
JvmtiVirtualThreadEventMark jem(thread);
JvmtiJavaThreadEventTransition jet(thread);
jvmtiExtensionEvent callback = env->ext_callbacks()->VirtualThreadUnmount;
if (callback != NULL) {
(*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread());
}
}
}
}
}
void JvmtiExport::continuation_yield_cleanup(JavaThread* thread, jint continuation_frame_count) {
if (JvmtiEnv::get_phase() < JVMTI_PHASE_PRIMORDIAL) {
return;
}
assert(thread == JavaThread::current(), "must be");
JvmtiThreadState *state = thread->jvmti_thread_state();
if (state == NULL) {
return;
}
state->invalidate_cur_stack_depth();
// 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);
}
}
}
}
}
void JvmtiExport::post_object_free(JvmtiEnv* env, GrowableArray<jlong>* objects) {
assert(objects != NULL, "Nothing to post");
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
}
EVT_TRIG_TRACE(JVMTI_EVENT_OBJECT_FREE, ("[?] Trg Object Free triggered" ));
EVT_TRACE(JVMTI_EVENT_OBJECT_FREE, ("[?] Evt Object Free sent"));
JvmtiThreadEventMark jem(javaThread);
JvmtiJavaThreadEventTransition jet(javaThread);
jvmtiEventObjectFree callback = env->callbacks()->ObjectFree;
if (callback != NULL) {
for (int index = 0; index < objects->length(); index++) {
(*callback)(env->jvmti_external(), objects->at(index));
}
}
}
void JvmtiExport::post_resource_exhausted(jint resource_exhausted_flags, const char* description) {
JavaThread *thread = JavaThread::current();
if (thread->is_in_any_VTMS_transition()) {
return; // no events should be posted if thread is in any VTMS transition
}
log_error(jvmti)("Posting Resource Exhausted event: %s",
description != nullptr ? description : "unknown");
// 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;
}
EVT_TRIG_TRACE(JVMTI_EVENT_RESOURCE_EXHAUSTED, ("Trg resource exhausted event triggered" ));
JvmtiEnvIterator it;
for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) {
if (env->is_enabled(JVMTI_EVENT_RESOURCE_EXHAUSTED)) {
EVT_TRACE(JVMTI_EVENT_RESOURCE_EXHAUSTED, ("Evt resource exhausted event sent" ));
JvmtiThreadEventMark jem(thread);
JvmtiJavaThreadEventTransition jet(thread);
jvmtiEventResourceExhausted callback = env->callbacks()->ResourceExhausted;
if (callback != NULL) {
(*callback)(env->jvmti_external(), jem.jni_env(),
resource_exhausted_flags, NULL, description);
}
}
}
}
void JvmtiExport::post_method_entry(JavaThread *thread, Method* method, frame current_frame) {
HandleMark hm(thread);
methodHandle mh(thread, method);
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() ));
state->incr_cur_stack_depth();
if (state->is_enabled(JVMTI_EVENT_METHOD_ENTRY)) {
JvmtiEnvThreadStateIterator it(state);
for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) {
if (ets->is_enabled(JVMTI_EVENT_METHOD_ENTRY)) {
EVT_TRACE(JVMTI_EVENT_METHOD_ENTRY, ("[%s] Evt Method Entry 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() ));
JvmtiEnv *env = ets->get_env();
JvmtiMethodEventMark jem(thread, mh);
JvmtiJavaThreadEventTransition jet(thread);
jvmtiEventMethodEntry callback = env->callbacks()->MethodEntry;
if (callback != NULL) {
--> --------------------
--> maximum size reached
--> --------------------
¤ Dauer der Verarbeitung: 0.117 Sekunden
(vorverarbeitet)
¤
|
Haftungshinweis
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 ist noch experimentell.
|