/*
* 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/classLoaderExt.hpp"
#include "classfile/javaClasses.inline.hpp"
#include "classfile/stringTable.hpp"
#include "classfile/modules.hpp"
#include "classfile/systemDictionary.hpp"
#include "classfile/vmClasses.hpp"
#include "classfile/vmSymbols.hpp"
#include "gc/shared/collectedHeap.hpp"
#include "interpreter/bytecodeStream.hpp"
#include "interpreter/interpreter.hpp"
#include "jfr/jfrEvents.hpp"
#include "jvmtifiles/jvmtiEnv.hpp"
#include "logging/log.hpp"
#include "logging/logConfiguration.hpp"
#include "memory/allocation.hpp"
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/klass.inline.hpp"
#include "oops/objArrayOop.inline.hpp"
#include "oops/oop.inline.hpp"
#include "prims/jniCheck.hpp"
#include "prims/jvm_misc.hpp"
#include "prims/jvmtiAgentThread.hpp"
#include "prims/jvmtiClassFileReconstituter.hpp"
#include "prims/jvmtiCodeBlobEvents.hpp"
#include "prims/jvmtiExtensions.hpp"
#include "prims/jvmtiGetLoadedClasses.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 "prims/jvmtiUtil.hpp"
#include "runtime/arguments.hpp"
#include "runtime/deoptimization.hpp"
#include "runtime/fieldDescriptor.inline.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/javaCalls.hpp"
#include "runtime/javaThread.inline.hpp"
#include "runtime/jfieldIDWorkaround.hpp"
#include "runtime/jniHandles.inline.hpp"
#include "runtime/objectMonitor.inline.hpp"
#include "runtime/os.hpp"
#include "runtime/osThread.hpp"
#include "runtime/reflectionUtils.hpp"
#include "runtime/signature.hpp"
#include "runtime/threadHeapSampler.hpp"
#include "runtime/threads.hpp"
#include "runtime/threadSMR.hpp"
#include "runtime/timerTrace.hpp"
#include "runtime/vframe.inline.hpp"
#include "runtime/vmThread.hpp"
#include "services/threadService.hpp"
#include "utilities/exceptions.hpp"
#include "utilities/preserveException.hpp"
#include "utilities/utf8.hpp"
#define FIXLATER 0 // REMOVE this when completed.
// FIXLATER: hook into JvmtiTrace
#define TraceJVMTICalls false
JvmtiEnv::JvmtiEnv(jint version) : JvmtiEnvBase(version) {
}
JvmtiEnv::~JvmtiEnv() {
}
JvmtiEnv*
JvmtiEnv::create_a_jvmti(jint version) {
return new JvmtiEnv(version);
}
// VM operation class to copy jni function table at safepoint.
// More than one java threads or jvmti agents may be reading/
// modifying jni function tables. To reduce the risk of bad
// interaction b/w these threads it is copied at safepoint.
class VM_JNIFunctionTableCopier : public VM_Operation {
private:
const struct JNINativeInterface_ *_function_table;
public:
VM_JNIFunctionTableCopier(const struct JNINativeInterface_ *func_tbl) {
_function_table = func_tbl;
};
VMOp_Type type() const { return VMOp_JNIFunctionTableCopier; }
void doit() {
copy_jni_function_table(_function_table);
};
};
//
// Do not change the "prefix" marker below, everything above it is copied
// unchanged into the filled stub, everything below is controlled by the
// stub filler (only method bodies are carried forward, and then only for
// functionality still in the spec).
//
// end file prefix
//
// Memory Management functions
//
// mem_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::Allocate(jlong size, unsigned char** mem_ptr) {
return allocate(size, mem_ptr);
} /* end Allocate */
// mem - NULL is a valid value, must be checked
jvmtiError
JvmtiEnv::Deallocate(unsigned char* mem) {
return deallocate(mem);
} /* end Deallocate */
// thread - NOT protected by ThreadsListHandle and NOT pre-checked
// data - NULL is a valid value, must be checked
jvmtiError
JvmtiEnv::SetThreadLocalStorage(jthread thread, const void* data) {
JavaThread* current = JavaThread::current();
JvmtiThreadState* state = NULL;
JvmtiVTMSTransitionDisabler disabler;
ThreadsListHandle tlh(current);
JavaThread* java_thread = NULL;
oop thread_obj = NULL;
if (thread == NULL) {
java_thread = current;
state = java_thread->jvmti_thread_state();
} else {
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_obj);
if (err != JVMTI_ERROR_NONE) {
return err;
}
state = java_lang_Thread::jvmti_thread_state(thread_obj);
}
if (state == NULL) {
if (data == NULL) {
// leaving state unset same as data set to NULL
return JVMTI_ERROR_NONE;
}
// otherwise, create the state
HandleMark hm(current);
Handle thread_handle(current, thread_obj);
state = JvmtiThreadState::state_for(java_thread, thread_handle);
if (state == NULL) {
return JVMTI_ERROR_THREAD_NOT_ALIVE;
}
}
state->env_thread_state(this)->set_agent_thread_local_storage_data((void*)data);
return JVMTI_ERROR_NONE;
} /* end SetThreadLocalStorage */
// thread - NOT protected by ThreadsListHandle and NOT pre-checked
// data_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetThreadLocalStorage(jthread thread, void** data_ptr) {
JavaThread* current_thread = JavaThread::current();
if (thread == NULL) {
JvmtiThreadState* state = current_thread->jvmti_thread_state();
*data_ptr = (state == NULL) ? NULL :
state->env_thread_state(this)->get_agent_thread_local_storage_data();
} else {
// jvmti_GetThreadLocalStorage is "in native" and doesn't transition
// the thread to _thread_in_vm. However, when the TLS for a thread
// other than the current thread is required we need to transition
// from native so as to resolve the jthread.
MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, current_thread));
ThreadInVMfromNative __tiv(current_thread);
VM_ENTRY_BASE(jvmtiError, JvmtiEnv::GetThreadLocalStorage , current_thread)
debug_only(VMNativeEntryWrapper __vew;)
JvmtiVTMSTransitionDisabler disabler;
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
oop thread_obj = NULL;
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_obj);
if (err != JVMTI_ERROR_NONE) {
return err;
}
HandleMark hm(current_thread);
Handle thread_handle(current_thread, thread_obj);
JvmtiThreadState* state = JvmtiThreadState::state_for(java_thread, thread_handle);
*data_ptr = (state == NULL) ? NULL :
state->env_thread_state(this)->get_agent_thread_local_storage_data();
}
return JVMTI_ERROR_NONE;
} /* end GetThreadLocalStorage */
//
// Module functions
//
// module_count_ptr - pre-checked for NULL
// modules_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetAllModules(jint* module_count_ptr, jobject** modules_ptr) {
JvmtiModuleClosure jmc;
return jmc.get_all_modules(this, module_count_ptr, modules_ptr);
} /* end GetAllModules */
// class_loader - NULL is a valid value, must be pre-checked
// package_name - pre-checked for NULL
// module_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetNamedModule(jobject class_loader, const char* package_name, jobject* module_ptr) {
JavaThread* THREAD = JavaThread::current(); // For exception macros.
ResourceMark rm(THREAD);
Handle h_loader (THREAD, JNIHandles::resolve(class_loader));
// Check that loader is a subclass of java.lang.ClassLoader.
if (h_loader.not_null() && !java_lang_ClassLoader::is_subclass(h_loader->klass())) {
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
}
oop module = Modules::get_named_module(h_loader, package_name);
*module_ptr = module != NULL ? JNIHandles::make_local(THREAD, module) : NULL;
return JVMTI_ERROR_NONE;
} /* end GetNamedModule */
// module - pre-checked for NULL
// to_module - pre-checked for NULL
jvmtiError
JvmtiEnv::AddModuleReads(jobject module, jobject to_module) {
JavaThread* THREAD = JavaThread::current(); // For exception macros.
// check module
Handle h_module(THREAD, JNIHandles::resolve(module));
if (!java_lang_Module::is_instance(h_module())) {
return JVMTI_ERROR_INVALID_MODULE;
}
// check to_module
Handle h_to_module(THREAD, JNIHandles::resolve(to_module));
if (!java_lang_Module::is_instance(h_to_module())) {
return JVMTI_ERROR_INVALID_MODULE;
}
return JvmtiExport::add_module_reads(h_module, h_to_module, THREAD);
} /* end AddModuleReads */
// module - pre-checked for NULL
// pkg_name - pre-checked for NULL
// to_module - pre-checked for NULL
jvmtiError
JvmtiEnv::AddModuleExports(jobject module, const char* pkg_name, jobject to_module) {
JavaThread* THREAD = JavaThread::current(); // For exception macros.
Handle h_pkg = java_lang_String::create_from_str(pkg_name, THREAD);
// check module
Handle h_module(THREAD, JNIHandles::resolve(module));
if (!java_lang_Module::is_instance(h_module())) {
return JVMTI_ERROR_INVALID_MODULE;
}
// check to_module
Handle h_to_module(THREAD, JNIHandles::resolve(to_module));
if (!java_lang_Module::is_instance(h_to_module())) {
return JVMTI_ERROR_INVALID_MODULE;
}
return JvmtiExport::add_module_exports(h_module, h_pkg, h_to_module, THREAD);
} /* end AddModuleExports */
// module - pre-checked for NULL
// pkg_name - pre-checked for NULL
// to_module - pre-checked for NULL
jvmtiError
JvmtiEnv::AddModuleOpens(jobject module, const char* pkg_name, jobject to_module) {
JavaThread* THREAD = JavaThread::current(); // For exception macros.
Handle h_pkg = java_lang_String::create_from_str(pkg_name, THREAD);
// check module
Handle h_module(THREAD, JNIHandles::resolve(module));
if (!java_lang_Module::is_instance(h_module())) {
return JVMTI_ERROR_INVALID_MODULE;
}
// check to_module
Handle h_to_module(THREAD, JNIHandles::resolve(to_module));
if (!java_lang_Module::is_instance(h_to_module())) {
return JVMTI_ERROR_INVALID_MODULE;
}
return JvmtiExport::add_module_opens(h_module, h_pkg, h_to_module, THREAD);
} /* end AddModuleOpens */
// module - pre-checked for NULL
// service - pre-checked for NULL
jvmtiError
JvmtiEnv::AddModuleUses(jobject module, jclass service) {
JavaThread* THREAD = JavaThread::current(); // For exception macros.
// check module
Handle h_module(THREAD, JNIHandles::resolve(module));
if (!java_lang_Module::is_instance(h_module())) {
return JVMTI_ERROR_INVALID_MODULE;
}
// check service
Handle h_service(THREAD, JNIHandles::resolve_external_guard(service));
if (!java_lang_Class::is_instance(h_service()) ||
java_lang_Class::is_primitive(h_service())) {
return JVMTI_ERROR_INVALID_CLASS;
}
return JvmtiExport::add_module_uses(h_module, h_service, THREAD);
} /* end AddModuleUses */
// module - pre-checked for NULL
// service - pre-checked for NULL
// impl_class - pre-checked for NULL
jvmtiError
JvmtiEnv::AddModuleProvides(jobject module, jclass service, jclass impl_class) {
JavaThread* THREAD = JavaThread::current(); // For exception macros.
// check module
Handle h_module(THREAD, JNIHandles::resolve(module));
if (!java_lang_Module::is_instance(h_module())) {
return JVMTI_ERROR_INVALID_MODULE;
}
// check service
Handle h_service(THREAD, JNIHandles::resolve_external_guard(service));
if (!java_lang_Class::is_instance(h_service()) ||
java_lang_Class::is_primitive(h_service())) {
return JVMTI_ERROR_INVALID_CLASS;
}
// check impl_class
Handle h_impl_class(THREAD, JNIHandles::resolve_external_guard(impl_class));
if (!java_lang_Class::is_instance(h_impl_class()) ||
java_lang_Class::is_primitive(h_impl_class())) {
return JVMTI_ERROR_INVALID_CLASS;
}
return JvmtiExport::add_module_provides(h_module, h_service, h_impl_class, THREAD);
} /* end AddModuleProvides */
// module - pre-checked for NULL
// is_modifiable_class_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::IsModifiableModule(jobject module, jboolean* is_modifiable_module_ptr) {
JavaThread* current = JavaThread::current();
// check module
Handle h_module(current, JNIHandles::resolve(module));
if (!java_lang_Module::is_instance(h_module())) {
return JVMTI_ERROR_INVALID_MODULE;
}
*is_modifiable_module_ptr = JNI_TRUE;
return JVMTI_ERROR_NONE;
} /* end IsModifiableModule */
//
// Class functions
//
// class_count_ptr - pre-checked for NULL
// classes_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetLoadedClasses(jint* class_count_ptr, jclass** classes_ptr) {
return JvmtiGetLoadedClasses::getLoadedClasses(this, class_count_ptr, classes_ptr);
} /* end GetLoadedClasses */
// initiating_loader - NULL is a valid value, must be checked
// class_count_ptr - pre-checked for NULL
// classes_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetClassLoaderClasses(jobject initiating_loader, jint* class_count_ptr, jclass** classes_ptr) {
return JvmtiGetLoadedClasses::getClassLoaderClasses(this, initiating_loader,
class_count_ptr, classes_ptr);
} /* end GetClassLoaderClasses */
// k_mirror - may be primitive, this must be checked
// is_modifiable_class_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::IsModifiableClass(oop k_mirror, jboolean* is_modifiable_class_ptr) {
*is_modifiable_class_ptr = VM_RedefineClasses::is_modifiable_class(k_mirror)?
JNI_TRUE : JNI_FALSE;
return JVMTI_ERROR_NONE;
} /* end IsModifiableClass */
// class_count - pre-checked to be greater than or equal to 0
// classes - pre-checked for NULL
jvmtiError
JvmtiEnv::RetransformClasses(jint class_count, const jclass* classes) {
//TODO: add locking
int index;
JavaThread* current_thread = JavaThread::current();
ResourceMark rm(current_thread);
jvmtiClassDefinition* class_definitions =
NEW_RESOURCE_ARRAY(jvmtiClassDefinition, class_count);
NULL_CHECK(class_definitions, JVMTI_ERROR_OUT_OF_MEMORY);
for (index = 0; index < class_count; index++) {
HandleMark hm(current_thread);
jclass jcls = classes[index];
oop k_mirror = JNIHandles::resolve_external_guard(jcls);
if (k_mirror == NULL) {
return JVMTI_ERROR_INVALID_CLASS;
}
if (!k_mirror->is_a(vmClasses::Class_klass())) {
return JVMTI_ERROR_INVALID_CLASS;
}
if (!VM_RedefineClasses::is_modifiable_class(k_mirror)) {
return JVMTI_ERROR_UNMODIFIABLE_CLASS;
}
Klass* klass = java_lang_Class::as_Klass(k_mirror);
jint status = klass->jvmti_class_status();
if (status & (JVMTI_CLASS_STATUS_ERROR)) {
return JVMTI_ERROR_INVALID_CLASS;
}
InstanceKlass* ik = InstanceKlass::cast(klass);
if (ik->get_cached_class_file_bytes() == NULL) {
// Not cached, we need to reconstitute the class file from the
// VM representation. We don't attach the reconstituted class
// bytes to the InstanceKlass here because they have not been
// validated and we're not at a safepoint.
JvmtiClassFileReconstituter reconstituter(ik);
if (reconstituter.get_error() != JVMTI_ERROR_NONE) {
return reconstituter.get_error();
}
class_definitions[index].class_byte_count = (jint)reconstituter.class_file_size();
class_definitions[index].class_bytes = (unsigned char*)
reconstituter.class_file_bytes();
} else {
// it is cached, get it from the cache
class_definitions[index].class_byte_count = ik->get_cached_class_file_len();
class_definitions[index].class_bytes = ik->get_cached_class_file_bytes();
}
class_definitions[index].klass = jcls;
}
EventRetransformClasses event;
VM_RedefineClasses op(class_count, class_definitions, jvmti_class_load_kind_retransform);
VMThread::execute(&op);
jvmtiError error = op.check_error();
if (error == JVMTI_ERROR_NONE) {
event.set_classCount(class_count);
event.set_redefinitionId(op.id());
event.commit();
}
return error;
} /* end RetransformClasses */
// class_count - pre-checked to be greater than or equal to 0
// class_definitions - pre-checked for NULL
jvmtiError
JvmtiEnv::RedefineClasses(jint class_count, const jvmtiClassDefinition* class_definitions) {
//TODO: add locking
EventRedefineClasses event;
VM_RedefineClasses op(class_count, class_definitions, jvmti_class_load_kind_redefine);
VMThread::execute(&op);
jvmtiError error = op.check_error();
if (error == JVMTI_ERROR_NONE) {
event.set_classCount(class_count);
event.set_redefinitionId(op.id());
event.commit();
}
return error;
} /* end RedefineClasses */
//
// Object functions
//
// size_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetObjectSize(jobject object, jlong* size_ptr) {
oop mirror = JNIHandles::resolve_external_guard(object);
NULL_CHECK(mirror, JVMTI_ERROR_INVALID_OBJECT);
*size_ptr = (jlong)mirror->size() * wordSize;
return JVMTI_ERROR_NONE;
} /* end GetObjectSize */
//
// Method functions
//
// prefix - NULL is a valid value, must be checked
jvmtiError
JvmtiEnv::SetNativeMethodPrefix(const char* prefix) {
return prefix == NULL?
SetNativeMethodPrefixes(0, NULL) :
SetNativeMethodPrefixes(1, (char**)&prefix);
} /* end SetNativeMethodPrefix */
// prefix_count - pre-checked to be greater than or equal to 0
// prefixes - pre-checked for NULL
jvmtiError
JvmtiEnv::SetNativeMethodPrefixes(jint prefix_count, char** prefixes) {
// Have to grab JVMTI thread state lock to be sure that some thread
// isn't accessing the prefixes at the same time we are setting them.
// No locks during VM bring-up.
if (Threads::number_of_threads() == 0) {
return set_native_method_prefixes(prefix_count, prefixes);
} else {
MutexLocker mu(JvmtiThreadState_lock);
return set_native_method_prefixes(prefix_count, prefixes);
}
} /* end SetNativeMethodPrefixes */
//
// Event Management functions
//
// callbacks - NULL is a valid value, must be checked
// size_of_callbacks - pre-checked to be greater than or equal to 0
jvmtiError
JvmtiEnv::SetEventCallbacks(const jvmtiEventCallbacks* callbacks, jint size_of_callbacks) {
JvmtiVTMSTransitionDisabler disabler;
JvmtiEventController::set_event_callbacks(this, callbacks, size_of_callbacks);
return JVMTI_ERROR_NONE;
} /* end SetEventCallbacks */
// event_thread - NULL is a valid value, must be checked
jvmtiError
JvmtiEnv::SetEventNotificationMode(jvmtiEventMode mode, jvmtiEvent event_type, jthread event_thread, ...) {
bool enabled = (mode == JVMTI_ENABLE);
// event_type must be valid
if (!JvmtiEventController::is_valid_event_type(event_type)) {
return JVMTI_ERROR_INVALID_EVENT_TYPE;
}
// assure that needed capabilities are present
if (enabled && !JvmtiUtil::has_event_capability(event_type, get_capabilities())) {
return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
}
if (event_type == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK && enabled) {
record_class_file_load_hook_enabled();
}
JvmtiVTMSTransitionDisabler disabler;
if (event_thread == NULL) {
// Can be called at Agent_OnLoad() time with event_thread == NULL
// when Thread::current() does not work yet so we cannot create a
// ThreadsListHandle that is common to both thread-specific and
// global code paths.
JvmtiEventController::set_user_enabled(this, (JavaThread*) NULL, (oop) NULL, event_type, enabled);
} else {
// We have a specified event_thread.
ThreadsListHandle tlh;
JavaThread* java_thread = NULL;
oop thread_obj = NULL;
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), event_thread, &java_thread, &thread_obj);
if (err != JVMTI_ERROR_NONE) {
return err;
}
// global events cannot be controlled at thread level.
if (JvmtiEventController::is_global_event(event_type)) {
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
}
JvmtiEventController::set_user_enabled(this, java_thread, thread_obj, event_type, enabled);
}
return JVMTI_ERROR_NONE;
} /* end SetEventNotificationMode */
//
// Capability functions
//
// capabilities_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetPotentialCapabilities(jvmtiCapabilities* capabilities_ptr) {
JvmtiManageCapabilities::get_potential_capabilities(get_capabilities(),
get_prohibited_capabilities(),
capabilities_ptr);
return JVMTI_ERROR_NONE;
} /* end GetPotentialCapabilities */
// capabilities_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::AddCapabilities(const jvmtiCapabilities* capabilities_ptr) {
return JvmtiManageCapabilities::add_capabilities(get_capabilities(),
get_prohibited_capabilities(),
capabilities_ptr,
get_capabilities());
} /* end AddCapabilities */
// capabilities_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::RelinquishCapabilities(const jvmtiCapabilities* capabilities_ptr) {
JvmtiManageCapabilities::relinquish_capabilities(get_capabilities(), capabilities_ptr, get_capabilities());
return JVMTI_ERROR_NONE;
} /* end RelinquishCapabilities */
// capabilities_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetCapabilities(jvmtiCapabilities* capabilities_ptr) {
JvmtiManageCapabilities::copy_capabilities(get_capabilities(), capabilities_ptr);
return JVMTI_ERROR_NONE;
} /* end GetCapabilities */
//
// Class Loader Search functions
//
// segment - pre-checked for NULL
jvmtiError
JvmtiEnv::AddToBootstrapClassLoaderSearch(const char* segment) {
jvmtiPhase phase = get_phase();
if (phase == JVMTI_PHASE_ONLOAD) {
Arguments::append_sysclasspath(segment);
return JVMTI_ERROR_NONE;
} else if (use_version_1_0_semantics()) {
// This JvmtiEnv requested version 1.0 semantics and this function
// is only allowed in the ONLOAD phase in version 1.0 so we need to
// return an error here.
return JVMTI_ERROR_WRONG_PHASE;
} else if (phase == JVMTI_PHASE_LIVE) {
// The phase is checked by the wrapper that called this function,
// but this thread could be racing with the thread that is
// terminating the VM so we check one more time.
// create the zip entry
ClassPathZipEntry* zip_entry = ClassLoader::create_class_path_zip_entry(segment, true);
if (zip_entry == NULL) {
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
}
// add the jar file to the bootclasspath
log_info(class, load)("opened: %s", zip_entry->name());
#if INCLUDE_CDS
ClassLoaderExt::append_boot_classpath(zip_entry);
#else
ClassLoader::add_to_boot_append_entries(zip_entry);
#endif
return JVMTI_ERROR_NONE;
} else {
return JVMTI_ERROR_WRONG_PHASE;
}
} /* end AddToBootstrapClassLoaderSearch */
// segment - pre-checked for NULL
jvmtiError
JvmtiEnv::AddToSystemClassLoaderSearch(const char* segment) {
jvmtiPhase phase = get_phase();
if (phase == JVMTI_PHASE_ONLOAD) {
for (SystemProperty* p = Arguments::system_properties(); p != NULL; p = p->next()) {
if (strcmp("java.class.path", p->key()) == 0) {
p->append_value(segment);
break;
}
}
return JVMTI_ERROR_NONE;
} else if (phase == JVMTI_PHASE_LIVE) {
// The phase is checked by the wrapper that called this function,
// but this thread could be racing with the thread that is
// terminating the VM so we check one more time.
JavaThread* THREAD = JavaThread::current(); // For exception macros.
HandleMark hm(THREAD);
// create the zip entry (which will open the zip file and hence
// check that the segment is indeed a zip file).
ClassPathZipEntry* zip_entry = ClassLoader::create_class_path_zip_entry(segment, false);
if (zip_entry == NULL) {
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
}
delete zip_entry; // no longer needed
Handle loader(THREAD, SystemDictionary::java_system_loader());
// need the path as java.lang.String
Handle path = java_lang_String::create_from_platform_dependent_str(segment, THREAD);
if (HAS_PENDING_EXCEPTION) {
CLEAR_PENDING_EXCEPTION;
return JVMTI_ERROR_INTERNAL;
}
// Invoke the appendToClassPathForInstrumentation method - if the method
// is not found it means the loader doesn't support adding to the class path
// in the live phase.
{
JavaValue res(T_VOID);
JavaCalls::call_special(&res,
loader,
loader->klass(),
vmSymbols::appendToClassPathForInstrumentation_name(),
vmSymbols::appendToClassPathForInstrumentation_signature(),
path,
THREAD);
if (HAS_PENDING_EXCEPTION) {
Symbol* ex_name = PENDING_EXCEPTION->klass()->name();
CLEAR_PENDING_EXCEPTION;
if (ex_name == vmSymbols::java_lang_NoSuchMethodError()) {
return JVMTI_ERROR_CLASS_LOADER_UNSUPPORTED;
} else {
return JVMTI_ERROR_INTERNAL;
}
}
}
return JVMTI_ERROR_NONE;
} else {
return JVMTI_ERROR_WRONG_PHASE;
}
} /* end AddToSystemClassLoaderSearch */
//
// General functions
//
// phase_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetPhase(jvmtiPhase* phase_ptr) {
*phase_ptr = phase();
return JVMTI_ERROR_NONE;
} /* end GetPhase */
jvmtiError
JvmtiEnv::DisposeEnvironment() {
dispose();
return JVMTI_ERROR_NONE;
} /* end DisposeEnvironment */
// data - NULL is a valid value, must be checked
jvmtiError
JvmtiEnv::SetEnvironmentLocalStorage(const void* data) {
set_env_local_storage(data);
return JVMTI_ERROR_NONE;
} /* end SetEnvironmentLocalStorage */
// data_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetEnvironmentLocalStorage(void** data_ptr) {
*data_ptr = (void*)get_env_local_storage();
return JVMTI_ERROR_NONE;
} /* end GetEnvironmentLocalStorage */
// version_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetVersionNumber(jint* version_ptr) {
*version_ptr = JVMTI_VERSION;
return JVMTI_ERROR_NONE;
} /* end GetVersionNumber */
// name_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetErrorName(jvmtiError error, char** name_ptr) {
if (error < JVMTI_ERROR_NONE || error > JVMTI_ERROR_MAX) {
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
}
const char *name = JvmtiUtil::error_name(error);
if (name == NULL) {
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
}
size_t len = strlen(name) + 1;
jvmtiError err = allocate(len, (unsigned char**)name_ptr);
if (err == JVMTI_ERROR_NONE) {
memcpy(*name_ptr, name, len);
}
return err;
} /* end GetErrorName */
jvmtiError
JvmtiEnv::SetVerboseFlag(jvmtiVerboseFlag flag, jboolean value) {
LogLevelType level = value == 0 ? LogLevel::Off : LogLevel::Info;
switch (flag) {
case JVMTI_VERBOSE_OTHER:
// ignore
break;
case JVMTI_VERBOSE_CLASS:
LogConfiguration::configure_stdout(level, false, LOG_TAGS(class, unload));
LogConfiguration::configure_stdout(level, false, LOG_TAGS(class, load));
break;
case JVMTI_VERBOSE_GC:
LogConfiguration::configure_stdout(level, true, LOG_TAGS(gc));
break;
case JVMTI_VERBOSE_JNI:
level = value == 0 ? LogLevel::Off : LogLevel::Debug;
LogConfiguration::configure_stdout(level, true, LOG_TAGS(jni, resolve));
break;
default:
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
};
return JVMTI_ERROR_NONE;
} /* end SetVerboseFlag */
// format_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetJLocationFormat(jvmtiJlocationFormat* format_ptr) {
*format_ptr = JVMTI_JLOCATION_JVMBCI;
return JVMTI_ERROR_NONE;
} /* end GetJLocationFormat */
//
// Thread functions
//
// thread - NOT protected by ThreadsListHandle and NOT pre-checked
// thread_state_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetThreadState(jthread thread, jint* thread_state_ptr) {
JavaThread* current_thread = JavaThread::current();
JvmtiVTMSTransitionDisabler disabler;
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
oop thread_oop = NULL;
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_oop);
if (err != JVMTI_ERROR_NONE && err != JVMTI_ERROR_THREAD_NOT_ALIVE) {
// We got an error code so we don't have a JavaThread*, but only
// return an error from here if the error is not because the thread
// is a virtual thread.
return err;
}
if (java_lang_VirtualThread::is_instance(thread_oop)) {
*thread_state_ptr = JvmtiEnvBase::get_vthread_state(thread_oop, java_thread);
} else {
*thread_state_ptr = JvmtiEnvBase::get_thread_state(thread_oop, java_thread);
}
return JVMTI_ERROR_NONE;
} /* end GetThreadState */
// thread_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetCurrentThread(jthread* thread_ptr) {
JavaThread* cur_thread = JavaThread::current();
oop thread_oop = get_vthread_or_thread_oop(cur_thread);
*thread_ptr = (jthread)JNIHandles::make_local(cur_thread, thread_oop);
return JVMTI_ERROR_NONE;
} /* end GetCurrentThread */
// threads_count_ptr - pre-checked for NULL
// threads_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetAllThreads(jint* threads_count_ptr, jthread** threads_ptr) {
int nthreads = 0;
Handle *thread_objs = NULL;
Thread* current_thread = Thread::current();
ResourceMark rm(current_thread);
HandleMark hm(current_thread);
// enumerate threads (including agent threads)
ThreadsListEnumerator tle(current_thread, true);
nthreads = tle.num_threads();
*threads_count_ptr = nthreads;
if (nthreads == 0) {
*threads_ptr = NULL;
return JVMTI_ERROR_NONE;
}
thread_objs = NEW_RESOURCE_ARRAY(Handle, nthreads);
NULL_CHECK(thread_objs, JVMTI_ERROR_OUT_OF_MEMORY);
for (int i = 0; i < nthreads; i++) {
thread_objs[i] = Handle(tle.get_threadObj(i));
}
jthread *jthreads = new_jthreadArray(nthreads, thread_objs);
NULL_CHECK(jthreads, JVMTI_ERROR_OUT_OF_MEMORY);
*threads_ptr = jthreads;
return JVMTI_ERROR_NONE;
} /* end GetAllThreads */
// thread - NOT protected by ThreadsListHandle and NOT pre-checked
jvmtiError
JvmtiEnv::SuspendThread(jthread thread) {
JavaThread* current = JavaThread::current();
jvmtiError err;
JavaThread* java_thread = NULL;
oop thread_oop = NULL;
{
JvmtiVTMSTransitionDisabler disabler(true);
ThreadsListHandle tlh(current);
err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_oop);
if (err != JVMTI_ERROR_NONE) {
return err;
}
// Do not use JvmtiVTMSTransitionDisabler in context of self suspend to avoid deadlocks.
if (java_thread != current) {
err = suspend_thread(thread_oop, java_thread, /* single_suspend */ true, NULL);
return err;
}
}
// Do self suspend for current JavaThread.
err = suspend_thread(thread_oop, current, /* single_suspend */ true, NULL);
return err;
} /* end SuspendThread */
// request_count - pre-checked to be greater than or equal to 0
// request_list - pre-checked for NULL
// results - pre-checked for NULL
jvmtiError
JvmtiEnv::SuspendThreadList(jint request_count, const jthread* request_list, jvmtiError* results) {
JavaThread* current = JavaThread::current();
HandleMark hm(current);
Handle self_tobj = Handle(current, NULL);
int self_idx = -1;
{
JvmtiVTMSTransitionDisabler disabler(true);
ThreadsListHandle tlh(current);
for (int i = 0; i < request_count; i++) {
JavaThread *java_thread = NULL;
oop thread_oop = NULL;
jthread thread = request_list[i];
jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &java_thread, &thread_oop);
if (thread_oop != NULL &&
java_lang_VirtualThread::is_instance(thread_oop) &&
!JvmtiEnvBase::is_vthread_alive(thread_oop)) {
err = JVMTI_ERROR_THREAD_NOT_ALIVE;
}
if (err != JVMTI_ERROR_NONE) {
if (thread_oop == NULL || err != JVMTI_ERROR_INVALID_THREAD) {
results[i] = err;
continue;
}
}
if (java_thread == current) {
self_idx = i;
self_tobj = Handle(current, thread_oop);
continue; // self suspend after all other suspends
}
results[i] = suspend_thread(thread_oop, java_thread, /* single_suspend */ true, NULL);
}
}
// Self suspend after all other suspends if necessary.
// Do not use JvmtiVTMSTransitionDisabler in context of self suspend to avoid deadlocks.
if (self_tobj() != NULL) {
// there should not be any error for current java_thread
results[self_idx] = suspend_thread(self_tobj(), current, /* single_suspend */ true, NULL);
}
// per-thread suspend results returned via results parameter
return JVMTI_ERROR_NONE;
} /* end SuspendThreadList */
jvmtiError
JvmtiEnv::SuspendAllVirtualThreads(jint except_count, const jthread* except_list) {
if (!JvmtiExport::can_support_virtual_threads()) {
return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
}
if (!Continuations::enabled()) {
return JVMTI_ERROR_NONE; // Nothing to do when there are no virtual threads;
}
JavaThread* current = JavaThread::current();
HandleMark hm(current);
Handle self_tobj = Handle(current, NULL);
{
ResourceMark rm(current);
JvmtiVTMSTransitionDisabler disabler(true);
ThreadsListHandle tlh(current);
GrowableArray<jthread>* elist = new GrowableArray<jthread>(except_count);
jvmtiError err = JvmtiEnvBase::check_thread_list(except_count, except_list);
if (err != JVMTI_ERROR_NONE) {
return err;
}
// Collect threads from except_list for which resumed status must be restored.
for (int idx = 0; idx < except_count; idx++) {
jthread thread = except_list[idx];
oop thread_oop = JNIHandles::resolve_external_guard(thread);
if (!JvmtiVTSuspender::is_vthread_suspended(thread_oop)) {
// is not suspended, so its resumed status must be restored
elist->append(except_list[idx]);
}
}
for (JavaThreadIteratorWithHandle jtiwh; JavaThread *java_thread = jtiwh.next(); ) {
oop vt_oop = java_thread->jvmti_vthread();
if (!java_thread->is_exiting() &&
!java_thread->is_jvmti_agent_thread() &&
!java_thread->is_hidden_from_external_view() &&
vt_oop != NULL &&
java_lang_VirtualThread::is_instance(vt_oop) &&
JvmtiEnvBase::is_vthread_alive(vt_oop) &&
!JvmtiVTSuspender::is_vthread_suspended(vt_oop) &&
!is_in_thread_list(except_count, except_list, vt_oop)
) {
if (java_thread == current) {
self_tobj = Handle(current, vt_oop);
continue; // self suspend after all other suspends
}
suspend_thread(vt_oop, java_thread, /* single_suspend */ false, NULL);
}
}
JvmtiVTSuspender::register_all_vthreads_suspend();
// Restore resumed state for threads from except list that were not suspended before.
for (int idx = 0; idx < elist->length(); idx++) {
jthread thread = elist->at(idx);
oop thread_oop = JNIHandles::resolve_external_guard(thread);
if (JvmtiVTSuspender::is_vthread_suspended(thread_oop)) {
JvmtiVTSuspender::register_vthread_resume(thread_oop);
}
}
}
// Self suspend after all other suspends if necessary.
// Do not use JvmtiVTMSTransitionDisabler in context of self suspend to avoid deadlocks.
if (self_tobj() != NULL) {
suspend_thread(self_tobj(), current, /* single_suspend */ false, NULL);
}
return JVMTI_ERROR_NONE;
} /* end SuspendAllVirtualThreads */
// thread - NOT protected by ThreadsListHandle and NOT pre-checked
jvmtiError
JvmtiEnv::ResumeThread(jthread thread) {
JvmtiVTMSTransitionDisabler disabler(true);
ThreadsListHandle tlh;
JavaThread* java_thread = NULL;
oop thread_oop = NULL;
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_oop);
if (err != JVMTI_ERROR_NONE) {
return err;
}
err = resume_thread(thread_oop, java_thread, /* single_resume */ true);
return err;
} /* end ResumeThread */
// request_count - pre-checked to be greater than or equal to 0
// request_list - pre-checked for NULL
// results - pre-checked for NULL
jvmtiError
JvmtiEnv::ResumeThreadList(jint request_count, const jthread* request_list, jvmtiError* results) {
oop thread_oop = NULL;
JavaThread* java_thread = NULL;
JvmtiVTMSTransitionDisabler disabler(true);
ThreadsListHandle tlh;
for (int i = 0; i < request_count; i++) {
jthread thread = request_list[i];
jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &java_thread, &thread_oop);
if (thread_oop != NULL &&
java_lang_VirtualThread::is_instance(thread_oop) &&
!JvmtiEnvBase::is_vthread_alive(thread_oop)) {
err = JVMTI_ERROR_THREAD_NOT_ALIVE;
}
if (err != JVMTI_ERROR_NONE) {
if (thread_oop == NULL || err != JVMTI_ERROR_INVALID_THREAD) {
results[i] = err;
continue;
}
}
results[i] = resume_thread(thread_oop, java_thread, /* single_resume */ true);
}
// per-thread resume results returned via results parameter
return JVMTI_ERROR_NONE;
} /* end ResumeThreadList */
jvmtiError
JvmtiEnv::ResumeAllVirtualThreads(jint except_count, const jthread* except_list) {
if (!JvmtiExport::can_support_virtual_threads()) {
return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
}
if (!Continuations::enabled()) {
return JVMTI_ERROR_NONE; // Nothing to do when there are no virtual threads;
}
jvmtiError err = JvmtiEnvBase::check_thread_list(except_count, except_list);
if (err != JVMTI_ERROR_NONE) {
return err;
}
ResourceMark rm;
JvmtiVTMSTransitionDisabler disabler(true);
GrowableArray<jthread>* elist = new GrowableArray<jthread>(except_count);
// Collect threads from except_list for which suspended status must be restored.
for (int idx = 0; idx < except_count; idx++) {
jthread thread = except_list[idx];
oop thread_oop = JNIHandles::resolve_external_guard(thread);
if (JvmtiVTSuspender::is_vthread_suspended(thread_oop)) {
// is suspended, so its suspended status must be restored
elist->append(except_list[idx]);
}
}
for (JavaThreadIteratorWithHandle jtiwh; JavaThread *java_thread = jtiwh.next(); ) {
oop vt_oop = java_thread->jvmti_vthread();
if (!java_thread->is_exiting() &&
!java_thread->is_jvmti_agent_thread() &&
!java_thread->is_hidden_from_external_view() &&
vt_oop != NULL &&
java_lang_VirtualThread::is_instance(vt_oop) &&
JvmtiEnvBase::is_vthread_alive(vt_oop) &&
JvmtiVTSuspender::is_vthread_suspended(vt_oop) &&
!is_in_thread_list(except_count, except_list, vt_oop)
) {
resume_thread(vt_oop, java_thread, /* single_resume */ false);
}
}
JvmtiVTSuspender::register_all_vthreads_resume();
// Restore suspended state for threads from except list that were suspended before.
for (int idx = 0; idx < elist->length(); idx++) {
jthread thread = elist->at(idx);
oop thread_oop = JNIHandles::resolve_external_guard(thread);
if (!JvmtiVTSuspender::is_vthread_suspended(thread_oop)) {
JvmtiVTSuspender::register_vthread_suspend(thread_oop);
}
}
return JVMTI_ERROR_NONE;
} /* end ResumeAllVirtualThreads */
jvmtiError
JvmtiEnv::StopThread(jthread thread, jobject exception) {
JavaThread* current_thread = JavaThread::current();
JvmtiVTMSTransitionDisabler disabler;
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
oop thread_oop = NULL;
NULL_CHECK(thread, JVMTI_ERROR_INVALID_THREAD);
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_oop);
if (thread_oop != NULL && java_lang_VirtualThread::is_instance(thread_oop)) {
// No support for virtual threads (yet).
return JVMTI_ERROR_UNSUPPORTED_OPERATION;
}
if (err != JVMTI_ERROR_NONE) {
return err;
}
oop e = JNIHandles::resolve_external_guard(exception);
NULL_CHECK(e, JVMTI_ERROR_NULL_POINTER);
JavaThread::send_async_exception(java_thread, e);
return JVMTI_ERROR_NONE;
} /* end StopThread */
// thread - NOT protected by ThreadsListHandle and NOT pre-checked
jvmtiError
JvmtiEnv::InterruptThread(jthread thread) {
JavaThread* current_thread = JavaThread::current();
HandleMark hm(current_thread);
JvmtiVTMSTransitionDisabler disabler;
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
oop thread_obj = NULL;
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_obj);
if (err != JVMTI_ERROR_NONE) {
return err;
}
if (java_lang_VirtualThread::is_instance(thread_obj)) {
// For virtual threads we have to call into Java to interrupt:
Handle obj(current_thread, thread_obj);
JavaValue result(T_VOID);
JavaCalls::call_virtual(&result,
obj,
vmClasses::Thread_klass(),
vmSymbols::interrupt_method_name(),
vmSymbols::void_method_signature(),
current_thread);
return JVMTI_ERROR_NONE;
}
// Really this should be a Java call to Thread.interrupt to ensure the same
// semantics, however historically this has not been done for some reason.
// So we continue with that (which means we don't interact with any Java-level
// Interruptible object) but we must set the Java-level interrupted state.
java_lang_Thread::set_interrupted(thread_obj, true);
java_thread->interrupt();
return JVMTI_ERROR_NONE;
} /* end InterruptThread */
// thread - NOT protected by ThreadsListHandle and NOT pre-checked
// info_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetThreadInfo(jthread thread, jvmtiThreadInfo* info_ptr) {
JavaThread* current_thread = JavaThread::current();
ResourceMark rm(current_thread);
HandleMark hm(current_thread);
JavaThread* java_thread = NULL;
oop thread_oop = NULL;
JvmtiVTMSTransitionDisabler disabler;
ThreadsListHandle tlh(current_thread);
// if thread is NULL the current thread is used
if (thread == NULL) {
java_thread = JavaThread::current();
thread_oop = get_vthread_or_thread_oop(java_thread);
if (thread_oop == NULL || !thread_oop->is_a(vmClasses::Thread_klass())) {
return JVMTI_ERROR_INVALID_THREAD;
}
} else {
jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &java_thread, &thread_oop);
if (err != JVMTI_ERROR_NONE) {
// We got an error code so we don't have a JavaThread *, but
// only return an error from here if we didn't get a valid
// thread_oop.
// In the virtual thread case the cv_external_thread_to_JavaThread is expected to correctly set
// the thread_oop and return JVMTI_ERROR_INVALID_THREAD which we ignore here.
if (thread_oop == NULL) {
return err;
}
}
}
// We have a valid thread_oop so we can return some thread info.
Handle thread_obj(current_thread, thread_oop);
Handle name;
ThreadPriority priority;
Handle thread_group;
Handle context_class_loader;
bool is_daemon;
name = Handle(current_thread, java_lang_Thread::name(thread_obj()));
if (java_lang_VirtualThread::is_instance(thread_obj())) {
priority = (ThreadPriority)JVMTI_THREAD_NORM_PRIORITY;
is_daemon = true;
if (java_lang_VirtualThread::state(thread_obj()) == java_lang_VirtualThread::TERMINATED) {
thread_group = Handle(current_thread, NULL);
} else {
thread_group = Handle(current_thread, java_lang_Thread_Constants::get_VTHREAD_GROUP());
}
} else {
priority = java_lang_Thread::priority(thread_obj());
is_daemon = java_lang_Thread::is_daemon(thread_obj());
if (java_lang_Thread::get_thread_status(thread_obj()) == JavaThreadStatus::TERMINATED) {
thread_group = Handle(current_thread, NULL);
} else {
thread_group = Handle(current_thread, java_lang_Thread::threadGroup(thread_obj()));
}
}
oop loader = java_lang_Thread::context_class_loader(thread_obj());
if (loader != NULL) {
// Do the same as Thread.getContextClassLoader and set context_class_loader to be
// the system class loader when the field value is the "not supported" placeholder.
if (loader == java_lang_Thread_Constants::get_NOT_SUPPORTED_CLASSLOADER()) {
loader = SystemDictionary::java_system_loader();
}
}
context_class_loader = Handle(current_thread, loader);
{ const char *n;
if (name() != NULL) {
n = java_lang_String::as_utf8_string(name());
} else {
int utf8_length = 0;
n = UNICODE::as_utf8((jchar*) NULL, utf8_length);
}
info_ptr->name = (char *) jvmtiMalloc(strlen(n)+1);
if (info_ptr->name == NULL)
return JVMTI_ERROR_OUT_OF_MEMORY;
strcpy(info_ptr->name, n);
}
info_ptr->is_daemon = is_daemon;
info_ptr->priority = priority;
info_ptr->context_class_loader = (context_class_loader.is_null()) ? NULL :
jni_reference(context_class_loader);
info_ptr->thread_group = jni_reference(thread_group);
return JVMTI_ERROR_NONE;
} /* end GetThreadInfo */
// thread - NOT protected by ThreadsListHandle and NOT pre-checked
// owned_monitor_count_ptr - pre-checked for NULL
// owned_monitors_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetOwnedMonitorInfo(jthread thread, jint* owned_monitor_count_ptr, jobject** owned_monitors_ptr) {
JavaThread* calling_thread = JavaThread::current();
HandleMark hm(calling_thread);
// growable array of jvmti monitors info on the C-heap
GrowableArray<jvmtiMonitorStackDepthInfo*> *owned_monitors_list =
new (mtServiceability) GrowableArray<jvmtiMonitorStackDepthInfo*>(1, mtServiceability);
JvmtiVTMSTransitionDisabler disabler;
ThreadsListHandle tlh(calling_thread);
JavaThread* java_thread = NULL;
oop thread_oop = NULL;
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_oop);
if (err != JVMTI_ERROR_NONE) {
delete owned_monitors_list;
return err;
}
if (java_lang_VirtualThread::is_instance(thread_oop)) {
// There is no monitor info to collect if target virtual thread is unmounted.
if (java_thread != NULL) {
VirtualThreadGetOwnedMonitorInfoClosure op(this,
Handle(calling_thread, thread_oop),
owned_monitors_list);
Handshake::execute(&op, java_thread);
err = op.result();
}
} else {
EscapeBarrier eb(true, calling_thread, java_thread);
if (!eb.deoptimize_objects(MaxJavaStackTraceDepth)) {
delete owned_monitors_list;
return JVMTI_ERROR_OUT_OF_MEMORY;
}
if (java_thread == calling_thread) {
// It is only safe to make a direct call on the current thread.
// All other usage needs to use a direct handshake for safety.
err = get_owned_monitors(calling_thread, java_thread, owned_monitors_list);
} else {
// get owned monitors info with handshake
GetOwnedMonitorInfoClosure op(calling_thread, this, owned_monitors_list);
Handshake::execute(&op, java_thread);
err = op.result();
}
}
jint owned_monitor_count = owned_monitors_list->length();
if (err == JVMTI_ERROR_NONE) {
if ((err = allocate(owned_monitor_count * sizeof(jobject *),
(unsigned char**)owned_monitors_ptr)) == JVMTI_ERROR_NONE) {
// copy into the returned array
for (int i = 0; i < owned_monitor_count; i++) {
(*owned_monitors_ptr)[i] =
((jvmtiMonitorStackDepthInfo*)owned_monitors_list->at(i))->monitor;
}
*owned_monitor_count_ptr = owned_monitor_count;
}
}
// clean up.
for (int i = 0; i < owned_monitor_count; i++) {
deallocate((unsigned char*)owned_monitors_list->at(i));
}
delete owned_monitors_list;
return err;
} /* end GetOwnedMonitorInfo */
// thread - NOT protected by ThreadsListHandle and NOT pre-checked
// monitor_info_count_ptr - pre-checked for NULL
// monitor_info_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetOwnedMonitorStackDepthInfo(jthread thread, jint* monitor_info_count_ptr, jvmtiMonitorStackDepthInfo** monitor_info_ptr) {
JavaThread* calling_thread = JavaThread::current();
HandleMark hm(calling_thread);
// growable array of jvmti monitors info on the C-heap
GrowableArray<jvmtiMonitorStackDepthInfo*> *owned_monitors_list =
new (mtServiceability) GrowableArray<jvmtiMonitorStackDepthInfo*>(1, mtServiceability);
JvmtiVTMSTransitionDisabler disabler;
ThreadsListHandle tlh(calling_thread);
JavaThread* java_thread = NULL;
oop thread_oop = NULL;
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_oop);
if (err != JVMTI_ERROR_NONE) {
delete owned_monitors_list;
return err;
}
if (java_lang_VirtualThread::is_instance(thread_oop)) {
// There is no monitor info to collect if target virtual thread is unmounted.
if (java_thread != NULL) {
VirtualThreadGetOwnedMonitorInfoClosure op(this,
Handle(calling_thread, thread_oop),
owned_monitors_list);
Handshake::execute(&op, java_thread);
err = op.result();
}
} else {
EscapeBarrier eb(true, calling_thread, java_thread);
if (!eb.deoptimize_objects(MaxJavaStackTraceDepth)) {
delete owned_monitors_list;
return JVMTI_ERROR_OUT_OF_MEMORY;
}
if (java_thread == calling_thread) {
// It is only safe to make a direct call on the current thread.
// All other usage needs to use a direct handshake for safety.
err = get_owned_monitors(calling_thread, java_thread, owned_monitors_list);
} else {
// get owned monitors info with handshake
GetOwnedMonitorInfoClosure op(calling_thread, this, owned_monitors_list);
Handshake::execute(&op, java_thread);
err = op.result();
}
}
jint owned_monitor_count = owned_monitors_list->length();
if (err == JVMTI_ERROR_NONE) {
if ((err = allocate(owned_monitor_count * sizeof(jvmtiMonitorStackDepthInfo),
(unsigned char**)monitor_info_ptr)) == JVMTI_ERROR_NONE) {
// copy to output array.
for (int i = 0; i < owned_monitor_count; i++) {
(*monitor_info_ptr)[i].monitor =
((jvmtiMonitorStackDepthInfo*)owned_monitors_list->at(i))->monitor;
(*monitor_info_ptr)[i].stack_depth =
((jvmtiMonitorStackDepthInfo*)owned_monitors_list->at(i))->stack_depth;
}
}
*monitor_info_count_ptr = owned_monitor_count;
}
// clean up.
for (int i = 0; i < owned_monitor_count; i++) {
deallocate((unsigned char*)owned_monitors_list->at(i));
}
delete owned_monitors_list;
return err;
} /* end GetOwnedMonitorStackDepthInfo */
// thread - NOT protected by ThreadsListHandle and NOT pre-checked
// monitor_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetCurrentContendedMonitor(jthread thread, jobject* monitor_ptr) {
JavaThread* calling_thread = JavaThread::current();
HandleMark hm(calling_thread);
JvmtiVTMSTransitionDisabler disabler;
ThreadsListHandle tlh(calling_thread);
JavaThread* java_thread = NULL;
oop thread_oop = NULL;
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_oop);
if (err != JVMTI_ERROR_NONE) {
return err;
}
if (java_lang_VirtualThread::is_instance(thread_oop)) {
// There is no monitor info to collect if target virtual thread is unmounted.
if (java_thread != NULL) {
GetCurrentContendedMonitorClosure op(calling_thread, this, monitor_ptr, /* is_virtual */ true);
Handshake::execute(&op, java_thread);
err = op.result();
} else {
*monitor_ptr = NULL;
if (!JvmtiEnvBase::is_vthread_alive(thread_oop)) {
err = JVMTI_ERROR_THREAD_NOT_ALIVE;
}
}
return err;
}
if (java_thread == calling_thread) {
// It is only safe to make a direct call on the current thread.
// All other usage needs to use a direct handshake for safety.
err = get_current_contended_monitor(calling_thread, java_thread, monitor_ptr, /* is_virtual */ false);
} else {
// get contended monitor information with handshake
GetCurrentContendedMonitorClosure op(calling_thread, this, monitor_ptr, /* is_virtual */ false);
Handshake::execute(&op, java_thread);
err = op.result();
}
return err;
} /* end GetCurrentContendedMonitor */
// thread - NOT protected by ThreadsListHandle and NOT pre-checked
// proc - pre-checked for NULL
// arg - NULL is a valid value, must be checked
jvmtiError
JvmtiEnv::RunAgentThread(jthread thread, jvmtiStartFunction proc, const void* arg, jint priority) {
JavaThread* current_thread = JavaThread::current();
JavaThread* java_thread = NULL;
oop thread_oop = NULL;
ThreadsListHandle tlh(current_thread);
jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &java_thread, &thread_oop);
if (err != JVMTI_ERROR_NONE) {
// We got an error code so we don't have a JavaThread *, but
// only return an error from here if we didn't get a valid
// thread_oop.
if (thread_oop == NULL) {
return err;
}
// We have a valid thread_oop.
}
if (java_thread != NULL) {
// 'thread' refers to an existing JavaThread.
return JVMTI_ERROR_INVALID_THREAD;
}
if (java_lang_VirtualThread::is_instance(thread_oop)) {
// No support for virtual threads.
return JVMTI_ERROR_UNSUPPORTED_OPERATION;
}
if (priority < JVMTI_THREAD_MIN_PRIORITY || priority > JVMTI_THREAD_MAX_PRIORITY) {
return JVMTI_ERROR_INVALID_PRIORITY;
}
Handle thread_hndl(current_thread, thread_oop);
JvmtiAgentThread* new_thread = new JvmtiAgentThread(this, proc, arg);
// At this point it may be possible that no osthread was created for the
// JavaThread due to lack of resources.
if (new_thread->osthread() == NULL) {
// The new thread is not known to Thread-SMR yet so we can just delete.
delete new_thread;
return JVMTI_ERROR_OUT_OF_MEMORY;
}
JavaThread::start_internal_daemon(current_thread, new_thread, thread_hndl,
(ThreadPriority)priority);
return JVMTI_ERROR_NONE;
} /* end RunAgentThread */
//
// Thread Group functions
//
// group_count_ptr - pre-checked for NULL
// groups_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetTopThreadGroups(jint* group_count_ptr, jthreadGroup** groups_ptr) {
JavaThread* current_thread = JavaThread::current();
// Only one top level thread group now.
*group_count_ptr = 1;
// Allocate memory to store global-refs to the thread groups.
// Assume this area is freed by caller.
*groups_ptr = (jthreadGroup *) jvmtiMalloc((sizeof(jthreadGroup)) * (*group_count_ptr));
NULL_CHECK(*groups_ptr, JVMTI_ERROR_OUT_OF_MEMORY);
// Convert oop to Handle, then convert Handle to global-ref.
{
HandleMark hm(current_thread);
Handle system_thread_group(current_thread, Universe::system_thread_group());
*groups_ptr[0] = jni_reference(system_thread_group);
}
return JVMTI_ERROR_NONE;
} /* end GetTopThreadGroups */
// info_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetThreadGroupInfo(jthreadGroup group, jvmtiThreadGroupInfo* info_ptr) {
Thread* current_thread = Thread::current();
ResourceMark rm(current_thread);
HandleMark hm(current_thread);
Handle group_obj (current_thread, JNIHandles::resolve_external_guard(group));
NULL_CHECK(group_obj(), JVMTI_ERROR_INVALID_THREAD_GROUP);
const char* name;
Handle parent_group;
bool is_daemon;
ThreadPriority max_priority;
name = java_lang_ThreadGroup::name(group_obj());
parent_group = Handle(current_thread, java_lang_ThreadGroup::parent(group_obj()));
is_daemon = java_lang_ThreadGroup::is_daemon(group_obj());
max_priority = java_lang_ThreadGroup::maxPriority(group_obj());
info_ptr->is_daemon = is_daemon;
info_ptr->max_priority = max_priority;
info_ptr->parent = jni_reference(parent_group);
if (name != NULL) {
info_ptr->name = (char*)jvmtiMalloc(strlen(name)+1);
NULL_CHECK(info_ptr->name, JVMTI_ERROR_OUT_OF_MEMORY);
strcpy(info_ptr->name, name);
} else {
info_ptr->name = NULL;
}
return JVMTI_ERROR_NONE;
} /* end GetThreadGroupInfo */
// thread_count_ptr - pre-checked for NULL
// threads_ptr - pre-checked for NULL
// group_count_ptr - pre-checked for NULL
// groups_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetThreadGroupChildren(jthreadGroup group, jint* thread_count_ptr, jthread** threads_ptr, jint* group_count_ptr, jthreadGroup** groups_ptr) {
jvmtiError err;
JavaThread* current_thread = JavaThread::current();
oop group_obj = JNIHandles::resolve_external_guard(group);
NULL_CHECK(group_obj, JVMTI_ERROR_INVALID_THREAD_GROUP);
Handle *thread_objs = NULL;
objArrayHandle group_objs;
jint nthreads = 0;
jint ngroups = 0;
int hidden_threads = 0;
ResourceMark rm(current_thread);
HandleMark hm(current_thread);
Handle group_hdl(current_thread, group_obj);
err = get_live_threads(current_thread, group_hdl, &nthreads, &thread_objs);
if (err != JVMTI_ERROR_NONE) {
return err;
}
err = get_subgroups(current_thread, group_hdl, &ngroups, &group_objs);
if (err != JVMTI_ERROR_NONE) {
return err;
}
*group_count_ptr = ngroups;
*thread_count_ptr = nthreads;
*threads_ptr = new_jthreadArray(nthreads, thread_objs);
*groups_ptr = new_jthreadGroupArray(ngroups, group_objs);
if (nthreads > 0 && *threads_ptr == NULL) {
return JVMTI_ERROR_OUT_OF_MEMORY;
}
if (ngroups > 0 && *groups_ptr == NULL) {
return JVMTI_ERROR_OUT_OF_MEMORY;
}
return JVMTI_ERROR_NONE;
} /* end GetThreadGroupChildren */
//
// Stack Frame functions
//
// thread - NOT protected by ThreadsListHandle and NOT pre-checked
// max_frame_count - pre-checked to be greater than or equal to 0
// frame_buffer - pre-checked for NULL
// count_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetStackTrace(jthread thread, jint start_depth, jint max_frame_count, jvmtiFrameInfo* frame_buffer, jint* count_ptr) {
JavaThread* current_thread = JavaThread::current();
HandleMark hm(current_thread);
JvmtiVTMSTransitionDisabler disabler;
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
oop thread_obj = NULL;
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_obj);
if (err != JVMTI_ERROR_NONE) {
return err;
}
if (java_lang_VirtualThread::is_instance(thread_obj)) {
if (java_thread == NULL) { // Target virtual thread is unmounted.
ResourceMark rm(current_thread);
VM_VirtualThreadGetStackTrace op(this, Handle(current_thread, thread_obj),
start_depth, max_frame_count,
frame_buffer, count_ptr);
VMThread::execute(&op);
return op.result();
}
VirtualThreadGetStackTraceClosure op(this, Handle(current_thread, thread_obj),
start_depth, max_frame_count, frame_buffer, count_ptr);
Handshake::execute(&op, java_thread);
return op.result();
}
// It is only safe to perform the direct operation on the current
// thread. All other usage needs to use a direct handshake for safety.
if (java_thread == JavaThread::current()) {
err = get_stack_trace(java_thread, start_depth, max_frame_count, frame_buffer, count_ptr);
} else {
// Get stack trace with handshake.
GetStackTraceClosure op(this, start_depth, max_frame_count, frame_buffer, count_ptr);
Handshake::execute(&op, java_thread);
err = op.result();
}
return err;
} /* end GetStackTrace */
// max_frame_count - pre-checked to be greater than or equal to 0
// stack_info_ptr - pre-checked for NULL
// thread_count_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetAllStackTraces(jint max_frame_count, jvmtiStackInfo** stack_info_ptr, jint* thread_count_ptr) {
jvmtiError err = JVMTI_ERROR_NONE;
JavaThread* calling_thread = JavaThread::current();
// JVMTI get stack traces at safepoint.
VM_GetAllStackTraces op(this, calling_thread, max_frame_count);
VMThread::execute(&op);
*thread_count_ptr = op.final_thread_count();
*stack_info_ptr = op.stack_info();
err = op.result();
return err;
} /* end GetAllStackTraces */
// thread_count - pre-checked to be greater than or equal to 0
// thread_list - pre-checked for NULL
// max_frame_count - pre-checked to be greater than or equal to 0
// stack_info_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetThreadListStackTraces(jint thread_count, const jthread* thread_list, jint max_frame_count, jvmtiStackInfo** stack_info_ptr) {
jvmtiError err = JVMTI_ERROR_NONE;
if (thread_count == 1) {
JvmtiVTMSTransitionDisabler disabler;
// Use direct handshake if we need to get only one stack trace.
JavaThread *current_thread = JavaThread::current();
ThreadsListHandle tlh(current_thread);
jthread thread = thread_list[0];
JavaThread *java_thread;
oop thread_obj = NULL;
err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_obj);
if (err != JVMTI_ERROR_NONE) {
return err;
}
if (java_lang_VirtualThread::is_instance(thread_obj) && java_thread == NULL) {
// Target virtual thread is unmounted.
ResourceMark rm(current_thread);
MultipleStackTracesCollector collector(this, max_frame_count);
collector.fill_frames(thread, java_thread, thread_obj);
collector.allocate_and_fill_stacks(/* thread_count */ 1);
*stack_info_ptr = collector.stack_info();
return collector.result();
}
GetSingleStackTraceClosure op(this, current_thread, thread, max_frame_count);
Handshake::execute(&op, &tlh, java_thread);
err = op.result();
if (err == JVMTI_ERROR_NONE) {
*stack_info_ptr = op.stack_info();
}
} else {
// JVMTI get stack traces at safepoint.
VM_GetThreadListStackTraces op(this, thread_count, thread_list, max_frame_count);
VMThread::execute(&op);
err = op.result();
if (err == JVMTI_ERROR_NONE) {
*stack_info_ptr = op.stack_info();
}
}
return err;
} /* end GetThreadListStackTraces */
// thread - NOT protected by ThreadsListHandle and NOT pre-checked
// count_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetFrameCount(jthread thread, jint* count_ptr) {
JavaThread* current_thread = JavaThread::current();
HandleMark hm(current_thread);
JvmtiVTMSTransitionDisabler disabler;
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
oop thread_obj = NULL;
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_obj);
if (err != JVMTI_ERROR_NONE) {
return err;
}
if (java_lang_VirtualThread::is_instance(thread_obj)) {
if (java_thread == NULL) { // Target virtual thread is unmounted.
VM_VirtualThreadGetFrameCount op(this, Handle(current_thread, thread_obj), count_ptr);
VMThread::execute(&op);
return op.result();
}
VirtualThreadGetFrameCountClosure op(this, Handle(current_thread, thread_obj), count_ptr);
Handshake::execute(&op, java_thread);
return op.result();
}
// It is only safe to perform the direct operation on the current
// thread. All other usage needs to use a direct handshake for safety.
if (java_thread == JavaThread::current()) {
err = get_frame_count(java_thread, count_ptr);
} else {
// get java stack frame count with handshake.
GetFrameCountClosure op(this, count_ptr);
Handshake::execute(&op, java_thread);
err = op.result();
}
return err;
} /* end GetFrameCount */
// thread - NOT protected by ThreadsListHandle and NOT pre-checked
jvmtiError
JvmtiEnv::PopFrame(jthread thread) {
JavaThread* current_thread = JavaThread::current();
HandleMark hm(current_thread);
if (thread == NULL) {
--> --------------------
--> maximum size reached
--> --------------------
¤ Dauer der Verarbeitung: 0.123 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.
|