8307478: Implementation of Prepare to Restrict The Dynamic Loading of Agents
Reviewed-by: sspitsyn, cjplummer
This commit is contained in:
parent
325940b091
commit
5bd2af26e6
@ -181,6 +181,7 @@ JVM_NewArray
|
|||||||
JVM_NewInstanceFromConstructor
|
JVM_NewInstanceFromConstructor
|
||||||
JVM_NewMultiArray
|
JVM_NewMultiArray
|
||||||
JVM_PhantomReferenceRefersTo
|
JVM_PhantomReferenceRefersTo
|
||||||
|
JVM_PrintWarningAtDynamicAgentLoad
|
||||||
JVM_RaiseSignal
|
JVM_RaiseSignal
|
||||||
JVM_RawMonitorCreate
|
JVM_RawMonitorCreate
|
||||||
JVM_RawMonitorDestroy
|
JVM_RawMonitorDestroy
|
||||||
|
@ -1164,6 +1164,12 @@ JVM_VirtualThreadHideFrames(JNIEnv* env, jobject vthread, jboolean hide);
|
|||||||
JNIEXPORT jint JNICALL
|
JNIEXPORT jint JNICALL
|
||||||
JVM_GetClassFileVersion(JNIEnv *env, jclass current);
|
JVM_GetClassFileVersion(JNIEnv *env, jclass current);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return JNI_TRUE if warnings are printed when agents are dynamically loaded.
|
||||||
|
*/
|
||||||
|
JNIEXPORT jboolean JNICALL
|
||||||
|
JVM_PrintWarningAtDynamicAgentLoad(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This structure is used by the launcher to get the default thread
|
* This structure is used by the launcher to get the default thread
|
||||||
* stack size from the VM using JNI_GetDefaultJavaVMInitArgs() with a
|
* stack size from the VM using JNI_GetDefaultJavaVMInitArgs() with a
|
||||||
|
@ -4024,3 +4024,10 @@ JVM_END
|
|||||||
JVM_ENTRY(void, JVM_EnsureMaterializedForStackWalk_func(JNIEnv* env, jobject vthread, jobject value))
|
JVM_ENTRY(void, JVM_EnsureMaterializedForStackWalk_func(JNIEnv* env, jobject vthread, jobject value))
|
||||||
JVM_EnsureMaterializedForStackWalk(env, value);
|
JVM_EnsureMaterializedForStackWalk(env, value);
|
||||||
JVM_END
|
JVM_END
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return JNI_TRUE if warnings are printed when agents are dynamically loaded.
|
||||||
|
*/
|
||||||
|
JVM_LEAF(jboolean, JVM_PrintWarningAtDynamicAgentLoad(void))
|
||||||
|
return (EnableDynamicAgentLoading && !FLAG_IS_CMDLINE(EnableDynamicAgentLoading)) ? JNI_TRUE : JNI_FALSE;
|
||||||
|
JVM_END
|
||||||
|
@ -640,6 +640,17 @@ Agent_OnLoad_L(JavaVM *vm, char *options, void *reserved)</example>
|
|||||||
or implementation specific API, to attach to the running VM, and request it start a given
|
or implementation specific API, to attach to the running VM, and request it start a given
|
||||||
agent.
|
agent.
|
||||||
<p/>
|
<p/>
|
||||||
|
The VM prints a warning on the standard error stream for each agent that it attempts
|
||||||
|
to start in the live phase. If an agent was previously started (in the <code>OnLoad</code>
|
||||||
|
phase or in the live phase), then it is implementation specific as to whether a
|
||||||
|
warning is printed when attempting to start the same agent a second or subsequent time.
|
||||||
|
Warnings can be disabled by means of an implementation-specific command line option.
|
||||||
|
<p/>
|
||||||
|
<b>Implementation Note:</b> For the HotSpot VM, the VM option
|
||||||
|
<code>-XX:+EnableDynamicAgentLoading</code> is used to opt-in to allow dynamic loading
|
||||||
|
of agents in the live phase. This option suppresses the warning to standard error when
|
||||||
|
starting an agent in the live phase.
|
||||||
|
<p/>
|
||||||
If an agent is started during the live phase then its agent library
|
If an agent is started during the live phase then its agent library
|
||||||
must export a start-up function
|
must export a start-up function
|
||||||
with the following prototype:
|
with the following prototype:
|
||||||
|
@ -30,13 +30,16 @@
|
|||||||
#include "jvmtifiles/jvmtiEnv.hpp"
|
#include "jvmtifiles/jvmtiEnv.hpp"
|
||||||
#include "prims/jvmtiEnvBase.hpp"
|
#include "prims/jvmtiEnvBase.hpp"
|
||||||
#include "prims/jvmtiExport.hpp"
|
#include "prims/jvmtiExport.hpp"
|
||||||
|
#include "prims/jvmtiAgentList.hpp"
|
||||||
#include "runtime/arguments.hpp"
|
#include "runtime/arguments.hpp"
|
||||||
#include "runtime/handles.inline.hpp"
|
#include "runtime/handles.inline.hpp"
|
||||||
#include "runtime/interfaceSupport.inline.hpp"
|
#include "runtime/interfaceSupport.inline.hpp"
|
||||||
#include "runtime/java.hpp"
|
#include "runtime/java.hpp"
|
||||||
#include "runtime/jniHandles.hpp"
|
#include "runtime/jniHandles.hpp"
|
||||||
|
#include "runtime/globals_extension.hpp"
|
||||||
#include "runtime/os.inline.hpp"
|
#include "runtime/os.inline.hpp"
|
||||||
#include "runtime/thread.inline.hpp"
|
#include "runtime/thread.inline.hpp"
|
||||||
|
#include "utilities/defaultStream.hpp"
|
||||||
|
|
||||||
static inline const char* copy_string(const char* str) {
|
static inline const char* copy_string(const char* str) {
|
||||||
return str != nullptr ? os::strdup(str, mtServiceability) : nullptr;
|
return str != nullptr ? os::strdup(str, mtServiceability) : nullptr;
|
||||||
@ -260,9 +263,9 @@ static void assert_preload(const JvmtiAgent* agent) {
|
|||||||
|
|
||||||
// Check for a statically linked-in agent, i.e. in the executable.
|
// 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:
|
// 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
|
// For statically linked agents we can't rely on os_lib == nullptr because
|
||||||
// statically linked agents could have a handle of RTLD_DEFAULT which == 0 on some platforms.
|
// 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().
|
// 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) {
|
static bool load_agent_from_executable(JvmtiAgent* agent, const char* on_load_symbols[], size_t num_symbol_entries) {
|
||||||
DEBUG_ONLY(assert_preload(agent);)
|
DEBUG_ONLY(assert_preload(agent);)
|
||||||
assert(on_load_symbols != nullptr, "invariant");
|
assert(on_load_symbols != nullptr, "invariant");
|
||||||
@ -483,6 +486,7 @@ extern "C" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Loading the agent by invoking Agent_OnAttach.
|
// Loading the agent by invoking Agent_OnAttach.
|
||||||
|
// This function is called before the agent is added to JvmtiAgentList.
|
||||||
static bool invoke_Agent_OnAttach(JvmtiAgent* agent, outputStream* st) {
|
static bool invoke_Agent_OnAttach(JvmtiAgent* agent, outputStream* st) {
|
||||||
DEBUG_ONLY(assert_preload(agent);)
|
DEBUG_ONLY(assert_preload(agent);)
|
||||||
assert(agent->is_dynamic(), "invariant");
|
assert(agent->is_dynamic(), "invariant");
|
||||||
@ -491,7 +495,10 @@ static bool invoke_Agent_OnAttach(JvmtiAgent* agent, outputStream* st) {
|
|||||||
const char* on_attach_symbols[] = AGENT_ONATTACH_SYMBOLS;
|
const char* on_attach_symbols[] = AGENT_ONATTACH_SYMBOLS;
|
||||||
const size_t num_symbol_entries = ARRAY_SIZE(on_attach_symbols);
|
const size_t num_symbol_entries = ARRAY_SIZE(on_attach_symbols);
|
||||||
void* library = nullptr;
|
void* library = nullptr;
|
||||||
if (!load_agent_from_executable(agent, &on_attach_symbols[0], num_symbol_entries)) {
|
bool previously_loaded;
|
||||||
|
if (load_agent_from_executable(agent, &on_attach_symbols[0], num_symbol_entries)) {
|
||||||
|
previously_loaded = JvmtiAgentList::is_static_lib_loaded(agent->name());
|
||||||
|
} else {
|
||||||
library = load_library(agent, &on_attach_symbols[0], num_symbol_entries, /* vm_exit_on_error */ false);
|
library = load_library(agent, &on_attach_symbols[0], num_symbol_entries, /* vm_exit_on_error */ false);
|
||||||
if (library == nullptr) {
|
if (library == nullptr) {
|
||||||
st->print_cr("%s was not loaded.", agent->name());
|
st->print_cr("%s was not loaded.", agent->name());
|
||||||
@ -503,7 +510,17 @@ static bool invoke_Agent_OnAttach(JvmtiAgent* agent, outputStream* st) {
|
|||||||
agent->set_os_lib_path(&buffer[0]);
|
agent->set_os_lib_path(&buffer[0]);
|
||||||
agent->set_os_lib(library);
|
agent->set_os_lib(library);
|
||||||
agent->set_loaded();
|
agent->set_loaded();
|
||||||
|
previously_loaded = JvmtiAgentList::is_dynamic_lib_loaded(library);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Print warning if agent was not previously loaded and EnableDynamicAgentLoading not enabled on the command line.
|
||||||
|
if (!previously_loaded && !FLAG_IS_CMDLINE(EnableDynamicAgentLoading) && !agent->is_instrument_lib()) {
|
||||||
|
jio_fprintf(defaultStream::error_stream(),
|
||||||
|
"WARNING: A JVM TI agent has been loaded dynamically (%s)\n"
|
||||||
|
"WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading to hide this warning\n"
|
||||||
|
"WARNING: Dynamic loading of agents will be disallowed by default in a future release\n", agent->name());
|
||||||
|
}
|
||||||
|
|
||||||
assert(agent->is_loaded(), "invariant");
|
assert(agent->is_loaded(), "invariant");
|
||||||
// The library was loaded so we attempt to lookup and invoke the Agent_OnAttach function.
|
// 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,
|
OnAttachEntry_t on_attach_entry = CAST_TO_FN_PTR(OnAttachEntry_t,
|
||||||
|
@ -220,6 +220,30 @@ void JvmtiAgentList::unload_agents() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
static bool match(JvmtiEnv* env, const JvmtiAgent* agent, const void* os_module_address) {
|
||||||
assert(env != nullptr, "invariant");
|
assert(env != nullptr, "invariant");
|
||||||
assert(agent != nullptr, "invariant");
|
assert(agent != nullptr, "invariant");
|
||||||
|
@ -76,6 +76,9 @@ class JvmtiAgentList : AllStatic {
|
|||||||
static void load_xrun_agents() NOT_JVMTI_RETURN;
|
static void load_xrun_agents() NOT_JVMTI_RETURN;
|
||||||
static void unload_agents() NOT_JVMTI_RETURN;
|
static void unload_agents() NOT_JVMTI_RETURN;
|
||||||
|
|
||||||
|
static bool is_static_lib_loaded(const char* name);
|
||||||
|
static bool is_dynamic_lib_loaded(void* os_lib);
|
||||||
|
|
||||||
static JvmtiAgent* lookup(JvmtiEnv* env, void* f_ptr);
|
static JvmtiAgent* lookup(JvmtiEnv* env, void* f_ptr);
|
||||||
|
|
||||||
static Iterator agents() NOT_JVMTI({ Iterator it; return it; });
|
static Iterator agents() NOT_JVMTI({ Iterator it; return it; });
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -29,25 +29,20 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides services that allow Java programming language agents to instrument
|
* Provides services that allow Java programming language agents to instrument
|
||||||
* programs running on the JVM. The mechanism for instrumentation is modification
|
* programs running on the Java Virtual Machine (JVM). The mechanism for
|
||||||
* of the byte-codes of methods.
|
* instrumentation is modification of the bytecodes of methods.
|
||||||
*
|
*
|
||||||
* <p> An agent is deployed as a JAR file. An attribute in the JAR file manifest
|
* <p> The class files that comprise an agent are packaged into a JAR file, either
|
||||||
* specifies the agent class which will be loaded to start the agent. Agents can
|
* with the application in an executable JAR, or more commonly, as a separate JAR file
|
||||||
* be started in several ways:
|
* called an <em>agent JAR</em>. An attribute in the main manifest of the JAR file
|
||||||
|
* identifies one of the class files in the JAR file as the <em>agent class</em>.
|
||||||
|
* The agent class defines a special method that the JVM invokes to <em>start</em>
|
||||||
|
* the agent.
|
||||||
*
|
*
|
||||||
* <ol>
|
* <p> Agents that are packaged with an application in an executable JAR are started
|
||||||
* <li><p> For implementations that support a command-line interface, an agent
|
* at JVM statup time. Agents that are packaged into an agent JAR file may be started
|
||||||
* can be started by specifying an option on the command-line. </p></li>
|
* at JVM startup time via a command line option, or where an implementation supports
|
||||||
*
|
* it, started in a running JVM.
|
||||||
* <li><p> An implementation may support a mechanism to start agents some time
|
|
||||||
* after the VM has started. For example, an implementation may provide a
|
|
||||||
* mechanism that allows a tool to <i>attach</i> to a running application, and
|
|
||||||
* initiate the loading of the tool's agent into the running application. </p></li>
|
|
||||||
*
|
|
||||||
* <li><p> An agent may be packaged with an application in an executable JAR
|
|
||||||
* file.</p></li>
|
|
||||||
* </ol>
|
|
||||||
*
|
*
|
||||||
* <p> Agents can transform classes in arbitrary ways at load time, transform
|
* <p> Agents can transform classes in arbitrary ways at load time, transform
|
||||||
* modules, or transform the bytecode of methods of already loaded classes.
|
* modules, or transform the bytecode of methods of already loaded classes.
|
||||||
@ -56,13 +51,47 @@
|
|||||||
* running application, are responsible for verifying the trustworthiness of each
|
* running application, are responsible for verifying the trustworthiness of each
|
||||||
* agent including the content and structure of the agent JAR file.
|
* agent including the content and structure of the agent JAR file.
|
||||||
*
|
*
|
||||||
* <p> The three ways to start an agent are described below.
|
* <h2>Starting an agent</h2>
|
||||||
*
|
*
|
||||||
* <h2>Starting an Agent from the Command-Line Interface</h2>
|
* <h3>Starting an agent packaged with an application in an executable JAR file</h3>
|
||||||
*
|
*
|
||||||
* <p> Where an implementation provides a means to start agents from the
|
* <p> The <a href="{@docRoot}/../specs/jar/jar.html">JAR File Specification</a> defines
|
||||||
* command-line interface, an agent is started by adding the following option
|
* manifest attributes for standalone applications that are packaged as <em>executable
|
||||||
* to the command-line:
|
* JAR files</em>. If an implementation supports a mechanism to start an application as
|
||||||
|
* an executable JAR, then the main manifest of the JAR file can include the
|
||||||
|
* {@code Launcher-Agent-Class} attribute to specify the binary name of the Java agent
|
||||||
|
* class that is packaged with the application. If the attribute is present then the
|
||||||
|
* JVM starts the agent by loading the agent class and invoking its {@code agentmain}
|
||||||
|
* method. The method is invoked before the application {@code main} method is invoked.
|
||||||
|
* The {@code agentmain} method has one of two possible signatures. The JVM first
|
||||||
|
* attempts to invoke the following method on the agent class:
|
||||||
|
*
|
||||||
|
* <blockquote>{@code
|
||||||
|
* public static void agentmain(String agentArgs, Instrumentation inst)
|
||||||
|
* }</blockquote>
|
||||||
|
*
|
||||||
|
* <p> If the agent class does not define this method then the JVM will attempt
|
||||||
|
* to invoke:
|
||||||
|
*
|
||||||
|
* <blockquote>{@code
|
||||||
|
* public static void agentmain(String agentArgs)
|
||||||
|
* }</blockquote>
|
||||||
|
*
|
||||||
|
* <p> The value of the {@code agentArgs} parameter is always the empty string. In
|
||||||
|
* the first method, the {@code inst} parameter is an {@link Instrumentation} object
|
||||||
|
* that the agent can use to instrument code.
|
||||||
|
*
|
||||||
|
* <p> The {@code agentmain} method should do any necessary initialization
|
||||||
|
* required to start the agent and return. If the agent cannot be started, for
|
||||||
|
* example the agent class cannot be loaded, the agent class does not define a
|
||||||
|
* conformant {@code agentmain} method, or the {@code agentmain} method throws
|
||||||
|
* an uncaught exception or error, the JVM will abort before the application
|
||||||
|
* {@code main} method is invoked.
|
||||||
|
*
|
||||||
|
* <h3>Starting an agent from the command-line interface</h3>
|
||||||
|
*
|
||||||
|
* <p> Where an implementation provides a means to start agents from the command-line
|
||||||
|
* interface, an agent JAR is specified via the following command line option:
|
||||||
*
|
*
|
||||||
* <blockquote>{@code
|
* <blockquote>{@code
|
||||||
* -javaagent:<jarpath>[=<options>]
|
* -javaagent:<jarpath>[=<options>]
|
||||||
@ -71,40 +100,32 @@
|
|||||||
* where <i>{@code <jarpath>}</i> is the path to the agent JAR file and
|
* where <i>{@code <jarpath>}</i> is the path to the agent JAR file and
|
||||||
* <i>{@code <options>}</i> is the agent options.
|
* <i>{@code <options>}</i> is the agent options.
|
||||||
*
|
*
|
||||||
* <p> The manifest of the agent JAR file must contain the attribute {@code
|
* <p> The main manifest of the agent JAR file must contain the attribute {@code
|
||||||
* Premain-Class} in its main manifest. The value of this attribute is the
|
* Premain-Class}. The value of this attribute is the binary name of the agent class
|
||||||
* name of the <i>agent class</i>. The agent class must implement a public
|
* in the JAR file. The JVM starts the agent by loading the agent class and invoking its
|
||||||
* static {@code premain} method similar in principle to the {@code main}
|
* {@code premain} method. The method is invoked before the application {@code main}
|
||||||
* application entry point. After the Java Virtual Machine (JVM) has
|
* method is invoked. The {@code premain} method has one of two possible signatures.
|
||||||
* initialized, the {@code premain} method will be called, then the real
|
* The JVM first attempts to invoke the following method on the agent class:
|
||||||
* application {@code main} method. The {@code premain} method must return
|
|
||||||
* in order for the startup to proceed.
|
|
||||||
*
|
|
||||||
* <p> The {@code premain} method has one of two possible signatures. The
|
|
||||||
* JVM first attempts to invoke the following method on the agent class:
|
|
||||||
*
|
*
|
||||||
* <blockquote>{@code
|
* <blockquote>{@code
|
||||||
* public static void premain(String agentArgs, Instrumentation inst)
|
* public static void premain(String agentArgs, Instrumentation inst)
|
||||||
* }</blockquote>
|
* }</blockquote>
|
||||||
*
|
*
|
||||||
* <p> If the agent class does not implement this method then the JVM will
|
* <p> If the agent class does not define this method then the JVM will attempt to invoke:
|
||||||
* attempt to invoke:
|
|
||||||
* <blockquote>{@code
|
* <blockquote>{@code
|
||||||
* public static void premain(String agentArgs)
|
* public static void premain(String agentArgs)
|
||||||
* }</blockquote>
|
* }</blockquote>
|
||||||
|
|
||||||
* <p> The agent class may also have an {@code agentmain} method for use when
|
|
||||||
* the agent is started after VM startup (see below). When the agent is started
|
|
||||||
* using a command-line option, the {@code agentmain} method is not invoked.
|
|
||||||
*
|
*
|
||||||
* <p> Each agent is passed its agent options via the {@code agentArgs} parameter.
|
* <p> The agent is passed its agent options via the {@code agentArgs} parameter.
|
||||||
* The agent options are passed as a single string, any additional parsing
|
* The agent options are passed as a single string, any additional parsing
|
||||||
* should be performed by the agent itself.
|
* should be performed by the agent itself. In the first method, the {@code inst}
|
||||||
|
* parameter is an {@link Instrumentation} object that the agent can use to instrument
|
||||||
|
* code.
|
||||||
*
|
*
|
||||||
* <p> If the agent cannot be started (for example, because the agent class
|
* <p> If the agent cannot be started, for example the agent class cannot be loaded,
|
||||||
* cannot be loaded, or because the agent class does not have an appropriate
|
* the agent class does not define a conformant {@code premain} method, or the {@code
|
||||||
* {@code premain} method), the JVM will abort. If a {@code premain} method
|
* premain} method throws an uncaught exception or error, the JVM will abort before
|
||||||
* throws an uncaught exception, the JVM will abort.
|
* the application {@code main} method is invoked.
|
||||||
*
|
*
|
||||||
* <p> An implementation is not required to provide a way to start agents
|
* <p> An implementation is not required to provide a way to start agents
|
||||||
* from the command-line interface. When it does, then it supports the
|
* from the command-line interface. When it does, then it supports the
|
||||||
@ -114,52 +135,58 @@
|
|||||||
* agents are specified on the command line. More than one agent may use the
|
* agents are specified on the command line. More than one agent may use the
|
||||||
* same <i>{@code <jarpath>}</i>.
|
* same <i>{@code <jarpath>}</i>.
|
||||||
*
|
*
|
||||||
* <p> There are no modeling restrictions on what the agent {@code premain}
|
* <p> The agent class may also have an {@code agentmain} method for use when the agent
|
||||||
* method may do. Anything application {@code main} can do, including creating
|
* is started after in a running JVM (see below). When the agent is started using a
|
||||||
* threads, is legal from {@code premain}.
|
* command-line option, the {@code agentmain} method is not invoked.
|
||||||
*
|
*
|
||||||
|
* <h3>Starting an agent in a running JVM</h3>
|
||||||
*
|
*
|
||||||
* <h2>Starting an Agent After VM Startup</h2>
|
* <p> An implementation may provide a mechanism to start agents in a running JVM (meaning
|
||||||
*
|
* after JVM startup). The details as to how this is initiated are implementation specific
|
||||||
* <p> An implementation may provide a mechanism to start agents sometime after
|
* but typically the application has already started, and its {@code main} method has
|
||||||
* the VM has started. The details as to how this is initiated are
|
* already been invoked. Where an implementation supports starting an agent in a running
|
||||||
* implementation specific but typically the application has already started and
|
* JVM, the following applies:
|
||||||
* its {@code main} method has already been invoked. In cases where an
|
|
||||||
* implementation supports the starting of agents after the VM has started the
|
|
||||||
* following applies:
|
|
||||||
*
|
|
||||||
* <ol>
|
* <ol>
|
||||||
*
|
*
|
||||||
* <li><p> The manifest of the agent JAR must contain the attribute {@code
|
* <li><p> The agent class must be packaged into an agent JAR file. The main manifest
|
||||||
* Agent-Class} in its main manfiest. The value of this attribute is the name
|
* of the agent JAR file must contain the attribute {@code Agent-Class}. The value of
|
||||||
* of the <i>agent class</i>. </p></li>
|
* this attribute is the binary name of the agent class in the JAR file. </p></li>
|
||||||
*
|
*
|
||||||
* <li><p> The agent class must implement a public static {@code agentmain}
|
* <li><p> The agent class must define a public static {@code agentmain} method. </p></li>
|
||||||
* method. </p></li>
|
*
|
||||||
|
* <li><p> The JVM prints a warning on the standard error stream for each agent that it
|
||||||
|
* attempts to start in a running JVM. If an agent was previously started (at JVM
|
||||||
|
* startup, or started in a running JVM), then it is implementation specific as to whether
|
||||||
|
* a warning is printed when attempting to start the same agent a second or subsequent
|
||||||
|
* time. Warnings can be disabled by means of an implementation-specific command line
|
||||||
|
* option.
|
||||||
|
* <p><b>Implementation Note:</b> For the HotSpot VM, the JVM option
|
||||||
|
* {@code -XX:+EnableDynamicAgentLoading} is used to opt-in to allow dynamic loading of
|
||||||
|
* agents into a running JVM. This option suppresses the warning to standard error when
|
||||||
|
* starting an agent in a running JVM. </p></li>
|
||||||
*
|
*
|
||||||
* </ol>
|
* </ol>
|
||||||
*
|
*
|
||||||
* <p> The {@code agentmain} method has one of two possible signatures. The JVM
|
* <p> The JVM starts the agent by loading the agent class and invoking its {@code
|
||||||
* first attempts to invoke the following method on the agent class:
|
* agentmain} method. The {@code agentmain} method has one of two possible signatures.
|
||||||
|
* The JVM first attempts to invoke the following method on the agent class:
|
||||||
*
|
*
|
||||||
* <blockquote>{@code
|
* <blockquote>{@code
|
||||||
* public static void agentmain(String agentArgs, Instrumentation inst)
|
* public static void agentmain(String agentArgs, Instrumentation inst)
|
||||||
* }</blockquote>
|
* }</blockquote>
|
||||||
*
|
*
|
||||||
* <p> If the agent class does not implement this method then the JVM will
|
* <p> If the agent class does not define this method then the JVM will
|
||||||
* attempt to invoke:
|
* attempt to invoke:
|
||||||
*
|
*
|
||||||
* <blockquote>{@code
|
* <blockquote>{@code
|
||||||
* public static void agentmain(String agentArgs)
|
* public static void agentmain(String agentArgs)
|
||||||
* }</blockquote>
|
* }</blockquote>
|
||||||
*
|
*
|
||||||
* <p> The agent class may also have a {@code premain} method for use when the
|
* <p> The agent is passed its agent options via the {@code agentArgs} parameter.
|
||||||
* agent is started using a command-line option. When the agent is started after
|
* The agent options are passed as a single string, any additional parsing
|
||||||
* VM startup the {@code premain} method is not invoked.
|
* should be performed by the agent itself. In the first method, the {@code inst}
|
||||||
*
|
* parameter is an {@link Instrumentation} object that the agent can use to instrument
|
||||||
* <p> The agent is passed its agent options via the {@code agentArgs}
|
* code.
|
||||||
* parameter. The agent options are passed as a single string, any additional
|
|
||||||
* parsing should be performed by the agent itself.
|
|
||||||
*
|
*
|
||||||
* <p> The {@code agentmain} method should do any necessary initialization
|
* <p> The {@code agentmain} method should do any necessary initialization
|
||||||
* required to start the agent. When startup is complete the method should
|
* required to start the agent. When startup is complete the method should
|
||||||
@ -169,36 +196,9 @@
|
|||||||
* method throws an uncaught exception it will be ignored (but may be logged
|
* method throws an uncaught exception it will be ignored (but may be logged
|
||||||
* by the JVM for troubleshooting purposes).
|
* by the JVM for troubleshooting purposes).
|
||||||
*
|
*
|
||||||
*
|
* <p> The agent class may also have a {@code premain} method for use when the agent
|
||||||
* <h2>Including an Agent in an Executable JAR file</h2>
|
* is started using a command-line option. The {@code premain} method is not invoked
|
||||||
*
|
* when the agent is started in a running JVM.
|
||||||
* <p> The JAR File Specification defines manifest attributes for standalone
|
|
||||||
* applications that are packaged as <em>executable JAR files</em>. If an
|
|
||||||
* implementation supports a mechanism to start an application as an executable
|
|
||||||
* JAR then the main manifest may include the {@code Launcher-Agent-Class}
|
|
||||||
* attribute to specify the class name of an agent to start before the application
|
|
||||||
* {@code main} method is invoked. The Java virtual machine attempts to
|
|
||||||
* invoke the following method on the agent class:
|
|
||||||
*
|
|
||||||
* <blockquote>{@code
|
|
||||||
* public static void agentmain(String agentArgs, Instrumentation inst)
|
|
||||||
* }</blockquote>
|
|
||||||
*
|
|
||||||
* <p> If the agent class does not implement this method then the JVM will
|
|
||||||
* attempt to invoke:
|
|
||||||
*
|
|
||||||
* <blockquote>{@code
|
|
||||||
* public static void agentmain(String agentArgs)
|
|
||||||
* }</blockquote>
|
|
||||||
*
|
|
||||||
* <p> The value of the {@code agentArgs} parameter is always the empty string.
|
|
||||||
*
|
|
||||||
* <p> The {@code agentmain} method should do any necessary initialization
|
|
||||||
* required to start the agent and return. If the agent cannot be started, for
|
|
||||||
* example the agent class cannot be loaded, the agent class does not define a
|
|
||||||
* conformant {@code agentmain} method, or the {@code agentmain} method throws
|
|
||||||
* an uncaught exception or error, the JVM will abort.
|
|
||||||
*
|
|
||||||
*
|
*
|
||||||
* <h2> Loading agent classes and the modules/classes available to the agent
|
* <h2> Loading agent classes and the modules/classes available to the agent
|
||||||
* class </h2>
|
* class </h2>
|
||||||
@ -248,31 +248,33 @@
|
|||||||
* In other words, a custom system class loader must support the mechanism to
|
* In other words, a custom system class loader must support the mechanism to
|
||||||
* add an agent JAR file to the system class loader search.
|
* add an agent JAR file to the system class loader search.
|
||||||
*
|
*
|
||||||
* <h2>Manifest Attributes</h2>
|
* <h2>JAR File Manifest Attributes</h2>
|
||||||
*
|
*
|
||||||
* <p> The following manifest attributes are defined for an agent JAR file:
|
* <p> The following attributes in the main section of the application or agent
|
||||||
|
* JAR file manifest are defined for Java agents:
|
||||||
*
|
*
|
||||||
* <blockquote><dl>
|
* <blockquote><dl>
|
||||||
*
|
*
|
||||||
|
* <dt>{@code Launcher-Agent-Class}</dt>
|
||||||
|
* <dd> If an implementation supports a mechanism to start an application in an
|
||||||
|
* executable JAR file, then this attribute, if present, specifies the binary name
|
||||||
|
* of the agent class that is packaged with the application.
|
||||||
|
* The agent is started by invoking the agent class {@code agentmain} method. It is
|
||||||
|
* invoked before the application {@code main} method is invoked. </dd>
|
||||||
|
*
|
||||||
* <dt>{@code Premain-Class}</dt>
|
* <dt>{@code Premain-Class}</dt>
|
||||||
* <dd> When an agent is specified at JVM launch time this attribute specifies
|
* <dd> If an agent JAR is specified at JVM launch time, this attribute specifies
|
||||||
* the agent class. That is, the class containing the {@code premain} method.
|
* the binary name of the agent class in the JAR file.
|
||||||
* When an agent is specified at JVM launch time this attribute is required. If
|
* The agent is started by invoking the agent class {@code premain} method. It is
|
||||||
* the attribute is not present the JVM will abort. Note: this is a class name,
|
* invoked before the application {@code main} method is invoked.
|
||||||
* not a file name or path. </dd>
|
* If the attribute is not present the JVM will abort. </dd>
|
||||||
*
|
*
|
||||||
* <dt>{@code Agent-Class}</dt>
|
* <dt>{@code Agent-Class}</dt>
|
||||||
* <dd> If an implementation supports a mechanism to start agents sometime after
|
* <dd> If an implementation supports a mechanism to start an agent sometime after
|
||||||
* the VM has started then this attribute specifies the agent class. That is,
|
* the JVM has started, then this attribute specifies the binary name of the Java
|
||||||
* the class containing the {@code agentmain} method. This attribute is required
|
* agent class in the agent JAR file.
|
||||||
* if it is not present the agent will not be started. Note: this is a class name,
|
* The agent is started by invoking the agent class {@code agentmain} method.
|
||||||
* not a file name or path. </dd>
|
* This attribute is required; if not present the agent will not be started. </dd>
|
||||||
*
|
|
||||||
* <dt>{@code Launcher-Agent-Class}</dt>
|
|
||||||
* <dd> If an implementation supports a mechanism to start an application as an
|
|
||||||
* executable JAR then the main manifest may include this attribute to specify
|
|
||||||
* the class name of an agent to start before the application {@code main}
|
|
||||||
* method is invoked. </dd>
|
|
||||||
*
|
*
|
||||||
* <dt>{@code Boot-Class-Path}</dt>
|
* <dt>{@code Boot-Class-Path}</dt>
|
||||||
* <dd> A list of paths to be searched by the bootstrap class loader. Paths
|
* <dd> A list of paths to be searched by the bootstrap class loader. Paths
|
||||||
@ -284,7 +286,7 @@
|
|||||||
* URI. The path is absolute if it begins with a slash character ('/'), otherwise
|
* URI. The path is absolute if it begins with a slash character ('/'), otherwise
|
||||||
* it is relative. A relative path is resolved against the absolute path of the
|
* it is relative. A relative path is resolved against the absolute path of the
|
||||||
* agent JAR file. Malformed and non-existent paths are ignored. When an agent is
|
* agent JAR file. Malformed and non-existent paths are ignored. When an agent is
|
||||||
* started sometime after the VM has started then paths that do not represent a
|
* started sometime after the JVM has started then paths that do not represent a
|
||||||
* JAR file are ignored. This attribute is optional. </dd>
|
* JAR file are ignored. This attribute is optional. </dd>
|
||||||
*
|
*
|
||||||
* <dt>{@code Can-Redefine-Classes}</dt>
|
* <dt>{@code Can-Redefine-Classes}</dt>
|
||||||
@ -310,10 +312,10 @@
|
|||||||
* <p> An agent JAR file may have both the {@code Premain-Class} and {@code
|
* <p> An agent JAR file may have both the {@code Premain-Class} and {@code
|
||||||
* Agent-Class} attributes present in the manifest. When the agent is started
|
* Agent-Class} attributes present in the manifest. When the agent is started
|
||||||
* on the command-line using the {@code -javaagent} option then the {@code
|
* on the command-line using the {@code -javaagent} option then the {@code
|
||||||
* Premain-Class} attribute specifies the name of the agent class and the {@code
|
* Premain-Class} attribute specifies the binary name of the agent class and the {@code
|
||||||
* Agent-Class} attribute is ignored. Similarly, if the agent is started sometime
|
* Agent-Class} attribute is ignored. Similarly, if the agent is started sometime
|
||||||
* after the VM has started, then the {@code Agent-Class} attribute specifies
|
* after the JVM has started, then the {@code Agent-Class} attribute specifies
|
||||||
* the name of the agent class (the value of {@code Premain-Class} attribute is
|
* the binary name of the agent class (the value of {@code Premain-Class} attribute is
|
||||||
* ignored).
|
* ignored).
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -32,7 +32,12 @@ import java.lang.reflect.AccessibleObject;
|
|||||||
import java.lang.instrument.ClassFileTransformer;
|
import java.lang.instrument.ClassFileTransformer;
|
||||||
import java.lang.instrument.ClassDefinition;
|
import java.lang.instrument.ClassDefinition;
|
||||||
import java.lang.instrument.Instrumentation;
|
import java.lang.instrument.Instrumentation;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.InvalidPathException;
|
||||||
|
import java.net.URL;
|
||||||
import java.security.AccessController;
|
import java.security.AccessController;
|
||||||
|
import java.security.CodeSource;
|
||||||
import java.security.PrivilegedAction;
|
import java.security.PrivilegedAction;
|
||||||
import java.security.ProtectionDomain;
|
import java.security.ProtectionDomain;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -43,7 +48,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.jar.JarFile;
|
import java.util.jar.JarFile;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import jdk.internal.module.Modules;
|
import jdk.internal.module.Modules;
|
||||||
import jdk.internal.vm.annotation.IntrinsicCandidate;
|
import jdk.internal.vm.annotation.IntrinsicCandidate;
|
||||||
|
|
||||||
@ -59,6 +64,15 @@ import jdk.internal.vm.annotation.IntrinsicCandidate;
|
|||||||
* processing behind native methods.
|
* processing behind native methods.
|
||||||
*/
|
*/
|
||||||
public class InstrumentationImpl implements Instrumentation {
|
public class InstrumentationImpl implements Instrumentation {
|
||||||
|
private static final String TRACE_USAGE_PROP_NAME = "jdk.instrument.traceUsage";
|
||||||
|
private static final boolean TRACE_USAGE;
|
||||||
|
static {
|
||||||
|
PrivilegedAction<String> pa = () -> System.getProperty(TRACE_USAGE_PROP_NAME);
|
||||||
|
@SuppressWarnings("removal")
|
||||||
|
String s = AccessController.doPrivileged(pa);
|
||||||
|
TRACE_USAGE = (s != null) && (s.isEmpty() || Boolean.parseBoolean(s));
|
||||||
|
}
|
||||||
|
|
||||||
private final TransformerManager mTransformerManager;
|
private final TransformerManager mTransformerManager;
|
||||||
private TransformerManager mRetransfomableTransformerManager;
|
private TransformerManager mRetransfomableTransformerManager;
|
||||||
// needs to store a native pointer, so use 64 bits
|
// needs to store a native pointer, so use 64 bits
|
||||||
@ -71,7 +85,8 @@ public class InstrumentationImpl implements Instrumentation {
|
|||||||
private
|
private
|
||||||
InstrumentationImpl(long nativeAgent,
|
InstrumentationImpl(long nativeAgent,
|
||||||
boolean environmentSupportsRedefineClasses,
|
boolean environmentSupportsRedefineClasses,
|
||||||
boolean environmentSupportsNativeMethodPrefix) {
|
boolean environmentSupportsNativeMethodPrefix,
|
||||||
|
boolean printWarning) {
|
||||||
mTransformerManager = new TransformerManager(false);
|
mTransformerManager = new TransformerManager(false);
|
||||||
mRetransfomableTransformerManager = null;
|
mRetransfomableTransformerManager = null;
|
||||||
mNativeAgent = nativeAgent;
|
mNativeAgent = nativeAgent;
|
||||||
@ -79,60 +94,97 @@ public class InstrumentationImpl implements Instrumentation {
|
|||||||
mEnvironmentSupportsRetransformClassesKnown = false; // false = need to ask
|
mEnvironmentSupportsRetransformClassesKnown = false; // false = need to ask
|
||||||
mEnvironmentSupportsRetransformClasses = false; // don't know yet
|
mEnvironmentSupportsRetransformClasses = false; // don't know yet
|
||||||
mEnvironmentSupportsNativeMethodPrefix = environmentSupportsNativeMethodPrefix;
|
mEnvironmentSupportsNativeMethodPrefix = environmentSupportsNativeMethodPrefix;
|
||||||
|
|
||||||
|
if (printWarning) {
|
||||||
|
String source = jarFile(nativeAgent);
|
||||||
|
try {
|
||||||
|
Path path = Path.of(source);
|
||||||
|
PrivilegedAction<Path> pa = path::toAbsolutePath;
|
||||||
|
@SuppressWarnings("removal")
|
||||||
|
Path absolutePath = AccessController.doPrivileged(pa);
|
||||||
|
source = absolutePath.toString();
|
||||||
|
} catch (InvalidPathException e) {
|
||||||
|
// use original path
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("WARNING: A Java agent has been loaded dynamically (")
|
||||||
|
.append(source)
|
||||||
|
.append(")")
|
||||||
|
.append(System.lineSeparator());
|
||||||
|
sb.append("WARNING: If a serviceability tool is in use, please run with"
|
||||||
|
+ " -XX:+EnableDynamicAgentLoading to hide this warning")
|
||||||
|
.append(System.lineSeparator());
|
||||||
|
if (!TRACE_USAGE) {
|
||||||
|
sb.append("WARNING: If a serviceability tool is not in use, please run with"
|
||||||
|
+ " -D" + TRACE_USAGE_PROP_NAME + " for more information")
|
||||||
|
.append(System.lineSeparator());
|
||||||
|
}
|
||||||
|
sb.append("WARNING: Dynamic loading of agents will be disallowed by default in a future release");
|
||||||
|
String warningMessage = sb.toString();
|
||||||
|
System.err.println(warningMessage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
@Override
|
||||||
addTransformer(ClassFileTransformer transformer) {
|
public void addTransformer(ClassFileTransformer transformer) {
|
||||||
addTransformer(transformer, false);
|
addTransformer(transformer, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void
|
@Override
|
||||||
addTransformer(ClassFileTransformer transformer, boolean canRetransform) {
|
public void addTransformer(ClassFileTransformer transformer, boolean canRetransform) {
|
||||||
|
trace("addTransformer");
|
||||||
if (transformer == null) {
|
if (transformer == null) {
|
||||||
throw new NullPointerException("null passed as 'transformer' in addTransformer");
|
throw new NullPointerException("null passed as 'transformer' in addTransformer");
|
||||||
}
|
}
|
||||||
if (canRetransform) {
|
synchronized (this) {
|
||||||
if (!isRetransformClassesSupported()) {
|
if (canRetransform) {
|
||||||
throw new UnsupportedOperationException(
|
if (!isRetransformClassesSupported()) {
|
||||||
"adding retransformable transformers is not supported in this environment");
|
throw new UnsupportedOperationException(
|
||||||
}
|
"adding retransformable transformers is not supported in this environment");
|
||||||
if (mRetransfomableTransformerManager == null) {
|
}
|
||||||
mRetransfomableTransformerManager = new TransformerManager(true);
|
if (mRetransfomableTransformerManager == null) {
|
||||||
}
|
mRetransfomableTransformerManager = new TransformerManager(true);
|
||||||
mRetransfomableTransformerManager.addTransformer(transformer);
|
}
|
||||||
if (mRetransfomableTransformerManager.getTransformerCount() == 1) {
|
mRetransfomableTransformerManager.addTransformer(transformer);
|
||||||
setHasRetransformableTransformers(mNativeAgent, true);
|
if (mRetransfomableTransformerManager.getTransformerCount() == 1) {
|
||||||
}
|
setHasRetransformableTransformers(mNativeAgent, true);
|
||||||
} else {
|
}
|
||||||
mTransformerManager.addTransformer(transformer);
|
} else {
|
||||||
if (mTransformerManager.getTransformerCount() == 1) {
|
mTransformerManager.addTransformer(transformer);
|
||||||
setHasTransformers(mNativeAgent, true);
|
if (mTransformerManager.getTransformerCount() == 1) {
|
||||||
|
setHasTransformers(mNativeAgent, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean
|
@Override
|
||||||
removeTransformer(ClassFileTransformer transformer) {
|
public boolean removeTransformer(ClassFileTransformer transformer) {
|
||||||
|
trace("removeTransformer");
|
||||||
if (transformer == null) {
|
if (transformer == null) {
|
||||||
throw new NullPointerException("null passed as 'transformer' in removeTransformer");
|
throw new NullPointerException("null passed as 'transformer' in removeTransformer");
|
||||||
}
|
}
|
||||||
TransformerManager mgr = findTransformerManager(transformer);
|
synchronized (this) {
|
||||||
if (mgr != null) {
|
TransformerManager mgr = findTransformerManager(transformer);
|
||||||
mgr.removeTransformer(transformer);
|
if (mgr != null) {
|
||||||
if (mgr.getTransformerCount() == 0) {
|
mgr.removeTransformer(transformer);
|
||||||
if (mgr.isRetransformable()) {
|
if (mgr.getTransformerCount() == 0) {
|
||||||
setHasRetransformableTransformers(mNativeAgent, false);
|
if (mgr.isRetransformable()) {
|
||||||
} else {
|
setHasRetransformableTransformers(mNativeAgent, false);
|
||||||
setHasTransformers(mNativeAgent, false);
|
} else {
|
||||||
|
setHasTransformers(mNativeAgent, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean
|
@Override
|
||||||
isModifiableClass(Class<?> theClass) {
|
public boolean isModifiableClass(Class<?> theClass) {
|
||||||
|
trace("isModifiableClass");
|
||||||
if (theClass == null) {
|
if (theClass == null) {
|
||||||
throw new NullPointerException(
|
throw new NullPointerException(
|
||||||
"null passed as 'theClass' in isModifiableClass");
|
"null passed as 'theClass' in isModifiableClass");
|
||||||
@ -140,15 +192,18 @@ public class InstrumentationImpl implements Instrumentation {
|
|||||||
return isModifiableClass0(mNativeAgent, theClass);
|
return isModifiableClass0(mNativeAgent, theClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isModifiableModule(Module module) {
|
public boolean isModifiableModule(Module module) {
|
||||||
|
trace("isModifiableModule");
|
||||||
if (module == null) {
|
if (module == null) {
|
||||||
throw new NullPointerException("'module' is null");
|
throw new NullPointerException("'module' is null");
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean
|
@Override
|
||||||
isRetransformClassesSupported() {
|
public boolean isRetransformClassesSupported() {
|
||||||
|
trace("isRetransformClassesSupported");
|
||||||
// ask lazily since there is some overhead
|
// ask lazily since there is some overhead
|
||||||
if (!mEnvironmentSupportsRetransformClassesKnown) {
|
if (!mEnvironmentSupportsRetransformClassesKnown) {
|
||||||
mEnvironmentSupportsRetransformClasses = isRetransformClassesSupported0(mNativeAgent);
|
mEnvironmentSupportsRetransformClasses = isRetransformClassesSupported0(mNativeAgent);
|
||||||
@ -157,8 +212,9 @@ public class InstrumentationImpl implements Instrumentation {
|
|||||||
return mEnvironmentSupportsRetransformClasses;
|
return mEnvironmentSupportsRetransformClasses;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
@Override
|
||||||
retransformClasses(Class<?>... classes) {
|
public void retransformClasses(Class<?>... classes) {
|
||||||
|
trace("retransformClasses");
|
||||||
if (!isRetransformClassesSupported()) {
|
if (!isRetransformClassesSupported()) {
|
||||||
throw new UnsupportedOperationException(
|
throw new UnsupportedOperationException(
|
||||||
"retransformClasses is not supported in this environment");
|
"retransformClasses is not supported in this environment");
|
||||||
@ -169,14 +225,15 @@ public class InstrumentationImpl implements Instrumentation {
|
|||||||
retransformClasses0(mNativeAgent, classes);
|
retransformClasses0(mNativeAgent, classes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean
|
@Override
|
||||||
isRedefineClassesSupported() {
|
public boolean isRedefineClassesSupported() {
|
||||||
|
trace("isRedefineClassesSupported");
|
||||||
return mEnvironmentSupportsRedefineClasses;
|
return mEnvironmentSupportsRedefineClasses;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
@Override
|
||||||
redefineClasses(ClassDefinition... definitions)
|
public void redefineClasses(ClassDefinition... definitions) throws ClassNotFoundException {
|
||||||
throws ClassNotFoundException {
|
trace("retransformClasses");
|
||||||
if (!isRedefineClassesSupported()) {
|
if (!isRedefineClassesSupported()) {
|
||||||
throw new UnsupportedOperationException("redefineClasses is not supported in this environment");
|
throw new UnsupportedOperationException("redefineClasses is not supported in this environment");
|
||||||
}
|
}
|
||||||
@ -191,47 +248,53 @@ public class InstrumentationImpl implements Instrumentation {
|
|||||||
if (definitions.length == 0) {
|
if (definitions.length == 0) {
|
||||||
return; // short-circuit if there are no changes requested
|
return; // short-circuit if there are no changes requested
|
||||||
}
|
}
|
||||||
|
|
||||||
redefineClasses0(mNativeAgent, definitions);
|
redefineClasses0(mNativeAgent, definitions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
public Class[]
|
public Class[] getAllLoadedClasses() {
|
||||||
getAllLoadedClasses() {
|
trace("getAllLoadedClasses");
|
||||||
return getAllLoadedClasses0(mNativeAgent);
|
return getAllLoadedClasses0(mNativeAgent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
public Class[]
|
public Class[] getInitiatedClasses(ClassLoader loader) {
|
||||||
getInitiatedClasses(ClassLoader loader) {
|
trace("getInitiatedClasses");
|
||||||
return getInitiatedClasses0(mNativeAgent, loader);
|
return getInitiatedClasses0(mNativeAgent, loader);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long
|
@Override
|
||||||
getObjectSize(Object objectToSize) {
|
public long getObjectSize(Object objectToSize) {
|
||||||
|
trace("getObjectSize");
|
||||||
if (objectToSize == null) {
|
if (objectToSize == null) {
|
||||||
throw new NullPointerException("null passed as 'objectToSize' in getObjectSize");
|
throw new NullPointerException("null passed as 'objectToSize' in getObjectSize");
|
||||||
}
|
}
|
||||||
return getObjectSize0(mNativeAgent, objectToSize);
|
return getObjectSize0(mNativeAgent, objectToSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
@Override
|
||||||
appendToBootstrapClassLoaderSearch(JarFile jarfile) {
|
public void appendToBootstrapClassLoaderSearch(JarFile jarfile) {
|
||||||
|
trace("appendToBootstrapClassLoaderSearch");
|
||||||
appendToClassLoaderSearch0(mNativeAgent, jarfile.getName(), true);
|
appendToClassLoaderSearch0(mNativeAgent, jarfile.getName(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
@Override
|
||||||
appendToSystemClassLoaderSearch(JarFile jarfile) {
|
public void appendToSystemClassLoaderSearch(JarFile jarfile) {
|
||||||
|
trace("appendToSystemClassLoaderSearch");
|
||||||
appendToClassLoaderSearch0(mNativeAgent, jarfile.getName(), false);
|
appendToClassLoaderSearch0(mNativeAgent, jarfile.getName(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean
|
@Override
|
||||||
isNativeMethodPrefixSupported() {
|
public boolean isNativeMethodPrefixSupported() {
|
||||||
|
trace("isNativeMethodPrefixSupported");
|
||||||
return mEnvironmentSupportsNativeMethodPrefix;
|
return mEnvironmentSupportsNativeMethodPrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void
|
@Override
|
||||||
setNativeMethodPrefix(ClassFileTransformer transformer, String prefix) {
|
public void setNativeMethodPrefix(ClassFileTransformer transformer, String prefix) {
|
||||||
|
trace("setNativeMethodPrefix");
|
||||||
if (!isNativeMethodPrefixSupported()) {
|
if (!isNativeMethodPrefixSupported()) {
|
||||||
throw new UnsupportedOperationException(
|
throw new UnsupportedOperationException(
|
||||||
"setNativeMethodPrefix is not supported in this environment");
|
"setNativeMethodPrefix is not supported in this environment");
|
||||||
@ -240,14 +303,16 @@ public class InstrumentationImpl implements Instrumentation {
|
|||||||
throw new NullPointerException(
|
throw new NullPointerException(
|
||||||
"null passed as 'transformer' in setNativeMethodPrefix");
|
"null passed as 'transformer' in setNativeMethodPrefix");
|
||||||
}
|
}
|
||||||
TransformerManager mgr = findTransformerManager(transformer);
|
synchronized (this) {
|
||||||
if (mgr == null) {
|
TransformerManager mgr = findTransformerManager(transformer);
|
||||||
throw new IllegalArgumentException(
|
if (mgr == null) {
|
||||||
"transformer not registered in setNativeMethodPrefix");
|
throw new IllegalArgumentException(
|
||||||
|
"transformer not registered in setNativeMethodPrefix");
|
||||||
|
}
|
||||||
|
mgr.setNativeMethodPrefix(transformer, prefix);
|
||||||
|
String[] prefixes = mgr.getNativeMethodPrefixes();
|
||||||
|
setNativeMethodPrefixes(mNativeAgent, prefixes, mgr.isRetransformable());
|
||||||
}
|
}
|
||||||
mgr.setNativeMethodPrefix(transformer, prefix);
|
|
||||||
String[] prefixes = mgr.getNativeMethodPrefixes();
|
|
||||||
setNativeMethodPrefixes(mNativeAgent, prefixes, mgr.isRetransformable());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -258,6 +323,8 @@ public class InstrumentationImpl implements Instrumentation {
|
|||||||
Set<Class<?>> extraUses,
|
Set<Class<?>> extraUses,
|
||||||
Map<Class<?>, List<Class<?>>> extraProvides)
|
Map<Class<?>, List<Class<?>>> extraProvides)
|
||||||
{
|
{
|
||||||
|
trace("redefineModule");
|
||||||
|
|
||||||
if (!module.isNamed())
|
if (!module.isNamed())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -297,7 +364,6 @@ public class InstrumentationImpl implements Instrumentation {
|
|||||||
}
|
}
|
||||||
extraProvides = tmpProvides;
|
extraProvides = tmpProvides;
|
||||||
|
|
||||||
|
|
||||||
// update reads
|
// update reads
|
||||||
extraReads.forEach(m -> Modules.addReads(module, m));
|
extraReads.forEach(m -> Modules.addReads(module, m));
|
||||||
|
|
||||||
@ -351,8 +417,8 @@ public class InstrumentationImpl implements Instrumentation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private TransformerManager
|
private TransformerManager findTransformerManager(ClassFileTransformer transformer) {
|
||||||
findTransformerManager(ClassFileTransformer transformer) {
|
assert Thread.holdsLock(this);
|
||||||
if (mTransformerManager.includesTransformer(transformer)) {
|
if (mTransformerManager.includesTransformer(transformer)) {
|
||||||
return mTransformerManager;
|
return mTransformerManager;
|
||||||
}
|
}
|
||||||
@ -367,6 +433,9 @@ public class InstrumentationImpl implements Instrumentation {
|
|||||||
/*
|
/*
|
||||||
* Natives
|
* Natives
|
||||||
*/
|
*/
|
||||||
|
private native
|
||||||
|
String jarFile(long nativeAgent);
|
||||||
|
|
||||||
private native boolean
|
private native boolean
|
||||||
isModifiableClass0(long nativeAgent, Class<?> theClass);
|
isModifiableClass0(long nativeAgent, Class<?> theClass);
|
||||||
|
|
||||||
@ -557,4 +626,61 @@ public class InstrumentationImpl implements Instrumentation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static native void loadAgent0(String path);
|
private static native void loadAgent0(String path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints a trace message and stack trace when tracing is enabled.
|
||||||
|
*/
|
||||||
|
private void trace(String methodName) {
|
||||||
|
if (!TRACE_USAGE) return;
|
||||||
|
|
||||||
|
// stack trace without frames in java.instrument module
|
||||||
|
List<StackWalker.StackFrame> stack = HolderStackWalker.walker.walk(s ->
|
||||||
|
s.dropWhile(f -> f.getDeclaringClass().getModule() == Instrumentation.class.getModule())
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
|
||||||
|
// for tracing purposes, use the direct caller to code in java.instrument as the source
|
||||||
|
if (stack.size() > 0) {
|
||||||
|
Class<?> callerClass = stack.get(0).getDeclaringClass();
|
||||||
|
URL callerUrl = codeSource(callerClass);
|
||||||
|
String source;
|
||||||
|
if (callerUrl == null) {
|
||||||
|
source = callerClass.getName();
|
||||||
|
} else {
|
||||||
|
source = callerClass.getName() + " (" + callerUrl + ")";
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("java.lang.instrument.Instrumentation.")
|
||||||
|
.append(methodName)
|
||||||
|
.append(" has been called by ")
|
||||||
|
.append(source);
|
||||||
|
stack.forEach(f -> sb.append(System.lineSeparator()).append("\tat " + f));
|
||||||
|
String traceMessage = sb.toString();
|
||||||
|
System.out.println(traceMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the possibly-bnull code source of the given class.
|
||||||
|
*/
|
||||||
|
private static URL codeSource(Class<?> clazz) {
|
||||||
|
PrivilegedAction<ProtectionDomain> pa = clazz::getProtectionDomain;
|
||||||
|
@SuppressWarnings("removal")
|
||||||
|
CodeSource cs = AccessController.doPrivileged(pa).getCodeSource();
|
||||||
|
return (cs != null) ? cs.getLocation() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holder for StackWalker object.
|
||||||
|
*/
|
||||||
|
private static class HolderStackWalker {
|
||||||
|
static final StackWalker walker;
|
||||||
|
static {
|
||||||
|
PrivilegedAction<StackWalker> pa = () ->
|
||||||
|
StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
|
||||||
|
@SuppressWarnings("removal")
|
||||||
|
StackWalker w = AccessController.doPrivileged(pa);
|
||||||
|
walker = w;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -54,10 +54,22 @@
|
|||||||
*/
|
*/
|
||||||
DEF_STATIC_JNI_OnLoad
|
DEF_STATIC_JNI_OnLoad
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: sun_instrument_InstrumentationImpl
|
||||||
|
* Method: jarFile
|
||||||
|
* Signature: (J)Ljava/lang/String;
|
||||||
|
*/
|
||||||
|
JNIEXPORT jstring JNICALL
|
||||||
|
Java_sun_instrument_InstrumentationImpl_jarFile
|
||||||
|
(JNIEnv * jnienv, jobject implThis, jlong agent) {
|
||||||
|
return jarFile(jnienv, (JPLISAgent*)(intptr_t)agent);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Class: sun_instrument_InstrumentationImpl
|
* Class: sun_instrument_InstrumentationImpl
|
||||||
* Method: isModifiableClass0
|
* Method: isModifiableClass0
|
||||||
* Signature: (Ljava/lang/Class;)Z
|
* Signature: (JLjava/lang/Class;)Z
|
||||||
*/
|
*/
|
||||||
JNIEXPORT jboolean JNICALL
|
JNIEXPORT jboolean JNICALL
|
||||||
Java_sun_instrument_InstrumentationImpl_isModifiableClass0
|
Java_sun_instrument_InstrumentationImpl_isModifiableClass0
|
||||||
@ -68,7 +80,7 @@ Java_sun_instrument_InstrumentationImpl_isModifiableClass0
|
|||||||
/*
|
/*
|
||||||
* Class: sun_instrument_InstrumentationImpl
|
* Class: sun_instrument_InstrumentationImpl
|
||||||
* Method: isRetransformClassesSupported0
|
* Method: isRetransformClassesSupported0
|
||||||
* Signature: ()Z
|
* Signature: (J)Z
|
||||||
*/
|
*/
|
||||||
JNIEXPORT jboolean JNICALL
|
JNIEXPORT jboolean JNICALL
|
||||||
Java_sun_instrument_InstrumentationImpl_isRetransformClassesSupported0
|
Java_sun_instrument_InstrumentationImpl_isRetransformClassesSupported0
|
||||||
@ -79,7 +91,7 @@ Java_sun_instrument_InstrumentationImpl_isRetransformClassesSupported0
|
|||||||
/*
|
/*
|
||||||
* Class: sun_instrument_InstrumentationImpl
|
* Class: sun_instrument_InstrumentationImpl
|
||||||
* Method: setHasTransformers
|
* Method: setHasTransformers
|
||||||
* Signature: (Z)V
|
* Signature: (JZ)V
|
||||||
*/
|
*/
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_sun_instrument_InstrumentationImpl_setHasTransformers
|
Java_sun_instrument_InstrumentationImpl_setHasTransformers
|
||||||
@ -90,7 +102,7 @@ Java_sun_instrument_InstrumentationImpl_setHasTransformers
|
|||||||
/*
|
/*
|
||||||
* Class: sun_instrument_InstrumentationImpl
|
* Class: sun_instrument_InstrumentationImpl
|
||||||
* Method: setHasRetransformableTransformers
|
* Method: setHasRetransformableTransformers
|
||||||
* Signature: (Z)V
|
* Signature: (JZ)V
|
||||||
*/
|
*/
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_sun_instrument_InstrumentationImpl_setHasRetransformableTransformers
|
Java_sun_instrument_InstrumentationImpl_setHasRetransformableTransformers
|
||||||
@ -101,7 +113,7 @@ Java_sun_instrument_InstrumentationImpl_setHasRetransformableTransformers
|
|||||||
/*
|
/*
|
||||||
* Class: sun_instrument_InstrumentationImpl
|
* Class: sun_instrument_InstrumentationImpl
|
||||||
* Method: retransformClasses0
|
* Method: retransformClasses0
|
||||||
* Signature: ([Ljava/lang/Class;)V
|
* Signature: (J[Ljava/lang/Class;)V
|
||||||
*/
|
*/
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_sun_instrument_InstrumentationImpl_retransformClasses0
|
Java_sun_instrument_InstrumentationImpl_retransformClasses0
|
||||||
@ -112,7 +124,7 @@ Java_sun_instrument_InstrumentationImpl_retransformClasses0
|
|||||||
/*
|
/*
|
||||||
* Class: sun_instrument_InstrumentationImpl
|
* Class: sun_instrument_InstrumentationImpl
|
||||||
* Method: redefineClasses0
|
* Method: redefineClasses0
|
||||||
* Signature: ([Ljava/lang/instrument/ClassDefinition;)V
|
* Signature: (J[Ljava/lang/instrument/ClassDefinition;)V
|
||||||
*/
|
*/
|
||||||
JNIEXPORT void JNICALL Java_sun_instrument_InstrumentationImpl_redefineClasses0
|
JNIEXPORT void JNICALL Java_sun_instrument_InstrumentationImpl_redefineClasses0
|
||||||
(JNIEnv * jnienv, jobject implThis, jlong agent, jobjectArray classDefinitions) {
|
(JNIEnv * jnienv, jobject implThis, jlong agent, jobjectArray classDefinitions) {
|
||||||
@ -122,7 +134,7 @@ JNIEXPORT void JNICALL Java_sun_instrument_InstrumentationImpl_redefineClasses0
|
|||||||
/*
|
/*
|
||||||
* Class: sun_instrument_InstrumentationImpl
|
* Class: sun_instrument_InstrumentationImpl
|
||||||
* Method: getAllLoadedClasses0
|
* Method: getAllLoadedClasses0
|
||||||
* Signature: ()[Ljava/lang/Class;
|
* Signature: (J)[Ljava/lang/Class;
|
||||||
*/
|
*/
|
||||||
JNIEXPORT jobjectArray JNICALL Java_sun_instrument_InstrumentationImpl_getAllLoadedClasses0
|
JNIEXPORT jobjectArray JNICALL Java_sun_instrument_InstrumentationImpl_getAllLoadedClasses0
|
||||||
(JNIEnv * jnienv, jobject implThis, jlong agent) {
|
(JNIEnv * jnienv, jobject implThis, jlong agent) {
|
||||||
@ -132,7 +144,7 @@ JNIEXPORT jobjectArray JNICALL Java_sun_instrument_InstrumentationImpl_getAllLoa
|
|||||||
/*
|
/*
|
||||||
* Class: sun_instrument_InstrumentationImpl
|
* Class: sun_instrument_InstrumentationImpl
|
||||||
* Method: getInitiatedClasses0
|
* Method: getInitiatedClasses0
|
||||||
* Signature: (Ljava/lang/ClassLoader;)[Ljava/lang/Class;
|
* Signature: (JLjava/lang/ClassLoader;)[Ljava/lang/Class;
|
||||||
*/
|
*/
|
||||||
JNIEXPORT jobjectArray JNICALL Java_sun_instrument_InstrumentationImpl_getInitiatedClasses0
|
JNIEXPORT jobjectArray JNICALL Java_sun_instrument_InstrumentationImpl_getInitiatedClasses0
|
||||||
(JNIEnv * jnienv, jobject implThis, jlong agent, jobject classLoader) {
|
(JNIEnv * jnienv, jobject implThis, jlong agent, jobject classLoader) {
|
||||||
@ -142,7 +154,7 @@ JNIEXPORT jobjectArray JNICALL Java_sun_instrument_InstrumentationImpl_getInitia
|
|||||||
/*
|
/*
|
||||||
* Class: sun_instrument_InstrumentationImpl
|
* Class: sun_instrument_InstrumentationImpl
|
||||||
* Method: getObjectSize0
|
* Method: getObjectSize0
|
||||||
* Signature: (Ljava/lang/Object;)J
|
* Signature: (JLjava/lang/Object;)J
|
||||||
*/
|
*/
|
||||||
JNIEXPORT jlong JNICALL Java_sun_instrument_InstrumentationImpl_getObjectSize0
|
JNIEXPORT jlong JNICALL Java_sun_instrument_InstrumentationImpl_getObjectSize0
|
||||||
(JNIEnv * jnienv, jobject implThis, jlong agent, jobject objectToSize) {
|
(JNIEnv * jnienv, jobject implThis, jlong agent, jobject objectToSize) {
|
||||||
@ -153,7 +165,7 @@ JNIEXPORT jlong JNICALL Java_sun_instrument_InstrumentationImpl_getObjectSize0
|
|||||||
/*
|
/*
|
||||||
* Class: sun_instrument_InstrumentationImpl
|
* Class: sun_instrument_InstrumentationImpl
|
||||||
* Method: appendToClassLoaderSearch0
|
* Method: appendToClassLoaderSearch0
|
||||||
* Signature: (Ljava/lang/String;Z)V
|
* Signature: (JLjava/lang/String;Z)V
|
||||||
*/
|
*/
|
||||||
JNIEXPORT void JNICALL Java_sun_instrument_InstrumentationImpl_appendToClassLoaderSearch0
|
JNIEXPORT void JNICALL Java_sun_instrument_InstrumentationImpl_appendToClassLoaderSearch0
|
||||||
(JNIEnv * jnienv, jobject implThis, jlong agent, jstring jarFile, jboolean isBootLoader) {
|
(JNIEnv * jnienv, jobject implThis, jlong agent, jstring jarFile, jboolean isBootLoader) {
|
||||||
@ -164,7 +176,7 @@ JNIEXPORT void JNICALL Java_sun_instrument_InstrumentationImpl_appendToClassLoad
|
|||||||
/*
|
/*
|
||||||
* Class: sun_instrument_InstrumentationImpl
|
* Class: sun_instrument_InstrumentationImpl
|
||||||
* Method: setNativeMethodPrefixes
|
* Method: setNativeMethodPrefixes
|
||||||
* Signature: ([Ljava/lang/String;Z)V
|
* Signature: (J[Ljava/lang/String;Z)V
|
||||||
*/
|
*/
|
||||||
JNIEXPORT void JNICALL Java_sun_instrument_InstrumentationImpl_setNativeMethodPrefixes
|
JNIEXPORT void JNICALL Java_sun_instrument_InstrumentationImpl_setNativeMethodPrefixes
|
||||||
(JNIEnv * jnienv, jobject implThis, jlong agent, jobjectArray prefixArray, jboolean isRetransformable) {
|
(JNIEnv * jnienv, jobject implThis, jlong agent, jobjectArray prefixArray, jboolean isRetransformable) {
|
||||||
|
@ -147,24 +147,24 @@ DEF_Agent_OnLoad(JavaVM *vm, char *tail, void * reserved) {
|
|||||||
JPLISInitializationError initerror = JPLIS_INIT_ERROR_NONE;
|
JPLISInitializationError initerror = JPLIS_INIT_ERROR_NONE;
|
||||||
jint result = JNI_OK;
|
jint result = JNI_OK;
|
||||||
JPLISAgent * agent = NULL;
|
JPLISAgent * agent = NULL;
|
||||||
|
char * jarfile = NULL;
|
||||||
|
char * options = NULL;
|
||||||
|
|
||||||
initerror = createNewJPLISAgent(vm, &agent);
|
/*
|
||||||
|
* Parse <jarfile>[=options] into jarfile and options
|
||||||
|
*/
|
||||||
|
if (parseArgumentTail(tail, &jarfile, &options) != 0) {
|
||||||
|
fprintf(stderr, "-javaagent: memory allocation failure.\n");
|
||||||
|
return JNI_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
initerror = createNewJPLISAgent(vm, &agent, jarfile, JNI_FALSE);
|
||||||
if ( initerror == JPLIS_INIT_ERROR_NONE ) {
|
if ( initerror == JPLIS_INIT_ERROR_NONE ) {
|
||||||
int oldLen, newLen;
|
int oldLen, newLen;
|
||||||
char * jarfile;
|
|
||||||
char * options;
|
|
||||||
jarAttribute* attributes;
|
jarAttribute* attributes;
|
||||||
char * premainClass;
|
char * premainClass;
|
||||||
char * bootClassPath;
|
char * bootClassPath;
|
||||||
|
|
||||||
/*
|
|
||||||
* Parse <jarfile>[=options] into jarfile and options
|
|
||||||
*/
|
|
||||||
if (parseArgumentTail(tail, &jarfile, &options) != 0) {
|
|
||||||
fprintf(stderr, "-javaagent: memory allocation failure.\n");
|
|
||||||
return JNI_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Agent_OnLoad is specified to provide the agent options
|
* Agent_OnLoad is specified to provide the agent options
|
||||||
* argument tail in modified UTF8. However for 1.5.0 this is
|
* argument tail in modified UTF8. However for 1.5.0 this is
|
||||||
@ -192,9 +192,6 @@ DEF_Agent_OnLoad(JavaVM *vm, char *tail, void * reserved) {
|
|||||||
return JNI_ERR;
|
return JNI_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Save the jarfile name */
|
|
||||||
agent->mJarfile = jarfile;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The value of the Premain-Class attribute becomes the agent
|
* The value of the Premain-Class attribute becomes the agent
|
||||||
* class name. The manifest is in UTF8 so need to convert to
|
* class name. The manifest is in UTF8 so need to convert to
|
||||||
@ -254,11 +251,15 @@ DEF_Agent_OnLoad(JavaVM *vm, char *tail, void * reserved) {
|
|||||||
/*
|
/*
|
||||||
* Clean-up
|
* Clean-up
|
||||||
*/
|
*/
|
||||||
if (options != NULL) free(options);
|
|
||||||
freeAttributes(attributes);
|
freeAttributes(attributes);
|
||||||
free(premainClass);
|
free(premainClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (initerror != JPLIS_INIT_ERROR_NONE) {
|
||||||
|
free(jarfile);
|
||||||
|
}
|
||||||
|
if (options != NULL) free(options);
|
||||||
|
|
||||||
switch (initerror) {
|
switch (initerror) {
|
||||||
case JPLIS_INIT_ERROR_NONE:
|
case JPLIS_INIT_ERROR_NONE:
|
||||||
result = JNI_OK;
|
result = JNI_OK;
|
||||||
@ -307,6 +308,8 @@ DEF_Agent_OnAttach(JavaVM* vm, char *args, void * reserved) {
|
|||||||
jint result = JNI_OK;
|
jint result = JNI_OK;
|
||||||
JPLISAgent * agent = NULL;
|
JPLISAgent * agent = NULL;
|
||||||
JNIEnv * jni_env = NULL;
|
JNIEnv * jni_env = NULL;
|
||||||
|
char * jarfile = NULL;
|
||||||
|
char * options = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Need JNIEnv - guaranteed to be called from thread that is already
|
* Need JNIEnv - guaranteed to be called from thread that is already
|
||||||
@ -315,23 +318,22 @@ DEF_Agent_OnAttach(JavaVM* vm, char *args, void * reserved) {
|
|||||||
result = (*vm)->GetEnv(vm, (void**)&jni_env, JNI_VERSION_1_2);
|
result = (*vm)->GetEnv(vm, (void**)&jni_env, JNI_VERSION_1_2);
|
||||||
jplis_assert(result==JNI_OK);
|
jplis_assert(result==JNI_OK);
|
||||||
|
|
||||||
initerror = createNewJPLISAgent(vm, &agent);
|
/*
|
||||||
|
* Parse <jarfile>[=options] into jarfile and options
|
||||||
|
*/
|
||||||
|
if (parseArgumentTail(args, &jarfile, &options) != 0) {
|
||||||
|
return JNI_ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
jboolean print_warning = JVM_PrintWarningAtDynamicAgentLoad();
|
||||||
|
initerror = createNewJPLISAgent(vm, &agent, jarfile, print_warning);
|
||||||
if ( initerror == JPLIS_INIT_ERROR_NONE ) {
|
if ( initerror == JPLIS_INIT_ERROR_NONE ) {
|
||||||
int oldLen, newLen;
|
int oldLen, newLen;
|
||||||
char * jarfile;
|
|
||||||
char * options;
|
|
||||||
jarAttribute* attributes;
|
jarAttribute* attributes;
|
||||||
char * agentClass;
|
char * agentClass;
|
||||||
char * bootClassPath;
|
char * bootClassPath;
|
||||||
jboolean success;
|
jboolean success;
|
||||||
|
|
||||||
/*
|
|
||||||
* Parse <jarfile>[=options] into jarfile and options
|
|
||||||
*/
|
|
||||||
if (parseArgumentTail(args, &jarfile, &options) != 0) {
|
|
||||||
return JNI_ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Open the JAR file and parse the manifest
|
* Open the JAR file and parse the manifest
|
||||||
*/
|
*/
|
||||||
@ -450,12 +452,15 @@ DEF_Agent_OnAttach(JavaVM* vm, char *args, void * reserved) {
|
|||||||
/*
|
/*
|
||||||
* Clean-up
|
* Clean-up
|
||||||
*/
|
*/
|
||||||
free(jarfile);
|
|
||||||
if (options != NULL) free(options);
|
|
||||||
free(agentClass);
|
free(agentClass);
|
||||||
freeAttributes(attributes);
|
freeAttributes(attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (initerror != JPLIS_INIT_ERROR_NONE || result != JNI_OK) {
|
||||||
|
free(jarfile);
|
||||||
|
}
|
||||||
|
if (options != NULL) free(options);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -486,17 +491,18 @@ jint loadAgent(JNIEnv* env, jstring path) {
|
|||||||
return JNI_ERR;
|
return JNI_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// create JPLISAgent with JVMTI environment
|
|
||||||
if (createNewJPLISAgent(vm, &agent) != JPLIS_INIT_ERROR_NONE) {
|
|
||||||
return JNI_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get path to JAR file as UTF-8 string
|
// get path to JAR file as UTF-8 string
|
||||||
jarfile = (*env)->GetStringUTFChars(env, path, NULL);
|
jarfile = (*env)->GetStringUTFChars(env, path, NULL);
|
||||||
if (jarfile == NULL) {
|
if (jarfile == NULL) {
|
||||||
return JNI_ERR;
|
return JNI_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create JPLISAgent with JVMTI environment
|
||||||
|
if (createNewJPLISAgent(vm, &agent, jarfile, JNI_FALSE) != JPLIS_INIT_ERROR_NONE) {
|
||||||
|
(*env)->ReleaseStringUTFChars(env, path, jarfile);
|
||||||
|
return JNI_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
// read the attributes in the main section of JAR manifest
|
// read the attributes in the main section of JAR manifest
|
||||||
attributes = readAttributes(jarfile);
|
attributes = readAttributes(jarfile);
|
||||||
if (attributes == NULL) {
|
if (attributes == NULL) {
|
||||||
@ -570,7 +576,7 @@ releaseAndReturn:
|
|||||||
if (attributes != NULL) {
|
if (attributes != NULL) {
|
||||||
freeAttributes(attributes);
|
freeAttributes(attributes);
|
||||||
}
|
}
|
||||||
if (jarfile != NULL) {
|
if (result != JNI_OK && jarfile != NULL) {
|
||||||
(*env)->ReleaseStringUTFChars(env, path, jarfile);
|
(*env)->ReleaseStringUTFChars(env, path, jarfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -612,8 +618,6 @@ eventHandlerVMInit( jvmtiEnv * jvmtienv,
|
|||||||
free((void *)agent->mJarfile);
|
free((void *)agent->mJarfile);
|
||||||
abortJVM(jnienv, JPLIS_ERRORMESSAGE_CANNOTSTART ", appending to system class path failed");
|
abortJVM(jnienv, JPLIS_ERRORMESSAGE_CANNOTSTART ", appending to system class path failed");
|
||||||
}
|
}
|
||||||
free((void *)agent->mJarfile);
|
|
||||||
agent->mJarfile = NULL;
|
|
||||||
|
|
||||||
outstandingException = preserveThrowable(jnienv);
|
outstandingException = preserveThrowable(jnienv);
|
||||||
success = processJavaStart( environment->mAgent, jnienv);
|
success = processJavaStart( environment->mAgent, jnienv);
|
||||||
|
@ -63,7 +63,9 @@ allocateJPLISAgent(jvmtiEnv * jvmtiEnv);
|
|||||||
JPLISInitializationError
|
JPLISInitializationError
|
||||||
initializeJPLISAgent( JPLISAgent * agent,
|
initializeJPLISAgent( JPLISAgent * agent,
|
||||||
JavaVM * vm,
|
JavaVM * vm,
|
||||||
jvmtiEnv * jvmtienv);
|
jvmtiEnv * jvmtienv,
|
||||||
|
const char * jarfile,
|
||||||
|
jboolean printWarning);
|
||||||
/* De-allocates a JPLIS agent data structure. Only used in partial-failure cases at startup;
|
/* De-allocates a JPLIS agent data structure. Only used in partial-failure cases at startup;
|
||||||
* in normal usage the JPLIS agent lives forever
|
* in normal usage the JPLIS agent lives forever
|
||||||
*/
|
*/
|
||||||
@ -202,7 +204,7 @@ getJPLISEnvironment(jvmtiEnv * jvmtienv) {
|
|||||||
* or NULL if an error has occurred.
|
* or NULL if an error has occurred.
|
||||||
*/
|
*/
|
||||||
JPLISInitializationError
|
JPLISInitializationError
|
||||||
createNewJPLISAgent(JavaVM * vm, JPLISAgent **agent_ptr) {
|
createNewJPLISAgent(JavaVM * vm, JPLISAgent **agent_ptr, const char * jarfile, jboolean printWarning) {
|
||||||
JPLISInitializationError initerror = JPLIS_INIT_ERROR_NONE;
|
JPLISInitializationError initerror = JPLIS_INIT_ERROR_NONE;
|
||||||
jvmtiEnv * jvmtienv = NULL;
|
jvmtiEnv * jvmtienv = NULL;
|
||||||
jint jnierror = JNI_OK;
|
jint jnierror = JNI_OK;
|
||||||
@ -220,7 +222,9 @@ createNewJPLISAgent(JavaVM * vm, JPLISAgent **agent_ptr) {
|
|||||||
} else {
|
} else {
|
||||||
initerror = initializeJPLISAgent( agent,
|
initerror = initializeJPLISAgent( agent,
|
||||||
vm,
|
vm,
|
||||||
jvmtienv);
|
jvmtienv,
|
||||||
|
jarfile,
|
||||||
|
printWarning);
|
||||||
if ( initerror == JPLIS_INIT_ERROR_NONE ) {
|
if ( initerror == JPLIS_INIT_ERROR_NONE ) {
|
||||||
*agent_ptr = agent;
|
*agent_ptr = agent;
|
||||||
} else {
|
} else {
|
||||||
@ -251,7 +255,9 @@ allocateJPLISAgent(jvmtiEnv * jvmtienv) {
|
|||||||
JPLISInitializationError
|
JPLISInitializationError
|
||||||
initializeJPLISAgent( JPLISAgent * agent,
|
initializeJPLISAgent( JPLISAgent * agent,
|
||||||
JavaVM * vm,
|
JavaVM * vm,
|
||||||
jvmtiEnv * jvmtienv) {
|
jvmtiEnv * jvmtienv,
|
||||||
|
const char * jarfile,
|
||||||
|
jboolean printWarning) {
|
||||||
jvmtiError jvmtierror = JVMTI_ERROR_NONE;
|
jvmtiError jvmtierror = JVMTI_ERROR_NONE;
|
||||||
jvmtiPhase phase;
|
jvmtiPhase phase;
|
||||||
|
|
||||||
@ -272,7 +278,8 @@ initializeJPLISAgent( JPLISAgent * agent,
|
|||||||
agent->mNativeMethodPrefixAdded = JNI_FALSE;
|
agent->mNativeMethodPrefixAdded = JNI_FALSE;
|
||||||
agent->mAgentClassName = NULL;
|
agent->mAgentClassName = NULL;
|
||||||
agent->mOptionsString = NULL;
|
agent->mOptionsString = NULL;
|
||||||
agent->mJarfile = NULL;
|
agent->mJarfile = jarfile;
|
||||||
|
agent->mPrintWarning = printWarning;
|
||||||
|
|
||||||
/* make sure we can recover either handle in either direction.
|
/* make sure we can recover either handle in either direction.
|
||||||
* the agent has a ref to the jvmti; make it mutual
|
* the agent has a ref to the jvmti; make it mutual
|
||||||
@ -512,7 +519,8 @@ createInstrumentationImpl( JNIEnv * jnienv,
|
|||||||
constructorID,
|
constructorID,
|
||||||
peerReferenceAsScalar,
|
peerReferenceAsScalar,
|
||||||
agent->mRedefineAdded,
|
agent->mRedefineAdded,
|
||||||
agent->mNativeMethodPrefixAdded);
|
agent->mNativeMethodPrefixAdded,
|
||||||
|
agent->mPrintWarning);
|
||||||
errorOutstanding = checkForAndClearThrowable(jnienv);
|
errorOutstanding = checkForAndClearThrowable(jnienv);
|
||||||
errorOutstanding = errorOutstanding || (localReference == NULL);
|
errorOutstanding = errorOutstanding || (localReference == NULL);
|
||||||
jplis_assert_msg(!errorOutstanding, "call constructor on InstrumentationImpl failed");
|
jplis_assert_msg(!errorOutstanding, "call constructor on InstrumentationImpl failed");
|
||||||
@ -1605,3 +1613,8 @@ setNativeMethodPrefixes(JNIEnv * jnienv, JPLISAgent * agent, jobjectArray prefix
|
|||||||
deallocate(jvmtienv, (void*)originForRelease);
|
deallocate(jvmtienv, (void*)originForRelease);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jstring
|
||||||
|
jarFile(JNIEnv * jnienv, JPLISAgent * agent) {
|
||||||
|
return (*jnienv)->NewStringUTF(jnienv, agent->mJarfile);
|
||||||
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -59,7 +59,7 @@ typedef struct _JPLISEnvironment JPLISEnvironment;
|
|||||||
*/
|
*/
|
||||||
#define JPLIS_INSTRUMENTIMPL_CLASSNAME "sun/instrument/InstrumentationImpl"
|
#define JPLIS_INSTRUMENTIMPL_CLASSNAME "sun/instrument/InstrumentationImpl"
|
||||||
#define JPLIS_INSTRUMENTIMPL_CONSTRUCTOR_METHODNAME "<init>"
|
#define JPLIS_INSTRUMENTIMPL_CONSTRUCTOR_METHODNAME "<init>"
|
||||||
#define JPLIS_INSTRUMENTIMPL_CONSTRUCTOR_METHODSIGNATURE "(JZZ)V"
|
#define JPLIS_INSTRUMENTIMPL_CONSTRUCTOR_METHODSIGNATURE "(JZZZ)V"
|
||||||
#define JPLIS_INSTRUMENTIMPL_PREMAININVOKER_METHODNAME "loadClassAndCallPremain"
|
#define JPLIS_INSTRUMENTIMPL_PREMAININVOKER_METHODNAME "loadClassAndCallPremain"
|
||||||
#define JPLIS_INSTRUMENTIMPL_PREMAININVOKER_METHODSIGNATURE "(Ljava/lang/String;Ljava/lang/String;)V"
|
#define JPLIS_INSTRUMENTIMPL_PREMAININVOKER_METHODSIGNATURE "(Ljava/lang/String;Ljava/lang/String;)V"
|
||||||
#define JPLIS_INSTRUMENTIMPL_AGENTMAININVOKER_METHODNAME "loadClassAndCallAgentmain"
|
#define JPLIS_INSTRUMENTIMPL_AGENTMAININVOKER_METHODNAME "loadClassAndCallAgentmain"
|
||||||
@ -108,6 +108,7 @@ struct _JPLISAgent {
|
|||||||
char const * mAgentClassName; /* agent class name */
|
char const * mAgentClassName; /* agent class name */
|
||||||
char const * mOptionsString; /* -javaagent options string */
|
char const * mOptionsString; /* -javaagent options string */
|
||||||
const char * mJarfile; /* agent jar file name */
|
const char * mJarfile; /* agent jar file name */
|
||||||
|
jboolean mPrintWarning; /* print warning when started */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -151,7 +152,7 @@ getJPLISEnvironment(jvmtiEnv * jvmtienv);
|
|||||||
* or NULL if an error has occurred.
|
* or NULL if an error has occurred.
|
||||||
*/
|
*/
|
||||||
extern JPLISInitializationError
|
extern JPLISInitializationError
|
||||||
createNewJPLISAgent(JavaVM * vm, JPLISAgent **agent_ptr);
|
createNewJPLISAgent(JavaVM * vm, JPLISAgent **agent_ptr, const char * jarfile, jboolean printWarning);
|
||||||
|
|
||||||
/* Adds can_redefine_classes capability */
|
/* Adds can_redefine_classes capability */
|
||||||
extern void
|
extern void
|
||||||
@ -272,6 +273,9 @@ extern void
|
|||||||
setNativeMethodPrefixes(JNIEnv * jnienv, JPLISAgent * agent, jobjectArray prefixArray,
|
setNativeMethodPrefixes(JNIEnv * jnienv, JPLISAgent * agent, jobjectArray prefixArray,
|
||||||
jboolean isRetransformable);
|
jboolean isRetransformable);
|
||||||
|
|
||||||
|
extern jstring
|
||||||
|
jarFile(JNIEnv * jnienv, JPLISAgent * agent);
|
||||||
|
|
||||||
#define jvmti(a) a->mNormalEnvironment.mJVMTIEnv
|
#define jvmti(a) a->mNormalEnvironment.mJVMTIEnv
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -70,6 +70,7 @@ requires.properties= \
|
|||||||
vm.hasSA \
|
vm.hasSA \
|
||||||
vm.hasJFR \
|
vm.hasJFR \
|
||||||
vm.jvmci \
|
vm.jvmci \
|
||||||
|
vm.jvmti \
|
||||||
docker.support \
|
docker.support \
|
||||||
release.implementor \
|
release.implementor \
|
||||||
jdk.containerized \
|
jdk.containerized \
|
||||||
|
48
test/jdk/com/sun/tools/attach/warnings/Application.java
Normal file
48
test/jdk/com/sun/tools/attach/warnings/Application.java
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "application" launched by DyamicLoadWarningTest.
|
||||||
|
*
|
||||||
|
* The application phones home, sends its pid to the test, waits for a reply, then exits.
|
||||||
|
*/
|
||||||
|
public class Application {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
InetAddress lh = InetAddress.getLoopbackAddress();
|
||||||
|
int port = Integer.parseInt(args[0]);
|
||||||
|
try (Socket s = new Socket(lh, port);
|
||||||
|
DataOutputStream out = new DataOutputStream(s.getOutputStream())) {
|
||||||
|
|
||||||
|
// send pid
|
||||||
|
long pid = ProcessHandle.current().pid();
|
||||||
|
out.writeLong(pid);
|
||||||
|
|
||||||
|
// wait for shutdown
|
||||||
|
s.getInputStream().read();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,308 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @bug 8307478
|
||||||
|
* @summary Test that a warning is printed when an agent is dynamically loaded
|
||||||
|
* @requires vm.jvmti
|
||||||
|
* @modules jdk.attach jdk.jcmd
|
||||||
|
* @library /test/lib /test/jdk
|
||||||
|
* @build Application JavaAgent
|
||||||
|
* @run junit/othervm/native DynamicLoadWarningTest
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.jar.Attributes;
|
||||||
|
import java.util.jar.Manifest;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import com.sun.tools.attach.VirtualMachine;
|
||||||
|
|
||||||
|
import jdk.test.lib.JDKToolLauncher;
|
||||||
|
import jdk.test.lib.Platform;
|
||||||
|
import jdk.test.lib.Utils;
|
||||||
|
import jdk.test.lib.process.ProcessTools;
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
import jdk.test.lib.util.JarUtils;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class DynamicLoadWarningTest {
|
||||||
|
private static final String JVMTI_AGENT_WARNING = "WARNING: A JVM TI agent has been loaded dynamically";
|
||||||
|
private static final String JAVA_AGENT_WARNING = "WARNING: A Java agent has been loaded dynamically";
|
||||||
|
|
||||||
|
// JVM TI agents
|
||||||
|
private static final String JVMTI_AGENT1_LIB = "JvmtiAgent1";
|
||||||
|
private static final String JVMTI_AGENT2_LIB = "JvmtiAgent2";
|
||||||
|
private static String jvmtiAgentPath1;
|
||||||
|
private static String jvmtiAgentPath2;
|
||||||
|
|
||||||
|
// Java agent
|
||||||
|
private static final String TEST_CLASSES = System.getProperty("test.classes");
|
||||||
|
private static String javaAgent;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void setup() throws Exception {
|
||||||
|
// get absolute path to JVM TI agents
|
||||||
|
String prefix = Platform.isWindows() ? "" : "lib";
|
||||||
|
String libname1 = prefix + JVMTI_AGENT1_LIB + "." + Platform.sharedLibraryExt();
|
||||||
|
String libname2 = prefix + JVMTI_AGENT2_LIB + "." + Platform.sharedLibraryExt();
|
||||||
|
jvmtiAgentPath1 = Path.of(Utils.TEST_NATIVE_PATH, libname1).toAbsolutePath().toString();
|
||||||
|
jvmtiAgentPath2 = Path.of(Utils.TEST_NATIVE_PATH, libname2).toAbsolutePath().toString();
|
||||||
|
|
||||||
|
// create JAR file with Java agent
|
||||||
|
Manifest man = new Manifest();
|
||||||
|
Attributes attrs = man.getMainAttributes();
|
||||||
|
attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0");
|
||||||
|
attrs.put(new Attributes.Name("Agent-Class"), "JavaAgent");
|
||||||
|
Path jarfile = Path.of("javaagent.jar");
|
||||||
|
Path classes = Path.of(TEST_CLASSES);
|
||||||
|
JarUtils.createJarFile(jarfile, man, classes, Path.of("JavaAgent.class"));
|
||||||
|
javaAgent = jarfile.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actions to load JvmtiAgent1 into a running VM.
|
||||||
|
*/
|
||||||
|
private static Stream<OnAttachAction> loadJvmtiAgent1() {
|
||||||
|
// load agent with the attach API
|
||||||
|
OnAttachAction loadJvmtiAgent = (pid, vm) -> vm.loadAgentLibrary(JVMTI_AGENT1_LIB);
|
||||||
|
|
||||||
|
// jcmd <pid> JVMTI.agent_load <agent>
|
||||||
|
OnAttachAction jcmdAgentLoad = jcmdAgentLoad(jvmtiAgentPath1);
|
||||||
|
|
||||||
|
return Stream.of(loadJvmtiAgent, jcmdAgentLoad);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test loading JvmtiAgent1 into a running VM.
|
||||||
|
*/
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("loadJvmtiAgent1")
|
||||||
|
void testLoadOneJvmtiAgent(OnAttachAction loadJvmtiAgent1) throws Exception {
|
||||||
|
// dynamically load loadJvmtiAgent1
|
||||||
|
test().whenRunning(loadJvmtiAgent1)
|
||||||
|
.stderrShouldContain(JVMTI_AGENT_WARNING);
|
||||||
|
|
||||||
|
// dynamically load loadJvmtiAgent1 twice, should be one warning
|
||||||
|
test().whenRunning(loadJvmtiAgent1)
|
||||||
|
.whenRunning(loadJvmtiAgent1)
|
||||||
|
.stderrShouldContain(JVMTI_AGENT_WARNING, 1);
|
||||||
|
|
||||||
|
// opt-in via command line option to allow dynamic loading of agents
|
||||||
|
test().withOpts("-XX:+EnableDynamicAgentLoading")
|
||||||
|
.whenRunning(loadJvmtiAgent1)
|
||||||
|
.stderrShouldNotContain(JVMTI_AGENT_WARNING);
|
||||||
|
|
||||||
|
// start loadJvmtiAgent1 via the command line, then dynamically load loadJvmtiAgent1
|
||||||
|
test().withOpts("-agentpath:" + jvmtiAgentPath1)
|
||||||
|
.whenRunning(loadJvmtiAgent1)
|
||||||
|
.stderrShouldNotContain(JVMTI_AGENT_WARNING);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test loading JvmtiAgent1 and JvmtiAgent2 into a running VM.
|
||||||
|
*/
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("loadJvmtiAgent1")
|
||||||
|
void testLoadTwoJvmtiAgents(OnAttachAction loadJvmtiAgent1) throws Exception {
|
||||||
|
OnAttachAction loadJvmtiAgent2 = (pid, vm) -> vm.loadAgentLibrary(JVMTI_AGENT2_LIB);
|
||||||
|
OnAttachAction jcmdAgentLoad2 = jcmdAgentLoad(jvmtiAgentPath2);
|
||||||
|
|
||||||
|
// dynamically load loadJvmtiAgent1, then dynamically load loadJvmtiAgent2 with attach API
|
||||||
|
test().whenRunning(loadJvmtiAgent1)
|
||||||
|
.whenRunning(loadJvmtiAgent2)
|
||||||
|
.stderrShouldContain(JVMTI_AGENT_WARNING, 2);
|
||||||
|
|
||||||
|
// dynamically load loadJvmtiAgent1, then dynamically load loadJvmtiAgent2 with jcmd
|
||||||
|
test().whenRunning(loadJvmtiAgent1)
|
||||||
|
.whenRunning(jcmdAgentLoad2)
|
||||||
|
.stderrShouldContain(JVMTI_AGENT_WARNING, 2);
|
||||||
|
|
||||||
|
// start loadJvmtiAgent2 via the command line, then dynamically load loadJvmtiAgent1
|
||||||
|
test().withOpts("-agentpath:" + jvmtiAgentPath2)
|
||||||
|
.whenRunning(loadJvmtiAgent1)
|
||||||
|
.stderrShouldContain(JVMTI_AGENT_WARNING);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test loading Java agent into a running VM.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void testLoadJavaAgent() throws Exception {
|
||||||
|
OnAttachAction loadJavaAgent = (pid, vm) -> vm.loadAgent(javaAgent);
|
||||||
|
|
||||||
|
// agent dynamically loaded
|
||||||
|
test().whenRunning(loadJavaAgent)
|
||||||
|
.stderrShouldContain(JAVA_AGENT_WARNING);
|
||||||
|
|
||||||
|
// opt-in via command line option to allow dynamic loading of agents
|
||||||
|
test().withOpts("-XX:+EnableDynamicAgentLoading")
|
||||||
|
.whenRunning(loadJavaAgent)
|
||||||
|
.stderrShouldNotContain(JAVA_AGENT_WARNING);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an operation that accepts a process identifier and a VirtualMachine
|
||||||
|
* that the current JVM is attached to.
|
||||||
|
*/
|
||||||
|
private interface OnAttachAction {
|
||||||
|
void accept(long pid, VirtualMachine vm) throws Exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an operation that invokes "jcmd <pid> JVMTI.agent_load <agentpath>" to
|
||||||
|
* load the given agent library into the JVM that the current JVM is attached to.
|
||||||
|
*/
|
||||||
|
private static OnAttachAction jcmdAgentLoad(String agentPath) {
|
||||||
|
return (pid, vm) -> {
|
||||||
|
String[] jcmd = JDKToolLauncher.createUsingTestJDK("jcmd")
|
||||||
|
.addToolArg(Long.toString(pid))
|
||||||
|
.addToolArg("JVMTI.agent_load")
|
||||||
|
.addToolArg(agentPath)
|
||||||
|
.getCommand();
|
||||||
|
System.out.println(Arrays.stream(jcmd).collect(Collectors.joining(" ")));
|
||||||
|
Process p = new ProcessBuilder(jcmd).inheritIO().start();
|
||||||
|
assertEquals(0, p.waitFor());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new app runner.
|
||||||
|
*/
|
||||||
|
private static AppRunner test() {
|
||||||
|
return new AppRunner();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs an application in its own VM. Once started, it attachs to the VM, runs a set
|
||||||
|
* of actions, then checks that the output contains, or does not contain, a string.
|
||||||
|
*/
|
||||||
|
private static class AppRunner {
|
||||||
|
private String[] vmopts = new String[0];
|
||||||
|
private List<OnAttachAction> actions = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies VM options to run the application.
|
||||||
|
*/
|
||||||
|
AppRunner withOpts(String... vmopts) {
|
||||||
|
this.vmopts = vmopts;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies an action to run when the attached to the running application.
|
||||||
|
*/
|
||||||
|
AppRunner whenRunning(OnAttachAction action) {
|
||||||
|
actions.add(action);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputAnalyzer run() throws Exception {
|
||||||
|
// start a listener socket that the application will connect to
|
||||||
|
try (ServerSocket listener = new ServerSocket()) {
|
||||||
|
InetAddress lh = InetAddress.getLoopbackAddress();
|
||||||
|
listener.bind(new InetSocketAddress(lh, 0));
|
||||||
|
|
||||||
|
var done = new AtomicBoolean();
|
||||||
|
|
||||||
|
// start a thread to wait for the application to phone home
|
||||||
|
Thread.ofPlatform().daemon().start(() -> {
|
||||||
|
try (Socket s = listener.accept();
|
||||||
|
DataInputStream in = new DataInputStream(s.getInputStream())) {
|
||||||
|
|
||||||
|
// read pid
|
||||||
|
long pid = in.readLong();
|
||||||
|
|
||||||
|
// attach and run the actions with the vm object
|
||||||
|
VirtualMachine vm = VirtualMachine.attach(Long.toString(pid));
|
||||||
|
try {
|
||||||
|
for (OnAttachAction action : actions) {
|
||||||
|
action.accept(pid, vm);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
vm.detach();
|
||||||
|
}
|
||||||
|
done.set(true);
|
||||||
|
|
||||||
|
// shutdown
|
||||||
|
s.getOutputStream().write(0);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// launch application with the given VM options, waiting for it to terminate
|
||||||
|
Stream<String> s1 = Stream.of(vmopts);
|
||||||
|
Stream<String> s2 = Stream.of("Application", Integer.toString(listener.getLocalPort()));
|
||||||
|
String[] opts = Stream.concat(s1, s2).toArray(String[]::new);
|
||||||
|
OutputAnalyzer outputAnalyzer = ProcessTools
|
||||||
|
.executeTestJava(opts)
|
||||||
|
.outputTo(System.out)
|
||||||
|
.errorTo(System.out);
|
||||||
|
assertEquals(0, outputAnalyzer.getExitValue());
|
||||||
|
assertTrue(done.get(), "Attach or action failed, see log for details");
|
||||||
|
return outputAnalyzer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the application, checking that standard error contains a string.
|
||||||
|
*/
|
||||||
|
void stderrShouldContain(String s) throws Exception {
|
||||||
|
run().stderrShouldContain(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the application, checking that standard error contains the given number of
|
||||||
|
* occurrences of a string.
|
||||||
|
*/
|
||||||
|
void stderrShouldContain(String s, int occurrences) throws Exception {
|
||||||
|
List<String> lines = run().asLines();
|
||||||
|
int count = (int) lines.stream().filter(line -> line.indexOf(s) >= 0).count();
|
||||||
|
assertEquals(occurrences, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the application, checking that standard error does not contain a string.
|
||||||
|
*/
|
||||||
|
void stderrShouldNotContain(String s) throws Exception {
|
||||||
|
run().stderrShouldNotContain(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
test/jdk/com/sun/tools/attach/warnings/JavaAgent.java
Normal file
30
test/jdk/com/sun/tools/attach/warnings/JavaAgent.java
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A no-op Java agent.
|
||||||
|
*/
|
||||||
|
public class JavaAgent {
|
||||||
|
public static void agentmain(String args) {
|
||||||
|
}
|
||||||
|
}
|
40
test/jdk/com/sun/tools/attach/warnings/libJvmtiAgent1.cpp
Normal file
40
test/jdk/com/sun/tools/attach/warnings/libJvmtiAgent1.cpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* 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 "jvmti.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A no-op JVM TI agent.
|
||||||
|
*/
|
||||||
|
|
||||||
|
JNIEXPORT jint JNICALL
|
||||||
|
Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {
|
||||||
|
jvmtiEnv* jvmti;
|
||||||
|
return vm->GetEnv((void**)&jvmti, JVMTI_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jint JNICALL
|
||||||
|
Agent_OnAttach(JavaVM *vm, char *options, void *reserved) {
|
||||||
|
jvmtiEnv* jvmti;
|
||||||
|
return vm->GetEnv((void**)&jvmti, JVMTI_VERSION);
|
||||||
|
}
|
40
test/jdk/com/sun/tools/attach/warnings/libJvmtiAgent2.cpp
Normal file
40
test/jdk/com/sun/tools/attach/warnings/libJvmtiAgent2.cpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* 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 "jvmti.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A no-op JVM TI agent.
|
||||||
|
*/
|
||||||
|
|
||||||
|
JNIEXPORT jint JNICALL
|
||||||
|
Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {
|
||||||
|
jvmtiEnv* jvmti;
|
||||||
|
return vm->GetEnv((void**)&jvmti, JVMTI_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jint JNICALL
|
||||||
|
Agent_OnAttach(JavaVM *vm, char *options, void *reserved) {
|
||||||
|
jvmtiEnv* jvmti;
|
||||||
|
return vm->GetEnv((void**)&jvmti, JVMTI_VERSION);
|
||||||
|
}
|
62
test/jdk/java/lang/instrument/TraceUsageAgent.java
Normal file
62
test/jdk/java/lang/instrument/TraceUsageAgent.java
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.lang.instrument.ClassFileTransformer;
|
||||||
|
import java.lang.instrument.Instrumentation;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Agent used by TraceUsageTest. The premain and agentmain methods invoke Instrumentation
|
||||||
|
* methods so the usages can be traced by the test.
|
||||||
|
*/
|
||||||
|
public class TraceUsageAgent {
|
||||||
|
public static void premain(String methodNames, Instrumentation inst) throws Exception {
|
||||||
|
test(methodNames, inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void agentmain(String methodNames, Instrumentation inst) throws Exception {
|
||||||
|
test(methodNames, inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void test(String methodNames, Instrumentation inst) throws Exception {
|
||||||
|
for (String methodName : methodNames.split(",")) {
|
||||||
|
switch (methodName) {
|
||||||
|
case "addTransformer" -> {
|
||||||
|
var transformer = new ClassFileTransformer() { };
|
||||||
|
inst.addTransformer(transformer);
|
||||||
|
}
|
||||||
|
case "retransformClasses" -> {
|
||||||
|
inst.retransformClasses(Object.class);
|
||||||
|
}
|
||||||
|
case "redefineModule" -> {
|
||||||
|
Module base = Object.class.getModule();
|
||||||
|
inst.redefineModule(base, Set.of(), Map.of(), Map.of(), Set.of(), Map.of());
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
throw new RuntimeException("Unknown method name: " + methodName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
111
test/jdk/java/lang/instrument/TraceUsageTest.java
Normal file
111
test/jdk/java/lang/instrument/TraceUsageTest.java
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @bug 8307478
|
||||||
|
* @summary Test Instrumentation tracing is enabled with a system property
|
||||||
|
* @library /test/lib
|
||||||
|
* @run shell MakeJAR3.sh TraceUsageAgent 'Agent-Class: TraceUsageAgent' 'Can-Retransform-Classes: true'
|
||||||
|
* @run junit TraceUsageTest
|
||||||
|
*/
|
||||||
|
|
||||||
|
import com.sun.tools.attach.VirtualMachine;
|
||||||
|
|
||||||
|
import jdk.test.lib.process.ProcessTools;
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class TraceUsageTest {
|
||||||
|
private static final String JAVA_AGENT = "TraceUsageAgent.jar";
|
||||||
|
|
||||||
|
// Instrumentation methods to test
|
||||||
|
private static final String[] INSTRUMENTATION_METHODS = {
|
||||||
|
"addTransformer",
|
||||||
|
"retransformClasses",
|
||||||
|
"redefineModule"
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If launched with the argument "attach" then it loads the java agent into the
|
||||||
|
* current VM with the given options.
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
if (args.length > 0 && args[0].equals("attach")) {
|
||||||
|
String options = args[1];
|
||||||
|
long pid = ProcessHandle.current().pid();
|
||||||
|
VirtualMachine vm = VirtualMachine.attach(""+pid);
|
||||||
|
try {
|
||||||
|
vm.loadAgent(JAVA_AGENT, options);
|
||||||
|
} finally {
|
||||||
|
vm.detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test agent started on the command line with -javaagent.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void testPremain() throws Exception {
|
||||||
|
OutputAnalyzer outputAnalyzer = execute(
|
||||||
|
"-javaagent:" + JAVA_AGENT + "=" + String.join(",", INSTRUMENTATION_METHODS),
|
||||||
|
"-Djdk.instrument.traceUsage=true",
|
||||||
|
"TraceUsageTest"
|
||||||
|
);
|
||||||
|
for (String mn : INSTRUMENTATION_METHODS) {
|
||||||
|
String expected = "Instrumentation." + mn + " has been called by TraceUsageAgent";
|
||||||
|
outputAnalyzer.shouldContain(expected);
|
||||||
|
}
|
||||||
|
outputAnalyzer.shouldContain("at TraceUsageAgent.premain");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test agent loaded into a running VM with the attach mechanism.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void testAgentmain() throws Exception {
|
||||||
|
OutputAnalyzer outputAnalyzer = execute(
|
||||||
|
"-Djdk.attach.allowAttachSelf=true",
|
||||||
|
"-Djdk.instrument.traceUsage=true",
|
||||||
|
"TraceUsageTest",
|
||||||
|
"attach",
|
||||||
|
String.join(",", INSTRUMENTATION_METHODS)
|
||||||
|
);
|
||||||
|
for (String mn : INSTRUMENTATION_METHODS) {
|
||||||
|
String expected = "Instrumentation." + mn + " has been called by TraceUsageAgent";
|
||||||
|
outputAnalyzer.shouldContain(expected);
|
||||||
|
}
|
||||||
|
outputAnalyzer.shouldContain("at TraceUsageAgent.agentmain");
|
||||||
|
}
|
||||||
|
|
||||||
|
private OutputAnalyzer execute(String... command) throws Exception {
|
||||||
|
OutputAnalyzer outputAnalyzer = ProcessTools.executeTestJava(command)
|
||||||
|
.outputTo(System.out)
|
||||||
|
.errorTo(System.out);
|
||||||
|
assertEquals(0, outputAnalyzer.getExitValue());
|
||||||
|
return outputAnalyzer;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user