| /* |
| * Copyright (c) 2023, 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 "prims/jvmtiAgent.hpp" |
| |
| #include "cds/cds_globals.hpp" |
| #include "jni.h" |
| #include "jvm_io.h" |
| #include "jvmtifiles/jvmtiEnv.hpp" |
| #include "prims/jvmtiEnvBase.hpp" |
| #include "prims/jvmtiExport.hpp" |
| #include "prims/jvmtiAgentList.hpp" |
| #include "runtime/arguments.hpp" |
| #include "runtime/handles.inline.hpp" |
| #include "runtime/interfaceSupport.inline.hpp" |
| #include "runtime/java.hpp" |
| #include "runtime/jniHandles.hpp" |
| #include "runtime/globals_extension.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 _next; |
| } |
| |
| void JvmtiAgent::set_next(JvmtiAgent* agent) { |
| _next = agent; |
| } |
| |
| 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(char, 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_symbols[], size_t num_symbol_entries) { |
| DEBUG_ONLY(assert_preload(agent);) |
| assert(on_load_symbols != nullptr, "invariant"); |
| return os::find_builtin_agent(agent, &on_load_symbols[0], num_symbol_entries); |
| } |
| |
| // 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, const char* on_symbols[], size_t num_symbol_entries, 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. |
| // num_symbol_entries must be passed-in since only the caller knows the number of symbols in the array. |
| static OnLoadEntry_t lookup_On_Load_entry_point(JvmtiAgent* agent, const char* on_load_symbols[], size_t num_symbol_entries) { |
| assert(agent != nullptr, "invariant"); |
| if (!agent->is_loaded()) { |
| if (!load_agent_from_executable(agent, on_load_symbols, num_symbol_entries)) { |
| void* const library = load_library(agent, on_load_symbols, num_symbol_entries, /* vm exit on error */ true); |
| assert(library != nullptr, "invariant"); |
| agent->set_os_lib(library); |
| agent->set_loaded(); |
| } |
| } |
| assert(agent->is_loaded(), "invariant"); |
| // Find the OnLoad function. |
| return CAST_TO_FN_PTR(OnLoadEntry_t, os::find_agent_function(agent, false, on_load_symbols, num_symbol_entries)); |
| } |
| |
| static OnLoadEntry_t lookup_JVM_OnLoad_entry_point(JvmtiAgent* lib) { |
| const char* on_load_symbols[] = JVM_ONLOAD_SYMBOLS; |
| return lookup_On_Load_entry_point(lib, on_load_symbols, sizeof(on_load_symbols) / sizeof(char*)); |
| } |
| |
| static OnLoadEntry_t lookup_Agent_OnLoad_entry_point(JvmtiAgent* agent) { |
| const char* on_load_symbols[] = AGENT_ONLOAD_SYMBOLS; |
| return lookup_On_Load_entry_point(agent, on_load_symbols, sizeof(on_load_symbols) / sizeof(char*)); |
| } |
| |
| void JvmtiAgent::convert_xrun_agent() { |
| assert(is_xrun(), "invariant"); |
| assert(!is_loaded(), "invariant"); |
| assert(JvmtiEnvBase::get_phase() == JVMTI_PHASE_PRIMORDIAL, "invalid init sequence"); |
| OnLoadEntry_t on_load_entry = lookup_JVM_OnLoad_entry_point(this); |
| // 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); |
| if (on_load_entry == nullptr) { |
| vm_exit_during_initialization("Could not find JVM_OnLoad or Agent_OnLoad function in the library", name()); |
| } |
| _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); |
| if (on_load_entry == nullptr) { |
| vm_exit_during_initialization("Could not find JVM_OnLoad function in -Xrun library", agent->name()); |
| } |
| // 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_symbols[] = AGENT_ONATTACH_SYMBOLS; |
| const size_t num_symbol_entries = ARRAY_SIZE(on_attach_symbols); |
| void* library = nullptr; |
| bool previously_loaded; |
| if (load_agent_from_executable(agent, &on_attach_symbols[0], num_symbol_entries)) { |
| previously_loaded = JvmtiAgentList::is_static_lib_loaded(agent->name()); |
| } else { |
| library = load_library(agent, &on_attach_symbols[0], num_symbol_entries, /* 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_symbols[0], num_symbol_entries)); |
| |
| if (on_attach_entry == nullptr) { |
| st->print_cr("%s is not available in %s", on_attach_symbols[0], 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; |
| } |
| |
| // CDS dumping does not support native JVMTI agent. |
| // CDS dumping supports Java agent if the AllowArchivingWithJavaAgent diagnostic option is specified. |
| static void check_cds_dump(JvmtiAgent* agent) { |
| assert(agent != nullptr, "invariant"); |
| assert(Arguments::is_dumping_archive(), "invariant"); |
| if (!agent->is_instrument_lib()) { |
| vm_exit_during_cds_dumping("CDS dumping does not support native JVMTI agent, name", agent->name()); |
| } |
| if (!AllowArchivingWithJavaAgent) { |
| vm_exit_during_cds_dumping( |
| "Must enable AllowArchivingWithJavaAgent in order to run Java agent during CDS dumping"); |
| } |
| } |
| |
| // 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 (Arguments::is_dumping_archive()) { |
| check_cds_dump(agent); |
| } |
| OnLoadEntry_t on_load_entry = lookup_Agent_OnLoad_entry_point(agent); |
| if (on_load_entry == nullptr) { |
| vm_exit_during_initialization("Could not find Agent_OnLoad function in the agent library", agent->name()); |
| } |
| // 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_symbols[] = AGENT_ONUNLOAD_SYMBOLS; |
| // 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_symbols[0], ARRAY_SIZE(on_unload_symbols))); |
| 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); |
| } |
| } |