diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py
index f1fd53da29c35..2655fea01f85f 100644
--- a/substratevm/mx.substratevm/suite.py
+++ b/substratevm/mx.substratevm/suite.py
@@ -308,6 +308,9 @@
"jdk.internal.util",
"jdk.internal.org.objectweb.asm",
],
+ "java.instrument":[
+ "java.lang.instrument"
+ ],
"java.management": [
"com.sun.jmx.mbeanserver",
"sun.management",
@@ -639,6 +642,9 @@
"sun.util.locale",
"sun.invoke.util",
],
+ "java.instrument":[
+ "java.lang.instrument"
+ ],
"java.management": [
"com.sun.jmx.mbeanserver", # Needed for javadoc links (MXBeanIntrospector,DefaultMXBeanMappingFactory, MXBeanProxy)
"sun.management", # Needed for javadoc links (MappedMXBeanType)
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/JavaMainWrapper.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/JavaMainWrapper.java
index 57d0bb30441e8..f1ad4c4bc2054 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/JavaMainWrapper.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/JavaMainWrapper.java
@@ -166,19 +166,22 @@ public List getInputArguments() {
}
public static void invokeMain(String[] args) throws Throwable {
+ PreMainSupport preMainSupport = ImageSingletons.lookup(PreMainSupport.class);
+ String[] mainArgs = preMainSupport.retrievePremainArgs(args);
+ preMainSupport.invokePremain();
JavaMainSupport javaMainSupport = ImageSingletons.lookup(JavaMainSupport.class);
if (javaMainSupport.mainNonstatic) {
Object instance = javaMainSupport.javaMainClassCtorHandle.invoke();
if (javaMainSupport.mainWithoutArgs) {
javaMainSupport.javaMainHandle.invoke(instance);
} else {
- javaMainSupport.javaMainHandle.invoke(instance, args);
+ javaMainSupport.javaMainHandle.invoke(instance, mainArgs);
}
} else {
if (javaMainSupport.mainWithoutArgs) {
javaMainSupport.javaMainHandle.invokeExact();
} else {
- javaMainSupport.javaMainHandle.invokeExact(args);
+ javaMainSupport.javaMainHandle.invokeExact(mainArgs);
}
}
}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/PreMainSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/PreMainSupport.java
new file mode 100644
index 0000000000000..24378dbe27666
--- /dev/null
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/PreMainSupport.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2024, Alibaba Group Holding Limited. 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.svm.core;
+
+import com.oracle.svm.core.util.VMError;
+import org.graalvm.nativeimage.Platform;
+import org.graalvm.nativeimage.Platforms;
+
+import java.lang.instrument.ClassDefinition;
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.Instrumentation;
+import java.lang.instrument.UnmodifiableClassException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.jar.JarFile;
+
+/**
+ * Java agent can do instrumentation initialization work in premain phase. This class supports
+ * registering such premain methods at native image build time and invoking them at native image
+ * runtime.
+ * JVM supports two kind of premain methods:
+ *
+ *
{@code public static void premain(String agentArgs, Instrumentation inst)}
+ *
{@code public static void premain(String agentArgs)}
+ *
+ * For the first one, at registration time we will set the second parameter with an instance of
+ * {@link SVMRuntimeInstrumentImpl} class which does not do any actual work as no instrumentation
+ * can do in native code at runtime.
+ *
+ * Be noticed, the original agent premain method may not work well at native image runtime
+ * even if the input {@link Instrumentation} class is replaced with
+ * {@link SVMRuntimeInstrumentImpl}.
+ *
+ *
+ * It is the agent developers' responsibility to implement a native version of their agent premain
+ * method. It can be implemented in two ways:
+ *
+ *
Isolate code by checking current runtime. For example:
+ *
Use {@link com.oracle.svm.core.annotate.TargetClass} API to implement a native image version
+ * premain.
+ *
+ *
+ */
+public class PreMainSupport {
+
+ private static final String PREMAIN_OPTION_PREFIX = "-XX-premain:";
+
+ class PremainMethod {
+ String className; // full qualified premain class name
+ Method method;
+ Object[] args;
+
+ PremainMethod(String className, Method method, Object[] args) {
+ this.className = className;
+ this.method = method;
+ this.args = args;
+ }
+ }
+
+ private Map premainOptions = new HashMap<>();
+ // Order matters
+ private List premainMethods = new ArrayList<>();
+
+ @Platforms({Platform.HOSTED_ONLY.class})
+ public void registerPremainMethod(String className, Method executable, Object... args) {
+ premainMethods.add(new PremainMethod(className, executable, args));
+ }
+
+ /**
+ * Retrieve premain options from input args.
+ * Keep premain options and return the rest args as main args.
+ * The premain options format:
+ *
+ * -XX-premain:[full.qualified.premain.class]:[premain options]
+ *
+ *
+ * @param args
+ * @return
+ */
+ public String[] retrievePremainArgs(String[] args) {
+ List mainArgs = new ArrayList<>();
+
+ for (String arg : args) {
+ if (arg.startsWith(PREMAIN_OPTION_PREFIX)) {
+ String premainOptionKeyValue = arg.substring(PREMAIN_OPTION_PREFIX.length());
+ String[] pair = premainOptionKeyValue.split(":");
+ if (pair.length == 2) {
+ premainOptions.put(pair[0], pair[1]);
+ }
+ } else {
+ mainArgs.add(arg);
+ }
+ }
+ return mainArgs.toArray(new String[0]);
+ }
+
+ public void invokePremain() {
+ for (PremainMethod premainMethod : premainMethods) {
+ try {
+ Object[] args = premainMethod.args;
+ if (premainOptions.containsKey(premainMethod.className)) {
+ args[0] = premainOptions.get(premainMethod.className);
+ }
+ // premain method must be static
+ premainMethod.method.invoke(null, args);
+ } catch (Throwable t) {
+ VMError.shouldNotReachHere("Fail to execute " + premainMethod.className + ".premain", t);
+ }
+ }
+ }
+
+ /**
+ * This class is a dummy implementation of {@link Instrumentation} interface. It serves as the
+ * registered premain method's second parameter. At native image runtime, no actual
+ * instrumentation work can do. So all the methods here are empty.
+ */
+ public static class SVMRuntimeInstrumentImpl implements Instrumentation {
+
+ @Override
+ public void addTransformer(ClassFileTransformer transformer, boolean canRetransform) {
+ }
+
+ @Override
+ public void addTransformer(ClassFileTransformer transformer) {
+ }
+
+ @Override
+ public boolean removeTransformer(ClassFileTransformer transformer) {
+ return false;
+ }
+
+ @Override
+ public boolean isRetransformClassesSupported() {
+ return false;
+ }
+
+ @Override
+ public void retransformClasses(Class>... classes) throws UnmodifiableClassException {
+ }
+
+ @Override
+ public boolean isRedefineClassesSupported() {
+ return false;
+ }
+
+ @Override
+ public void redefineClasses(ClassDefinition... definitions) throws ClassNotFoundException, UnmodifiableClassException {
+ }
+
+ @Override
+ public boolean isModifiableClass(Class> theClass) {
+ return false;
+ }
+
+ @Override
+ public Class>[] getAllLoadedClasses() {
+ return new Class>[0];
+ }
+
+ @Override
+ public Class>[] getInitiatedClasses(ClassLoader loader) {
+ return new Class>[0];
+ }
+
+ @Override
+ public long getObjectSize(Object objectToSize) {
+ return 0;
+ }
+
+ @Override
+ public void appendToBootstrapClassLoaderSearch(JarFile jarfile) {
+ }
+
+ @Override
+ public void appendToSystemClassLoaderSearch(JarFile jarfile) {
+ }
+
+ @Override
+ public boolean isNativeMethodPrefixSupported() {
+ return false;
+ }
+
+ @Override
+ public void setNativeMethodPrefix(ClassFileTransformer transformer, String prefix) {
+ }
+
+ @Override
+ public void redefineModule(Module module, Set extraReads, Map> extraExports, Map> extraOpens, Set> extraUses,
+ Map, List>> extraProvides) {
+ }
+
+ @Override
+ public boolean isModifiableModule(Module module) {
+ return false;
+ }
+ }
+}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/InstrumentFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/InstrumentFeature.java
new file mode 100644
index 0000000000000..68f2efef44da8
--- /dev/null
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/InstrumentFeature.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2024, Alibaba Group Holding Limited. 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.svm.hosted;
+
+import com.oracle.svm.core.PreMainSupport;
+import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
+import com.oracle.svm.core.feature.InternalFeature;
+import com.oracle.svm.core.option.HostedOptionKey;
+import com.oracle.svm.core.option.LocatableMultiOptionValue;
+import com.oracle.svm.core.util.VMError;
+import com.oracle.svm.hosted.reflect.ReflectionFeature;
+import jdk.graal.compiler.options.Option;
+import org.graalvm.nativeimage.ImageSingletons;
+import org.graalvm.nativeimage.hosted.Feature;
+
+import java.lang.instrument.Instrumentation;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This feature supports instrumentation in native image.
+ */
+@AutomaticallyRegisteredFeature
+public class InstrumentFeature implements InternalFeature {
+ private ClassLoader cl;
+ private PreMainSupport preMainSupport;
+
+ public static class Options {
+ @Option(help = "Specify premain-class list. Multiple classes are separated by comma, and order matters.")//
+ public static final HostedOptionKey PremainClasses = new HostedOptionKey<>(null);
+
+ }
+
+ /**
+ * {@link ReflectionFeature} must come before this feature, because many instrumentation methods
+ * are called by reflection, e.g. premain.
+ */
+ @Override
+ public List> getRequiredFeatures() {
+ return List.of(ReflectionFeature.class);
+ }
+
+ @Override
+ public void afterRegistration(AfterRegistrationAccess access) {
+ FeatureImpl.AfterRegistrationAccessImpl a = (FeatureImpl.AfterRegistrationAccessImpl) access;
+ cl = a.getImageClassLoader().getClassLoader();
+ ImageSingletons.add(PreMainSupport.class, preMainSupport = new PreMainSupport());
+ if(Options.PremainClasses.hasBeenSet()) {
+ List premains = Options.PremainClasses.getValue().values();
+ for (String premain : premains) {
+ addPremainClass(premain);
+ }
+ }
+ }
+
+
+ /**
+ * Find the premain method from the given class and register it for runtime usage. According
+ * to java.lang.instrument API
+ * doc, there are two premain methods:
+ *
+ *
{@code public static void premain(String agentArgs, Instrumentation inst)}
+ *
{@code public static void premain(String agentArgs)}
+ *
+ * The first one is taken with higher priority. The second one is taken only when the first
+ * one is absent.
+ * So this method looks for them in the same order.
+ */
+ private void addPremainClass(String premainClass) {
+ try {
+ Class> clazz = Class.forName(premainClass, false, cl);
+ Method premain = null;
+ List