Skip to content

Commit 46edf40

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 5ac4fcd commit 46edf40

File tree

47 files changed

+2864
-64
lines changed

Some content is hidden

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

47 files changed

+2864
-64
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: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
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+
public class Advice {
41+
42+
public enum NotFoundAction {
43+
error, // throw an exception
44+
ignore, // ignore, nothing will be reported
45+
info // report as a log message
46+
}
47+
48+
public static class NoException extends Throwable {
49+
private static final long serialVersionUID = 2072039301986965302L;
50+
}
51+
52+
/**
53+
* This indicates if a substitution method is for agent support. User provided advice methods
54+
* could be conflicted with the existing substitution methods. In this case, the advice method
55+
* shall be ignored. This annotation is added by GraalVM framework when it generates the
56+
* substitutions from advice methods.
57+
*/
58+
@Retention(RetentionPolicy.RUNTIME)
59+
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
60+
@Platforms(Platform.HOSTED_ONLY.class)
61+
public @interface ForAgentSupport {
62+
63+
}
64+
65+
@Retention(RetentionPolicy.RUNTIME)
66+
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
67+
@Platforms(Platform.HOSTED_ONLY.class)
68+
public @interface Before {
69+
/**
70+
* The names of method to insert before. This value overrides the name of its annotated
71+
* method.
72+
*
73+
* @return
74+
*/
75+
String[] value() default {};
76+
77+
/**
78+
* The action to take when the specified method is not found.
79+
*
80+
* @return
81+
*/
82+
NotFoundAction notFoundAction() default NotFoundAction.error;
83+
84+
/**
85+
* Specify the returning type criteria. It must be a {@link BooleanSupplier} or
86+
* {@link Predicate} class.
87+
*
88+
* @return
89+
*/
90+
Class<?>[] onlyWithReturnType() default TargetClass.AlwaysIncluded.class;
91+
}
92+
93+
@Retention(RetentionPolicy.RUNTIME)
94+
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
95+
@Platforms(Platform.HOSTED_ONLY.class)
96+
public @interface After {
97+
/**
98+
* The names of method to insert before. This value overrides the name of its annotated
99+
* method.
100+
*
101+
* @return
102+
*/
103+
String[] value() default {};
104+
105+
/**
106+
* The action to take when the specified method is not found.
107+
*
108+
* @return
109+
*/
110+
NotFoundAction notFoundAction() default NotFoundAction.error;
111+
112+
/**
113+
* Specify the returning type criteria. It must be a {@link BooleanSupplier} or
114+
* {@link Predicate} class.
115+
*
116+
* @return
117+
*/
118+
Class<?>[] onlyWithReturnType() default TargetClass.AlwaysIncluded.class;
119+
120+
/**
121+
* Allow advice method to be invoked when the indicated {@link Throwable} class is thrown
122+
* from the original method. By default, the after advice method won't be invoked if any
123+
* exception is thrown from the original method.
124+
*
125+
* @return
126+
*/
127+
Class<? extends Throwable> onThrowable() default NoException.class;
128+
}
129+
130+
/**
131+
* Represent returned value from the target method. This can only appear in the parameter of
132+
* {@link After} annotated method.
133+
*/
134+
@Retention(RetentionPolicy.RUNTIME)
135+
@Target({ElementType.PARAMETER})
136+
@Platforms(Platform.HOSTED_ONLY.class)
137+
public @interface Return {
138+
}
139+
140+
/**
141+
* Represent returned value from its corresponding {@link Before} method. This can only appear
142+
* in the parameter of {@link After} annotated method.
143+
*/
144+
@Retention(RetentionPolicy.RUNTIME)
145+
@Target({ElementType.PARAMETER, ElementType.FIELD})
146+
@Platforms(Platform.HOSTED_ONLY.class)
147+
public @interface BeforeResult {
148+
}
149+
150+
/**
151+
* Represent the {@code this} reference of the original method.
152+
*/
153+
@Retention(RetentionPolicy.RUNTIME)
154+
@Target({ElementType.PARAMETER})
155+
@Platforms(Platform.HOSTED_ONLY.class)
156+
public @interface This {
157+
}
158+
159+
/**
160+
* Represent the exception value thrown by the original method. The annotated parameter type
161+
* must be the same as the returning type of {@link After#onThrowable()}. I.e.
162+
* {@link After#onThrowable()} must be present in order to use this annotation.
163+
*/
164+
@Retention(RetentionPolicy.RUNTIME)
165+
@Target({ElementType.PARAMETER})
166+
@Platforms(Platform.HOSTED_ONLY.class)
167+
public @interface Thrown {
168+
}
169+
170+
/**
171+
* Indicate the annotated parameter shall be rewritten to a new value and pass to the original
172+
* method. This must be annotated on the parameter in {@link Before} method. It takes effects
173+
* together with {@link ResultWrapper}, i.e. another class annotated with {@link ResultWrapper}
174+
* must present to hold the rewritten parameter values.
175+
*/
176+
@Retention(RetentionPolicy.RUNTIME)
177+
@Target({ElementType.PARAMETER})
178+
@Platforms(Platform.HOSTED_ONLY.class)
179+
public @interface Rewrite {
180+
/**
181+
* Indicate the {@link ResultWrapper} class' field name that holds this parameter's
182+
* rewritten value.
183+
*
184+
* @return
185+
*/
186+
String field();
187+
}
188+
189+
/**
190+
* Annotated on the class holds {@link Rewrite} parameter values and the returning
191+
* {@link BeforeResult} value in {@link Before} method.
192+
* <p>
193+
* An instance of the annotated class is created, filled and returned by the {@link Before}
194+
* method.
195+
* <p>
196+
* The annotated class and all its fields <b>must</b> all be <b>public</b> to other classes.
197+
* <p>
198+
* In the annotated class, field annotated with {@link BeforeResult} holds value for
199+
* {@link BeforeResult} parameter in {@link After} method. Other fields correspond to the
200+
* {@link Rewrite} parameter according to field name specified by {@link Rewrite#field()}. And
201+
* they must be of {@link Optional} type. A {@code null} value of the field means the parameter
202+
* is not rewritten.
203+
*
204+
*/
205+
@Retention(RetentionPolicy.RUNTIME)
206+
@Target({ElementType.TYPE})
207+
@Platforms(Platform.HOSTED_ONLY.class)
208+
public @interface ResultWrapper {
209+
}
210+
211+
/**
212+
* Check if {@code clazz} implements interface {@code interfaceClass} directly or indirectly.
213+
*
214+
* @param interfaceClass interface class
215+
* @param clazz the class to check
216+
* @return true if {@code clazz} implements {@code interfaceClass} directly or indirectly.
217+
*/
218+
public static boolean isInterfaceOf(Class<?> interfaceClass, Class<?> clazz) {
219+
if (clazz == null || clazz.equals(Object.class)) {
220+
return false;
221+
}
222+
// The fast path, if clazz directly implements interfaceClass
223+
for (Class<?> anInterface : clazz.getInterfaces()) {
224+
if (interfaceClass.equals(anInterface)) {
225+
return true;
226+
}
227+
}
228+
229+
// Check the interface hierarchy
230+
for (Class<?> anInterface : clazz.getInterfaces()) {
231+
if (isInterfaceOf(interfaceClass, anInterface)) {
232+
return true;
233+
}
234+
}
235+
236+
// Check the super class hierarchy
237+
return isInterfaceOf(interfaceClass, clazz.getSuperclass());
238+
}
239+
}

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,45 @@
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
81+
* substituted. It is used to surround the original method with advice. For example, given an
82+
* original method {@code foo} defined as following:
83+
*
84+
* <pre>
85+
* public class TestCase {
86+
* public void foo(){//...}
87+
* }
88+
* </pre>
89+
*
90+
* If we'd like to insert some code before {@code foo}, we can do like this:
91+
*
92+
* <pre>
93+
* {@literal @}TargetClass(TestCase.class)
94+
* public class Target_TestCase {
95+
*
96+
* {@literal @}Alias(noSubstitution=true)
97+
* {@literal @}TargetElement(name="foo")
98+
* public native void oldFoo();
99+
*
100+
* {@literal @}Substitute
101+
* {@literal @}TargetElement(name="foo")
102+
* public void newFoo(){
103+
* // ... do something before foo
104+
* oldFoo(); // still invoke the original foo
105+
* }
106+
* }
107+
* </pre>
108+
*
109+
* The {@code oldFoo} and {@code newFoo} both refer to the original {@code foo}. At build time,
110+
* {@code oldFoo} firstly refers to {@code foo}, but could be eventually replaced to
111+
* {@code newFoo}. Then the {@code newFoo} will recursively call itself.
112+
* <p>
113+
* Therefore, we need to declare {@code noSubstitution=true} on {@code oldFoo} to indicate that
114+
* {@code oldFoo} always refers to the original {@code foo}, and will never be substituted by
115+
* {@code newFoo}.
116+
*
117+
* @return
118+
*/
119+
boolean noSubstitution() default false;
79120
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
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+
*
60+
* @return the qualified name of interface
61+
*/
62+
String implementInterface() default "";
63+
64+
/**
65+
* Same as {@link TargetElement#onlyWith()}.
66+
*
67+
* @return
68+
*/
69+
Class<?>[] onlyWith() default TargetClass.AlwaysIncluded.class;
70+
71+
/**
72+
* Check if the given class is JDK class.
73+
*/
74+
class JDKClassOnly implements Predicate<Class<?>> {
75+
76+
@Override
77+
public boolean test(Class<?> c) {
78+
String moduleName = c.getModule().getName();
79+
if (moduleName == null) {
80+
return false;
81+
}
82+
return ModuleLoaderMap.bootModules().contains(moduleName) || ModuleLoaderMap.platformModules().contains(moduleName);
83+
}
84+
}
85+
}

0 commit comments

Comments
 (0)