diff --git a/make/TestImage.gmk b/make/TestImage.gmk index b719d6824da..617e0232a3e 100644 --- a/make/TestImage.gmk +++ b/make/TestImage.gmk @@ -46,6 +46,14 @@ $(README): TARGETS += $(BUILD_INFO_PROPERTIES) $(README) +# SapMachine 2025-08-11: Copy test jar for SAP JMC agent if needed. +ifeq ($(SAP_JMC_AGENT_ENABLED), true) + $(TEST_IMAGE_DIR)/jars/agent-tests.jar: $(SAP_JMC_AGENT_PATH)/agent-$(SAP_JMC_AGENT_VERSION)-tests.jar + $(call install-file) + + TARGETS += $(TEST_IMAGE_DIR)/jars/agent-tests.jar +endif + ################################################################################ prepare-test-image: $(TARGETS) diff --git a/make/autoconf/configure.ac b/make/autoconf/configure.ac index f7e9844a643..2cacfd165e8 100644 --- a/make/autoconf/configure.ac +++ b/make/autoconf/configure.ac @@ -217,6 +217,9 @@ JDKOPT_SETUP_CODE_COVERAGE # AddressSanitizer JDKOPT_SETUP_ADDRESS_SANITIZER +# SapMachine 2024-12-05: Setup SAP JMC agent if requested +JDKOPT_SETUP_SAP_JMC_AGENT + # UndefinedBehaviorSanitizer JDKOPT_SETUP_UNDEFINED_BEHAVIOR_SANITIZER diff --git a/make/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4 index 033a51115ea..f8c74717938 100644 --- a/make/autoconf/jdk-options.m4 +++ b/make/autoconf/jdk-options.m4 @@ -359,6 +359,29 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_DEBUG_SYMBOLS], AC_SUBST(SHIP_DEBUG_SYMBOLS) ]) +# SapMachine 2025-08-11: import JMC agent jars +AC_DEFUN_ONCE([JDKOPT_SETUP_SAP_JMC_AGENT], +[ + AC_ARG_WITH(sap-jmc-agent, [AS_HELP_STRING([--with-sap-jmc-agent], + [Set import path for the SAP JMC agent jars])]) + SAP_JMC_AGENT_ENABLED=false + if test "x$with_sap_jmc_agent" != x; then + SAP_JMC_AGENT_PATH="$with_sap_jmc_agent" + UTIL_FIXUP_PATH(SAP_JMC_AGENT_PATH) + if test -f "$SAP_JMC_AGENT_PATH"/agent-*-boot.jar; then + SAP_JMC_AGENT_VERSION=$($BASENAME "$SAP_JMC_AGENT_PATH"/agent-*-boot.jar | $SED -e 's/^agent-//' -e 's/-boot.jar$//') + SAP_JMC_AGENT_ENABLED=true + JVM_CFLAGS="$JVM_CFLAGS -DWITH_SAP_JMC_AGENT=1" + AC_MSG_NOTICE([SAP JMC agent found at $SAP_JMC_AGENT_PATH, version $SAP_JMC_AGENT_VERSION]) + else + AC_MSG_ERROR([SAP JMC agent was set, but the agent jars were not found]) + fi + fi + AC_SUBST(SAP_JMC_AGENT_PATH) + AC_SUBST(SAP_JMC_AGENT_VERSION) + AC_SUBST(SAP_JMC_AGENT_ENABLED) +]) + ################################################################################ # # Native and Java code coverage diff --git a/make/autoconf/spec.gmk.in b/make/autoconf/spec.gmk.in index 0e5539df5d5..de5eab02c9d 100644 --- a/make/autoconf/spec.gmk.in +++ b/make/autoconf/spec.gmk.in @@ -450,6 +450,11 @@ UNLIMITED_CRYPTO=@UNLIMITED_CRYPTO@ ASYNC_PROFILER_IMPORT_PATH=@ASYNC_PROFILER_IMPORT_PATH@ ASYNC_PROFILER_IMPORT_ENABLED=@ASYNC_PROFILER_IMPORT_ENABLED@ +# SapMachine 2025-08-11: import SAP JMC agent jars +SAP_JMC_AGENT_PATH=@SAP_JMC_AGENT_PATH@ +SAP_JMC_AGENT_VERSION=@SAP_JMC_AGENT_VERSION@ +SAP_JMC_AGENT_ENABLED=@SAP_JMC_AGENT_ENABLED@ + GCOV_ENABLED=@GCOV_ENABLED@ JCOV_ENABLED=@JCOV_ENABLED@ JCOV_HOME=@JCOV_HOME@ diff --git a/make/modules/java.base/Copy.gmk b/make/modules/java.base/Copy.gmk index a0756acb42f..554254c1d18 100644 --- a/make/modules/java.base/Copy.gmk +++ b/make/modules/java.base/Copy.gmk @@ -290,3 +290,14 @@ ifeq ($(call isTargetOs, linux macosx), true) endif ################################################################################ +# SapMachine 2025-08-11: Copy jmc agent jars +ifeq ($(SAP_JMC_AGENT_ENABLED), true) + $(LIB_DST_DIR)/agent.jar: $(SAP_JMC_AGENT_PATH)/agent-$(SAP_JMC_AGENT_VERSION).jar + $(call install-file) + $(LIB_DST_DIR)/agent-boot.jar: $(SAP_JMC_AGENT_PATH)/agent-$(SAP_JMC_AGENT_VERSION)-boot.jar + $(call install-file) + + TARGETS += $(LIB_DST_DIR)/agent.jar $(LIB_DST_DIR)/agent-boot.jar +endif + +################################################################################ diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 26e1a6f5ea7..e72381b4399 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -2405,6 +2405,29 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m } } #endif // !INCLUDE_JVMTI + // SapMachine 2025-08-11: Handle jmc agent if requested. + } else if (match_option(option, "-jmcagent:", &tail)) { +#if !defined(WITH_SAP_JMC_AGENT) + jio_fprintf(defaultStream::error_stream(), + "SAP JMC agent is not included in this VM\n"); + return JNI_ERR; +#else +#if !INCLUDE_JVMTI +#error "Must have JVMTI enabled with SAP JVM agent" +#endif + char const* agent_jar = "agent.jar"; + if (tail != nullptr) { + size_t length = strlen(tail) + strlen(_java_home->value()) + strlen(agent_jar) + 7; + char* options = NEW_C_HEAP_ARRAY(char, length, mtArguments); + jio_snprintf(options, length, "%s/lib/%s=%s", _java_home->value(), agent_jar, tail); + JvmtiAgentList::add("instrument", options, false); + + // java agents need module java.instrument + if (!create_numbered_module_property("jdk.module.addmods", "java.instrument", addmods_count++)) { + return JNI_ENOMEM; + } + } +#endif // !WITH_SAP_JMC_AGENT // --enable_preview } else if (match_option(option, "--enable-preview")) { set_enable_preview(); diff --git a/src/hotspot/share/services/diagnosticCommand.cpp b/src/hotspot/share/services/diagnosticCommand.cpp index b07f584a031..246e9d607ba 100644 --- a/src/hotspot/share/services/diagnosticCommand.cpp +++ b/src/hotspot/share/services/diagnosticCommand.cpp @@ -129,6 +129,10 @@ void DCmd::register_dcmds(){ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); #if INCLUDE_JVMTI // Both JVMTI and SERVICES have to be enabled to have this dcmd DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + // SapMachine 2025-08-11: Add support for SAP JMC agent if requested. +#if defined(WITH_SAP_JMC_AGENT) + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); +#endif #endif // INCLUDE_JVMTI #endif // INCLUDE_SERVICES #if INCLUDE_JVMTI @@ -350,6 +354,36 @@ void JVMTIAgentLoadDCmd::execute(DCmdSource source, TRAPS) { } } +// SapMachine 2025-08-11 +#if defined(WITH_SAP_JMC_AGENT) +JVMTIJmcAgentLoadDCmd::JVMTIJmcAgentLoadDCmd(outputStream* output, bool heap) : + DCmdWithParser(output, heap), + _option("agent option", "Option string to pass the JMC agent.", "STRING", false) { + _dcmdparser.add_dcmd_argument(&_option); +} + +void JVMTIJmcAgentLoadDCmd::execute(DCmdSource source, TRAPS) { + char const* java_home = Arguments::get_java_home(); + char const* agent_jar = "agent.jar"; + char const* option = _option.value(); + size_t len = strlen(java_home) + strlen(agent_jar) + (option == nullptr ? 0 : 1 + strlen(option)) + 7; + char* agent_line = (char*)os::malloc(len, mtInternal); + + if (agent_line == nullptr) { + output()->print_cr("JVMTI JMC agent attach failed: " + "Could not allocate " SIZE_FORMAT_X_0 " bytes for argument.", + len); + return; + } + + jio_snprintf(agent_line, len, "%s/lib/%s%s%s", java_home, agent_jar, option == nullptr ? "" : "=", + option == nullptr ? "" : option); + JvmtiAgentList::load_agent("instrument", false, agent_line, output()); + + os::free(agent_line); +} +#endif // WITH_SAP_JMC_AGENT + #endif // INCLUDE_JVMTI #endif // INCLUDE_SERVICES diff --git a/src/hotspot/share/services/diagnosticCommand.hpp b/src/hotspot/share/services/diagnosticCommand.hpp index ad018259f83..18d4117680e 100644 --- a/src/hotspot/share/services/diagnosticCommand.hpp +++ b/src/hotspot/share/services/diagnosticCommand.hpp @@ -193,6 +193,31 @@ class JVMTIAgentLoadDCmd : public DCmdWithParser { } virtual void execute(DCmdSource source, TRAPS); }; + +// SapMachine 2025-08-11 +#if defined(WITH_SAP_JMC_AGENT) +class JVMTIJmcAgentLoadDCmd : public DCmdWithParser { +protected: + DCmdArgument _option; +public: + JVMTIJmcAgentLoadDCmd(outputStream* output, bool heap); + static int num_arguments() { return 1; } + static const char* name() { return "JVMTI.jmc_agent_load"; } + static const char* description() { + return "Load the SAP jmc agent."; + } + static const char* impact() { + return "Medium: Depends on the command or trace"; + } + static const JavaPermission permission() { + JavaPermission p = { "java.lang.management.ManagementPermission", + "control", nullptr }; + return p; + } + virtual void execute(DCmdSource source, TRAPS); +}; +#endif // WITH_SAP_JMC_AGENT + #endif // INCLUDE_JVMTI #endif // INCLUDE_SERVICES diff --git a/test/jdk/TEST.groups b/test/jdk/TEST.groups index 4ac6464540e..6186a297e8e 100644 --- a/test/jdk/TEST.groups +++ b/test/jdk/TEST.groups @@ -65,6 +65,7 @@ tier1_sapmachine = \ java/net/Socket/ExceptionText.java \ java/util/jar/Manifest/IncludeInExceptionsTest.java \ jdk/security/JavaDotSecurity/TestJDKIncludeInExceptions.java \ + sap \ sun/net/www/http/KeepAliveCache/TestConnectionIDFeature.java \ sun/security/lib/cacerts/VerifyCACerts.java \ tools/jlink/plugins/AddSapMachineToolsTest.java \ diff --git a/test/jdk/sap/JmcAgentIntegrationTest.java b/test/jdk/sap/JmcAgentIntegrationTest.java new file mode 100644 index 00000000000..30cf72ceab4 --- /dev/null +++ b/test/jdk/sap/JmcAgentIntegrationTest.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2025 SAP SE. 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 + * @summary Runs the test for the jmc agent integration. + * + * @run main/othervm JmcAgentIntegrationTest + */ + +import java.lang.reflect.Method; +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; + +public class JmcAgentIntegrationTest { + + public static void main(String[] args) throws Exception { + File jar = new File(System.getenv("TEST_IMAGE_DIR") + "/jars/agent-tests.jar"); + + if (!jar.exists()) { + return; // Feature was not enabled. + } + + ArrayList testOptions = new ArrayList<>(); + testOptions.add("-dump"); + + // Only run VM-agnostic tests if the ASM version we are using cannot handle the class file spec of this VM. + int spec = Integer.getInteger("java.vm.specification.version", 99999); + String javaHome = System.getProperty("java.home"); + File agentJar = new File(javaHome + "/lib/agent.jar"); + ClassLoader parentLoader = JmcAgentIntegrationTest.class.getClassLoader(); + URLClassLoader agentLoader = new URLClassLoader(new URL[] {agentJar.toURI().toURL()}, parentLoader); + try { + Class.forName("org.openjdk.jmc.internal.org.objectweb.asm.Opcodes", true, agentLoader).getDeclaredField("V" + spec); + } catch (NoSuchFieldException e) { + System.out.println("Incompatible class file version. Skipping VM specific tests."); + testOptions.add("-vm-agnostic-tests"); + } + + URL url = jar.toURI().toURL(); + String classPath = System.getProperty("java.class.path", "."); + + System.setProperty("java.class.path", classPath + System.getProperty("path.separator") + jar.toString()); + System.setProperty("useJmcAgentOption", "true"); + System.setProperty("traceExecs", "true"); + + URLClassLoader cl = new URLClassLoader(new URL[] {url}, parentLoader); + Class testClass = Class.forName("org.openjdk.jmc.agent.sap.test.TestRunner", true, cl); + Method mainMethod = testClass.getDeclaredMethod("main", String[].class, String[].class); + mainMethod.invoke(null, new Object[] {testOptions.toArray(new String[0]), new String[] {"-XX:+EnableDynamicAgentLoading"}}); + } +}