8257967: JFR: Events for loaded agents
Reviewed-by: dholmes, sspitsyn
This commit is contained in:
parent
2a062f1654
commit
5c95bb1c51
@ -88,7 +88,7 @@ ifneq ($(call check-jvm-feature, jvmti), true)
|
||||
jvmtiImpl.cpp jvmtiManageCapabilities.cpp jvmtiRawMonitor.cpp jvmtiUtil.cpp jvmtiTrace.cpp \
|
||||
jvmtiCodeBlobEvents.cpp jvmtiEnv.cpp jvmtiRedefineClasses.cpp jvmtiEnvBase.cpp jvmtiEnvThreadState.cpp \
|
||||
jvmtiTagMap.cpp jvmtiEventController.cpp evmCompat.cpp jvmtiEnter.xsl jvmtiExport.cpp \
|
||||
jvmtiClassFileReconstituter.cpp jvmtiTagMapTable.cpp
|
||||
jvmtiClassFileReconstituter.cpp jvmtiTagMapTable.cpp jvmtiAgent.cpp jvmtiAgentList.cpp
|
||||
endif
|
||||
|
||||
ifneq ($(call check-jvm-feature, jvmci), true)
|
||||
|
@ -1172,6 +1172,25 @@
|
||||
<Field type="ulong" name="totalFinalizersRun" label="Finalizers Run" description="Total number of finalizers run since JVM start" />
|
||||
</Event>
|
||||
|
||||
<Event name="JavaAgent" category="Java Virtual Machine, Diagnostics" label="Java Agent" description="A Java programming language agent making use of the java.lang.instrument package for instrumenting programs running on the JVM"
|
||||
thread="false" startTime="false" period="endChunk" stackTrace="false">
|
||||
<Field type="string" name="name" label="Name" />
|
||||
<Field type="string" name="options" label="Options" />
|
||||
<Field type="boolean" name="dynamic" label="Dynamic" description="If the agent attached to the JVM dynamically during runtime, i.e. not at startup" />
|
||||
<Field type="Ticks" name="initializationTime" label="Initialization Time" description="The time the JVM initialized the agent" />
|
||||
<Field type="Tickspan" name="initializationDuration" label="Initialization Duration" description="The duration of executing the initialization method, either premain or agentmain" />
|
||||
</Event>
|
||||
|
||||
<Event name="NativeAgent" category="Java Virtual Machine, Diagnostics" label="Native Agent" description="A native programming language agent making use of the JVMTI interface used by development, profiling and monitoring tools"
|
||||
thread="false" startTime="false" period="endChunk" stackTrace="false">
|
||||
<Field type="string" name="name" label="Name" />
|
||||
<Field type="string" name="options" label="Options" />
|
||||
<Field type="boolean" name="dynamic" label="Dynamic" description="If the library attached to the JVM dynamically during runtime, i.e. not at startup" />
|
||||
<Field type="Ticks" name="initializationTime" label="Initialization Time" description="The time the JVM initialized the agent" />
|
||||
<Field type="Tickspan" name="initializationDuration" label="Initialization Duration" description="The duration of executing the JVMTI VMInit event callback. If no VMInit callback is specified, the duration is 0. For a dynamically loaded agent, it is the duration of executing the call to Agent_OnAttach." />
|
||||
<Field type="string" name="path" label="Path" description="The path of the library" />
|
||||
</Event>
|
||||
|
||||
<Type name="DeoptimizationReason" label="Deoptimization Reason">
|
||||
<Field type="string" name="reason" label="Reason" />
|
||||
</Type>
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 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
|
||||
@ -52,6 +52,7 @@
|
||||
#include "memory/heapInspection.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "prims/jvmtiAgentList.hpp"
|
||||
#include "runtime/arguments.hpp"
|
||||
#include "runtime/flags/jvmFlag.hpp"
|
||||
#include "runtime/globals.hpp"
|
||||
@ -269,6 +270,43 @@ TRACE_REQUEST_FUNC(SystemProcess) {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename AgentEvent>
|
||||
static void send_agent_event(AgentEvent& event, const JvmtiAgent* agent) {
|
||||
event.set_name(agent->name());
|
||||
event.set_options(agent->options());
|
||||
event.set_dynamic(agent->is_dynamic());
|
||||
event.set_initializationTime(agent->initialization_time());
|
||||
event.set_initializationDuration(agent->initialization_duration());
|
||||
event.commit();
|
||||
}
|
||||
|
||||
TRACE_REQUEST_FUNC(JavaAgent) {
|
||||
const JvmtiAgentList::Iterator it =JvmtiAgentList::java_agents();
|
||||
while (it.has_next()) {
|
||||
const JvmtiAgent* agent = it.next();
|
||||
assert(agent->is_jplis(), "invariant");
|
||||
EventJavaAgent event;
|
||||
send_agent_event(event, agent);
|
||||
}
|
||||
}
|
||||
|
||||
static void send_native_agent_events(const JvmtiAgentList::Iterator& it) {
|
||||
while (it.has_next()) {
|
||||
const JvmtiAgent* agent = it.next();
|
||||
assert(!agent->is_jplis(), "invariant");
|
||||
EventNativeAgent event;
|
||||
event.set_path(agent->os_lib_path());
|
||||
send_agent_event(event, agent);
|
||||
}
|
||||
}
|
||||
|
||||
TRACE_REQUEST_FUNC(NativeAgent) {
|
||||
const JvmtiAgentList::Iterator native_agents_it = JvmtiAgentList::native_agents();
|
||||
send_native_agent_events(native_agents_it);
|
||||
const JvmtiAgentList::Iterator xrun_agents_it = JvmtiAgentList::xrun_agents();
|
||||
send_native_agent_events(xrun_agents_it);
|
||||
}
|
||||
|
||||
TRACE_REQUEST_FUNC(ThreadContextSwitchRate) {
|
||||
double rate = 0.0;
|
||||
int ret_val = OS_ERR;
|
||||
|
620
src/hotspot/share/prims/jvmtiAgent.cpp
Normal file
620
src/hotspot/share/prims/jvmtiAgent.cpp
Normal file
@ -0,0 +1,620 @@
|
||||
/*
|
||||
* 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 "runtime/arguments.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"
|
||||
|
||||
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 cant'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()), NULL);
|
||||
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.
|
||||
static bool invoke_Agent_OnAttach(JvmtiAgent* agent, outputStream* st) {
|
||||
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;
|
||||
if (!load_agent_from_executable(agent, &on_attach_symbols[0], num_symbol_entries)) {
|
||||
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();
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
88
src/hotspot/share/prims/jvmtiAgent.hpp
Normal file
88
src/hotspot/share/prims/jvmtiAgent.hpp
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHARE_PRIMS_JVMTIAGENT_HPP
|
||||
#define SHARE_PRIMS_JVMTIAGENT_HPP
|
||||
|
||||
#include "memory/allocation.hpp"
|
||||
#include "utilities/ticks.hpp"
|
||||
|
||||
class JvmtiEnv;
|
||||
class outputStream;
|
||||
|
||||
// Represents an agent launched on the command-line by -agentlib, -agentpath or -Xrun.
|
||||
// Also agents loaded dynamically during runtime, for example using the Attach API.
|
||||
class JvmtiAgent : public CHeapObj<mtServiceability> {
|
||||
friend class JvmtiAgentList;
|
||||
private:
|
||||
Ticks _initialization_time;
|
||||
Tickspan _initialization_duration;
|
||||
JvmtiAgent* _next;
|
||||
const char* _name;
|
||||
const char* _options;
|
||||
void* _os_lib;
|
||||
const char* _os_lib_path;
|
||||
const void* _jplis;
|
||||
bool _loaded;
|
||||
bool _absolute_path;
|
||||
bool _static_lib;
|
||||
bool _instrument_lib;
|
||||
bool _dynamic;
|
||||
bool _xrun;
|
||||
|
||||
JvmtiAgent* next() const;
|
||||
void set_next(JvmtiAgent* agent);
|
||||
void convert_xrun_agent();
|
||||
void set_xrun();
|
||||
|
||||
public:
|
||||
JvmtiAgent(const char* name, const char* options, bool is_absolute_path, bool dynamic = false);
|
||||
const char* name() const;
|
||||
const char* options() const;
|
||||
bool is_absolute_path() const;
|
||||
void* os_lib() const;
|
||||
void set_os_lib(void* os_lib);
|
||||
const char* os_lib_path() const;
|
||||
void set_os_lib_path(const char* path);
|
||||
bool is_static_lib() const;
|
||||
void set_static_lib();
|
||||
bool is_dynamic() const;
|
||||
bool is_xrun() const;
|
||||
bool is_instrument_lib() const;
|
||||
bool is_loaded() const;
|
||||
void set_loaded();
|
||||
bool is_jplis() const;
|
||||
bool is_jplis(JvmtiEnv* env) const;
|
||||
void set_jplis(const void* jplis);
|
||||
bool is_initialized() const;
|
||||
void initialization_begin();
|
||||
void initialization_end();
|
||||
const Ticks& initialization_time() const;
|
||||
const Tickspan& initialization_duration() const;
|
||||
|
||||
bool load(outputStream* st = nullptr);
|
||||
void unload();
|
||||
};
|
||||
|
||||
#endif // SHARE_PRIMS_JVMTIAGENT_HPP
|
265
src/hotspot/share/prims/jvmtiAgentList.cpp
Normal file
265
src/hotspot/share/prims/jvmtiAgentList.cpp
Normal file
@ -0,0 +1,265 @@
|
||||
/*
|
||||
* 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/jvmtiAgentList.hpp"
|
||||
|
||||
#include "prims/jvmtiEnvBase.hpp"
|
||||
#include "prims/jvmtiExport.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
#include "runtime/os.inline.hpp"
|
||||
#include "utilities/growableArray.hpp"
|
||||
|
||||
JvmtiAgent* JvmtiAgentList::_list = nullptr;
|
||||
|
||||
// Selection as a function of the filter.
|
||||
JvmtiAgent* JvmtiAgentList::Iterator::select(JvmtiAgent* agent) const {
|
||||
while (agent != nullptr) {
|
||||
if (_filter == ALL) {
|
||||
return agent;
|
||||
} else if (_filter == NOT_XRUN) {
|
||||
if (!agent->is_xrun()) {
|
||||
return agent;
|
||||
}
|
||||
} else if (_filter == JAVA) {
|
||||
if (agent->is_jplis()) {
|
||||
return agent;
|
||||
}
|
||||
} else if (_filter == NATIVE) {
|
||||
if (!agent->is_jplis() && !agent->is_xrun()) {
|
||||
return agent;
|
||||
}
|
||||
} else {
|
||||
assert(_filter == XRUN, "invariant");
|
||||
if (agent->is_xrun()) {
|
||||
return agent;
|
||||
}
|
||||
}
|
||||
agent = agent->next();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static inline JvmtiAgent* head(JvmtiAgent** list) {
|
||||
assert(list != nullptr, "invariant");
|
||||
return Atomic::load_acquire(list);
|
||||
}
|
||||
|
||||
|
||||
// The storage list is a single cas-linked-list, to allow for concurrent iterations.
|
||||
// Especially during initial loading of agents, there exist an order requirement to iterate oldest -> newest.
|
||||
// Our concurrent storage linked-list is newest -> oldest.
|
||||
// The correct order is preserved by the iterator, by storing a filtered set of entries in a stack.
|
||||
JvmtiAgentList::Iterator::Iterator(JvmtiAgent** list, Filter filter) :
|
||||
_stack(new GrowableArrayCHeap<JvmtiAgent*, mtServiceability>(16)), _filter(filter) {
|
||||
JvmtiAgent* next = head(list);
|
||||
while (next != nullptr) {
|
||||
next = select(next);
|
||||
if (next != nullptr) {
|
||||
_stack->push(next);
|
||||
next = next->next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JvmtiAgentList::Iterator::~Iterator() {
|
||||
delete _stack;
|
||||
}
|
||||
|
||||
bool JvmtiAgentList::Iterator::has_next() const {
|
||||
assert(_stack != nullptr, "invariant");
|
||||
return _stack->is_nonempty();
|
||||
}
|
||||
|
||||
const JvmtiAgent* JvmtiAgentList::Iterator::next() const {
|
||||
assert(has_next(), "invariant");
|
||||
return _stack->pop();
|
||||
}
|
||||
|
||||
JvmtiAgent* JvmtiAgentList::Iterator::next() {
|
||||
return const_cast<JvmtiAgent*>(const_cast<const Iterator*>(this)->next());
|
||||
}
|
||||
|
||||
JvmtiAgentList::Iterator JvmtiAgentList::agents() {
|
||||
return Iterator(&_list, Iterator::NOT_XRUN);
|
||||
}
|
||||
|
||||
JvmtiAgentList::Iterator JvmtiAgentList::java_agents() {
|
||||
return Iterator(&_list, Iterator::JAVA);
|
||||
}
|
||||
|
||||
JvmtiAgentList::Iterator JvmtiAgentList::native_agents() {
|
||||
return Iterator(&_list, Iterator::NATIVE);
|
||||
}
|
||||
|
||||
JvmtiAgentList::Iterator JvmtiAgentList::xrun_agents() {
|
||||
return Iterator(&_list, Iterator::XRUN);
|
||||
}
|
||||
|
||||
JvmtiAgentList::Iterator JvmtiAgentList::all() {
|
||||
return Iterator(&_list, Iterator::ALL);
|
||||
}
|
||||
|
||||
void JvmtiAgentList::add(JvmtiAgent* agent) {
|
||||
assert(agent != nullptr, "invariant");
|
||||
JvmtiAgent* next;
|
||||
do {
|
||||
next = head(&_list);
|
||||
agent->set_next(next);
|
||||
} while (Atomic::cmpxchg(&_list, next, agent) != next);
|
||||
}
|
||||
|
||||
void JvmtiAgentList::add(const char* name, char* options, bool absolute_path) {
|
||||
add(new JvmtiAgent(name, options, absolute_path));
|
||||
}
|
||||
|
||||
void JvmtiAgentList::add_xrun(const char* name, char* options, bool absolute_path) {
|
||||
JvmtiAgent* agent = new JvmtiAgent(name, options, absolute_path);
|
||||
agent->set_xrun();
|
||||
add(agent);
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
static void assert_initialized(JvmtiAgentList::Iterator& it) {
|
||||
while (it.has_next()) {
|
||||
assert(it.next()->is_initialized(), "invariant");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// In case an agent did not enable the VMInit callback, or if it is an -Xrun agent,
|
||||
// it gets an initializiation timestamp here.
|
||||
void JvmtiAgentList::initialize() {
|
||||
Iterator it = all();
|
||||
while (it.has_next()) {
|
||||
JvmtiAgent* agent = it.next();
|
||||
if (!agent->is_initialized()) {
|
||||
agent->initialization_begin();
|
||||
}
|
||||
}
|
||||
DEBUG_ONLY(Iterator assert_it = all(); assert_initialized(assert_it);)
|
||||
}
|
||||
|
||||
void JvmtiAgentList::convert_xrun_agents() {
|
||||
Iterator it = xrun_agents();
|
||||
while (it.has_next()) {
|
||||
it.next()->convert_xrun_agent();
|
||||
}
|
||||
}
|
||||
|
||||
class JvmtiPhaseTransition : public StackObj {
|
||||
public:
|
||||
JvmtiPhaseTransition() {
|
||||
assert(JvmtiEnvBase::get_phase() == JVMTI_PHASE_PRIMORDIAL, "invalid init sequence");
|
||||
JvmtiExport::enter_onload_phase();
|
||||
}
|
||||
~JvmtiPhaseTransition() {
|
||||
assert(JvmtiEnvBase::get_phase() == JVMTI_PHASE_ONLOAD, "invariant");
|
||||
JvmtiExport::enter_primordial_phase();
|
||||
}
|
||||
};
|
||||
|
||||
static void load_agents(JvmtiAgentList::Iterator& it) {
|
||||
while (it.has_next()) {
|
||||
it.next()->load();
|
||||
}
|
||||
}
|
||||
|
||||
// Invokes Agent_OnLoad for -agentlib:.. -agentpath: and converted -Xrun agents.
|
||||
// Called very early -- before JavaThreads exist
|
||||
void JvmtiAgentList::load_agents() {
|
||||
// Convert -Xrun to -agentlib: if there is no JVM_OnLoad
|
||||
convert_xrun_agents();
|
||||
JvmtiPhaseTransition transition;
|
||||
Iterator it = agents();
|
||||
::load_agents(it);
|
||||
}
|
||||
|
||||
// Launch -Xrun agents
|
||||
void JvmtiAgentList::load_xrun_agents() {
|
||||
assert(JvmtiEnvBase::get_phase() == JVMTI_PHASE_PRIMORDIAL, "invalid init sequence");
|
||||
Iterator it = xrun_agents();
|
||||
::load_agents(it);
|
||||
}
|
||||
|
||||
// Invokes Agent_OnAttach for agents loaded dynamically during runtime.
|
||||
jint JvmtiAgentList::load_agent(const char* agent_name, const char* absParam,
|
||||
const char* options, outputStream* st) {
|
||||
// The abs parameter should be "true" or "false"
|
||||
const bool is_absolute_path = (absParam != nullptr) && (strcmp(absParam, "true") == 0);
|
||||
JvmtiAgent* const agent = new JvmtiAgent(agent_name, options, is_absolute_path, /* dynamic agent */ true);
|
||||
if (agent->load(st)) {
|
||||
add(agent);
|
||||
} else {
|
||||
delete agent;
|
||||
}
|
||||
// Agent_OnAttach executed so completion status is JNI_OK
|
||||
return JNI_OK;
|
||||
}
|
||||
|
||||
// Send any Agent_OnUnload notifications
|
||||
void JvmtiAgentList::unload_agents() {
|
||||
Iterator it = agents();
|
||||
while (it.has_next()) {
|
||||
it.next()->unload();
|
||||
}
|
||||
}
|
||||
|
||||
static bool match(JvmtiEnv* env, const JvmtiAgent* agent, const void* os_module_address) {
|
||||
assert(env != nullptr, "invariant");
|
||||
assert(agent != nullptr, "invariant");
|
||||
if (agent->is_static_lib()) {
|
||||
return os::get_default_process_handle() == os_module_address;
|
||||
}
|
||||
if (agent->os_lib() != os_module_address) {
|
||||
return false;
|
||||
}
|
||||
return agent->is_instrument_lib() ? agent->is_jplis(env) : true;
|
||||
}
|
||||
|
||||
// The function pointer is a JVMTI callback function.
|
||||
// Find the os module (dll) that exports this function.
|
||||
// Now we can map a JVMTI env to its corresponding agent.
|
||||
JvmtiAgent* JvmtiAgentList::lookup(JvmtiEnv* env, void* f_ptr) {
|
||||
assert(env != nullptr, "invariant");
|
||||
assert(f_ptr != nullptr, "invariant");
|
||||
static char ebuf[1024];
|
||||
static char buffer[JVM_MAXPATHLEN];
|
||||
int offset;
|
||||
if (!os::dll_address_to_library_name(reinterpret_cast<address>(f_ptr), &buffer[0], JVM_MAXPATHLEN, &offset)) {
|
||||
return nullptr;
|
||||
}
|
||||
assert(buffer[0] != '\0', "invariant");
|
||||
assert(offset >= 0, "invariant");
|
||||
const void* const os_module_address = reinterpret_cast<address>(f_ptr) - offset;
|
||||
|
||||
JvmtiAgentList::Iterator it = JvmtiAgentList::agents();
|
||||
while (it.has_next()) {
|
||||
JvmtiAgent* const agent = it.next();
|
||||
if (match(env, agent, os_module_address)) {
|
||||
agent->set_os_lib_path(&buffer[0]);
|
||||
return agent;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
87
src/hotspot/share/prims/jvmtiAgentList.hpp
Normal file
87
src/hotspot/share/prims/jvmtiAgentList.hpp
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHARE_PRIMS_JVMTIAGENTLIST_HPP
|
||||
#define SHARE_PRIMS_JVMTIAGENTLIST_HPP
|
||||
|
||||
#include "memory/allocation.hpp"
|
||||
#include "prims/jvmtiAgent.hpp"
|
||||
|
||||
template <typename, MEMFLAGS>
|
||||
class GrowableArrayCHeap;
|
||||
class JvmtiEnv;
|
||||
|
||||
// Maintains a single cas linked-list of JvmtiAgents.
|
||||
class JvmtiAgentList : AllStatic {
|
||||
friend class Iterator;
|
||||
friend class JvmtiExport;
|
||||
public:
|
||||
class Iterator {
|
||||
friend class JvmtiAgentList;
|
||||
private:
|
||||
enum Filter {
|
||||
JAVA,
|
||||
NATIVE,
|
||||
XRUN,
|
||||
NOT_XRUN,
|
||||
ALL
|
||||
};
|
||||
GrowableArrayCHeap<JvmtiAgent*, mtServiceability>* _stack;
|
||||
const Filter _filter;
|
||||
Iterator(JvmtiAgent** list, Filter filter);
|
||||
JvmtiAgent* select(JvmtiAgent* agent) const;
|
||||
public:
|
||||
bool has_next() const;
|
||||
JvmtiAgent* next();
|
||||
const JvmtiAgent* next() const;
|
||||
~Iterator();
|
||||
};
|
||||
|
||||
private:
|
||||
static JvmtiAgent* _list;
|
||||
|
||||
static Iterator all();
|
||||
static void initialize();
|
||||
static void convert_xrun_agents();
|
||||
|
||||
public:
|
||||
static void add(JvmtiAgent* agent);
|
||||
static void add(const char* name, char* options, bool absolute_path);
|
||||
static void add_xrun(const char* name, char* options, bool absolute_path);
|
||||
|
||||
static void load_agents();
|
||||
static jint load_agent(const char* agent, const char* absParam,
|
||||
const char* options, outputStream* st);
|
||||
static void load_xrun_agents();
|
||||
static void unload_agents();
|
||||
|
||||
static JvmtiAgent* lookup(JvmtiEnv* env, void* f_ptr);
|
||||
|
||||
static Iterator agents();
|
||||
static Iterator java_agents();
|
||||
static Iterator native_agents();
|
||||
static Iterator xrun_agents();
|
||||
};
|
||||
|
||||
#endif // SHARE_PRIMS_JVMTIAGENTLIST_HPP
|
@ -135,7 +135,6 @@ class JvmtiEnvBase : public CHeapObj<mtInternal> {
|
||||
void env_dispose();
|
||||
|
||||
void set_env_local_storage(const void* data) { _env_local_storage = data; }
|
||||
const void* get_env_local_storage() { return _env_local_storage; }
|
||||
|
||||
void record_class_file_load_hook_enabled();
|
||||
void record_first_time_class_file_load_hook_enabled();
|
||||
@ -170,6 +169,8 @@ class JvmtiEnvBase : public CHeapObj<mtInternal> {
|
||||
|
||||
bool is_retransformable() { return _is_retransformable; }
|
||||
|
||||
const void* get_env_local_storage() { return _env_local_storage; }
|
||||
|
||||
static ByteSize jvmti_external_offset() {
|
||||
return byte_offset_of(JvmtiEnvBase, _jvmti_external);
|
||||
};
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "oops/objArrayOop.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "oops/oopHandle.inline.hpp"
|
||||
#include "prims/jvmtiAgentList.hpp"
|
||||
#include "prims/jvmtiCodeBlobEvents.hpp"
|
||||
#include "prims/jvmtiEventController.hpp"
|
||||
#include "prims/jvmtiEventController.inline.hpp"
|
||||
@ -697,6 +698,13 @@ void JvmtiExport::initialize_oop_storage() {
|
||||
_weak_tag_storage->register_num_dead_callback(&JvmtiTagMap::gc_notification);
|
||||
}
|
||||
|
||||
// Lookup an agent from an JvmtiEnv. Return agent only if it is not yet initialized.
|
||||
// An agent can create multiple JvmtiEnvs, but for agent initialization, we are only interested in the initial one.
|
||||
static JvmtiAgent* lookup_uninitialized_agent(JvmtiEnv* env, void* callback) {
|
||||
JvmtiAgent* const agent = JvmtiAgentList::lookup(env, callback);
|
||||
return agent == nullptr || agent->is_initialized() ? nullptr : agent;
|
||||
}
|
||||
|
||||
void JvmtiExport::post_vm_initialized() {
|
||||
EVT_TRIG_TRACE(JVMTI_EVENT_VM_INIT, ("Trg VM init event triggered" ));
|
||||
|
||||
@ -713,12 +721,25 @@ void JvmtiExport::post_vm_initialized() {
|
||||
JvmtiJavaThreadEventTransition jet(thread);
|
||||
jvmtiEventVMInit callback = env->callbacks()->VMInit;
|
||||
if (callback != nullptr) {
|
||||
// We map the JvmtiEnv to its Agent to measure when and for how long
|
||||
// it took to initialize so that JFR can report this information.
|
||||
JvmtiAgent* const agent = lookup_uninitialized_agent(env, reinterpret_cast<void*>(callback));
|
||||
if (agent != nullptr) {
|
||||
agent->initialization_begin();
|
||||
}
|
||||
(*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread());
|
||||
if (agent != nullptr) {
|
||||
agent->initialization_end();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Agents are initialized as part of posting the VMInit event above.
|
||||
// For -Xrun agents and agents with no VMInit callback, we explicitly ensure they are also initialized.
|
||||
// JVM_OnLoad and Agent_OnLoad callouts are performed too early for the proper timestamp logic.
|
||||
JvmtiAgentList::initialize();
|
||||
}
|
||||
|
||||
void JvmtiExport::post_vm_death() {
|
||||
EVT_TRIG_TRACE(JVMTI_EVENT_VM_DEATH, ("Trg VM death event triggered" ));
|
||||
@ -2929,118 +2950,6 @@ void JvmtiExport::transition_pending_onload_raw_monitors() {
|
||||
JvmtiPendingMonitors::transition_raw_monitors();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#if INCLUDE_SERVICES
|
||||
// Attach is disabled if SERVICES is not included
|
||||
|
||||
// type for the Agent_OnAttach entry point
|
||||
extern "C" {
|
||||
typedef jint (JNICALL *OnAttachEntry_t)(JavaVM*, char *, void *);
|
||||
}
|
||||
|
||||
jint JvmtiExport::load_agent_library(const char *agent, const char *absParam,
|
||||
const char *options, outputStream* st) {
|
||||
char ebuf[1024] = {0};
|
||||
char buffer[JVM_MAXPATHLEN];
|
||||
void* library = nullptr;
|
||||
jint result = JNI_ERR;
|
||||
const char *on_attach_symbols[] = AGENT_ONATTACH_SYMBOLS;
|
||||
size_t num_symbol_entries = ARRAY_SIZE(on_attach_symbols);
|
||||
|
||||
// The abs parameter should be "true" or "false"
|
||||
bool is_absolute_path = (absParam != nullptr) && (strcmp(absParam,"true")==0);
|
||||
|
||||
// Initially marked as invalid. It will be set to valid if we can find the agent
|
||||
AgentLibrary *agent_lib = new AgentLibrary(agent, options, is_absolute_path, nullptr);
|
||||
|
||||
// Check for statically linked in agent. If not found then if the path is
|
||||
// absolute we attempt to load the library. Otherwise we try to load it
|
||||
// from the standard dll directory.
|
||||
|
||||
if (!os::find_builtin_agent(agent_lib, on_attach_symbols, num_symbol_entries)) {
|
||||
if (is_absolute_path) {
|
||||
library = os::dll_load(agent, ebuf, sizeof ebuf);
|
||||
} else {
|
||||
// Try to load the agent from the standard dll directory
|
||||
if (os::dll_locate_lib(buffer, sizeof(buffer), Arguments::get_dll_dir(),
|
||||
agent)) {
|
||||
library = os::dll_load(buffer, ebuf, sizeof ebuf);
|
||||
}
|
||||
if (library == nullptr) {
|
||||
// not found - try OS default library path
|
||||
if (os::dll_build_name(buffer, sizeof(buffer), agent)) {
|
||||
library = os::dll_load(buffer, ebuf, sizeof ebuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (library != nullptr) {
|
||||
agent_lib->set_os_lib(library);
|
||||
agent_lib->set_valid();
|
||||
}
|
||||
}
|
||||
// If the library was loaded then we attempt to invoke the Agent_OnAttach
|
||||
// function
|
||||
if (agent_lib->valid()) {
|
||||
// Lookup the Agent_OnAttach function
|
||||
OnAttachEntry_t on_attach_entry = nullptr;
|
||||
on_attach_entry = CAST_TO_FN_PTR(OnAttachEntry_t,
|
||||
os::find_agent_function(agent_lib, false, on_attach_symbols, num_symbol_entries));
|
||||
if (on_attach_entry == nullptr) {
|
||||
// Agent_OnAttach missing - unload library
|
||||
if (!agent_lib->is_static_lib()) {
|
||||
os::dll_unload(library);
|
||||
}
|
||||
st->print_cr("%s is not available in %s",
|
||||
on_attach_symbols[0], agent_lib->name());
|
||||
delete agent_lib;
|
||||
} else {
|
||||
// Invoke the Agent_OnAttach function
|
||||
JavaThread* THREAD = JavaThread::current(); // For exception macros.
|
||||
{
|
||||
extern struct JavaVM_ main_vm;
|
||||
JvmtiThreadEventMark jem(THREAD);
|
||||
JvmtiJavaThreadEventTransition jet(THREAD);
|
||||
|
||||
result = (*on_attach_entry)(&main_vm, (char*)options, nullptr);
|
||||
|
||||
// Agent_OnAttach may have used JNI
|
||||
if (THREAD->is_pending_jni_exception_check()) {
|
||||
THREAD->clear_pending_jni_exception_check();
|
||||
}
|
||||
}
|
||||
|
||||
// Agent_OnAttach may have used JNI
|
||||
if (HAS_PENDING_EXCEPTION) {
|
||||
CLEAR_PENDING_EXCEPTION;
|
||||
}
|
||||
|
||||
// If OnAttach returns JNI_OK then we add it to the list of
|
||||
// agent libraries so that we can call Agent_OnUnload later.
|
||||
if (result == JNI_OK) {
|
||||
Arguments::add_loaded_agent(agent_lib);
|
||||
} else {
|
||||
if (!agent_lib->is_static_lib()) {
|
||||
os::dll_unload(library);
|
||||
}
|
||||
delete agent_lib;
|
||||
}
|
||||
|
||||
// Agent_OnAttach executed so completion status is JNI_OK
|
||||
st->print_cr("return code: %d", result);
|
||||
result = JNI_OK;
|
||||
}
|
||||
} else {
|
||||
st->print_cr("%s was not loaded.", agent);
|
||||
if (*ebuf != '\0') {
|
||||
st->print_cr("%s", ebuf);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // INCLUDE_SERVICES
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Setup current current thread for event collection.
|
||||
void JvmtiEventCollector::setup_jvmti_thread_state() {
|
||||
// set this event collector to be the current one.
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "oops/method.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "oops/symbol.hpp"
|
||||
#include "prims/jvmtiAgentList.hpp"
|
||||
#include "prims/jvm_misc.hpp"
|
||||
#include "prims/jvmtiExport.hpp"
|
||||
#include "prims/nativeLookup.hpp"
|
||||
@ -285,9 +286,9 @@ address NativeLookup::lookup_style(const methodHandle& method, char* pure_name,
|
||||
|
||||
if (entry == nullptr) {
|
||||
// findNative didn't find it, if there are any agent libraries look in them
|
||||
AgentLibrary* agent;
|
||||
for (agent = Arguments::agents(); agent != nullptr; agent = agent->next()) {
|
||||
entry = (address) os::dll_lookup(agent->os_lib(), jni_name);
|
||||
JvmtiAgentList::Iterator it = JvmtiAgentList::agents();
|
||||
while (it.has_next()) {
|
||||
entry = (address)os::dll_lookup(it.next()->os_lib(), jni_name);
|
||||
if (entry != nullptr) {
|
||||
return entry;
|
||||
}
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "memory/allocation.inline.hpp"
|
||||
#include "oops/instanceKlass.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "prims/jvmtiAgentList.hpp"
|
||||
#include "prims/jvmtiExport.hpp"
|
||||
#include "runtime/arguments.hpp"
|
||||
#include "runtime/flags/jvmFlag.hpp"
|
||||
@ -105,9 +106,6 @@ char* Arguments::SharedDynamicArchivePath = nullptr;
|
||||
|
||||
LegacyGCLogging Arguments::_legacyGCLogging = { 0, 0 };
|
||||
|
||||
AgentLibraryList Arguments::_libraryList;
|
||||
AgentLibraryList Arguments::_agentList;
|
||||
|
||||
// These are not set by the JDK's built-in launchers, but they can be set by
|
||||
// programs that embed the JVM using JNI_CreateJavaVM. See comments around
|
||||
// JavaVMOption in jni.h.
|
||||
@ -217,25 +215,6 @@ SystemProperty::SystemProperty(const char* key, const char* value, bool writeabl
|
||||
_writeable = writeable;
|
||||
}
|
||||
|
||||
AgentLibrary::AgentLibrary(const char* name, const char* options,
|
||||
bool is_absolute_path, void* os_lib,
|
||||
bool instrument_lib) {
|
||||
_name = AllocateHeap(strlen(name)+1, mtArguments);
|
||||
strcpy(_name, name);
|
||||
if (options == nullptr) {
|
||||
_options = nullptr;
|
||||
} else {
|
||||
_options = AllocateHeap(strlen(options)+1, mtArguments);
|
||||
strcpy(_options, options);
|
||||
}
|
||||
_is_absolute_path = is_absolute_path;
|
||||
_os_lib = os_lib;
|
||||
_next = nullptr;
|
||||
_state = agent_invalid;
|
||||
_is_static_lib = false;
|
||||
_is_instrument_lib = instrument_lib;
|
||||
}
|
||||
|
||||
// Check if head of 'option' matches 'name', and sets 'tail' to the remaining
|
||||
// part of the option string.
|
||||
static bool match_option(const JavaVMOption *option, const char* name,
|
||||
@ -326,23 +305,6 @@ bool needs_module_property_warning = false;
|
||||
#define ENABLE_NATIVE_ACCESS "enable.native.access"
|
||||
#define ENABLE_NATIVE_ACCESS_LEN 20
|
||||
|
||||
void Arguments::add_init_library(const char* name, const char* options) {
|
||||
_libraryList.add(new AgentLibrary(name, options, false, nullptr));
|
||||
}
|
||||
|
||||
void Arguments::add_init_agent(const char* name, const char* options, bool absolute_path) {
|
||||
_agentList.add(new AgentLibrary(name, options, absolute_path, nullptr));
|
||||
}
|
||||
|
||||
void Arguments::add_instrument_agent(const char* name, const char* options, bool absolute_path) {
|
||||
_agentList.add(new AgentLibrary(name, options, absolute_path, nullptr, true));
|
||||
}
|
||||
|
||||
// Late-binding agents not started via arguments
|
||||
void Arguments::add_loaded_agent(AgentLibrary *agentLib) {
|
||||
_agentList.add(agentLib);
|
||||
}
|
||||
|
||||
// Return TRUE if option matches 'property', or 'property=', or 'property.'.
|
||||
static bool matches_property_suffix(const char* option, const char* property, size_t len) {
|
||||
return ((strncmp(option, property, len) == 0) &&
|
||||
@ -2381,7 +2343,7 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m
|
||||
return JNI_ERR;
|
||||
}
|
||||
#endif // !INCLUDE_JVMTI
|
||||
add_init_library(name, options);
|
||||
JvmtiAgentList::add_xrun(name, options, false);
|
||||
FREE_C_HEAP_ARRAY(char, name);
|
||||
FREE_C_HEAP_ARRAY(char, options);
|
||||
}
|
||||
@ -2453,7 +2415,7 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m
|
||||
return JNI_ERR;
|
||||
}
|
||||
#endif // !INCLUDE_JVMTI
|
||||
add_init_agent(name, options, is_absolute_path);
|
||||
JvmtiAgentList::add(name, options, is_absolute_path);
|
||||
os::free(name);
|
||||
os::free(options);
|
||||
}
|
||||
@ -2468,8 +2430,9 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m
|
||||
size_t length = strlen(tail) + 1;
|
||||
char *options = NEW_C_HEAP_ARRAY(char, length, mtArguments);
|
||||
jio_snprintf(options, length, "%s", tail);
|
||||
add_instrument_agent("instrument", options, false);
|
||||
JvmtiAgentList::add("instrument", options, false);
|
||||
FREE_C_HEAP_ARRAY(char, options);
|
||||
|
||||
// java agents need module java.instrument
|
||||
if (!create_numbered_module_property("jdk.module.addmods", "java.instrument", addmods_count++)) {
|
||||
return JNI_ENOMEM;
|
||||
|
@ -138,98 +138,6 @@ class SystemProperty : public PathString {
|
||||
SystemProperty(const char* key, const char* value, bool writeable, bool internal = false);
|
||||
};
|
||||
|
||||
|
||||
// For use by -agentlib, -agentpath and -Xrun
|
||||
class AgentLibrary : public CHeapObj<mtArguments> {
|
||||
friend class AgentLibraryList;
|
||||
public:
|
||||
// Is this library valid or not. Don't rely on os_lib == nullptr as statically
|
||||
// linked lib could have handle of RTLD_DEFAULT which == 0 on some platforms
|
||||
enum AgentState {
|
||||
agent_invalid = 0,
|
||||
agent_valid = 1
|
||||
};
|
||||
|
||||
private:
|
||||
char* _name;
|
||||
char* _options;
|
||||
void* _os_lib;
|
||||
bool _is_absolute_path;
|
||||
bool _is_static_lib;
|
||||
bool _is_instrument_lib;
|
||||
AgentState _state;
|
||||
AgentLibrary* _next;
|
||||
|
||||
public:
|
||||
// Accessors
|
||||
const char* name() const { return _name; }
|
||||
char* options() const { return _options; }
|
||||
bool is_absolute_path() const { return _is_absolute_path; }
|
||||
void* os_lib() const { return _os_lib; }
|
||||
void set_os_lib(void* os_lib) { _os_lib = os_lib; }
|
||||
AgentLibrary* next() const { return _next; }
|
||||
bool is_static_lib() const { return _is_static_lib; }
|
||||
bool is_instrument_lib() const { return _is_instrument_lib; }
|
||||
void set_static_lib(bool is_static_lib) { _is_static_lib = is_static_lib; }
|
||||
bool valid() { return (_state == agent_valid); }
|
||||
void set_valid() { _state = agent_valid; }
|
||||
|
||||
// Constructor
|
||||
AgentLibrary(const char* name, const char* options, bool is_absolute_path,
|
||||
void* os_lib, bool instrument_lib=false);
|
||||
};
|
||||
|
||||
// maintain an order of entry list of AgentLibrary
|
||||
class AgentLibraryList {
|
||||
private:
|
||||
AgentLibrary* _first;
|
||||
AgentLibrary* _last;
|
||||
public:
|
||||
bool is_empty() const { return _first == nullptr; }
|
||||
AgentLibrary* first() const { return _first; }
|
||||
|
||||
// add to the end of the list
|
||||
void add(AgentLibrary* lib) {
|
||||
if (is_empty()) {
|
||||
_first = _last = lib;
|
||||
} else {
|
||||
_last->_next = lib;
|
||||
_last = lib;
|
||||
}
|
||||
lib->_next = nullptr;
|
||||
}
|
||||
|
||||
// search for and remove a library known to be in the list
|
||||
void remove(AgentLibrary* lib) {
|
||||
AgentLibrary* curr;
|
||||
AgentLibrary* prev = nullptr;
|
||||
for (curr = first(); curr != nullptr; prev = curr, curr = curr->next()) {
|
||||
if (curr == lib) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(curr != nullptr, "always should be found");
|
||||
|
||||
if (curr != nullptr) {
|
||||
// it was found, by-pass this library
|
||||
if (prev == nullptr) {
|
||||
_first = curr->_next;
|
||||
} else {
|
||||
prev->_next = curr->_next;
|
||||
}
|
||||
if (curr == _last) {
|
||||
_last = prev;
|
||||
}
|
||||
curr->_next = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
AgentLibraryList() {
|
||||
_first = nullptr;
|
||||
_last = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
// Helper class for controlling the lifetime of JavaVMInitArgs objects.
|
||||
class ScopedVMInitArgs;
|
||||
|
||||
@ -331,18 +239,6 @@ class Arguments : AllStatic {
|
||||
// Value of the conservative maximum heap alignment needed
|
||||
static size_t _conservative_max_heap_alignment;
|
||||
|
||||
// -Xrun arguments
|
||||
static AgentLibraryList _libraryList;
|
||||
static void add_init_library(const char* name, const char* options);
|
||||
|
||||
// -agentlib and -agentpath arguments
|
||||
static AgentLibraryList _agentList;
|
||||
static void add_init_agent(const char* name, const char* options, bool absolute_path);
|
||||
static void add_instrument_agent(const char* name, const char* options, bool absolute_path);
|
||||
|
||||
// Late-binding agents not started via arguments
|
||||
static void add_loaded_agent(AgentLibrary *agentLib);
|
||||
|
||||
// Operation modi
|
||||
static Mode _mode;
|
||||
static void set_mode_flags(Mode mode);
|
||||
@ -542,17 +438,6 @@ class Arguments : AllStatic {
|
||||
// -Dsun.java.launcher.is_altjvm
|
||||
static bool sun_java_launcher_is_altjvm();
|
||||
|
||||
// -Xrun
|
||||
static AgentLibrary* libraries() { return _libraryList.first(); }
|
||||
static bool init_libraries_at_startup() { return !_libraryList.is_empty(); }
|
||||
static void convert_library_to_agent(AgentLibrary* lib)
|
||||
{ _libraryList.remove(lib);
|
||||
_agentList.add(lib); }
|
||||
|
||||
// -agentlib -agentpath
|
||||
static AgentLibrary* agents() { return _agentList.first(); }
|
||||
static bool init_agents_at_startup() { return !_agentList.is_empty(); }
|
||||
|
||||
// abort, exit, vfprintf hooks
|
||||
static abort_hook_t abort_hook() { return _abort_hook; }
|
||||
static exit_hook_t exit_hook() { return _exit_hook; }
|
||||
|
@ -54,6 +54,7 @@
|
||||
#include "oops/objArrayOop.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "oops/symbol.hpp"
|
||||
#include "prims/jvmtiAgentList.hpp"
|
||||
#include "prims/jvmtiExport.hpp"
|
||||
#include "runtime/continuation.hpp"
|
||||
#include "runtime/deoptimization.hpp"
|
||||
@ -516,7 +517,7 @@ void before_exit(JavaThread* thread, bool halt) {
|
||||
// Always call even when there are not JVMTI environments yet, since environments
|
||||
// may be attached late and JVMTI must track phases of VM execution
|
||||
JvmtiExport::post_vm_death();
|
||||
Threads::shutdown_vm_agents();
|
||||
JvmtiAgentList::unload_agents();
|
||||
|
||||
// Terminate the signal thread
|
||||
// Note: we don't wait until it actually dies.
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include "memory/universe.hpp"
|
||||
#include "oops/compressedOops.inline.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "prims/jvmtiAgent.hpp"
|
||||
#include "prims/jvm_misc.hpp"
|
||||
#include "runtime/arguments.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
@ -534,7 +535,7 @@ void* os::native_java_library() {
|
||||
* executable if agent_lib->is_static_lib() == true or in the shared library
|
||||
* referenced by 'handle'.
|
||||
*/
|
||||
void* os::find_agent_function(AgentLibrary *agent_lib, bool check_lib,
|
||||
void* os::find_agent_function(JvmtiAgent *agent_lib, bool check_lib,
|
||||
const char *syms[], size_t syms_len) {
|
||||
assert(agent_lib != nullptr, "sanity check");
|
||||
const char *lib_name;
|
||||
@ -561,29 +562,29 @@ void* os::find_agent_function(AgentLibrary *agent_lib, bool check_lib,
|
||||
}
|
||||
|
||||
// See if the passed in agent is statically linked into the VM image.
|
||||
bool os::find_builtin_agent(AgentLibrary *agent_lib, const char *syms[],
|
||||
bool os::find_builtin_agent(JvmtiAgent* agent, const char *syms[],
|
||||
size_t syms_len) {
|
||||
void *ret;
|
||||
void *proc_handle;
|
||||
void *save_handle;
|
||||
|
||||
assert(agent_lib != nullptr, "sanity check");
|
||||
if (agent_lib->name() == nullptr) {
|
||||
assert(agent != nullptr, "sanity check");
|
||||
if (agent->name() == nullptr) {
|
||||
return false;
|
||||
}
|
||||
proc_handle = get_default_process_handle();
|
||||
// Check for Agent_OnLoad/Attach_lib_name function
|
||||
save_handle = agent_lib->os_lib();
|
||||
save_handle = agent->os_lib();
|
||||
// We want to look in this process' symbol table.
|
||||
agent_lib->set_os_lib(proc_handle);
|
||||
ret = find_agent_function(agent_lib, true, syms, syms_len);
|
||||
agent->set_os_lib(proc_handle);
|
||||
ret = find_agent_function(agent, true, syms, syms_len);
|
||||
if (ret != nullptr) {
|
||||
// Found an entry point like Agent_OnLoad_lib_name so we have a static agent
|
||||
agent_lib->set_valid();
|
||||
agent_lib->set_static_lib(true);
|
||||
agent->set_static_lib();
|
||||
agent->set_loaded();
|
||||
return true;
|
||||
}
|
||||
agent_lib->set_os_lib(save_handle);
|
||||
agent->set_os_lib(save_handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -34,8 +34,8 @@
|
||||
# include <mach/mach_time.h>
|
||||
#endif
|
||||
|
||||
class AgentLibrary;
|
||||
class frame;
|
||||
class JvmtiAgent;
|
||||
|
||||
// Rules for using and implementing methods declared in the "os" class
|
||||
// ===================================================================
|
||||
@ -732,11 +732,11 @@ class os: AllStatic {
|
||||
static void* get_default_process_handle();
|
||||
|
||||
// Check for static linked agent library
|
||||
static bool find_builtin_agent(AgentLibrary *agent_lib, const char *syms[],
|
||||
static bool find_builtin_agent(JvmtiAgent *agent_lib, const char *syms[],
|
||||
size_t syms_len);
|
||||
|
||||
// Find agent entry point
|
||||
static void *find_agent_function(AgentLibrary *agent_lib, bool check_lib,
|
||||
static void *find_agent_function(JvmtiAgent *agent_lib, bool check_lib,
|
||||
const char *syms[], size_t syms_len);
|
||||
|
||||
// Provide C99 compliant versions of these functions, since some versions
|
||||
|
@ -56,6 +56,7 @@
|
||||
#include "oops/klass.inline.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "oops/symbol.hpp"
|
||||
#include "prims/jvmtiAgentList.hpp"
|
||||
#include "prims/jvm_misc.hpp"
|
||||
#include "runtime/arguments.hpp"
|
||||
#include "runtime/fieldDescriptor.inline.hpp"
|
||||
@ -332,10 +333,6 @@ static void call_initPhase3(TRAPS) {
|
||||
void Threads::initialize_java_lang_classes(JavaThread* main_thread, TRAPS) {
|
||||
TraceTime timer("Initialize java.lang classes", TRACETIME_LOG(Info, startuptime));
|
||||
|
||||
if (EagerXrunInit && Arguments::init_libraries_at_startup()) {
|
||||
create_vm_init_libraries();
|
||||
}
|
||||
|
||||
initialize_class(vmSymbols::java_lang_String(), CHECK);
|
||||
|
||||
// Inject CompactStrings value after the static initializers for String ran.
|
||||
@ -499,16 +496,8 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
|
||||
// Initialize output stream logging
|
||||
ostream_init_log();
|
||||
|
||||
// Convert -Xrun to -agentlib: if there is no JVM_OnLoad
|
||||
// Must be before create_vm_init_agents()
|
||||
if (Arguments::init_libraries_at_startup()) {
|
||||
convert_vm_init_libraries_to_agents();
|
||||
}
|
||||
|
||||
// Launch -agentlib/-agentpath and converted -Xrun agents
|
||||
if (Arguments::init_agents_at_startup()) {
|
||||
create_vm_init_agents();
|
||||
}
|
||||
JvmtiAgentList::load_agents();
|
||||
|
||||
// Initialize Threads state
|
||||
_number_of_threads = 0;
|
||||
@ -623,6 +612,11 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
|
||||
// Notify JVMTI agents that VM has started (JNI is up) - nop if no agents.
|
||||
JvmtiExport::post_early_vm_start();
|
||||
|
||||
// Launch -Xrun agents early if EagerXrunInit is set
|
||||
if (EagerXrunInit) {
|
||||
JvmtiAgentList::load_xrun_agents();
|
||||
}
|
||||
|
||||
initialize_java_lang_classes(main_thread, CHECK_JNI_ERR);
|
||||
|
||||
quicken_jni_functions();
|
||||
@ -658,11 +652,9 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
|
||||
}
|
||||
}
|
||||
|
||||
// Launch -Xrun agents
|
||||
// Must be done in the JVMTI live phase so that for backward compatibility the JDWP
|
||||
// back-end can launch with -Xdebug -Xrunjdwp.
|
||||
if (!EagerXrunInit && Arguments::init_libraries_at_startup()) {
|
||||
create_vm_init_libraries();
|
||||
// Launch -Xrun agents if EagerXrunInit is not set.
|
||||
if (!EagerXrunInit) {
|
||||
JvmtiAgentList::load_xrun_agents();
|
||||
}
|
||||
|
||||
Chunk::start_chunk_pool_cleaner_task();
|
||||
@ -804,208 +796,6 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
|
||||
return JNI_OK;
|
||||
}
|
||||
|
||||
// type for the Agent_OnLoad and JVM_OnLoad entry points
|
||||
extern "C" {
|
||||
typedef jint (JNICALL *OnLoadEntry_t)(JavaVM *, char *, void *);
|
||||
}
|
||||
// Find a command line agent library and return its entry point for
|
||||
// -agentlib: -agentpath: -Xrun
|
||||
// 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(AgentLibrary* agent,
|
||||
const char *on_load_symbols[],
|
||||
size_t num_symbol_entries) {
|
||||
OnLoadEntry_t on_load_entry = nullptr;
|
||||
void *library = nullptr;
|
||||
|
||||
if (!agent->valid()) {
|
||||
char buffer[JVM_MAXPATHLEN];
|
||||
char ebuf[1024] = "";
|
||||
const char *name = agent->name();
|
||||
const char *msg = "Could not find agent library ";
|
||||
|
||||
// First check to see if agent is statically linked into executable
|
||||
if (os::find_builtin_agent(agent, on_load_symbols, num_symbol_entries)) {
|
||||
library = agent->os_lib();
|
||||
} else if (agent->is_absolute_path()) {
|
||||
library = os::dll_load(name, ebuf, sizeof ebuf);
|
||||
if (library == nullptr) {
|
||||
const char *sub_msg = " in absolute path, with error: ";
|
||||
size_t len = strlen(msg) + strlen(name) + strlen(sub_msg) + strlen(ebuf) + 1;
|
||||
char *buf = NEW_C_HEAP_ARRAY(char, len, mtThread);
|
||||
jio_snprintf(buf, len, "%s%s%s%s", msg, name, sub_msg, ebuf);
|
||||
// If we can't find the agent, exit.
|
||||
vm_exit_during_initialization(buf, nullptr);
|
||||
FREE_C_HEAP_ARRAY(char, buf);
|
||||
}
|
||||
} else {
|
||||
// Try to load the agent from the standard dll directory
|
||||
if (os::dll_locate_lib(buffer, sizeof(buffer), Arguments::get_dll_dir(),
|
||||
name)) {
|
||||
library = os::dll_load(buffer, ebuf, sizeof ebuf);
|
||||
}
|
||||
if (library == nullptr) { // Try the library path directory.
|
||||
if (os::dll_build_name(buffer, sizeof(buffer), name)) {
|
||||
library = os::dll_load(buffer, ebuf, sizeof ebuf);
|
||||
}
|
||||
if (library == nullptr) {
|
||||
const char *sub_msg = " on the library path, with error: ";
|
||||
const char *sub_msg2 = "\nModule java.instrument may be missing from runtime image.";
|
||||
|
||||
size_t len = strlen(msg) + strlen(name) + strlen(sub_msg) +
|
||||
strlen(ebuf) + strlen(sub_msg2) + 1;
|
||||
char *buf = NEW_C_HEAP_ARRAY(char, len, mtThread);
|
||||
if (!agent->is_instrument_lib()) {
|
||||
jio_snprintf(buf, len, "%s%s%s%s", msg, name, sub_msg, ebuf);
|
||||
} else {
|
||||
jio_snprintf(buf, len, "%s%s%s%s%s", msg, name, sub_msg, ebuf, sub_msg2);
|
||||
}
|
||||
// If we can't find the agent, exit.
|
||||
vm_exit_during_initialization(buf, nullptr);
|
||||
FREE_C_HEAP_ARRAY(char, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
agent->set_os_lib(library);
|
||||
agent->set_valid();
|
||||
}
|
||||
|
||||
// Find the OnLoad function.
|
||||
on_load_entry =
|
||||
CAST_TO_FN_PTR(OnLoadEntry_t, os::find_agent_function(agent,
|
||||
false,
|
||||
on_load_symbols,
|
||||
num_symbol_entries));
|
||||
return on_load_entry;
|
||||
}
|
||||
|
||||
// Find the JVM_OnLoad entry point
|
||||
static OnLoadEntry_t lookup_jvm_on_load(AgentLibrary* agent) {
|
||||
const char *on_load_symbols[] = JVM_ONLOAD_SYMBOLS;
|
||||
return lookup_on_load(agent, on_load_symbols, sizeof(on_load_symbols) / sizeof(char*));
|
||||
}
|
||||
|
||||
// Find the Agent_OnLoad entry point
|
||||
static OnLoadEntry_t lookup_agent_on_load(AgentLibrary* agent) {
|
||||
const char *on_load_symbols[] = AGENT_ONLOAD_SYMBOLS;
|
||||
return lookup_on_load(agent, on_load_symbols, sizeof(on_load_symbols) / sizeof(char*));
|
||||
}
|
||||
|
||||
// For backwards compatibility with -Xrun
|
||||
// Convert libraries with no JVM_OnLoad, but which have Agent_OnLoad to be
|
||||
// treated like -agentpath:
|
||||
// Must be called before agent libraries are created
|
||||
void Threads::convert_vm_init_libraries_to_agents() {
|
||||
AgentLibrary* agent;
|
||||
AgentLibrary* next;
|
||||
|
||||
for (agent = Arguments::libraries(); agent != nullptr; agent = next) {
|
||||
next = agent->next(); // cache the next agent now as this agent may get moved off this list
|
||||
OnLoadEntry_t on_load_entry = lookup_jvm_on_load(agent);
|
||||
|
||||
// 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_on_load(agent);
|
||||
if (on_load_entry != nullptr) {
|
||||
// switch it to the agent list -- so that Agent_OnLoad will be called,
|
||||
// JVM_OnLoad won't be attempted and Agent_OnUnload will
|
||||
Arguments::convert_library_to_agent(agent);
|
||||
} else {
|
||||
vm_exit_during_initialization("Could not find JVM_OnLoad or Agent_OnLoad function in the library", agent->name());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create agents for -agentlib: -agentpath: and converted -Xrun
|
||||
// Invokes Agent_OnLoad
|
||||
// Called very early -- before JavaThreads exist
|
||||
void Threads::create_vm_init_agents() {
|
||||
extern struct JavaVM_ main_vm;
|
||||
AgentLibrary* agent;
|
||||
|
||||
JvmtiExport::enter_onload_phase();
|
||||
|
||||
for (agent = Arguments::agents(); agent != nullptr; agent = agent->next()) {
|
||||
// CDS dumping does not support native JVMTI agent.
|
||||
// CDS dumping supports Java agent if the AllowArchivingWithJavaAgent diagnostic option is specified.
|
||||
if (Arguments::is_dumping_archive()) {
|
||||
if(!agent->is_instrument_lib()) {
|
||||
vm_exit_during_cds_dumping("CDS dumping does not support native JVMTI agent, name", agent->name());
|
||||
} else if (!AllowArchivingWithJavaAgent) {
|
||||
vm_exit_during_cds_dumping(
|
||||
"Must enable AllowArchivingWithJavaAgent in order to run Java agent during CDS dumping");
|
||||
}
|
||||
}
|
||||
|
||||
OnLoadEntry_t on_load_entry = lookup_agent_on_load(agent);
|
||||
|
||||
if (on_load_entry != nullptr) {
|
||||
// Invoke the Agent_OnLoad function
|
||||
jint err = (*on_load_entry)(&main_vm, agent->options(), nullptr);
|
||||
if (err != JNI_OK) {
|
||||
vm_exit_during_initialization("agent library failed to init", agent->name());
|
||||
}
|
||||
} else {
|
||||
vm_exit_during_initialization("Could not find Agent_OnLoad function in the agent library", agent->name());
|
||||
}
|
||||
}
|
||||
|
||||
JvmtiExport::enter_primordial_phase();
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
typedef void (JNICALL *Agent_OnUnload_t)(JavaVM *);
|
||||
}
|
||||
|
||||
void Threads::shutdown_vm_agents() {
|
||||
// Send any Agent_OnUnload notifications
|
||||
const char *on_unload_symbols[] = AGENT_ONUNLOAD_SYMBOLS;
|
||||
size_t num_symbol_entries = ARRAY_SIZE(on_unload_symbols);
|
||||
extern struct JavaVM_ main_vm;
|
||||
for (AgentLibrary* agent = Arguments::agents(); agent != nullptr; agent = agent->next()) {
|
||||
|
||||
// Find the Agent_OnUnload function.
|
||||
Agent_OnUnload_t unload_entry = CAST_TO_FN_PTR(Agent_OnUnload_t,
|
||||
os::find_agent_function(agent,
|
||||
false,
|
||||
on_unload_symbols,
|
||||
num_symbol_entries));
|
||||
|
||||
// Invoke the Agent_OnUnload function
|
||||
if (unload_entry != nullptr) {
|
||||
JavaThread* thread = JavaThread::current();
|
||||
ThreadToNativeFromVM ttn(thread);
|
||||
HandleMark hm(thread);
|
||||
(*unload_entry)(&main_vm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Called for after the VM is initialized for -Xrun libraries which have not been converted to agent libraries
|
||||
// Invokes JVM_OnLoad
|
||||
void Threads::create_vm_init_libraries() {
|
||||
extern struct JavaVM_ main_vm;
|
||||
AgentLibrary* agent;
|
||||
|
||||
for (agent = Arguments::libraries(); agent != nullptr; agent = agent->next()) {
|
||||
OnLoadEntry_t on_load_entry = lookup_jvm_on_load(agent);
|
||||
|
||||
if (on_load_entry != nullptr) {
|
||||
// Invoke the JVM_OnLoad function
|
||||
JavaThread* thread = JavaThread::current();
|
||||
ThreadToNativeFromVM ttn(thread);
|
||||
HandleMark hm(thread);
|
||||
jint err = (*on_load_entry)(&main_vm, agent->options(), nullptr);
|
||||
if (err != JNI_OK) {
|
||||
vm_exit_during_initialization("-Xrun library failed to init", agent->name());
|
||||
}
|
||||
} else {
|
||||
vm_exit_during_initialization("Could not find JVM_OnLoad function in -Xrun library", agent->name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Threads::destroy_vm() is normally called from jni_DestroyJavaVM() when
|
||||
// the program falls off the end of main(). Another VM exit path is through
|
||||
// vm_exit() when the program calls System.exit() to return a value or when
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "memory/universe.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "oops/typeArrayOop.inline.hpp"
|
||||
#include "prims/jvmtiAgentList.hpp"
|
||||
#include "prims/jvmtiExport.hpp"
|
||||
#include "runtime/arguments.hpp"
|
||||
#include "runtime/flags/jvmFlag.hpp"
|
||||
@ -134,7 +135,7 @@ static jint load_agent(AttachOperation* op, outputStream* out) {
|
||||
}
|
||||
}
|
||||
|
||||
return JvmtiExport::load_agent_library(agent, absParam, options, out);
|
||||
return JvmtiAgentList::load_agent(agent, absParam, options, out);
|
||||
}
|
||||
|
||||
// Implementation of "properties" command.
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "oops/objArrayOop.inline.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "oops/typeArrayOop.inline.hpp"
|
||||
#include "prims/jvmtiAgentList.hpp"
|
||||
#include "runtime/fieldDescriptor.inline.hpp"
|
||||
#include "runtime/flags/jvmFlag.hpp"
|
||||
#include "runtime/handles.inline.hpp"
|
||||
@ -297,8 +298,7 @@ void JVMTIAgentLoadDCmd::execute(DCmdSource source, TRAPS) {
|
||||
|
||||
if (is_java_agent) {
|
||||
if (_option.value() == nullptr) {
|
||||
JvmtiExport::load_agent_library("instrument", "false",
|
||||
_libpath.value(), output());
|
||||
JvmtiAgentList::load_agent("instrument", "false", _libpath.value(), output());
|
||||
} else {
|
||||
size_t opt_len = strlen(_libpath.value()) + strlen(_option.value()) + 2;
|
||||
if (opt_len > 4096) {
|
||||
@ -315,13 +315,12 @@ void JVMTIAgentLoadDCmd::execute(DCmdSource source, TRAPS) {
|
||||
}
|
||||
|
||||
jio_snprintf(opt, opt_len, "%s=%s", _libpath.value(), _option.value());
|
||||
JvmtiExport::load_agent_library("instrument", "false", opt, output());
|
||||
JvmtiAgentList::load_agent("instrument", "false", opt, output());
|
||||
|
||||
os::free(opt);
|
||||
}
|
||||
} else {
|
||||
JvmtiExport::load_agent_library(_libpath.value(), "true",
|
||||
_option.value(), output());
|
||||
JvmtiAgentList::load_agent(_libpath.value(), "true", _option.value(), output());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1031,7 +1030,9 @@ void DebugOnCmdStartDCmd::execute(DCmdSource source, TRAPS) {
|
||||
const char *error = "Could not find jdwp agent.";
|
||||
|
||||
if (!dvc_start_ptr) {
|
||||
for (AgentLibrary* agent = Arguments::agents(); agent != nullptr; agent = agent->next()) {
|
||||
JvmtiAgentList::Iterator it = JvmtiAgentList::agents();
|
||||
while (it.has_next()) {
|
||||
JvmtiAgent* agent = it.next();
|
||||
if ((strcmp("jdwp", agent->name()) == 0) && (dvc_start_ptr == nullptr)) {
|
||||
char const* func = "debugInit_startDebuggingViaCommand";
|
||||
dvc_start_ptr = (debugInit_startDebuggingViaCommandPtr) os::find_agent_function(agent, false, &func, 1);
|
||||
|
@ -858,6 +858,16 @@
|
||||
<setting name="period">endChunk</setting>
|
||||
</event>
|
||||
|
||||
<event name="jdk.JavaAgent">
|
||||
<setting name="enabled">true</setting>
|
||||
<setting name="period">endChunk</setting>
|
||||
</event>
|
||||
|
||||
<event name="jdk.NativeAgent">
|
||||
<setting name="enabled">true</setting>
|
||||
<setting name="period">endChunk</setting>
|
||||
</event>
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -858,7 +858,15 @@
|
||||
<setting name="period">endChunk</setting>
|
||||
</event>
|
||||
|
||||
<event name="jdk.JavaAgent">
|
||||
<setting name="enabled">true</setting>
|
||||
<setting name="period">endChunk</setting>
|
||||
</event>
|
||||
|
||||
<event name="jdk.NativeAgent">
|
||||
<setting name="enabled">true</setting>
|
||||
<setting name="period">endChunk</setting>
|
||||
</event>
|
||||
|
||||
|
||||
|
||||
|
37
test/jdk/jdk/jfr/event/runtime/JavaAgent.java
Normal file
37
test/jdk/jdk/jfr/event/runtime/JavaAgent.java
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package jdk.jfr.event.runtime;
|
||||
|
||||
import java.lang.instrument.Instrumentation;
|
||||
|
||||
public class JavaAgent {
|
||||
|
||||
public static void agentmain(String agentArgs, Instrumentation inst) throws Exception {
|
||||
System.out.println("agentmain: " + agentArgs);
|
||||
}
|
||||
|
||||
public static void premain(String agentArgs, Instrumentation inst) throws Exception {
|
||||
System.out.println("premain: " + agentArgs);
|
||||
}
|
||||
}
|
161
test/jdk/jdk/jfr/event/runtime/TestAgentEvent.java
Normal file
161
test/jdk/jdk/jfr/event/runtime/TestAgentEvent.java
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package jdk.jfr.event.runtime;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.security.Timestamp;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import com.sun.tools.attach.VirtualMachine;
|
||||
|
||||
import jdk.jfr.Recording;
|
||||
import jdk.jfr.consumer.RecordedClass;
|
||||
import jdk.jfr.consumer.RecordedClassLoader;
|
||||
import jdk.jfr.consumer.RecordedEvent;
|
||||
import jdk.jfr.Event;
|
||||
import jdk.test.lib.Asserts;
|
||||
import jdk.test.lib.jfr.EventNames;
|
||||
import jdk.test.lib.jfr.Events;
|
||||
import jdk.test.lib.jfr.TestClassLoader;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @key jfr
|
||||
* @summary Tests Agent Loaded event by starting native and Java agents
|
||||
* @requires vm.hasJFR
|
||||
*
|
||||
* @library /test/lib
|
||||
* @modules java.instrument
|
||||
*
|
||||
* @build jdk.jfr.event.runtime.JavaAgent
|
||||
*
|
||||
* @run driver jdk.test.lib.util.JavaAgentBuilder
|
||||
* jdk.jfr.event.runtime.JavaAgent
|
||||
* JavaAgent.jar
|
||||
*
|
||||
* @run main/othervm -javaagent:JavaAgent.jar=foo=bar
|
||||
* jdk.jfr.event.runtime.TestAgentEvent
|
||||
* testJavaStatic
|
||||
*
|
||||
* @run main/othervm -Djdk.attach.allowAttachSelf=true
|
||||
* jdk.jfr.event.runtime.TestAgentEvent
|
||||
* testJavaDynamic
|
||||
*
|
||||
* @run main/othervm -agentlib:jdwp=transport=dt_socket,server=y,address=any,onjcmd=y
|
||||
* jdk.jfr.event.runtime.TestAgentEvent
|
||||
* testNativeStatic
|
||||
*/
|
||||
public final class TestAgentEvent {
|
||||
private static final String JAVA_AGENT_JAR = "JavaAgent.jar";
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
String testMethod = args[0];
|
||||
Method m = TestAgentEvent.class.getDeclaredMethod(testMethod, new Class[0]);
|
||||
if (m == null) {
|
||||
throw new Exception("Unknown test method: " + testMethod);
|
||||
}
|
||||
m.invoke(null, new Object[0]);
|
||||
}
|
||||
|
||||
private static void testJavaStatic() throws Throwable {
|
||||
try (Recording r = new Recording()) {
|
||||
r.enable(EventNames.JavaAgent);
|
||||
r.start();
|
||||
r.stop();
|
||||
List<RecordedEvent> events = Events.fromRecording(r);
|
||||
RecordedEvent e = events.get(0);
|
||||
System.out.println(e);
|
||||
Events.assertField(e, "name").equal(JAVA_AGENT_JAR);
|
||||
Events.assertField(e, "options").equal("foo=bar");
|
||||
Events.assertField(e, "dynamic").equal(false);
|
||||
Instant initializationTime = e.getInstant("initializationTime");
|
||||
if (initializationTime.isAfter(r.getStartTime())) {
|
||||
throw new Exception("Expected a static JavaAgent to be initialized before recording start");
|
||||
}
|
||||
Events.assertField(e, "initializationDuration").atLeast(0L);
|
||||
}
|
||||
}
|
||||
|
||||
private static void testNativeStatic() throws Throwable {
|
||||
try (Recording r = new Recording()) {
|
||||
r.enable(EventNames.NativeAgent);
|
||||
r.start();
|
||||
r.stop();
|
||||
List<RecordedEvent> events = Events.fromRecording(r);
|
||||
RecordedEvent e = events.get(0);
|
||||
System.out.println(e);
|
||||
Events.assertField(e, "name").equal("jdwp");
|
||||
Events.assertField(e, "options").equal("transport=dt_socket,server=y,address=any,onjcmd=y");
|
||||
Events.assertField(e, "dynamic").equal(false);
|
||||
Instant initializationTime = e.getInstant("initializationTime");
|
||||
if (initializationTime.isAfter(r.getStartTime())) {
|
||||
throw new Exception("Expected a static NativeAgent to be initialized before recording start");
|
||||
}
|
||||
Events.assertField(e, "initializationDuration").atLeast(0L);
|
||||
}
|
||||
}
|
||||
|
||||
private static void testJavaDynamic() throws Throwable {
|
||||
try (Recording r = new Recording()) {
|
||||
r.enable(EventNames.JavaAgent);
|
||||
r.start();
|
||||
long pid = ProcessHandle.current().pid();
|
||||
VirtualMachine vm = VirtualMachine.attach(Long.toString(pid));
|
||||
vm.loadAgent(JAVA_AGENT_JAR, "bar=baz");
|
||||
vm.detach();
|
||||
vm = VirtualMachine.attach(Long.toString(pid));
|
||||
vm.loadAgent(JAVA_AGENT_JAR); // options = null
|
||||
vm.detach();
|
||||
vm = VirtualMachine.attach(Long.toString(pid));
|
||||
vm.loadAgent(JAVA_AGENT_JAR, "");
|
||||
vm.loadAgent(JAVA_AGENT_JAR, "=");
|
||||
vm.detach();
|
||||
r.stop();
|
||||
List<RecordedEvent> events = Events.fromRecording(r);
|
||||
for (RecordedEvent e : events) {
|
||||
System.out.println(e);
|
||||
Instant initializationTime = e.getInstant("initializationTime");
|
||||
if (initializationTime.isBefore(r.getStartTime())) {
|
||||
throw new Exception("Expected a dynamic JavaAgent to be initialized after recording start");
|
||||
}
|
||||
if (initializationTime.isAfter(r.getStopTime())) {
|
||||
throw new Exception("Expected a dynamic JavaAgent to be initialized before recording stop");
|
||||
}
|
||||
Duration initializationDuration = e.getDuration("initializationDuration");
|
||||
if (initializationDuration.isNegative()) {
|
||||
throw new Exception("Expected initalizationDuration to be positive value");
|
||||
}
|
||||
if (initializationDuration.toSeconds() > 3600) {
|
||||
throw new Exception("Expected initializationDuration to be less than 1 hour");
|
||||
}
|
||||
Events.assertField(e, "name").equal(JAVA_AGENT_JAR);
|
||||
}
|
||||
Events.assertField(events.get(0), "options").equal("bar=baz");
|
||||
Events.assertField(events.get(1), "options").equal(null);
|
||||
Events.assertField(events.get(2), "options").equal("");
|
||||
Events.assertField(events.get(3), "options").equal("=");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 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
|
||||
@ -88,6 +88,8 @@ public class EventNames {
|
||||
public static final String FinalizerStatistics = PREFIX + "FinalizerStatistics";
|
||||
public static final String NativeMemoryUsage = PREFIX + "NativeMemoryUsage";
|
||||
public static final String NativeMemoryUsageTotal = PREFIX + "NativeMemoryUsageTotal";
|
||||
public static final String JavaAgent = PREFIX + "JavaAgent";
|
||||
public static final String NativeAgent = PREFIX + "NativeAgent";
|
||||
|
||||
// This event is hard to test
|
||||
public static final String ReservedStackActivation = PREFIX + "ReservedStackActivation";
|
||||
|
Loading…
Reference in New Issue
Block a user