-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add premain for static agent support
Java agent always has a premain method to initialize the agent. SVM needs the premain as well to support agent in native image. At compile time, -H:PremainClasses= option is used to set the premain classes. At runtime, premain runtime options are set along with main class' arguments in the format of -XX-premain:[class]:[options]
- Loading branch information
Showing
12 changed files
with
677 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
231 changes: 231 additions & 0 deletions
231
substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/PreMainSupport.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 1,231 @@ | ||
/* | ||
* 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. <br> | ||
* JVM supports two kind of premain methods: | ||
* <ol> | ||
* <li>{@code public static void premain(String agentArgs, Instrumentation inst)}</li> | ||
* <li>{@code public static void premain(String agentArgs)}</li> | ||
* </ol> | ||
* For the first one, at registration time we will set the second parameter with an instance of | ||
* {@link NativeImageNoOpRuntimeInstrumentation} class which does not do any actual work as no | ||
* instrumentation can do in native code at runtime. | ||
* <p> | ||
* <b>Be noticed</b>, the original agent premain method may not work well at native image runtime | ||
* even if the input {@link Instrumentation} class is replaced with | ||
* {@link NativeImageNoOpRuntimeInstrumentation}. | ||
* </p> | ||
* <p> | ||
* It is the agent developers' responsibility to implement a native version of their agent premain | ||
* method. It can be implemented in two ways: | ||
* <ul> | ||
* <li>Isolate code by checking current runtime. For example: <code> | ||
* <pre> | ||
* if (ImageInfo.inImageRuntimeCode()) { | ||
* // native image runtime | ||
* } else{ | ||
* // JVM runtime | ||
* } | ||
* </pre> | ||
* </code></li> | ||
* <li>Use {@link com.oracle.svm.core.annotate.TargetClass} API to implement a native image version | ||
* premain.</li> | ||
* </ul> | ||
* </p> | ||
*/ | ||
public class PreMainSupport { | ||
|
||
// todo: Not finally decided, could be changed in the future | ||
private static final String PREMAIN_OPTION_PREFIX = "-XX-premain:"; | ||
|
||
/** | ||
* A record for premain method. | ||
* | ||
* @param className the full qualified class name that declares the premain method | ||
* @param method the premain method | ||
* @param args the arguments of premain method | ||
*/ | ||
record PremainMethod(String className, Method method, Object[] args) { | ||
} | ||
|
||
private Map<String, String> premainOptions = new HashMap<>(); | ||
// Order matters | ||
private List<PremainMethod> 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. Multiple agent options should be given in separated {@code -XX-premain:} leading | ||
* arguments. The premain options format: <br> | ||
* -XX-premain:[full.qualified.premain.class]:[premain options] | ||
* -XX-premain:[full.qualified.premain.class2]:[premain options] <br> | ||
* | ||
* @param args original arguments for premain and main | ||
* @return arguments for main class | ||
*/ | ||
public String[] retrievePremainArgs(String[] args) { | ||
List<String> 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 NativeImageNoOpRuntimeInstrumentation 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<Module> extraReads, Map<String, Set<Module>> extraExports, Map<String, Set<Module>> extraOpens, Set<Class<?>> extraUses, | ||
Map<Class<?>, List<Class<?>>> extraProvides) { | ||
} | ||
|
||
@Override | ||
public boolean isModifiableModule(Module module) { | ||
return false; | ||
} | ||
} | ||
} |
Oops, something went wrong.