Skip to content

Commit 7b089dd

Browse files
committed
Support instrumentation class by agent
This commit supports the static class instrumentation by agent, i.e. the target class is transformed in Agent's preMain method. Instrumentation is supported in 3 stages: 1. Interception stage: native-image-agent records all the transfromed classes, dynamic generated classes, and the target agent's premain method. 2. Build time stage: Instrumented classes are prepended to the beginning of imageCp or patched to modules, so they will override their original classes at build time. 3. Runtime stage: Add premain in JavaMainWrapper before executing Java main. The recorded agent premain method shall be executed at this moment. It is the agent developer's responsibility to make sure the premain is compatible with native image. For example, the byte code transformation should be removed from the native premain.
1 parent 29ebe6e commit 7b089dd

File tree

48 files changed

+2836
-67
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+2836
-67
lines changed

sdk/mx.sdk/suite.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,9 @@
320320
"dependencies" : [
321321
"org.graalvm.nativeimage",
322322
],
323+
"requiresConcealed" : {
324+
"java.base" : ["jdk.internal.module"]
325+
},
323326
"checkstyle" : "org.graalvm.word",
324327
"javaCompliance" : "11+",
325328
"workingSets" : "API,SDK",
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2024, Alibaba Group Holding Limited. All rights reserved.
4+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5+
*
6+
* This code is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 only, as
8+
* published by the Free Software Foundation. Oracle designates this
9+
* particular file as subject to the "Classpath" exception as provided
10+
* by Oracle in the LICENSE file that accompanied this code.
11+
*
12+
* This code is distributed in the hope that it will be useful, but WITHOUT
13+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15+
* version 2 for more details (a copy is included in the LICENSE file that
16+
* accompanied this code).
17+
*
18+
* You should have received a copy of the GNU General Public License version
19+
* 2 along with this work; if not, write to the Free Software Foundation,
20+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21+
*
22+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23+
* or visit www.oracle.com if you need additional information or have any
24+
* questions.
25+
*/
26+
27+
package com.oracle.svm.core.annotate;
28+
29+
import org.graalvm.nativeimage.Platform;
30+
import org.graalvm.nativeimage.Platforms;
31+
32+
import java.lang.annotation.ElementType;
33+
import java.lang.annotation.Retention;
34+
import java.lang.annotation.RetentionPolicy;
35+
import java.lang.annotation.Target;
36+
import java.util.Optional;
37+
import java.util.function.BooleanSupplier;
38+
import java.util.function.Predicate;
39+
40+
41+
public class Advice {
42+
43+
public enum NotFoundAction {
44+
error, // throw an exception
45+
ignore, // ignore, nothing will be reported
46+
info // report as a log message
47+
}
48+
49+
public static class NoException extends Throwable {
50+
private static final long serialVersionUID = 2072039301986965302L;
51+
}
52+
53+
54+
/**
55+
* This indicates if a substitution method is for agent support.
56+
* User provided advice methods could be conflicted with the existing substitution methods. In this case, the advice
57+
* method shall be ignored.
58+
* This annotation is added by GraalVM framework when it generates the substitutions from advice methods.
59+
*/
60+
@Retention(RetentionPolicy.RUNTIME)
61+
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
62+
@Platforms(Platform.HOSTED_ONLY.class)
63+
public @interface ForAgentSupport {
64+
65+
}
66+
67+
@Retention(RetentionPolicy.RUNTIME)
68+
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
69+
@Platforms(Platform.HOSTED_ONLY.class)
70+
public @interface Before {
71+
/**
72+
* The names of method to insert before. This value overrides the name of its annotated method.
73+
* @return
74+
*/
75+
String[] value() default {};
76+
77+
/**
78+
* The action to take when the specified method is not found.
79+
* @return
80+
*/
81+
NotFoundAction notFoundAction() default NotFoundAction.error;
82+
83+
/**
84+
* Specify the returning type criteria. It must be a {@link BooleanSupplier} or {@link Predicate} class.
85+
* @return
86+
*/
87+
Class<?>[] onlyWithReturnType() default TargetClass.AlwaysIncluded.class;
88+
}
89+
90+
@Retention(RetentionPolicy.RUNTIME)
91+
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
92+
@Platforms(Platform.HOSTED_ONLY.class)
93+
public @interface After {
94+
/**
95+
* The names of method to insert before. This value overrides the name of its annotated method.
96+
* @return
97+
*/
98+
String[] value() default {};
99+
100+
/**
101+
* The action to take when the specified method is not found.
102+
* @return
103+
*/
104+
NotFoundAction notFoundAction() default NotFoundAction.error;
105+
106+
/**
107+
* Specify the returning type criteria. It must be a {@link BooleanSupplier} or {@link Predicate} class.
108+
* @return
109+
*/
110+
Class<?>[] onlyWithReturnType() default TargetClass.AlwaysIncluded.class;
111+
112+
/**
113+
* Allow advice method to be invoked when the indicated {@link Throwable} class is thrown from the original method.
114+
* By default, the after advice method won't be invoked if any exception is thrown from the original method.
115+
* @return
116+
*/
117+
Class<? extends Throwable> onThrowable() default NoException.class;
118+
}
119+
120+
/**
121+
* Represent returned value from the target method.
122+
* This can only appear in the parameter of {@link After} annotated method.
123+
*/
124+
@Retention(RetentionPolicy.RUNTIME)
125+
@Target({ElementType.PARAMETER})
126+
@Platforms(Platform.HOSTED_ONLY.class)
127+
public @interface Return {
128+
}
129+
130+
/**
131+
* Represent returned value from its corresponding {@link Before} method.
132+
* This can only appear in the parameter of {@link After} annotated method.
133+
*/
134+
@Retention(RetentionPolicy.RUNTIME)
135+
@Target({ElementType.PARAMETER, ElementType.FIELD})
136+
@Platforms(Platform.HOSTED_ONLY.class)
137+
public @interface BeforeResult {
138+
}
139+
140+
/**
141+
* Represent the {@code this} reference of the original method.
142+
*/
143+
@Retention(RetentionPolicy.RUNTIME)
144+
@Target({ElementType.PARAMETER})
145+
@Platforms(Platform.HOSTED_ONLY.class)
146+
public @interface This {
147+
}
148+
149+
/**
150+
* Represent the exception value thrown by the original method.
151+
* The annotated parameter type must be the same as the returning type of {@link After#onThrowable()}.
152+
* I.e. {@link After#onThrowable()} must be present in order to use this annotation.
153+
*/
154+
@Retention(RetentionPolicy.RUNTIME)
155+
@Target({ElementType.PARAMETER})
156+
@Platforms(Platform.HOSTED_ONLY.class)
157+
public @interface Thrown {
158+
}
159+
160+
/**
161+
* Indicate the annotated parameter shall be rewritten to a new value and pass to the original method.
162+
* This must be annotated on the parameter in {@link Before} method.
163+
* It takes effects together with {@link ResultWrapper}, i.e. another class annotated with {@link ResultWrapper} must
164+
* present to hold the rewritten parameter values.
165+
*/
166+
@Retention(RetentionPolicy.RUNTIME)
167+
@Target({ElementType.PARAMETER})
168+
@Platforms(Platform.HOSTED_ONLY.class)
169+
public @interface Rewrite {
170+
/**
171+
* Indicate the {@link ResultWrapper} class' field name that holds this parameter's rewritten value.
172+
* @return
173+
*/
174+
String field();
175+
}
176+
177+
/**
178+
* Annotated on the class holds {@link Rewrite} parameter values and the returning {@link BeforeResult} value in
179+
* {@link Before} method.
180+
* <p>
181+
* An instance of the annotated class is created, filled and returned by the {@link Before} method.
182+
* <p>
183+
* The annotated class and all its fields <b>must</b> all be <b>public</b> to other classes.
184+
* <p>
185+
* In the annotated class, field annotated with {@link BeforeResult} holds value for {@link BeforeResult} parameter in
186+
* {@link After} method.
187+
* Other fields correspond to the {@link Rewrite} parameter according to field name specified by {@link Rewrite#field()}.
188+
* And they must be of {@link Optional} type. A {@code null} value of the field means the parameter is not rewritten.
189+
*
190+
*/
191+
@Retention(RetentionPolicy.RUNTIME)
192+
@Target({ElementType.TYPE})
193+
@Platforms(Platform.HOSTED_ONLY.class)
194+
public @interface ResultWrapper {
195+
}
196+
197+
/**
198+
* Check if {@code clazz} implements interface {@code interfaceClass} directly or indirectly.
199+
*
200+
* @param interfaceClass interface class
201+
* @param clazz the class to check
202+
* @return true if {@code clazz} implements {@code interfaceClass} directly or indirectly.
203+
*/
204+
public static boolean isInterfaceOf(Class<?> interfaceClass, Class<?> clazz) {
205+
if (clazz == null || clazz.equals(Object.class)) {
206+
return false;
207+
}
208+
// The fast path, if clazz directly implements interfaceClass
209+
for (Class<?> anInterface : clazz.getInterfaces()) {
210+
if (interfaceClass.equals(anInterface)) {
211+
return true;
212+
}
213+
}
214+
215+
// Check the interface hierarchy
216+
for (Class<?> anInterface : clazz.getInterfaces()) {
217+
if (isInterfaceOf(interfaceClass, anInterface)) {
218+
return true;
219+
}
220+
}
221+
222+
// Check the super class hierarchy
223+
return isInterfaceOf(interfaceClass, clazz.getSuperclass());
224+
}
225+
}

