From c8ab4b68590161f14245182115e722b9cf734be4 Mon Sep 17 00:00:00 2001 From: Jason Feng Date: Wed, 9 Aug 2023 21:24:23 -0400 Subject: [PATCH] Implement JEP 451: Prepare to Disallow the Dynamic Loading of Agents Print warning message when loading an agent via AttachAPI loadAgentLibraryImpl() if -XX:+EnableDynamicAgentLoading is not specified, and the agent wasn't loaded before; Added isAgentLibraryLoaded() to determine if an agent has been loaded already; Implemented JVM_PrintWarningAtDynamicAgentLoad(); Added jcmd command JVMTI.agent_load; Updated a few trace points within loadAgentLibraryGeneric(). Signed-off-by: Jason Feng --- .../tools/attach/target/DiagnosticUtils.java | 40 ++++++++++-- runtime/j9vm/javanextvmi.cpp | 13 ++-- runtime/jvmti/j9jvmti.tdf | 15 +++-- runtime/jvmti/jvmtiStartup.c | 64 ++++++++++--------- runtime/oti/j9nonbuilder.h | 1 + runtime/vm/BytecodeInterpreter.hpp | 39 +++++++---- 6 files changed, 119 insertions(+), 53 deletions(-) diff --git a/jcl/src/java.base/share/classes/openj9/internal/tools/attach/target/DiagnosticUtils.java b/jcl/src/java.base/share/classes/openj9/internal/tools/attach/target/DiagnosticUtils.java index fded086fda3..f136dacaefb 100644 --- a/jcl/src/java.base/share/classes/openj9/internal/tools/attach/target/DiagnosticUtils.java +++ b/jcl/src/java.base/share/classes/openj9/internal/tools/attach/target/DiagnosticUtils.java @@ -46,7 +46,7 @@ public class DiagnosticUtils { private static final String FORMAT_PREFIX = " Format: "; //$NON-NLS-1$ @SuppressWarnings("nls") - private static final String HEAP_DUMP_OPTION_HELP = " [request=] [opts=]%n" + private static final String HEAP_DUMP_OPTION_HELP = " [request=] [opts=] []%n" + " Set optional request= and opts= -Xdump options. The order of the parameters does not matter.%n"; @SuppressWarnings("nls") @@ -100,7 +100,10 @@ public class DiagnosticUtils { * Get JVM statistics */ private static final String DIAGNOSTICS_STAT_CLASS = "jstat.class"; //$NON-NLS-1$ - + + // load JVMTI agent + private static final String DIAGNOSTICS_LOAD_JVMTI_AGENT = "JVMTI.agent_load"; //$NON-NLS-1$ + /** * Key for the command sent to executeDiagnosticCommand() */ @@ -354,7 +357,27 @@ private static DiagnosticProperties getJstatClass(String diagnosticCommand) { bufferPrinter.flush(); return DiagnosticProperties.makeStringResult(buffer.toString()); } - + + @SuppressWarnings("nls") + private static DiagnosticProperties loadJVMTIAgent(String diagnosticCommand) { + DiagnosticProperties result; + String[] parts = diagnosticCommand.split(DIAGNOSTICS_OPTION_SEPARATOR); + // parts[0] is already verified as DIAGNOSTICS_LOAD_JVMTI_AGENT since we are here + if (parts.length < 2) { + result = DiagnosticProperties.makeErrorProperties("Too few arguments, the absolute path of the agent is required: " + diagnosticCommand); + } else if (parts.length > 3) { + result = DiagnosticProperties.makeErrorProperties("Command not recognized due to more than 3 arguments: " + diagnosticCommand); + } else { + String attachError = Attachment.loadAgentLibrary(parts[1], (parts.length == 3) ? parts[2] : "", false); + if (attachError == null) { + result = DiagnosticProperties.makeStringResult(DIAGNOSTICS_LOAD_JVMTI_AGENT + " succeeded"); + } else { + result = DiagnosticProperties.makeStatusProperties(true, attachError); + } + } + return result; + } + private static DiagnosticProperties doHelp(String diagnosticCommand) { String[] parts = diagnosticCommand.split(DIAGNOSTICS_OPTION_SEPARATOR); /* print a list of the available commands */ @@ -416,7 +439,13 @@ private static DiagnosticProperties doHelp(String diagnosticCommand) { private static final String DIAGNOSTICS_JSTAT_CLASS_HELP = "Show JVM classloader statistics.%n" //$NON-NLS-1$ + FORMAT_PREFIX + DIAGNOSTICS_STAT_CLASS + "%n" //$NON-NLS-1$ + "NOTE: this utility might significantly affect the performance of the target VM.%n"; //$NON-NLS-1$ - + + @SuppressWarnings("nls") + private static final String DIAGNOSTICS_LOAD_JVMTI_AGENT_HELP = "Load JVMTI agent.%n" + + FORMAT_PREFIX + DIAGNOSTICS_LOAD_JVMTI_AGENT + " []%n" + + " agentLibrary: the absolute path of the agent%n" + + " agent option: (Optional) the agent option string%n"; + /* Initialize the command and help text tables */ static { commandTable = new HashMap<>(); @@ -451,5 +480,8 @@ private static DiagnosticProperties doHelp(String diagnosticCommand) { commandTable.put(DIAGNOSTICS_STAT_CLASS, DiagnosticUtils::getJstatClass); helpTable.put(DIAGNOSTICS_STAT_CLASS, DIAGNOSTICS_JSTAT_CLASS_HELP); + + commandTable.put(DIAGNOSTICS_LOAD_JVMTI_AGENT, DiagnosticUtils::loadJVMTIAgent); + helpTable.put(DIAGNOSTICS_LOAD_JVMTI_AGENT, DIAGNOSTICS_LOAD_JVMTI_AGENT_HELP); } } diff --git a/runtime/j9vm/javanextvmi.cpp b/runtime/j9vm/javanextvmi.cpp index b73cb829c6a..1d501b1de3f 100644 --- a/runtime/j9vm/javanextvmi.cpp +++ b/runtime/j9vm/javanextvmi.cpp @@ -24,6 +24,7 @@ #include "bcverify_api.h" #include "j9.h" #include "j9cfg.h" +#include "jvminit.h" #include "rommeth.h" #include "ut_j9scar.h" #include "util_api.h" @@ -548,10 +549,14 @@ JVM_VirtualThreadHideFrames(JNIEnv *env, jobject vthread, jboolean hide) JNIEXPORT jboolean JNICALL JVM_PrintWarningAtDynamicAgentLoad() { - /* A temporary implementation, an actual solution is provided via - * https://github.com/eclipse-openj9/openj9/issues/17500 - */ - return JNI_TRUE; + jboolean result = JNI_TRUE; + J9JavaVM *vm = BFUjavaVM; + if (J9_ARE_ANY_BITS_SET(vm->runtimeFlags, J9_RUNTIME_ALLOW_DYNAMIC_AGENT) + && (0 <= FIND_ARG_IN_VMARGS(EXACT_MATCH, VMOPT_XXENABLEDYNAMICAGENTLOADING, NULL)) + ) { + result = JNI_FALSE; + } + return result; } JNIEXPORT void JNICALL diff --git a/runtime/jvmti/j9jvmti.tdf b/runtime/jvmti/j9jvmti.tdf index 1ca6a38a108..26bdaed3a75 100644 --- a/runtime/jvmti/j9jvmti.tdf +++ b/runtime/jvmti/j9jvmti.tdf @@ -563,7 +563,7 @@ TraceExit=Trc_JVMTI_issueAgentOnLoadAttach_Exit Overhead=1 Level=1 Noenv Templat TraceEvent=Trc_JVMTI_issueAgentOnLoadAttach_close_shared_library Overhead=1 Level=1 Noenv Template="issueAgentOnLoadAttach unloading %s" TraceEntry=Trc_JVMTI_loadAgentLibraryGeneric_Entry Overhead=1 Level=1 Noenv Template="loadAgentLibraryGeneric loading library %s" -TraceExit=Trc_JVMTI_loadAgentLibraryGeneric_Exit Overhead=1 Level=1 Noenv Template="loadAgentLibraryGeneric successfully loaded library %s" +TraceExit=Trc_JVMTI_loadAgentLibraryGeneric_Exit Obsolete Overhead=1 Level=1 Noenv Template="loadAgentLibraryGeneric successfully loaded library %s" TraceEntry=Trc_JVMTI_loadAgentLibraryOnAttach_Entry Overhead=1 Level=1 Noenv Template="loadAgentLibraryOnAttach loading library %s" TraceExit=Trc_JVMTI_loadAgentLibraryOnAttach_Exit Overhead=1 Level=1 Noenv Template="loadAgentLibraryOnAttach loaded library %s, status=%d" @@ -602,11 +602,11 @@ TraceException=Trc_JVMTI_issueAgentOnLoadAttach_loadFunctionFailed Overhead=1 Le TraceEvent=Trc_JVMTI_issueAgentOnLoadAttach_loadFunctionSucceeded Overhead=1 Level=5 Noenv Template="issueAgentOnLoadAttach: Load function %s succeeded" TraceEvent=Trc_JVMTI_loadAgentLibraryGeneric_loadingAgentAs Overhead=1 Level=5 Noenv Template="loadAgentLibraryGeneric: Loading agent as %s" -TraceException=Trc_JVMTI_loadAgentLibraryGeneric_execNameNotFound Overhead=1 Level=1 Noenv Template="loadAgentLibraryGeneric: failed obtaining executable name; error code=%d" -TraceException=Trc_JVMTI_loadAgentLibraryGeneric_failedOpeningAgentLibrary Overhead=1 Level=1 Noenv Template="loadAgentLibraryGeneric: Failed opening agent library %s" +TraceException=Trc_JVMTI_loadAgentLibraryGeneric_execNameNotFound Obsolete Overhead=1 Level=1 Noenv Template="loadAgentLibraryGeneric: failed obtaining executable name; error code=%d" +TraceException=Trc_JVMTI_loadAgentLibraryGeneric_failedOpeningAgentLibrary Obsolete Overhead=1 Level=1 Noenv Template="loadAgentLibraryGeneric: Failed opening agent library %s" TraceEvent=Trc_JVMTI_loadAgentLibraryGeneric_openedAgentLibrary Overhead=1 Level=5 Noenv Template="loadAgentLibraryGeneric: Opened agent %s (address=%p) %s" -TraceException=Trc_JVMTI_loadAgentLibraryGeneric_agentAttachFailed1 Overhead=1 Level=1 Noenv Template="loadAgentLibraryGeneric: Failed attaching agent library; load function %s not found" -TraceException=Trc_JVMTI_loadAgentLibraryGeneric_agentAttachFailed2 Overhead=1 Level=1 Noenv Template="loadAgentLibraryGeneric: Failed attaching agent library; load function failed with error code %zd" +TraceException=Trc_JVMTI_loadAgentLibraryGeneric_agentAttachFailed1 Obsolete Overhead=1 Level=1 Noenv Template="loadAgentLibraryGeneric: Failed attaching agent library; load function %s not found" +TraceException=Trc_JVMTI_loadAgentLibraryGeneric_agentAttachFailed2 Obsolete Overhead=1 Level=1 Noenv Template="loadAgentLibraryGeneric: Failed attaching agent library; load function failed with error code %zd" TraceEvent=Trc_JVMTI_loadAgentLibraryGeneric_agentAttachedSuccessfully Overhead=1 Level=5 Noenv Template="loadAgentLibraryGeneric: Attached agent %s successfully" TraceEvent=Trc_JVMTI_loadAgentLibraryOnAttach_attachingAgentDynamically Overhead=1 Level=5 Noenv Template="loadAgentLibraryOnAttach: Attached agent %s dynamically" @@ -660,3 +660,8 @@ TraceEntry=Trc_JVMTI_jvmtiHookVirtualThreadMount_Entry Overhead=1 Level=5 Noenv TraceExit=Trc_JVMTI_jvmtiHookVirtualThreadMount_Exit Overhead=1 Level=5 Noenv Template="HookVirtualThreadMount" TraceEvent=Trc_JVMTI_loadAgentLibraryOnAttach_agentLoadingDisabled Overhead=1 Level=5 Noenv Template="loadAgentLibraryOnAttach: Dynamic agent loading is disabled" +TraceExit-Exception=Trc_JVMTI_loadAgentLibraryGeneric_execNameNotFound_Exit Overhead=1 Level=1 Noenv Template="loadAgentLibraryGeneric failed to obtain executable name with error code=%d while loading library %s" +TraceExit-Exception=Trc_JVMTI_loadAgentLibraryGeneric_failedOpeningAgentLibrary_Exit Overhead==1 Level=1 Noenv Template="loadAgentLibraryGeneric failed to open agent library %s with error message %s" +TraceExit-Exception=Trc_JVMTI_loadAgentLibraryGeneric_agentAttachFailed1_Exit Overhead==1 Level=1 Noenv Template="loadAgentLibraryGeneric failed attaching agent library %s; load function %s not found" +TraceExit-Exception=Trc_JVMTI_loadAgentLibraryGeneric_agentAttachFailed2_Exit Overhead==1 Level=1 Noenv Template="loadAgentLibraryGeneric failed attaching agent library %s; load function %s failed with error code=%d" +TraceExit=Trc_JVMTI_loadAgentLibraryGeneric_succeed_Exit Overhead=1 Level=1 Noenv Template="loadAgentLibraryGeneric successfully loaded library %s and function %s" diff --git a/runtime/jvmti/jvmtiStartup.c b/runtime/jvmti/jvmtiStartup.c index b711737c838..48b7385541b 100644 --- a/runtime/jvmti/jvmtiStartup.c +++ b/runtime/jvmti/jvmtiStartup.c @@ -52,8 +52,8 @@ static jint loadAgentLibrary (J9JavaVM * vm, J9JVMTIAgentLibrary * agentLibrary) static jint createXrunLibraries (J9JavaVM * vm); static J9JVMTIAgentLibrary * findAgentLibrary(J9JavaVM * vm, const char *libraryAndOptions, UDATA libraryLength); static jint issueAgentOnLoadAttach(J9JavaVM * vm, J9JVMTIAgentLibrary * agentLibrary, const char* options, char *loadFunctionName, BOOLEAN * foundLoadFn); -I_32 JNICALL loadAgentLibraryOnAttach(struct J9JavaVM * vm, const char * library, const char *options, UDATA decorate) ; - +I_32 JNICALL loadAgentLibraryOnAttach(struct J9JavaVM *vm, const char *library, const char *options, UDATA decorate); +static BOOLEAN isAgentLibraryLoaded(J9JavaVM *vm, const char *library); #define INSTRUMENT_LIBRARY "instrument" @@ -212,6 +212,7 @@ IDATA J9VMDllMain(J9JavaVM* vm, IDATA stage, void* reserved) } vm->loadAgentLibraryOnAttach = &loadAgentLibraryOnAttach; + vm->isAgentLibraryLoaded = &isAgentLibraryLoaded; break; } @@ -446,27 +447,32 @@ issueAgentOnLoadAttach(J9JavaVM * vm, J9JVMTIAgentLibrary * agentLibrary, const } /** - * Load a JVMTI agent and call the agent's initialization function - * @param vm Java VM - * @param agentLibrary environment for the agent - * @param name of the initialization function - * @return JNI_ERR, JNI_OK + * Load a JVMTI agent and call the agent's initialization function. + * + * @param[in] vm Java VM + * @param[in] agentLibrary environment for the agent + * @param[in] loadFunctionName name of the initialization function + * @param[in] loadStatically a boolean indicating if the library is to be loaded statically + * @param[in/out] found a pointer to a boolean indicating whether the load function was found + * @param[in/out] errorMessage a pointer to the error message when opening the shared library + * + * @return JNI_ERR if failed, otherwise JNI_OK */ static jint -loadAgentLibraryGeneric(J9JavaVM * vm, J9JVMTIAgentLibrary * agentLibrary, char *loadFunctionName, BOOLEAN loadStatically, BOOLEAN * found, const char **errorMessage) +loadAgentLibraryGeneric(J9JavaVM *vm, J9JVMTIAgentLibrary *agentLibrary, char *loadFunctionName, BOOLEAN loadStatically, BOOLEAN *found, const char **errorMessage) { PORT_ACCESS_FROM_JAVAVM(vm); - jint rc; - J9JVMTIData * jvmtiData = J9JVMTI_DATA_FROM_VM(vm); - J9NativeLibrary * nativeLib = &(agentLibrary->nativeLib); + jint rc = JNI_OK; + J9JVMTIData *jvmtiData = J9JVMTI_DATA_FROM_VM(vm); + J9NativeLibrary *nativeLib = &(agentLibrary->nativeLib); Trc_JVMTI_loadAgentLibraryGeneric_Entry(agentLibrary->nativeLib.name); if (NULL == agentLibrary->xRunLibrary) { jint i = 0; - char * fullLibName = NULL; - const char * systemAgentName; + char *fullLibName = NULL; + const char *systemAgentName = NULL; UDATA openFlags = agentLibrary->decorate ? J9PORT_SLOPEN_DECORATE | J9PORT_SLOPEN_LAZY : J9PORT_SLOPEN_LAZY; - char * agentPath = NULL; /* Don't free agentPath; may point at persistent, system areas. */ + char *agentPath = NULL; /* Don't free agentPath; may point at persistent, system areas. */ if (loadStatically) { /* If flag is set, the agent library ought to be opened statically, that is, the executable itself. */ @@ -474,8 +480,8 @@ loadAgentLibraryGeneric(J9JavaVM * vm, J9JVMTIAgentLibrary * agentLibrary, char openFlags |= J9PORT_SLOPEN_OPEN_EXECUTABLE; } else { /* Report failure to obtain executable name; caller will proceed with dynamic linking. */ - Trc_JVMTI_loadAgentLibraryGeneric_execNameNotFound(j9error_last_error_number()); - Trc_JVMTI_loadAgentLibraryGeneric_Exit(agentLibrary->nativeLib.name); + I_32 errorno = j9error_last_error_number(); + Trc_JVMTI_loadAgentLibraryGeneric_execNameNotFound_Exit(errorno, agentLibrary->nativeLib.name); return JNI_ERR; } } else { @@ -483,8 +489,8 @@ loadAgentLibraryGeneric(J9JavaVM * vm, J9JVMTIAgentLibrary * agentLibrary, char * is loaded from our current jre tree. This avoids picking up stray agents that might * be found on the library path. See CMVC 144382. */ - while ((systemAgentName = systemAgentNames[i++]) != NULL) { - if (strcmp(nativeLib->name, systemAgentName) == 0) { + while (NULL != (systemAgentName = systemAgentNames[i++])) { + if (0 == strcmp(nativeLib->name, systemAgentName)) { fullLibName = prependSystemAgentPath(vm, agentLibrary, nativeLib->name); Trc_JVMTI_loadAgentLibraryGeneric_loadingAgentAs(fullLibName); break; @@ -493,9 +499,7 @@ loadAgentLibraryGeneric(J9JavaVM * vm, J9JVMTIAgentLibrary * agentLibrary, char agentPath = (NULL != fullLibName) ? fullLibName : nativeLib->name; } - if (j9sl_open_shared_library(agentPath, &(nativeLib->handle), openFlags) != 0) { - Trc_JVMTI_loadAgentLibraryGeneric_failedOpeningAgentLibrary(agentPath); - + if (0 != j9sl_open_shared_library(agentPath, &(nativeLib->handle), openFlags)) { /* We may attempt to open the shared library again so save the error message * and print it once we know it is the final attempt */ *errorMessage = j9error_last_error_message(); @@ -503,7 +507,7 @@ loadAgentLibraryGeneric(J9JavaVM * vm, J9JVMTIAgentLibrary * agentLibrary, char if (NULL != fullLibName) { j9mem_free_memory(fullLibName); } - Trc_JVMTI_loadAgentLibraryGeneric_Exit(agentPath); + Trc_JVMTI_loadAgentLibraryGeneric_failedOpeningAgentLibrary_Exit(agentPath, *errorMessage); return JNI_ERR; } Trc_JVMTI_loadAgentLibraryGeneric_openedAgentLibrary(agentPath, @@ -519,15 +523,13 @@ loadAgentLibraryGeneric(J9JavaVM * vm, J9JVMTIAgentLibrary * agentLibrary, char rc = issueAgentOnLoadAttach(vm, agentLibrary, agentLibrary->options, loadFunctionName, found); if (!(*found)) { /* If the load function wasn't found, issueAgentOnLoadAttach already closed the library. */ - Trc_JVMTI_loadAgentLibraryGeneric_agentAttachFailed1(loadFunctionName); - Trc_JVMTI_loadAgentLibraryGeneric_Exit(agentLibrary->nativeLib.name); + Trc_JVMTI_loadAgentLibraryGeneric_agentAttachFailed1_Exit(agentLibrary->nativeLib.name, loadFunctionName); return rc; } /* For errors other than load function not found ... */ if (JNI_OK != rc) { - Trc_JVMTI_loadAgentLibraryGeneric_agentAttachFailed2(rc); - Trc_JVMTI_loadAgentLibraryGeneric_Exit(agentLibrary->nativeLib.name); + Trc_JVMTI_loadAgentLibraryGeneric_agentAttachFailed2_Exit(agentLibrary->nativeLib.name, loadFunctionName, rc); return rc; } @@ -540,14 +542,14 @@ loadAgentLibraryGeneric(J9JavaVM * vm, J9JVMTIAgentLibrary * agentLibrary, char /* Add the library to the linked list */ issueWriteBarrier(); omrthread_monitor_enter(jvmtiData->mutex); - if (jvmtiData->agentLibrariesTail == NULL) { + if (NULL == jvmtiData->agentLibrariesTail) { jvmtiData->agentLibrariesHead = jvmtiData->agentLibrariesTail = nativeLib; } else { jvmtiData->agentLibrariesTail->next = nativeLib; jvmtiData->agentLibrariesTail = nativeLib; } omrthread_monitor_exit(jvmtiData->mutex); - Trc_JVMTI_loadAgentLibraryGeneric_Exit(agentLibrary->nativeLib.name); + Trc_JVMTI_loadAgentLibraryGeneric_succeed_Exit(agentLibrary->nativeLib.name, loadFunctionName); return JNI_OK; } @@ -1060,6 +1062,11 @@ createXrunLibraries(J9JavaVM * vm) return JNI_OK; } +static BOOLEAN +isAgentLibraryLoaded(J9JavaVM *vm, const char *library) +{ + return NULL != findAgentLibrary(vm, library, strlen(library)); +} static J9JVMTIAgentLibrary * findAgentLibrary(J9JavaVM * vm, const char *libraryAndOptions, UDATA libraryLength) @@ -1087,4 +1094,3 @@ findAgentLibrary(J9JavaVM * vm, const char *libraryAndOptions, UDATA libraryLeng return NULL; } - diff --git a/runtime/oti/j9nonbuilder.h b/runtime/oti/j9nonbuilder.h index bf9f3838081..0b5caf0679e 100644 --- a/runtime/oti/j9nonbuilder.h +++ b/runtime/oti/j9nonbuilder.h @@ -5802,6 +5802,7 @@ typedef struct J9JavaVM { omrthread_monitor_t statisticsMutex; struct J9Statistic* nextStatistic; I_32 (JNICALL *loadAgentLibraryOnAttach)(struct J9JavaVM * vm, const char * library, const char *options, UDATA decorate) ; + BOOLEAN (*isAgentLibraryLoaded)(struct J9JavaVM *vm, const char *library); struct J9AttachContext attachContext; UDATA hotSwapCount; UDATA zombieThreadCount; diff --git a/runtime/vm/BytecodeInterpreter.hpp b/runtime/vm/BytecodeInterpreter.hpp index 74106afb846..f30f43b2163 100644 --- a/runtime/vm/BytecodeInterpreter.hpp +++ b/runtime/vm/BytecodeInterpreter.hpp @@ -27,26 +27,27 @@ #include #endif /* JAVA_SPEC_VERSION >= 20 */ +#include "bcnames.h" +#define FFI_BUILDING /* Needed on Windows to link libffi statically */ +#include "ffi.h" +#include "j2sever.h" #include "j9.h" +#include "j9bcvnls.h" #include "j9cfg.h" -#include "j9protos.h" #include "j9consts.h" -#include "j9vmnls.h" #include "j9jclnls.h" -#include "j9bcvnls.h" -#include "bcnames.h" +#include "j9protos.h" +#include "j9vmnls.h" +#include "jitregmap.h" +#include "jni.h" +#include "jvminit.h" +#include "objhelp.h" #include "rommeth.h" #include "stackwalk.h" #include "ut_j9vm.h" #include "util_api.h" #include "vm_internal.h" -#include "jni.h" -#define FFI_BUILDING /* Needed on Windows to link libffi statically */ -#include "ffi.h" -#include "jitregmap.h" -#include "j2sever.h" #include "vmaccess.h" -#include "objhelp.h" #include "ArrayCopyHelpers.hpp" #include "AtomicSupport.hpp" @@ -4863,8 +4864,24 @@ class INTERPRETER_CLASS // Trc_JCL_attach_loadAgentLibrary(env, agentLibraryUTF, agentOptions, decorate); const char *agentOptionsUTF = env->GetStringUTFChars(agentOptions, NULL); if (NULL != agentOptionsUTF) { + if (J9_ARE_ANY_BITS_SET(_vm->runtimeFlags, J9_RUNTIME_ALLOW_DYNAMIC_AGENT)) { +#if JAVA_SPEC_VERSION >= 21 + /* The name "vm" is used by FIND_ARG_IN_VMARGS(). */ + J9JavaVM *vm = _vm; + /* No warning if -XX:+EnableDynamicAgentLoading is specified. */ + if (0 > FIND_ARG_IN_VMARGS(EXACT_MATCH, VMOPT_XXENABLEDYNAMICAGENTLOADING, NULL)) { + /* No warning if the same agent is already loaded. */ + if (!vm->isAgentLibraryLoaded(vm, agentLibraryUTF)) { + fprintf(stderr, "WARNING: A JVM TI agent has been loaded dynamically (%s)\n", agentOptionsUTF); + fprintf(stderr, "WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading to hide this warning\n"); + fprintf(stderr, "WARNING: If a serviceability tool is not in use, please run with -Djdk.instrument.traceUsage for more information\n"); + fprintf(stderr, "WARNING: Dynamic loading of agents will be disallowed by default in a future release\n"); + } + } +#endif /* JAVA_SPEC_VERSION >= 21 */ status = (_vm->loadAgentLibraryOnAttach)(_vm, agentLibraryUTF, agentOptionsUTF, decorate); - env->ReleaseStringUTFChars(agentOptions, agentOptionsUTF); + } + env->ReleaseStringUTFChars(agentOptions, agentOptionsUTF); } env->ReleaseStringUTFChars(agentLibrary, agentLibraryUTF); }