blob: a4b7606056d4777d32007fa09f8772e4ee9b1ba0 [file] [edit]
/*
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "cds/cdsConfig.hpp"
#include "cds/cds_globals.hpp"
#include "logging/log.hpp"
#include "memory/universe.hpp"
#include "prims/jvmtiAgentList.hpp"
#include "prims/jvmtiEnvBase.hpp"
#include "prims/jvmtiExport.hpp"
#include "runtime/atomic.hpp"
#include "runtime/os.inline.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();
}
}
}
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, const char* options, bool absolute_path) {
add(new JvmtiAgent(name, options, absolute_path));
}
void JvmtiAgentList::add_xrun(const char* name, const 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.
void JvmtiAgentList::load_agent(const char* agent_name, bool is_absolute_path,
const char* options, outputStream* st) {
JvmtiAgent* const agent = new JvmtiAgent(agent_name, options, is_absolute_path, /* dynamic agent */ true);
if (agent->load(st)) {
add(agent);
} else {
delete agent;
}
}
// Send any Agent_OnUnload notifications
void JvmtiAgentList::unload_agents() {
Iterator it = agents();
while (it.has_next()) {
it.next()->unload();
}
}
// Return true if a statically linked agent is on the list
bool JvmtiAgentList::is_static_lib_loaded(const char* name) {
JvmtiAgentList::Iterator it = JvmtiAgentList::agents();
while (it.has_next()) {
JvmtiAgent* const agent = it.next();
if (agent->is_static_lib() && strcmp(agent->name(), name) == 0) {
return true;
}
}
return false;
}
// Return true if a agent library on the list
bool JvmtiAgentList::is_dynamic_lib_loaded(void* os_lib) {
JvmtiAgentList::Iterator it = JvmtiAgentList::agents();
while (it.has_next()) {
JvmtiAgent* const agent = it.next();
if (!agent->is_static_lib() && agent->os_lib() == os_lib) {
return true;
}
}
return false;
}
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 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");
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;
}
void JvmtiAgentList::disable_agent_list() {
#if INCLUDE_CDS
assert(CDSConfig::is_dumping_final_static_archive(), "use this only for -XX:AOTMode=create!");
assert(!Universe::is_bootstrapping() && !Universe::is_fully_initialized(), "must do this very early");
log_info(aot)("Disabled all JVMTI agents during -XX:AOTMode=create");
_list = nullptr; // Pretend that no agents have been added.
#endif
}