sdk/src/com.oracle.svm.core.annotate/src/com/oracle/svm/core/annotate/Alias.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,38 @@
7676
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD})
7777
@Platforms(Platform.HOSTED_ONLY.class)
7878
public @interface Alias {
79+
/**
80+
* Indicates the annotated method will always refer to the original method and not be substituted. It is used to surround
81+
* the original method with advice. For example, given an original method {@code foo} defined as following:
82+
* <pre>
83+
* public class TestCase {
84+
* public void foo(){//...}
85+
* }
86+
* </pre>
87+
* If we'd like to insert some code before {@code foo}, we can do like this:
88+
* <pre>
89+
* {@literal @}TargetClass(TestCase.class)
90+
* public class Target_TestCase {
91+
*
92+
* {@literal @}Alias(noSubstitution=true)
93+
* {@literal @}TargetElement(name="foo")
94+
* public native void oldFoo();
95+
*
96+
* {@literal @}Substitute
97+
* {@literal @}TargetElement(name="foo")
98+
* public void newFoo(){
99+
* // ... do something before foo
100+
* oldFoo(); // still invoke the original foo
101+
* }
102+
* }
103+
* </pre>
104+
* The {@code oldFoo} and {@code newFoo} both refer to the original {@code foo}.
105+
* At build time, {@code oldFoo} firstly refers to {@code foo}, but could be eventually replaced to {@code newFoo}. Then
106+
* the {@code newFoo} will recursively call itself.
107+
* <p>
108+
* Therefore, we need to declare {@code noSubstitution=true} on {@code oldFoo} to indicate that {@code oldFoo} always refers
109+
* to the original {@code foo}, and will never be substituted by {@code newFoo}.
110+
* @return
111+
*/
112+
boolean noSubstitution() default false;
79113
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2024, Alibaba Group Holding Limited. All rights reserved.
4+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5+
*
6+
* This code is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 only, as
8+
* published by the Free Software Foundation. Oracle designates this
9+
* particular file as subject to the "Classpath" exception as provided
10+
* by Oracle in the LICENSE file that accompanied this code.
11+
*
12+
* This code is distributed in the hope that it will be useful, but WITHOUT
13+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15+
* version 2 for more details (a copy is included in the LICENSE file that
16+
* accompanied this code).
17+
*
18+
* You should have received a copy of the GNU General Public License version
19+
* 2 along with this work; if not, write to the Free Software Foundation,
20+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21+
*
22+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23+
* or visit www.oracle.com if you need additional information or have any
24+
* questions.
25+
*/
26+
27+
package com.oracle.svm.core.annotate;
28+
29+
import jdk.internal.module.ModuleLoaderMap;
30+
import org.graalvm.nativeimage.Platform;
31+
import org.graalvm.nativeimage.Platforms;
32+
33+
import java.lang.annotation.ElementType;
34+
import java.lang.annotation.Retention;
35+
import java.lang.annotation.RetentionPolicy;
36+
import java.lang.annotation.Target;
37+
import java.util.function.Predicate;
38+
39+
@Retention(RetentionPolicy.RUNTIME)
40+
@Target({ElementType.TYPE})
41+
@Platforms(Platform.HOSTED_ONLY.class)
42+
public @interface Aspect {
43+
/**
44+
* Specify a list of class names that will be enhanced.
45+
*
46+
* @return full qualified class names
47+
*/
48+
String[] matchers() default {};
49+
50+
/**
51+
* All subClasses of the specified class will be enhanced.
52+
*
53+
* @return
54+
*/
55+
String subClassOf() default "";
56+
57+
/**
58+
* Match all classes that implement the specified interface.
59+
* @return the qualified name of interface
60+
*/
61+
String implementInterface() default "";
62+
63+
/**
64+
* Same as {@link TargetElement#onlyWith()}.
65+
*
66+
* @return
67+
*/
68+
Class<?>[] onlyWith() default TargetClass.AlwaysIncluded.class;
69+
70+
/**
71+
* Check if the given class is JDK class.
72+
*/
73+
class JDKClassOnly implements Predicate<Class<?>> {
74+
75+
@Override
76+
public boolean test(Class<?> c) {
77+
String moduleName = c.getModule().getName();
78+
if (moduleName == null) {
79+
return false;
80+
}
81+
return ModuleLoaderMap.bootModules().contains(moduleName) || ModuleLoaderMap.platformModules().contains(moduleName);
82+
}
83+
}
84+
}

0 commit comments

Comments
 (0)