Skip to content

Commit ef0cd18

Browse files
committed
8354949: JFR: Split up the EventInstrumentation class
Reviewed-by: mgronlun, liach
1 parent 82c2494 commit ef0cd18

File tree

5 files changed

+621
-529
lines changed

5 files changed

+621
-529
lines changed
Lines changed: 361 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
1+
/*
2+
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package jdk.jfr.internal;
26+
27+
import static jdk.jfr.internal.util.Bytecode.classDesc;
28+
29+
import java.lang.classfile.Annotation;
30+
import java.lang.classfile.AnnotationElement;
31+
import java.lang.classfile.AnnotationValue;
32+
import java.lang.classfile.Attribute;
33+
import java.lang.classfile.ClassFile;
34+
import java.lang.classfile.ClassModel;
35+
import java.lang.classfile.FieldModel;
36+
import java.lang.classfile.MethodModel;
37+
import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute;
38+
import java.lang.constant.ClassDesc;
39+
import java.lang.constant.MethodTypeDesc;
40+
import java.lang.reflect.Field;
41+
import java.lang.reflect.Modifier;
42+
import java.lang.constant.ConstantDescs;
43+
import java.util.ArrayList;
44+
import java.util.HashSet;
45+
import java.util.List;
46+
import java.util.Set;
47+
48+
import jdk.jfr.Enabled;
49+
import jdk.jfr.Name;
50+
import jdk.jfr.Registered;
51+
import jdk.jfr.SettingControl;
52+
import jdk.jfr.SettingDefinition;
53+
import jdk.jfr.internal.util.Bytecode;
54+
import jdk.jfr.internal.util.ImplicitFields;
55+
import jdk.jfr.internal.util.Bytecode.FieldDesc;
56+
import jdk.jfr.internal.util.Bytecode.MethodDesc;
57+
import jdk.jfr.internal.util.Bytecode.SettingDesc;
58+
import jdk.jfr.internal.util.Utils;
59+
60+
final class ClassInspector {
61+
private static final ClassDesc TYPE_SETTING_DEFINITION = Bytecode.classDesc(SettingDefinition.class);
62+
private static final ClassDesc ANNOTATION_REGISTERED = classDesc(Registered.class);
63+
private static final ClassDesc ANNOTATION_NAME = classDesc(Name.class);
64+
private static final ClassDesc ANNOTATION_ENABLED = classDesc(Enabled.class);
65+
private static final ClassDesc ANNOTATION_REMOVE_FIELDS = classDesc(RemoveFields.class);
66+
67+
private final ClassModel classModel;
68+
private final Class<?> superClass;
69+
private final boolean isJDK;
70+
private final ImplicitFields implicitFields;
71+
private final List<SettingDesc> settingsDescs = new ArrayList<>();
72+
private final List<FieldDesc> fieldDescs = new ArrayList<>();
73+
private final String className;
74+
75+
ClassInspector(Class<?> superClass, byte[] bytes, boolean isJDK) {
76+
this.superClass = superClass;
77+
this.classModel = ClassFile.of().parse(bytes);
78+
this.isJDK = isJDK;
79+
this.className = classModel.thisClass().asInternalName().replace("/", ".");
80+
this.implicitFields = determineImplicitFields();
81+
}
82+
83+
String getClassName() {
84+
return className;
85+
}
86+
87+
MethodDesc findStaticCommitMethod() {
88+
if (!isJDK) {
89+
return null;
90+
}
91+
StringBuilder sb = new StringBuilder();
92+
sb.append("(");
93+
for (FieldDesc field : fieldDescs) {
94+
sb.append(field.type().descriptorString());
95+
}
96+
sb.append(")V");
97+
MethodDesc m = MethodDesc.of("commit", sb.toString());
98+
for (MethodModel method : classModel.methods()) {
99+
if (m.matches(method)) {
100+
return m;
101+
}
102+
}
103+
return null;
104+
}
105+
106+
String getEventName() {
107+
String name = annotationValue(ANNOTATION_NAME, String.class);
108+
return name == null ? getClassName() : name;
109+
}
110+
111+
boolean isRegistered() {
112+
Boolean result = annotationValue(ANNOTATION_REGISTERED, Boolean.class);
113+
if (result != null) {
114+
return result.booleanValue();
115+
}
116+
if (superClass != null) {
117+
Registered r = superClass.getAnnotation(Registered.class);
118+
if (r != null) {
119+
return r.value();
120+
}
121+
}
122+
return true;
123+
}
124+
125+
boolean isEnabled() {
126+
Boolean result = annotationValue(ANNOTATION_ENABLED, Boolean.class);
127+
if (result != null) {
128+
return result.booleanValue();
129+
}
130+
if (superClass != null) {
131+
Enabled e = superClass.getAnnotation(Enabled.class);
132+
if (e != null) {
133+
return e.value();
134+
}
135+
}
136+
return true;
137+
}
138+
139+
boolean hasStaticMethod(MethodDesc method) {
140+
for (MethodModel m : classModel.methods()) {
141+
if (Modifier.isStatic(m.flags().flagsMask())) {
142+
return method.matches(m);
143+
}
144+
}
145+
return false;
146+
}
147+
148+
static boolean isValidField(int access, ClassDesc classDesc) {
149+
String className = classDesc.packageName();
150+
if (!className.isEmpty()) {
151+
className = className + ".";
152+
}
153+
className += classDesc.displayName();
154+
return isValidField(access, className);
155+
}
156+
157+
static boolean isValidField(int access, String className) {
158+
if (Modifier.isTransient(access) || Modifier.isStatic(access)) {
159+
return false;
160+
}
161+
return Type.isValidJavaFieldType(className);
162+
}
163+
164+
List<SettingDesc> getSettings() {
165+
return settingsDescs;
166+
}
167+
168+
List<FieldDesc> getFields() {
169+
return fieldDescs;
170+
}
171+
172+
boolean hasDuration() {
173+
return implicitFields.hasDuration();
174+
}
175+
176+
boolean hasStackTrace() {
177+
return implicitFields.hasStackTrace();
178+
}
179+
180+
boolean hasEventThread() {
181+
return implicitFields.hasEventThread();
182+
}
183+
184+
ClassDesc getClassDesc() {
185+
return classModel.thisClass().asSymbol();
186+
}
187+
188+
ClassModel getClassModel() {
189+
return classModel;
190+
}
191+
192+
boolean isJDK() {
193+
return isJDK;
194+
}
195+
196+
private ImplicitFields determineImplicitFields() {
197+
if (isJDK) {
198+
Class<?> eventClass = MirrorEvents.find(isJDK, getClassName());
199+
if (eventClass != null) {
200+
return new ImplicitFields(eventClass);
201+
}
202+
}
203+
ImplicitFields ifs = new ImplicitFields(superClass);
204+
String[] value = annotationValue(ANNOTATION_REMOVE_FIELDS, String[].class);
205+
if (value != null) {
206+
ifs.removeFields(value);
207+
}
208+
return ifs;
209+
}
210+
211+
private List<AnnotationValue> getAnnotationValues(ClassDesc classDesc) {
212+
List<AnnotationValue> list = new ArrayList<>();
213+
for (Attribute<?> attribute: classModel.attributes()) {
214+
if (attribute instanceof RuntimeVisibleAnnotationsAttribute rvaa) {
215+
for (Annotation a : rvaa.annotations()) {
216+
if (a.classSymbol().equals(classDesc) && a.elements().size() == 1) {
217+
AnnotationElement ae = a.elements().getFirst();
218+
if (ae.name().equalsString("value")) {
219+
list.add(ae.value());
220+
}
221+
}
222+
}
223+
}
224+
}
225+
return list;
226+
}
227+
228+
@SuppressWarnings("unchecked")
229+
// Only supports String, String[] and Boolean values
230+
private <T> T annotationValue(ClassDesc classDesc, Class<T> type) {
231+
for (AnnotationValue a : getAnnotationValues(classDesc)) {
232+
if (a instanceof AnnotationValue.OfBoolean ofb && type.equals(Boolean.class)) {
233+
Boolean b = ofb.booleanValue();
234+
return (T) b;
235+
}
236+
if (a instanceof AnnotationValue.OfString ofs && type.equals(String.class)) {
237+
String s = ofs.stringValue();
238+
return (T) s;
239+
}
240+
if (a instanceof AnnotationValue.OfArray ofa && type.equals(String[].class)) {
241+
List<AnnotationValue> list = ofa.values();
242+
String[] array = new String[list.size()];
243+
int index = 0;
244+
for (AnnotationValue av : list) {
245+
var avs = (AnnotationValue.OfString) av;
246+
array[index++] = avs.stringValue();
247+
}
248+
return (T) array;
249+
}
250+
}
251+
return null;
252+
}
253+
254+
void buildSettings() {
255+
Set<String> foundMethods = new HashSet<>();
256+
buildClassSettings(foundMethods);
257+
buildSuperClassSettings(foundMethods);
258+
}
259+
260+
private void buildClassSettings(Set<String> foundMethods) {
261+
for (MethodModel m : classModel.methods()) {
262+
for (Attribute<?> attribute : m.attributes()) {
263+
if (attribute instanceof RuntimeVisibleAnnotationsAttribute rvaa) {
264+
for (Annotation a : rvaa.annotations()) {
265+
// We can't really validate the method at this
266+
// stage. We would need to check that the parameter
267+
// is an instance of SettingControl.
268+
if (a.classSymbol().equals(TYPE_SETTING_DEFINITION)) {
269+
String name = m.methodName().stringValue();
270+
// Use @Name if it exists
271+
for (Annotation nameCandidate : rvaa.annotations()) {
272+
if (nameCandidate.className().equalsString(ANNOTATION_NAME.descriptorString())) {
273+
if (nameCandidate.elements().size() == 1) {
274+
AnnotationElement ae = nameCandidate.elements().getFirst();
275+
if (ae.name().equalsString("value")) {
276+
if (ae.value() instanceof AnnotationValue.OfString s) {
277+
name = Utils.validJavaIdentifier(s.stringValue(), name);
278+
}
279+
}
280+
}
281+
}
282+
}
283+
// Add setting if method returns boolean and has one parameter
284+
MethodTypeDesc mtd = m.methodTypeSymbol();
285+
if (ConstantDescs.CD_boolean.equals(mtd.returnType())) {
286+
if (mtd.parameterList().size() == 1) {
287+
ClassDesc type = mtd.parameterList().getFirst();
288+
if (type.isClassOrInterface()) {
289+
String methodName = m.methodName().stringValue();
290+
foundMethods.add(methodName);
291+
settingsDescs.add(new SettingDesc(type, methodName));
292+
}
293+
}
294+
}
295+
}
296+
}
297+
}
298+
}
299+
}
300+
}
301+
302+
private void buildSuperClassSettings(Set<String> foundMethods) {
303+
for (Class<?> c = superClass; jdk.internal.event.Event.class != c; c = c.getSuperclass()) {
304+
for (java.lang.reflect.Method method : c.getDeclaredMethods()) {
305+
if (!foundMethods.contains(method.getName())) {
306+
buildSettingsMethod(foundMethods, method);
307+
}
308+
}
309+
}
310+
}
311+
312+
private void buildSettingsMethod(Set<String> foundMethods, java.lang.reflect.Method method) {
313+
// Skip private methods in base classes
314+
if (!Modifier.isPrivate(method.getModifiers())) {
315+
if (method.getReturnType().equals(Boolean.TYPE)) {
316+
if (method.getParameterCount() == 1) {
317+
Class<?> type = method.getParameters()[0].getType();
318+
if (SettingControl.class.isAssignableFrom(type)) {
319+
ClassDesc paramType = Bytecode.classDesc(type);
320+
foundMethods.add(method.getName());
321+
settingsDescs.add(new SettingDesc(paramType, method.getName()));
322+
}
323+
}
324+
}
325+
}
326+
}
327+
328+
void buildFields() {
329+
Set<String> foundFields = new HashSet<>();
330+
// These two fields are added by native as 'transient' so they will be
331+
// ignored by the loop below.
332+
// The benefit of adding them manually is that we can
333+
// control in which order they occur and we can add @Name, @Description
334+
// in Java, instead of in native. It also means code for adding implicit
335+
// fields for native can be reused by Java.
336+
fieldDescs.add(ImplicitFields.FIELD_START_TIME);
337+
if (implicitFields.hasDuration()) {
338+
fieldDescs.add(ImplicitFields.FIELD_DURATION);
339+
}
340+
for (FieldModel field : classModel.fields()) {
341+
if (!foundFields.contains(field.fieldName().stringValue()) && isValidField(field.flags().flagsMask(), field.fieldTypeSymbol())) {
342+
fieldDescs.add(FieldDesc.of(field.fieldTypeSymbol(), field.fieldName().stringValue()));
343+
foundFields.add(field.fieldName().stringValue());
344+
}
345+
}
346+
for (Class<?> c = superClass; jdk.internal.event.Event.class != c; c = c.getSuperclass()) {
347+
for (Field field : c.getDeclaredFields()) {
348+
// Skip private fields in base classes
349+
if (!Modifier.isPrivate(field.getModifiers())) {
350+
if (isValidField(field.getModifiers(), field.getType().getName())) {
351+
String fieldName = field.getName();
352+
if (!foundFields.contains(fieldName)) {
353+
fieldDescs.add(FieldDesc.of(field.getType(), fieldName));
354+
foundFields.add(fieldName);
355+
}
356+
}
357+
}
358+
}
359+
}
360+
}
361+
}

0 commit comments

Comments
 (0)