/
githubmirror
/
panama-vector
Обзор
Документация
Войти
/
githubmirror
/
panama-vector
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/hotspot/share/prims/jvmtiAgent.cpp
640 строк
22 KB
Stefan Karlsson
8382401: Remove return type parameters from FREE_ and REALLOC_ macros
20 апр 2026, 11:42
20 апр 2026, 11:42
abd3d46
Код
Авторство
О чём код?
/* * Copyright (c) 2023, 2025, 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 "cds/cds_globals.hpp" #include "cds/cdsConfig.hpp" #include "jni.h" #include "jvm_io.h" #include "jvmtifiles/jvmtiEnv.hpp" #include "prims/jvmtiAgent.hpp" #include "prims/jvmtiAgentList.hpp" #include "prims/jvmtiEnvBase.hpp" #include "prims/jvmtiExport.hpp" #include "runtime/arguments.hpp" #include "runtime/atomicAccess.hpp" #include "runtime/globals_extension.hpp" #include "runtime/handles.inline.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/java.hpp" #include "runtime/jniHandles.hpp" #include "runtime/os.inline.hpp" #include "runtime/thread.inline.hpp" #include "utilities/defaultStream.hpp" static inline const char* copy_string(const char* str) { return str != nullptr ? os::strdup(str, mtServiceability) : nullptr; } // Returns the lhs before '=', parsed_options output param gets the rhs. static const char* split_options_and_allocate_copy(const char* options, const char** parsed_options) { assert(options != nullptr, "invariant"); assert(parsed_options != nullptr, "invariant"); const char* const equal_sign = strchr(options, '='); const size_t length = strlen(options); size_t name_length = length; if (equal_sign != nullptr) { name_length = equal_sign - options; const size_t options_length = length - name_length - 1; *parsed_options = copy_string(equal_sign + 1); } else { *parsed_options = nullptr; name_length = length; } char* const name = AllocateHeap(name_length + 1, mtServiceability); jio_snprintf(name, name_length + 1, "%s", options); assert(strncmp(name, options, name_length) == 0, "invariant"); return name; } JvmtiAgent::JvmtiAgent(const char* name, const char* options, bool is_absolute_path, bool dynamic /* false */) : _initialization_time(), _initialization_duration(), _next(nullptr), _name(copy_string(name)), _options(copy_string(options)), _os_lib(nullptr), _os_lib_path(nullptr), _jplis(nullptr), _loaded(false), _absolute_path(is_absolute_path), _static_lib(false), _instrument_lib(strcmp(name, "instrument") == 0), _dynamic(dynamic), _xrun(false) {} JvmtiAgent* JvmtiAgent::next() const { return AtomicAccess::load_acquire(&_next); } const char* JvmtiAgent::name() const { return _name; } const char* JvmtiAgent::options() const { return _options; } void* JvmtiAgent::os_lib() const { return _os_lib; } void JvmtiAgent::set_os_lib(void* os_lib) { _os_lib = os_lib; } void JvmtiAgent::set_os_lib_path(const char* path) { assert(path != nullptr, "invariant"); if (_os_lib_path == nullptr) { _os_lib_path = copy_string(path); } assert(strcmp(_os_lib_path, path) == 0, "invariant"); } const char* JvmtiAgent::os_lib_path() const { return _os_lib_path; } bool JvmtiAgent::is_loaded() const { return _loaded; } void JvmtiAgent::set_loaded() { _loaded = true; } bool JvmtiAgent::is_absolute_path() const { return _absolute_path; } bool JvmtiAgent::is_static_lib() const { return _static_lib; } void JvmtiAgent::set_static_lib() { _static_lib = true; } bool JvmtiAgent::is_dynamic() const { return _dynamic; } bool JvmtiAgent:: is_instrument_lib() const { return _instrument_lib; } bool JvmtiAgent::is_xrun() const { return _xrun; } void JvmtiAgent::set_xrun() { _xrun = true; } bool JvmtiAgent::is_jplis() const { return _jplis != nullptr; } const Ticks& JvmtiAgent::initialization_time() const { return _initialization_time; } const Tickspan& JvmtiAgent::initialization_duration() const { return _initialization_duration; } bool JvmtiAgent::is_initialized() const { return _initialization_time.value() != 0; } void JvmtiAgent::initialization_begin() { assert(!is_initialized(), "invariant"); _initialization_time = Ticks::now(); } void JvmtiAgent::initialization_end() { assert(is_initialized(), "invariant"); assert(_initialization_duration.value() == 0, "invariant"); _initialization_duration = Ticks::now() - initialization_time(); } /* * The implementation builds a mapping bewteen JvmtiEnvs and JPLIS agents, * using internal JDK implementation knowledge about the way JPLIS agents * store data in their JvmtiEnv local storage. * * Please see JPLISAgent.h and JPLISAgent.c in module java.instrument. * * jvmtierror = (*jvmtienv)->SetEnvironmentLocalStorage( jvmtienv, &(agent->mNormalEnvironment)); * * It is the pointer to the field agent->mNormalEnvironment that is stored in the jvmtiEnv local storage. * It has the following type: * * struct _JPLISEnvironment { * jvmtiEnv* mJVMTIEnv; // the JVMTI environment * JPLISAgent* mAgent; // corresponding agent * jboolean mIsRetransformer; // indicates if special environment * }; * * We mirror this struct to get the mAgent field as an identifier. */ struct JPLISEnvironmentMirror { jvmtiEnv* mJVMTIEnv; // the JVMTI environment const void* mAgent; // corresponding agent jboolean mIsRetransformer; // indicates if special environment }; static inline const JPLISEnvironmentMirror* get_env_local_storage(JvmtiEnv* env) { assert(env != nullptr, "invariant"); return reinterpret_cast<const JPLISEnvironmentMirror*>(env->get_env_local_storage()); } bool JvmtiAgent::is_jplis(JvmtiEnv* env) const { assert(env != nullptr, "invariant"); assert(is_instrument_lib(), "invariant"); const JPLISEnvironmentMirror* const jplis_env = get_env_local_storage(env); return jplis_env != nullptr && _jplis == jplis_env->mAgent; } void JvmtiAgent::set_jplis(const void* jplis) { assert(jplis != nullptr, "invaiant"); assert(is_instrument_lib(), "invariant"); assert(_jplis == nullptr, "invariant"); if (_options != nullptr) { // For JPLIS agents, update with the java name and options. os::free(const_cast<char*>(_name)); const char* options = _options; _name = split_options_and_allocate_copy(options, &_options); os::free(const_cast<char*>(options)); } _jplis = jplis; } static const char* not_found_error_msg = "Could not find agent library "; static const char* missing_module_error_msg = "\nModule java.instrument may be missing from runtime image."; static char ebuf[1024]; static char buffer[JVM_MAXPATHLEN]; static void vm_exit(const JvmtiAgent* agent, const char* sub_msg1, const char* sub_msg2) { assert(agent != nullptr, "invariant"); assert(sub_msg1 != nullptr, "invariant"); assert(!agent->is_instrument_lib() || sub_msg2 != nullptr, "invariant"); const size_t len = strlen(not_found_error_msg) + strlen(agent->name()) + strlen(sub_msg1) + strlen(&ebuf[0]) + 1 + (agent->is_instrument_lib() ? strlen(sub_msg2) : 0); char* buf = NEW_C_HEAP_ARRAY(char, len, mtServiceability); if (agent->is_instrument_lib()) { jio_snprintf(buf, len, "%s%s%s%s%s", not_found_error_msg, agent->name(), sub_msg1, &ebuf[0], sub_msg2); } else { jio_snprintf(buf, len, "%s%s%s%s", not_found_error_msg, agent->name(), sub_msg1, &ebuf[0]); } vm_exit_during_initialization(buf, nullptr); FREE_C_HEAP_ARRAY(buf); } #ifdef ASSERT static void assert_preload(const JvmtiAgent* agent) { assert(agent != nullptr, "invariant"); assert(!agent->is_loaded(), "invariant"); } #endif // Check for a statically linked-in agent, i.e. in the executable. // This should be the first function called when loading an agent. It is a bit special: // For statically linked agents we can't rely on os_lib == nullptr because // statically linked agents could have a handle of RTLD_DEFAULT which == 0 on some platforms. // If this function returns true, then agent->is_static_lib() && agent->is_loaded(). static bool load_agent_from_executable(JvmtiAgent* agent, const char* on_load_symbol) { DEBUG_ONLY(assert_preload(agent);) assert(on_load_symbol != nullptr, "invariant"); return os::find_builtin_agent(agent, on_load_symbol); } // Load the library from the absolute path of the agent, if available. static void* load_agent_from_absolute_path(JvmtiAgent* agent, bool vm_exit_on_error) { DEBUG_ONLY(assert_preload(agent);) assert(agent->is_absolute_path(), "invariant"); assert(!agent->is_instrument_lib(), "invariant"); void* const library = os::dll_load(agent->name(), &ebuf[0], sizeof ebuf); if (library == nullptr && vm_exit_on_error) { vm_exit(agent, " in absolute path, with error: ", nullptr); } return library; } // Agents with relative paths are loaded from the standard dll directory. static void* load_agent_from_relative_path(JvmtiAgent* agent, bool vm_exit_on_error) { DEBUG_ONLY(assert_preload(agent);) assert(!agent->is_absolute_path(), "invariant"); const char* const name = agent->name(); void* library = nullptr; // Try to load the agent from the standard dll directory if (os::dll_locate_lib(&buffer[0], sizeof buffer, Arguments::get_dll_dir(), name)) { library = os::dll_load(&buffer[0], &ebuf[0], sizeof ebuf); } if (library == nullptr && os::dll_build_name(&buffer[0], sizeof buffer, name)) { // Try the library path directory. library = os::dll_load(&buffer[0], &ebuf[0], sizeof ebuf); if (library != nullptr) { return library; } if (vm_exit_on_error) { vm_exit(agent, " on the library path, with error: ", missing_module_error_msg); } } return library; } // For absolute and relative paths. static void* load_library(JvmtiAgent* agent, bool vm_exit_on_error) { return agent->is_absolute_path() ? load_agent_from_absolute_path(agent, vm_exit_on_error) : load_agent_from_relative_path(agent, vm_exit_on_error); } // Type for the Agent_OnLoad and JVM_OnLoad entry points. extern "C" { typedef jint(JNICALL* OnLoadEntry_t)(JavaVM*, char*, void*); } // Find the OnLoad entry point for -agentlib: -agentpath: -Xrun agents. static OnLoadEntry_t lookup_On_Load_entry_point(JvmtiAgent* agent, const char* on_load_symbol, bool vm_exit_on_error) { assert(agent != nullptr, "invariant"); if (!agent->is_loaded()) { if (!load_agent_from_executable(agent, on_load_symbol)) { void* const library = load_library(agent, vm_exit_on_error); assert(library != nullptr || !vm_exit_on_error, "invariant"); if (library != nullptr) { agent->set_os_lib(library); agent->set_loaded(); } else { // Did not load agent from the executable or library. assert(!vm_exit_on_error, "invariant"); return nullptr; } } } assert(agent->is_loaded(), "invariant"); // Find the OnLoad function. return CAST_TO_FN_PTR(OnLoadEntry_t, os::find_agent_function(agent, false, on_load_symbol)); } static OnLoadEntry_t lookup_JVM_OnLoad_entry_point(JvmtiAgent* lib, bool vm_exit_on_error) { return lookup_On_Load_entry_point(lib, "JVM_OnLoad", vm_exit_on_error); } static OnLoadEntry_t lookup_Agent_OnLoad_entry_point(JvmtiAgent* agent, bool vm_exit_on_error) { return lookup_On_Load_entry_point(agent, "Agent_OnLoad", vm_exit_on_error); } void JvmtiAgent::convert_xrun_agent() { assert(is_xrun(), "invariant"); assert(!is_loaded(), "invariant"); assert(JvmtiEnvBase::get_phase() == JVMTI_PHASE_PRIMORDIAL, "invalid init sequence"); // Don't report any error and bail out too early in // lookup_JVM_OnLoad_entry_point if it does not succeed, since we want // to try lookup_Agent_OnLoad_entry_point for Agent_OnLoad as well. OnLoadEntry_t on_load_entry = lookup_JVM_OnLoad_entry_point(this, /* vm exit on error */ false); // If there is an JVM_OnLoad function it will get called later, // otherwise see if there is an Agent_OnLoad. if (on_load_entry == nullptr) { on_load_entry = lookup_Agent_OnLoad_entry_point(this, /* vm exit on error */ true); assert(on_load_entry != nullptr, "invariant"); _xrun = false; // converted } } // Called after the VM is initialized for -Xrun agents which have not been converted to JVMTI agents. static bool invoke_JVM_OnLoad(JvmtiAgent* agent) { assert(agent != nullptr, "invariant"); assert(agent->is_xrun(), "invariant"); assert(JvmtiEnvBase::get_phase() == JVMTI_PHASE_PRIMORDIAL, "invalid init sequence"); OnLoadEntry_t on_load_entry = lookup_JVM_OnLoad_entry_point(agent, /* vm exit on error */ true); assert(on_load_entry != nullptr, "invariant"); // Invoke the JVM_OnLoad function JavaThread* thread = JavaThread::current(); ThreadToNativeFromVM ttn(thread); HandleMark hm(thread); extern struct JavaVM_ main_vm; const jint err = (*on_load_entry)(&main_vm, const_cast<char*>(agent->options()), nullptr); if (err != JNI_OK) { vm_exit_during_initialization("-Xrun library failed to init", agent->name()); } return true; } // The newest jvmtiEnv is appended to the list, // hence the JvmtiEnvIterator order is from oldest to newest. static JvmtiEnv* get_last_jplis_jvmtienv() { JvmtiEnvIterator it; JvmtiEnv* env = it.first(); assert(env != nullptr, "invariant"); JvmtiEnv* next = it.next(env); while (next != nullptr) { assert(env != nullptr, "invariant"); // get_env_local_storage() lets us find which JVMTI env map to which JPLIS agent. if (next->get_env_local_storage() == nullptr) { JvmtiEnv* temp = it.next(next); if (temp != nullptr) { next = temp; continue; } break; } env = next; next = it.next(env); } assert(env != nullptr, "invariant"); assert(env->get_env_local_storage() != nullptr, "invariant"); return env; } // Associate the last, i.e. most recent, JvmtiEnv that is a JPLIS agent with the current agent. static void convert_to_jplis(JvmtiAgent* agent) { assert(agent != nullptr, "invariant"); assert(agent->is_instrument_lib(), "invariant"); JvmtiEnv* const env = get_last_jplis_jvmtienv(); assert(env != nullptr, "invariant"); const JPLISEnvironmentMirror* const jplis_env = get_env_local_storage(env); assert(jplis_env != nullptr, "invaiant"); assert(reinterpret_cast<JvmtiEnv*>(jplis_env->mJVMTIEnv) == env, "invariant"); agent->set_jplis(jplis_env->mAgent); } // Use this for JavaThreads and state is _thread_in_vm. class AgentJavaThreadEventTransition : StackObj { private: ResourceMark _rm; ThreadToNativeFromVM _transition; HandleMark _hm; public: AgentJavaThreadEventTransition(JavaThread* thread) : _rm(), _transition(thread), _hm(thread) {}; }; class AgentEventMark : StackObj { private: JavaThread* _thread; JNIEnv* _jni_env; JvmtiThreadState::ExceptionState _saved_exception_state; public: AgentEventMark(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 != nullptr) { _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(); } ~AgentEventMark() { _thread->pop_jni_handle_block(); JvmtiThreadState* state = _thread->jvmti_thread_state(); // we are continuing after an event. if (state != nullptr) { // Restore the jvmti thread exception state. state->restore_exception_state(_saved_exception_state); } } }; class AgentThreadEventMark : public AgentEventMark { private: jobject _jthread; public: AgentThreadEventMark(JavaThread* thread) : AgentEventMark(thread), _jthread(JNIHandles::make_local(thread, thread->threadObj())) {} jthread jni_thread() { return (jthread)_jthread; } }; static void unload_library(JvmtiAgent* agent, void* library) { assert(agent != nullptr, "invariant"); assert(agent->is_loaded(), "invariant"); if (!agent->is_static_lib()) { assert(library != nullptr, "invariant"); os::dll_unload(library); } } // type for the Agent_OnAttach entry point extern "C" { typedef jint(JNICALL* OnAttachEntry_t)(JavaVM*, char*, void*); } // Loading the agent by invoking Agent_OnAttach. // This function is called before the agent is added to JvmtiAgentList. static bool invoke_Agent_OnAttach(JvmtiAgent* agent, outputStream* st) { if (!EnableDynamicAgentLoading) { st->print_cr("Dynamic agent loading is not enabled. " "Use -XX:+EnableDynamicAgentLoading to launch target VM."); return false; } DEBUG_ONLY(assert_preload(agent);) assert(agent->is_dynamic(), "invariant"); assert(st != nullptr, "invariant"); assert(JvmtiEnvBase::get_phase() == JVMTI_PHASE_LIVE, "not in live phase!"); const char* on_attach_symbol = "Agent_OnAttach"; void* library = nullptr; bool previously_loaded; if (load_agent_from_executable(agent, on_attach_symbol)) { previously_loaded = JvmtiAgentList::is_static_lib_loaded(agent->name()); } else { library = load_library(agent, /* vm_exit_on_error */ false); if (library == nullptr) { st->print_cr("%s was not loaded.", agent->name()); if (*ebuf != '\0') { st->print_cr("%s", &ebuf[0]); } return false; } agent->set_os_lib_path(&buffer[0]); agent->set_os_lib(library); agent->set_loaded(); previously_loaded = JvmtiAgentList::is_dynamic_lib_loaded(library); } // Print warning if agent was not previously loaded and EnableDynamicAgentLoading not enabled on the command line. if (!previously_loaded && !FLAG_IS_CMDLINE(EnableDynamicAgentLoading) && !agent->is_instrument_lib()) { jio_fprintf(defaultStream::error_stream(), "WARNING: A JVM TI agent has been loaded dynamically (%s)\n" "WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading to hide this warning\n" "WARNING: Dynamic loading of agents will be disallowed by default in a future release\n", agent->name()); } assert(agent->is_loaded(), "invariant"); // The library was loaded so we attempt to lookup and invoke the Agent_OnAttach function. OnAttachEntry_t on_attach_entry = CAST_TO_FN_PTR(OnAttachEntry_t, os::find_agent_function(agent, false, on_attach_symbol)); if (on_attach_entry == nullptr) { st->print_cr("%s is not available in %s", on_attach_symbol, agent->name()); unload_library(agent, library); return false; } // Invoke the Agent_OnAttach function JavaThread* thread = JavaThread::current(); jint result = JNI_ERR; { extern struct JavaVM_ main_vm; AgentThreadEventMark jem(thread); AgentJavaThreadEventTransition jet(thread); agent->initialization_begin(); result = (*on_attach_entry)(&main_vm, (char*)agent->options(), nullptr); agent->initialization_end(); // Agent_OnAttach may have used JNI if (thread->is_pending_jni_exception_check()) { thread->clear_pending_jni_exception_check(); } } // Agent_OnAttach may have used JNI if (thread->has_pending_exception()) { thread->clear_pending_exception(); } st->print_cr("return code: %d", result); if (result != JNI_OK) { unload_library(agent, library); return false; } if (agent->is_instrument_lib()) { // Convert the instrument lib to the actual JPLIS / javaagent it represents. convert_to_jplis(agent); } return true; } #if INCLUDE_CDS static void check_cds_dump(JvmtiAgent* agent) { if (CDSConfig::new_aot_flags_used()) { // JEP 483 // Agents are allowed with -XX:AOTMode=record and -XX:AOTMode=on/auto. // Agents are completely disabled when -XX:AOTMode=create (see cdsConfig.cpp) assert(!CDSConfig::is_dumping_final_static_archive(), "agents should have been disabled with -XX:AOTMode=create"); } else if (CDSConfig::is_dumping_classic_static_archive() || CDSConfig::is_dumping_dynamic_archive()) { // Classic CDS (static or dynamic dump). Disallow agents. vm_exit_during_cds_dumping("JVMTI agents are not allowed when dumping CDS archives"); } } #endif // INCLUDE_CDS // Loading the agent by invoking Agent_OnLoad. static bool invoke_Agent_OnLoad(JvmtiAgent* agent) { assert(agent != nullptr, "invariant"); assert(!agent->is_xrun(), "invariant"); assert(!agent->is_dynamic(), "invariant"); assert(JvmtiEnvBase::get_phase() == JVMTI_PHASE_ONLOAD, "invariant"); #if INCLUDE_CDS if (CDSConfig::is_dumping_archive()) { check_cds_dump(agent); } #endif OnLoadEntry_t on_load_entry = lookup_Agent_OnLoad_entry_point(agent, /* vm exit on error */ true); assert(on_load_entry != nullptr, "invariant"); // Invoke the Agent_OnLoad function extern struct JavaVM_ main_vm; if ((*on_load_entry)(&main_vm, const_cast<char*>(agent->options()), nullptr) != JNI_OK) { vm_exit_during_initialization("agent library failed Agent_OnLoad", agent->name()); } // Convert the instrument lib to the actual JPLIS / javaagent it represents. if (agent->is_instrument_lib()) { convert_to_jplis(agent); } return true; } bool JvmtiAgent::load(outputStream* st /* nullptr */) { if (is_xrun()) { return invoke_JVM_OnLoad(this); } return is_dynamic() ? invoke_Agent_OnAttach(this, st) : invoke_Agent_OnLoad(this); } extern "C" { typedef void (JNICALL* Agent_OnUnload_t)(JavaVM*); } void JvmtiAgent::unload() { const char* on_unload_symbol = "Agent_OnUnload"; // Find the Agent_OnUnload function. Agent_OnUnload_t unload_entry = CAST_TO_FN_PTR(Agent_OnUnload_t, os::find_agent_function(this, false, on_unload_symbol)); if (unload_entry != nullptr) { // Invoke the Agent_OnUnload function JavaThread* thread = JavaThread::current(); ThreadToNativeFromVM ttn(thread); HandleMark hm(thread); extern struct JavaVM_ main_vm; (*unload_entry)(&main_vm); } }