8354949: JFR: Split up the EventInstrumentation class

Reviewed-by: mgronlun, liach
This commit is contained in:
Erik Gahlin 2025-04-23 11:48:48 +00:00
parent 82c249446f
commit ef0cd1823d
5 changed files with 643 additions and 551 deletions

View File

@ -0,0 +1,361 @@
/*
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. 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 jdk.jfr.internal;
import static jdk.jfr.internal.util.Bytecode.classDesc;
import java.lang.classfile.Annotation;
import java.lang.classfile.AnnotationElement;
import java.lang.classfile.AnnotationValue;
import java.lang.classfile.Attribute;
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassModel;
import java.lang.classfile.FieldModel;
import java.lang.classfile.MethodModel;
import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute;
import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.constant.ConstantDescs;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import jdk.jfr.Enabled;
import jdk.jfr.Name;
import jdk.jfr.Registered;
import jdk.jfr.SettingControl;
import jdk.jfr.SettingDefinition;
import jdk.jfr.internal.util.Bytecode;
import jdk.jfr.internal.util.ImplicitFields;
import jdk.jfr.internal.util.Bytecode.FieldDesc;
import jdk.jfr.internal.util.Bytecode.MethodDesc;
import jdk.jfr.internal.util.Bytecode.SettingDesc;
import jdk.jfr.internal.util.Utils;
final class ClassInspector {
private static final ClassDesc TYPE_SETTING_DEFINITION = Bytecode.classDesc(SettingDefinition.class);
private static final ClassDesc ANNOTATION_REGISTERED = classDesc(Registered.class);
private static final ClassDesc ANNOTATION_NAME = classDesc(Name.class);
private static final ClassDesc ANNOTATION_ENABLED = classDesc(Enabled.class);
private static final ClassDesc ANNOTATION_REMOVE_FIELDS = classDesc(RemoveFields.class);
private final ClassModel classModel;
private final Class<?> superClass;
private final boolean isJDK;
private final ImplicitFields implicitFields;
private final List<SettingDesc> settingsDescs = new ArrayList<>();
private final List<FieldDesc> fieldDescs = new ArrayList<>();
private final String className;
ClassInspector(Class<?> superClass, byte[] bytes, boolean isJDK) {
this.superClass = superClass;
this.classModel = ClassFile.of().parse(bytes);
this.isJDK = isJDK;
this.className = classModel.thisClass().asInternalName().replace("/", ".");
this.implicitFields = determineImplicitFields();
}
String getClassName() {
return className;
}
MethodDesc findStaticCommitMethod() {
if (!isJDK) {
return null;
}
StringBuilder sb = new StringBuilder();
sb.append("(");
for (FieldDesc field : fieldDescs) {
sb.append(field.type().descriptorString());
}
sb.append(")V");
MethodDesc m = MethodDesc.of("commit", sb.toString());
for (MethodModel method : classModel.methods()) {
if (m.matches(method)) {
return m;
}
}
return null;
}
String getEventName() {
String name = annotationValue(ANNOTATION_NAME, String.class);
return name == null ? getClassName() : name;
}
boolean isRegistered() {
Boolean result = annotationValue(ANNOTATION_REGISTERED, Boolean.class);
if (result != null) {
return result.booleanValue();
}
if (superClass != null) {
Registered r = superClass.getAnnotation(Registered.class);
if (r != null) {
return r.value();
}
}
return true;
}
boolean isEnabled() {
Boolean result = annotationValue(ANNOTATION_ENABLED, Boolean.class);
if (result != null) {
return result.booleanValue();
}
if (superClass != null) {
Enabled e = superClass.getAnnotation(Enabled.class);
if (e != null) {
return e.value();
}
}
return true;
}
boolean hasStaticMethod(MethodDesc method) {
for (MethodModel m : classModel.methods()) {
if (Modifier.isStatic(m.flags().flagsMask())) {
return method.matches(m);
}
}
return false;
}
static boolean isValidField(int access, ClassDesc classDesc) {
String className = classDesc.packageName();
if (!className.isEmpty()) {
className = className + ".";
}
className += classDesc.displayName();
return isValidField(access, className);
}
static boolean isValidField(int access, String className) {
if (Modifier.isTransient(access) || Modifier.isStatic(access)) {
return false;
}
return Type.isValidJavaFieldType(className);
}
List<SettingDesc> getSettings() {
return settingsDescs;
}
List<FieldDesc> getFields() {
return fieldDescs;
}
boolean hasDuration() {
return implicitFields.hasDuration();
}
boolean hasStackTrace() {
return implicitFields.hasStackTrace();
}
boolean hasEventThread() {
return implicitFields.hasEventThread();
}
ClassDesc getClassDesc() {
return classModel.thisClass().asSymbol();
}
ClassModel getClassModel() {
return classModel;
}
boolean isJDK() {
return isJDK;
}
private ImplicitFields determineImplicitFields() {
if (isJDK) {
Class<?> eventClass = MirrorEvents.find(isJDK, getClassName());
if (eventClass != null) {
return new ImplicitFields(eventClass);
}
}
ImplicitFields ifs = new ImplicitFields(superClass);
String[] value = annotationValue(ANNOTATION_REMOVE_FIELDS, String[].class);
if (value != null) {
ifs.removeFields(value);
}
return ifs;
}
private List<AnnotationValue> getAnnotationValues(ClassDesc classDesc) {
List<AnnotationValue> list = new ArrayList<>();
for (Attribute<?> attribute: classModel.attributes()) {
if (attribute instanceof RuntimeVisibleAnnotationsAttribute rvaa) {
for (Annotation a : rvaa.annotations()) {
if (a.classSymbol().equals(classDesc) && a.elements().size() == 1) {
AnnotationElement ae = a.elements().getFirst();
if (ae.name().equalsString("value")) {
list.add(ae.value());
}
}
}
}
}
return list;
}
@SuppressWarnings("unchecked")
// Only supports String, String[] and Boolean values
private <T> T annotationValue(ClassDesc classDesc, Class<T> type) {
for (AnnotationValue a : getAnnotationValues(classDesc)) {
if (a instanceof AnnotationValue.OfBoolean ofb && type.equals(Boolean.class)) {
Boolean b = ofb.booleanValue();
return (T) b;
}
if (a instanceof AnnotationValue.OfString ofs && type.equals(String.class)) {
String s = ofs.stringValue();
return (T) s;
}
if (a instanceof AnnotationValue.OfArray ofa && type.equals(String[].class)) {
List<AnnotationValue> list = ofa.values();
String[] array = new String[list.size()];
int index = 0;
for (AnnotationValue av : list) {
var avs = (AnnotationValue.OfString) av;
array[index++] = avs.stringValue();
}
return (T) array;
}
}
return null;
}
void buildSettings() {
Set<String> foundMethods = new HashSet<>();
buildClassSettings(foundMethods);
buildSuperClassSettings(foundMethods);
}
private void buildClassSettings(Set<String> foundMethods) {
for (MethodModel m : classModel.methods()) {
for (Attribute<?> attribute : m.attributes()) {
if (attribute instanceof RuntimeVisibleAnnotationsAttribute rvaa) {
for (Annotation a : rvaa.annotations()) {
// We can't really validate the method at this
// stage. We would need to check that the parameter
// is an instance of SettingControl.
if (a.classSymbol().equals(TYPE_SETTING_DEFINITION)) {
String name = m.methodName().stringValue();
// Use @Name if it exists
for (Annotation nameCandidate : rvaa.annotations()) {
if (nameCandidate.className().equalsString(ANNOTATION_NAME.descriptorString())) {
if (nameCandidate.elements().size() == 1) {
AnnotationElement ae = nameCandidate.elements().getFirst();
if (ae.name().equalsString("value")) {
if (ae.value() instanceof AnnotationValue.OfString s) {
name = Utils.validJavaIdentifier(s.stringValue(), name);
}
}
}
}
}
// Add setting if method returns boolean and has one parameter
MethodTypeDesc mtd = m.methodTypeSymbol();
if (ConstantDescs.CD_boolean.equals(mtd.returnType())) {
if (mtd.parameterList().size() == 1) {
ClassDesc type = mtd.parameterList().getFirst();
if (type.isClassOrInterface()) {
String methodName = m.methodName().stringValue();
foundMethods.add(methodName);
settingsDescs.add(new SettingDesc(type, methodName));
}
}
}
}
}
}
}
}
}
private void buildSuperClassSettings(Set<String> foundMethods) {
for (Class<?> c = superClass; jdk.internal.event.Event.class != c; c = c.getSuperclass()) {
for (java.lang.reflect.Method method : c.getDeclaredMethods()) {
if (!foundMethods.contains(method.getName())) {
buildSettingsMethod(foundMethods, method);
}
}
}
}
private void buildSettingsMethod(Set<String> foundMethods, java.lang.reflect.Method method) {
// Skip private methods in base classes
if (!Modifier.isPrivate(method.getModifiers())) {
if (method.getReturnType().equals(Boolean.TYPE)) {
if (method.getParameterCount() == 1) {
Class<?> type = method.getParameters()[0].getType();
if (SettingControl.class.isAssignableFrom(type)) {
ClassDesc paramType = Bytecode.classDesc(type);
foundMethods.add(method.getName());
settingsDescs.add(new SettingDesc(paramType, method.getName()));
}
}
}
}
}
void buildFields() {
Set<String> foundFields = new HashSet<>();
// These two fields are added by native as 'transient' so they will be
// ignored by the loop below.
// The benefit of adding them manually is that we can
// control in which order they occur and we can add @Name, @Description
// in Java, instead of in native. It also means code for adding implicit
// fields for native can be reused by Java.
fieldDescs.add(ImplicitFields.FIELD_START_TIME);
if (implicitFields.hasDuration()) {
fieldDescs.add(ImplicitFields.FIELD_DURATION);
}
for (FieldModel field : classModel.fields()) {
if (!foundFields.contains(field.fieldName().stringValue()) && isValidField(field.flags().flagsMask(), field.fieldTypeSymbol())) {
fieldDescs.add(FieldDesc.of(field.fieldTypeSymbol(), field.fieldName().stringValue()));
foundFields.add(field.fieldName().stringValue());
}
}
for (Class<?> c = superClass; jdk.internal.event.Event.class != c; c = c.getSuperclass()) {
for (Field field : c.getDeclaredFields()) {
// Skip private fields in base classes
if (!Modifier.isPrivate(field.getModifiers())) {
if (isValidField(field.getModifiers(), field.getType().getName())) {
String fieldName = field.getName();
if (!foundFields.contains(fieldName)) {
fieldDescs.add(FieldDesc.of(field.getType(), fieldName));
foundFields.add(fieldName);
}
}
}
}
}
}
}

View File

@ -25,71 +25,49 @@
package jdk.jfr.internal;
import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import static jdk.jfr.internal.util.Bytecode.classDesc;
import static jdk.jfr.internal.util.Bytecode.getfield;
import static jdk.jfr.internal.util.Bytecode.invokestatic;
import static jdk.jfr.internal.util.Bytecode.invokevirtual;
import static jdk.jfr.internal.util.Bytecode.putfield;
import java.lang.classfile.Annotation;
import java.lang.classfile.AnnotationElement;
import java.lang.classfile.AnnotationValue;
import java.lang.classfile.ClassBuilder;
import java.lang.classfile.ClassElement;
import java.lang.classfile.ClassModel;
import java.lang.classfile.ClassFile;
import java.lang.classfile.CodeBuilder;
import java.lang.classfile.CodeBuilder.BlockCodeBuilder;
import java.lang.classfile.FieldModel;
import java.lang.classfile.Label;
import java.lang.classfile.MethodModel;
import java.lang.classfile.MethodTransform;
import java.lang.classfile.TypeKind;
import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute;
import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;
import java.util.List;
import java.util.function.Consumer;
import jdk.jfr.Event;
import jdk.jfr.SettingControl;
import jdk.jfr.internal.event.EventConfiguration;
import jdk.jfr.internal.event.EventWriter;
import jdk.jfr.Enabled;
import jdk.jfr.Event;
import jdk.jfr.Name;
import jdk.jfr.Registered;
import jdk.jfr.SettingControl;
import jdk.jfr.SettingDefinition;
import jdk.jfr.internal.util.Utils;
import jdk.jfr.internal.util.Bytecode;
import jdk.jfr.internal.util.ImplicitFields;
import jdk.jfr.internal.util.Bytecode.FieldDesc;
import jdk.jfr.internal.util.Bytecode.MethodDesc;
import static jdk.jfr.internal.util.Bytecode.invokevirtual;
import static jdk.jfr.internal.util.Bytecode.invokestatic;
import static jdk.jfr.internal.util.Bytecode.getfield;
import static jdk.jfr.internal.util.Bytecode.putfield;
import static jdk.jfr.internal.util.Bytecode.classDesc;
import jdk.jfr.internal.util.Bytecode.SettingDesc;
import jdk.jfr.internal.util.ImplicitFields;
/**
* Class responsible for adding instrumentation to a subclass of {@link Event}.
*
*/
final class EventInstrumentation {
private static final FieldDesc FIELD_EVENT_CONFIGURATION = FieldDesc.of(Object.class, "eventConfiguration");
private record SettingDesc(ClassDesc paramType, String methodName) {
}
private static final FieldDesc FIELD_DURATION = FieldDesc.of(long.class, ImplicitFields.DURATION);
private static final FieldDesc FIELD_EVENT_CONFIGURATION = FieldDesc.of(Object.class, "eventConfiguration");;
private static final FieldDesc FIELD_START_TIME = FieldDesc.of(long.class, ImplicitFields.START_TIME);
private static final ClassDesc ANNOTATION_ENABLED = classDesc(Enabled.class);
private static final ClassDesc ANNOTATION_NAME = classDesc(Name.class);
private static final ClassDesc ANNOTATION_REGISTERED = classDesc(Registered.class);
private static final ClassDesc ANNOTATION_REMOVE_FIELDS = classDesc(RemoveFields.class);
private static final ClassDesc TYPE_EVENT_CONFIGURATION = classDesc(EventConfiguration.class);
private static final ClassDesc TYPE_ISE = Bytecode.classDesc(IllegalStateException.class);
private static final ClassDesc TYPE_ISE = classDesc(IllegalStateException.class);
private static final ClassDesc TYPE_EVENT_WRITER = classDesc(EventWriter.class);
private static final ClassDesc TYPE_OBJECT = Bytecode.classDesc(Object.class);
private static final ClassDesc TYPE_SETTING_DEFINITION = Bytecode.classDesc(SettingDefinition.class);
private static final ClassDesc TYPE_OBJECT = classDesc(Object.class);
private static final MethodDesc METHOD_BEGIN = MethodDesc.of("begin", "()V");
private static final MethodDesc METHOD_COMMIT = MethodDesc.of("commit", "()V");
private static final MethodDesc METHOD_DURATION = MethodDesc.of("duration", "(J)J");
@ -104,301 +82,234 @@ final class EventInstrumentation {
private static final MethodDesc METHOD_SHOULD_COMMIT_LONG = MethodDesc.of("shouldCommit", "(J)Z");
private static final MethodDesc METHOD_TIME_STAMP = MethodDesc.of("timestamp", "()J");
private final ClassModel classModel;
private final List<SettingDesc> settingDescs;
private final List<FieldDesc> fieldDescs;;
private final String eventName;
private final String className;
private final Class<?> superClass;
private final boolean untypedEventConfiguration;
private final MethodDesc staticCommitMethod;
private final ClassInspector inspector;
private final long eventTypeId;
private final ClassDesc eventClassDesc;
private final MethodDesc staticCommitMethod;
private final boolean untypedEventConfiguration;
private final boolean guardEventConfiguration;
private final boolean isJDK;
private final Map<MethodDesc, Consumer<CodeBuilder>> methodUpdates = new LinkedHashMap<>();
private final ImplicitFields implicitFields;
EventInstrumentation(Class<?> superClass, byte[] bytes, long id, boolean isJDK, boolean guardEventConfiguration) {
/**
* Creates an EventInstrumentation object.
*
* @param inspector class inspector
* @param id the event type ID to use
* @param guardEventConfiguration guard against event configuration being null.
* Needed when instrumentation is added before
* registration (bytesForEagerInstrumentation)
*/
EventInstrumentation(ClassInspector inspector, long id, boolean guardEventConfiguration) {
inspector.buildFields();
if (!inspector.isJDK()) {
// Only user-defined events have custom settings.
inspector.buildSettings();
}
this.inspector = inspector;
this.eventTypeId = id;
this.superClass = superClass;
this.isJDK = isJDK;
this.classModel = createClassModel(bytes);
this.className = classModel.thisClass().asInternalName().replace("/", ".");
String name = annotationValue(classModel, ANNOTATION_NAME, String.class);
this.eventName = name == null ? className : name;
this.implicitFields = determineImplicitFields();
this.settingDescs = buildSettingDescs(superClass, classModel);
this.fieldDescs = buildFieldDescs(superClass, classModel);
this.staticCommitMethod = isJDK ? findStaticCommitMethod(classModel, fieldDescs) : null;
this.untypedEventConfiguration = hasUntypedConfiguration();
// Corner case when we are forced to generate bytecode
// (bytesForEagerInstrumentation)
// We can't reference EventConfiguration::isEnabled() before event class has
// been registered,
// so we add a guard against a null reference.
this.guardEventConfiguration = guardEventConfiguration;
this.eventClassDesc = inspector.getClassDesc();
this.staticCommitMethod = inspector.findStaticCommitMethod();
this.untypedEventConfiguration = hasUntypedConfiguration();
}
private ImplicitFields determineImplicitFields() {
if (isJDK) {
Class<?> eventClass = MirrorEvents.find(isJDK, className);
if (eventClass != null) {
return new ImplicitFields(eventClass);
byte[] buildInstrumented() {
return ClassFile.of().transformClass(inspector.getClassModel(), this::transform);
}
private void transform(ClassBuilder clb, ClassElement cle) {
if (cle instanceof MethodModel method && instrumentable(method) instanceof Consumer<CodeBuilder> modification) {
clb.transformMethod(method, MethodTransform.transformingCode((codeBuilder, _) -> modification.accept(codeBuilder)));
} else {
clb.with(cle);
}
}
private Consumer<CodeBuilder> instrumentable(MethodModel method) {
if (isMethod(method, METHOD_IS_ENABLED)) {
return this::methodIsEnabled;
}
if (isMethod(method, METHOD_BEGIN)) {
return this::methodBegin;
}
if (isMethod(method, METHOD_END)) {
return this::methodEnd;
}
if (isMethod(method, METHOD_EVENT_SHOULD_COMMIT)) {
return this::methodShouldCommit;
}
if (staticCommitMethod == null && isMethod(method, METHOD_COMMIT)) {
return this::methodCommit;
}
if (inspector.isJDK() && isStatic(method)) {
if (isMethod(method, METHOD_ENABLED)) {
return this::methodEnabledStatic;
}
}
ImplicitFields ifs = new ImplicitFields(superClass);
String[] value = annotationValue(classModel, ANNOTATION_REMOVE_FIELDS, String[].class);
if (value != null) {
ifs.removeFields(value);
}
return ifs;
}
static MethodDesc findStaticCommitMethod(ClassModel classModel, List<FieldDesc> fields) {
StringBuilder sb = new StringBuilder();
sb.append("(");
for (FieldDesc field : fields) {
sb.append(field.type().descriptorString());
}
sb.append(")V");
MethodDesc m = MethodDesc.of("commit", sb.toString());
for (MethodModel method : classModel.methods()) {
String d = method.methodTypeSymbol().descriptorString();
if (method.methodName().equalsString("commit") && m.descriptor().descriptorString().equals(d)) {
return m;
if (isMethod(method, METHOD_SHOULD_COMMIT_LONG)) {
return this::methodShouldCommitStatic;
}
if (isMethod(method, METHOD_TIME_STAMP)) {
return this::methodTimestamp;
}
if (staticCommitMethod != null && isMethod(method, staticCommitMethod)) {
return this::methodCommit;
}
}
return null;
}
private boolean hasUntypedConfiguration() {
for (FieldModel f : classModel.fields()) {
if (f.fieldName().equalsString(FIELD_EVENT_CONFIGURATION.name())) {
return f.fieldType().equalsString(TYPE_OBJECT.descriptorString());
}
}
throw new InternalError("Class missing configuration field");
}
public String getClassName() {
return classModel.thisClass().asInternalName().replace("/", ".");
}
private ClassModel createClassModel(byte[] bytes) {
return ClassFile.of().parse(bytes);
}
boolean isRegistered() {
Boolean result = annotationValue(classModel, ANNOTATION_REGISTERED, Boolean.class);
if (result != null) {
return result.booleanValue();
}
if (superClass != null) {
Registered r = superClass.getAnnotation(Registered.class);
if (r != null) {
return r.value();
}
}
return true;
}
boolean isEnabled() {
Boolean result = annotationValue(classModel, ANNOTATION_ENABLED, Boolean.class);
if (result != null) {
return result.booleanValue();
}
if (superClass != null) {
Enabled e = superClass.getAnnotation(Enabled.class);
if (e != null) {
return e.value();
}
}
return true;
}
@SuppressWarnings("unchecked")
// Only supports String, String[] and Boolean values
private static <T> T annotationValue(ClassModel classModel, ClassDesc classDesc, Class<T> type) {
String typeDescriptor = classDesc.descriptorString();
for (ClassElement ce : classModel) {
if (ce instanceof RuntimeVisibleAnnotationsAttribute rvaa) {
for (Annotation a : rvaa.annotations()) {
if (a.className().equalsString(typeDescriptor)) {
if (a.elements().size() == 1) {
AnnotationElement ae = a.elements().getFirst();
if (ae.name().equalsString("value")) {
if (ae.value() instanceof AnnotationValue.OfBoolean ofb && type.equals(Boolean.class)) {
Boolean b = ofb.booleanValue();
return (T)b;
}
if (ae.value() instanceof AnnotationValue.OfString ofs && type.equals(String.class)) {
String s = ofs.stringValue();
return (T)s;
}
if (ae.value() instanceof AnnotationValue.OfArray ofa && type.equals(String[].class)) {
List<AnnotationValue> list = ofa.values();
String[] array = new String[list.size()];
int index = 0;
for (AnnotationValue av : list) {
var avs = (AnnotationValue.OfString)av;
array[index++] = avs.stringValue();
}
return (T)array;
}
}
}
}
}
}
}
return null;
}
private static List<SettingDesc> buildSettingDescs(Class<?> superClass, ClassModel classModel) {
Set<String> methodSet = new HashSet<>();
List<SettingDesc> settingDescs = new ArrayList<>();
for (MethodModel m : classModel.methods()) {
for (var me : m) {
if (me instanceof RuntimeVisibleAnnotationsAttribute rvaa) {
for (Annotation a : rvaa.annotations()) {
// We can't really validate the method at this
// stage. We would need to check that the parameter
// is an instance of SettingControl.
if (a.className().equalsString(TYPE_SETTING_DEFINITION.descriptorString())) {
String name = m.methodName().stringValue();
// Use @Name if it exists
for (Annotation nameCandidate : rvaa.annotations()) {
if (nameCandidate.className().equalsString(ANNOTATION_NAME.descriptorString())) {
if (nameCandidate.elements().size() == 1) {
AnnotationElement ae = nameCandidate.elements().getFirst();
if (ae.name().equalsString("value")) {
if (ae.value() instanceof AnnotationValue.OfString s) {
name = Utils.validJavaIdentifier(s.stringValue(), name);
}
}
}
}
}
// Add setting if method returns boolean and has one parameter
MethodTypeDesc mtd = m.methodTypeSymbol();
if ("Z".equals(mtd.returnType().descriptorString())) {
if (mtd.parameterList().size() == 1) {
ClassDesc type = mtd.parameterList().getFirst();
if (type.isClassOrInterface()) {
String methodName = m.methodName().stringValue();
methodSet.add(methodName);
settingDescs.add(new SettingDesc(type, methodName));
}
}
}
}
}
}
}
}
for (Class<?> c = superClass; jdk.internal.event.Event.class != c; c = c.getSuperclass()) {
for (java.lang.reflect.Method method : c.getDeclaredMethods()) {
if (!methodSet.contains(method.getName())) {
// skip private method in base classes
if (!Modifier.isPrivate(method.getModifiers())) {
if (method.getReturnType().equals(Boolean.TYPE)) {
if (method.getParameterCount() == 1) {
Class<?> type = method.getParameters()[0].getType();
if (SettingControl.class.isAssignableFrom(type)) {
ClassDesc paramType = Bytecode.classDesc(type);
methodSet.add(method.getName());
settingDescs.add(new SettingDesc(paramType, method.getName()));
}
}
}
}
}
}
}
return settingDescs;
}
private List<FieldDesc> buildFieldDescs(Class<?> superClass, ClassModel classModel) {
Set<String> fieldSet = new HashSet<>();
List<FieldDesc> fieldDescs = new ArrayList<>(classModel.fields().size());
// These two fields are added by native as 'transient' so they will be
// ignored by the loop below.
// The benefit of adding them manually is that we can
// control in which order they occur and we can add @Name, @Description
// in Java, instead of in native. It also means code for adding implicit
// fields for native can be reused by Java.
fieldDescs.add(FIELD_START_TIME);
if (implicitFields.hasDuration()) {
fieldDescs.add(FIELD_DURATION);
}
for (FieldModel field : classModel.fields()) {
if (!fieldSet.contains(field.fieldName().stringValue()) && isValidField(field.flags().flagsMask(), field.fieldTypeSymbol())) {
FieldDesc fi = FieldDesc.of(field.fieldTypeSymbol(), field.fieldName().stringValue());
fieldDescs.add(fi);
fieldSet.add(field.fieldName().stringValue());
}
}
for (Class<?> c = superClass; jdk.internal.event.Event.class != c; c = c.getSuperclass()) {
for (Field field : c.getDeclaredFields()) {
// skip private field in base classes
if (!Modifier.isPrivate(field.getModifiers())) {
if (isValidField(field.getModifiers(), field.getType().getName())) {
String fieldName = field.getName();
if (!fieldSet.contains(fieldName)) {
fieldDescs.add(FieldDesc.of(field.getType(), fieldName));
fieldSet.add(fieldName);
}
}
}
}
}
return fieldDescs;
}
public static boolean isValidField(int access, ClassDesc classDesc) {
String className = classDesc.packageName();
if (!className.isEmpty()) {
className = className + ".";
}
className += classDesc.displayName();
return isValidField(access, className);
}
public static boolean isValidField(int access, String className) {
if (Modifier.isTransient(access) || Modifier.isStatic(access)) {
return false;
}
return Type.isValidJavaFieldType(className);
}
public byte[] buildInstrumented() {
makeInstrumented();
return toByteArray();
}
byte[] toByteArray() {
return ClassFile.of().build(classModel.thisClass().asSymbol(), classBuilder -> {
for (ClassElement ce : classModel) {
boolean updated = false;
if (ce instanceof MethodModel method) {
Consumer<CodeBuilder> methodUpdate = findMethodUpdate(method);
if (methodUpdate != null) {
classBuilder.withMethod(method.methodName().stringValue(), method.methodTypeSymbol(), method.flags().flagsMask(), methodBuilder -> {
methodBuilder.withCode(methodUpdate);
});
updated = true;
}
}
if (!updated) {
classBuilder.with(ce);
}
private void methodIsEnabled(CodeBuilder codeBuilder) {
Label nullLabel = codeBuilder.newLabel();
if (guardEventConfiguration) {
getEventConfiguration(codeBuilder);
codeBuilder.ifnull(nullLabel);
}
getEventConfiguration(codeBuilder);
invokevirtual(codeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_IS_ENABLED);
codeBuilder.ireturn();
if (guardEventConfiguration) {
codeBuilder.labelBinding(nullLabel);
codeBuilder.iconst_0();
codeBuilder.ireturn();
}
}
private void methodBegin(CodeBuilder codeBuilder) {
if (!inspector.hasDuration()) {
throwMissingDuration(codeBuilder, "begin");
} else {
codeBuilder.aload(0);
invokestatic(codeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_TIME_STAMP);
putfield(codeBuilder, eventClassDesc, ImplicitFields.FIELD_START_TIME);
codeBuilder.return_();
}
}
private void methodEnd(CodeBuilder codeBuilder) {
if (!inspector.hasDuration()) {
throwMissingDuration(codeBuilder, "end");
} else {
codeBuilder.aload(0);
codeBuilder.aload(0);
getfield(codeBuilder, eventClassDesc, ImplicitFields.FIELD_START_TIME);
invokestatic(codeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_DURATION);
putfield(codeBuilder, eventClassDesc, ImplicitFields.FIELD_DURATION);
codeBuilder.return_();
}
}
private void methodShouldCommit(CodeBuilder codeBuilder) {
Label fail = codeBuilder.newLabel();
if (guardEventConfiguration) {
getEventConfiguration(codeBuilder);
codeBuilder.ifnull(fail);
}
// if (!eventConfiguration.shouldCommit(duration) goto fail;
getEventConfiguration(codeBuilder);
codeBuilder.aload(0);
getfield(codeBuilder, eventClassDesc, ImplicitFields.FIELD_DURATION);
invokevirtual(codeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_EVENT_CONFIGURATION_SHOULD_COMMIT);
codeBuilder.ifeq(fail);
List<SettingDesc> settingDescs = inspector.getSettings();
for (int index = 0; index < settingDescs.size(); index++) {
SettingDesc sd = settingDescs.get(index);
// if (!settingsMethod(eventConfiguration.settingX)) goto fail;
codeBuilder.aload(0);
getEventConfiguration(codeBuilder);
codeBuilder.loadConstant(index);
invokevirtual(codeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_EVENT_CONFIGURATION_GET_SETTING);
MethodTypeDesc mdesc = MethodTypeDesc.ofDescriptor("(" + sd.paramType().descriptorString() + ")Z");
codeBuilder.checkcast(sd.paramType());
codeBuilder.invokevirtual(eventClassDesc, sd.methodName(), mdesc);
codeBuilder.ifeq(fail);
}
// return true
codeBuilder.iconst_1();
codeBuilder.ireturn();
// return false
codeBuilder.labelBinding(fail);
codeBuilder.iconst_0();
codeBuilder.ireturn();
}
private void methodCommit(CodeBuilder codeBuilder) {
Label excluded = codeBuilder.newLabel();
Label end = codeBuilder.newLabel();
codeBuilder.trying(blockCodeBuilder -> {
if (staticCommitMethod != null) {
updateStaticCommit(blockCodeBuilder, excluded);
} else {
updateInstanceCommit(blockCodeBuilder, end, excluded);
}
// stack: [integer]
// notified -> restart event write attempt
blockCodeBuilder.ifeq(blockCodeBuilder.startLabel());
// stack: []
blockCodeBuilder.goto_(end);
}, catchBuilder -> {
catchBuilder.catchingAll(catchAllHandler -> {
getEventWriter(catchAllHandler);
// stack: [ex] [EW]
catchAllHandler.dup();
// stack: [ex] [EW] [EW]
Label rethrow = catchAllHandler.newLabel();
catchAllHandler.ifnull(rethrow);
// stack: [ex] [EW]
catchAllHandler.dup();
// stack: [ex] [EW] [EW]
invokevirtual(catchAllHandler, TYPE_EVENT_WRITER, METHOD_RESET);
catchAllHandler.labelBinding(rethrow);
// stack:[ex] [EW]
catchAllHandler.pop();
// stack:[ex]
catchAllHandler.athrow();
});
});
codeBuilder.labelBinding(excluded);
// stack: [EW]
codeBuilder.pop();
codeBuilder.labelBinding(end);
// stack: []
codeBuilder.return_();
}
public byte[] buildUninstrumented() {
makeUninstrumented();
return toByteArray();
private void methodEnabledStatic(CodeBuilder codeBuilder) {
Label nullLabel = codeBuilder.newLabel();
if (guardEventConfiguration) {
getEventConfiguration(codeBuilder);
codeBuilder.ifnull(nullLabel);
}
getEventConfiguration(codeBuilder);
invokevirtual(codeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_IS_ENABLED);
codeBuilder.ireturn();
if (guardEventConfiguration) {
codeBuilder.labelBinding(nullLabel);
codeBuilder.iconst_0();
codeBuilder.ireturn();
}
}
private void methodTimestamp(CodeBuilder codeBuilder) {
invokestatic(codeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_TIME_STAMP);
codeBuilder.lreturn();
}
private void methodShouldCommitStatic(CodeBuilder codeBuilder) {
Label fail = codeBuilder.newLabel();
if (guardEventConfiguration) {
// if (eventConfiguration == null) goto fail;
getEventConfiguration(codeBuilder);
codeBuilder.ifnull(fail);
}
// return eventConfiguration.shouldCommit(duration);
getEventConfiguration(codeBuilder);
codeBuilder.lload(0);
codeBuilder.invokevirtual(TYPE_EVENT_CONFIGURATION, METHOD_EVENT_CONFIGURATION_SHOULD_COMMIT.name(), METHOD_EVENT_CONFIGURATION_SHOULD_COMMIT.descriptor());
codeBuilder.ireturn();
// fail:
codeBuilder.labelBinding(fail);
// return false
codeBuilder.iconst_0();
codeBuilder.ireturn();
}
private void throwMissingDuration(CodeBuilder codeBuilder, String method) {
@ -406,144 +317,7 @@ final class EventInstrumentation {
Bytecode.throwException(codeBuilder, TYPE_ISE, message);
}
private void makeInstrumented() {
// MyEvent#isEnabled()
updateEnabledMethod(METHOD_IS_ENABLED);
// MyEvent#begin()
updateMethod(METHOD_BEGIN, codeBuilder -> {
if (!implicitFields.hasDuration()) {
throwMissingDuration(codeBuilder, "begin");
} else {
codeBuilder.aload(0);
invokestatic(codeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_TIME_STAMP);
putfield(codeBuilder, getEventClassDesc(), FIELD_START_TIME);
codeBuilder.return_();
}
});
// MyEvent#end()
updateMethod(METHOD_END, codeBuilder -> {
if (!implicitFields.hasDuration()) {
throwMissingDuration(codeBuilder, "end");
} else {
codeBuilder.aload(0);
codeBuilder.aload(0);
getfield(codeBuilder, getEventClassDesc(), FIELD_START_TIME);
invokestatic(codeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_DURATION);
putfield(codeBuilder, getEventClassDesc(), FIELD_DURATION);
codeBuilder.return_();
}
});
// MyEvent#commit() or static MyEvent#commit(...)
MethodDesc m = staticCommitMethod == null ? METHOD_COMMIT : staticCommitMethod;
updateMethod(m, codeBuilder -> {
Label excluded = codeBuilder.newLabel();
Label end = codeBuilder.newLabel();
codeBuilder.trying(blockCodeBuilder -> {
if (staticCommitMethod != null) {
updateStaticCommit(blockCodeBuilder, excluded);
} else {
updateInstanceCommit(blockCodeBuilder, end, excluded);
}
// stack: [integer]
// notified -> restart event write attempt
blockCodeBuilder.ifeq(blockCodeBuilder.startLabel());
// stack: []
blockCodeBuilder.goto_(end);
}, catchBuilder -> {
catchBuilder.catchingAll(catchAllHandler -> {
getEventWriter(catchAllHandler);
// stack: [ex] [EW]
catchAllHandler.dup();
// stack: [ex] [EW] [EW]
Label rethrow = catchAllHandler.newLabel();
catchAllHandler.ifnull(rethrow);
// stack: [ex] [EW]
catchAllHandler.dup();
// stack: [ex] [EW] [EW]
invokevirtual(catchAllHandler, TYPE_EVENT_WRITER, METHOD_RESET);
catchAllHandler.labelBinding(rethrow);
// stack:[ex] [EW]
catchAllHandler.pop();
// stack:[ex]
catchAllHandler.athrow();
});
});
codeBuilder.labelBinding(excluded);
// stack: [EW]
codeBuilder.pop();
codeBuilder.labelBinding(end);
// stack: []
codeBuilder.return_();
});
// MyEvent#shouldCommit()
updateMethod(METHOD_EVENT_SHOULD_COMMIT, codeBuilder -> {
Label fail = codeBuilder.newLabel();
if (guardEventConfiguration) {
getEventConfiguration(codeBuilder);
codeBuilder.ifnull(fail);
}
// if (!eventConfiguration.shouldCommit(duration) goto fail;
getEventConfiguration(codeBuilder);
codeBuilder.aload(0);
getfield(codeBuilder, getEventClassDesc(), FIELD_DURATION);
invokevirtual(codeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_EVENT_CONFIGURATION_SHOULD_COMMIT);
codeBuilder.ifeq(fail);
for (int index = 0; index < settingDescs.size(); index++) {
SettingDesc sd = settingDescs.get(index);
// if (!settingsMethod(eventConfiguration.settingX)) goto fail;
codeBuilder.aload(0);
getEventConfiguration(codeBuilder);
codeBuilder.loadConstant(index);
invokevirtual(codeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_EVENT_CONFIGURATION_GET_SETTING);
MethodTypeDesc mdesc = MethodTypeDesc.ofDescriptor("(" + sd.paramType().descriptorString() + ")Z");
codeBuilder.checkcast(sd.paramType());
codeBuilder.invokevirtual(getEventClassDesc(), sd.methodName(), mdesc);
codeBuilder.ifeq(fail);
}
// return true
codeBuilder.iconst_1();
codeBuilder.ireturn();
// return false
codeBuilder.labelBinding(fail);
codeBuilder.iconst_0();
codeBuilder.ireturn();
});
if (isJDK) {
if (hasStaticMethod(METHOD_ENABLED)) {
updateEnabledMethod(METHOD_ENABLED);
}
updateIfStaticMethodExists(METHOD_SHOULD_COMMIT_LONG, codeBuilder -> {
Label fail = codeBuilder.newLabel();
if (guardEventConfiguration) {
// if (eventConfiguration == null) goto fail;
getEventConfiguration(codeBuilder);
codeBuilder.ifnull(fail);
}
// return eventConfiguration.shouldCommit(duration);
getEventConfiguration(codeBuilder);
codeBuilder.lload(0);
codeBuilder.invokevirtual(TYPE_EVENT_CONFIGURATION, METHOD_EVENT_CONFIGURATION_SHOULD_COMMIT.name(), METHOD_EVENT_CONFIGURATION_SHOULD_COMMIT.descriptor());
codeBuilder.ireturn();
// fail:
codeBuilder.labelBinding(fail);
// return false
codeBuilder.iconst_0();
codeBuilder.ireturn();
});
updateIfStaticMethodExists(METHOD_TIME_STAMP, codeBuilder -> {
invokestatic(codeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_TIME_STAMP);
codeBuilder.lreturn();
});
}
}
void updateStaticCommit(BlockCodeBuilder blockCodeBuilder, Label excluded) {
private void updateStaticCommit(BlockCodeBuilder blockCodeBuilder, Label excluded) {
// indexes the argument type array, the argument type array does not include
// 'this'
int argIndex = 0;
@ -576,7 +350,7 @@ final class EventInstrumentation {
invokevirtual(blockCodeBuilder, TYPE_EVENT_WRITER, EventWriterMethod.PUT_LONG.method());
fieldIndex++;
// stack: [EW]
if (implicitFields.hasDuration()) {
if (inspector.hasDuration()) {
// write duration
blockCodeBuilder.dup();
// stack: [EW], [EW]
@ -588,14 +362,14 @@ final class EventInstrumentation {
fieldIndex++;
}
// stack: [EW]
if (implicitFields.hasEventThread()) {
if (inspector.hasEventThread()) {
// write eventThread
blockCodeBuilder.dup();
// stack: [EW], [EW]
invokevirtual(blockCodeBuilder, TYPE_EVENT_WRITER, EventWriterMethod.PUT_EVENT_THREAD.method());
}
// stack: [EW]
if (implicitFields.hasStackTrace()) {
if (inspector.hasStackTrace()) {
// write stackTrace
blockCodeBuilder.dup();
// stack: [EW], [EW]
@ -603,6 +377,7 @@ final class EventInstrumentation {
}
// stack: [EW]
// write custom fields
List<FieldDesc> fieldDescs = inspector.getFields();
while (fieldIndex < fieldDescs.size()) {
blockCodeBuilder.dup();
// stack: [EW], [EW]
@ -622,19 +397,19 @@ final class EventInstrumentation {
// stack: [int]
}
void updateInstanceCommit(BlockCodeBuilder blockCodeBuilder, Label end, Label excluded) {
private void updateInstanceCommit(BlockCodeBuilder blockCodeBuilder, Label end, Label excluded) {
// if (!isEnable()) {
// return;
// }
blockCodeBuilder.aload(0);
invokevirtual(blockCodeBuilder, getEventClassDesc(), METHOD_IS_ENABLED);
invokevirtual(blockCodeBuilder, eventClassDesc, METHOD_IS_ENABLED);
Label l0 = blockCodeBuilder.newLabel();
blockCodeBuilder.ifne(l0);
blockCodeBuilder.return_();
blockCodeBuilder.labelBinding(l0);
// long startTime = this.startTime
blockCodeBuilder.aload(0);
getfield(blockCodeBuilder, getEventClassDesc(), FIELD_START_TIME);
getfield(blockCodeBuilder, eventClassDesc, ImplicitFields.FIELD_START_TIME);
blockCodeBuilder.lstore(1);
// if (startTime == 0) {
// startTime = EventWriter.timestamp();
@ -654,7 +429,7 @@ final class EventInstrumentation {
// }
blockCodeBuilder.labelBinding(durationEvent);
blockCodeBuilder.aload(0);
getfield(blockCodeBuilder, getEventClassDesc(), FIELD_DURATION);
getfield(blockCodeBuilder, eventClassDesc, ImplicitFields.FIELD_DURATION);
blockCodeBuilder.lconst_0();
blockCodeBuilder.lcmp();
blockCodeBuilder.ifne(commit);
@ -662,11 +437,11 @@ final class EventInstrumentation {
invokestatic(blockCodeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_TIME_STAMP);
blockCodeBuilder.lload(1);
blockCodeBuilder.lsub();
putfield(blockCodeBuilder, getEventClassDesc(), FIELD_DURATION);
putfield(blockCodeBuilder, eventClassDesc, ImplicitFields.FIELD_DURATION);
blockCodeBuilder.labelBinding(commit);
// if (shouldCommit()) {
blockCodeBuilder.aload(0);
invokevirtual(blockCodeBuilder, getEventClassDesc(), METHOD_EVENT_SHOULD_COMMIT);
invokevirtual(blockCodeBuilder, eventClassDesc, METHOD_EVENT_SHOULD_COMMIT);
blockCodeBuilder.ifeq(end);
getEventWriter(blockCodeBuilder);
// stack: [EW]
@ -687,39 +462,40 @@ final class EventInstrumentation {
invokevirtual(blockCodeBuilder, TYPE_EVENT_WRITER, EventWriterMethod.PUT_LONG.method());
fieldIndex++;
// stack: [EW]
if (implicitFields.hasDuration()) {
if (inspector.hasDuration()) {
// write duration
blockCodeBuilder.dup();
// stack: [EW] [EW]
blockCodeBuilder.aload(0);
// stack: [EW] [EW] [this]
getfield(blockCodeBuilder, getEventClassDesc(), FIELD_DURATION);
getfield(blockCodeBuilder, eventClassDesc, ImplicitFields.FIELD_DURATION);
// stack: [EW] [EW] [long]
invokevirtual(blockCodeBuilder, TYPE_EVENT_WRITER, EventWriterMethod.PUT_LONG.method());
fieldIndex++;
}
// stack: [EW]
if (implicitFields.hasEventThread()) {
if (inspector.hasEventThread()) {
// write eventThread
blockCodeBuilder.dup();
// stack: [EW] [EW]
invokevirtual(blockCodeBuilder, TYPE_EVENT_WRITER, EventWriterMethod.PUT_EVENT_THREAD.method());
}
// stack: [EW]
if (implicitFields.hasStackTrace()) {
if (inspector.hasStackTrace()) {
// write stack trace
blockCodeBuilder.dup();
// stack: [EW] [EW]
invokevirtual(blockCodeBuilder, TYPE_EVENT_WRITER, EventWriterMethod.PUT_STACK_TRACE.method());
}
// stack: [EW]
List<FieldDesc> fieldDescs = inspector.getFields();
while (fieldIndex < fieldDescs.size()) {
FieldDesc field = fieldDescs.get(fieldIndex);
blockCodeBuilder.dup();
// stack: [EW] [EW]
blockCodeBuilder.aload(0);
// stack: [EW] [EW] [this]
getfield(blockCodeBuilder, getEventClassDesc(), field);
getfield(blockCodeBuilder, eventClassDesc, field);
// stack: [EW] [EW] <T>
EventWriterMethod eventMethod = EventWriterMethod.lookupMethod(field);
invokevirtual(blockCodeBuilder, TYPE_EVENT_WRITER, eventMethod.method());
@ -731,90 +507,33 @@ final class EventInstrumentation {
// stack:[int]
}
private void updateEnabledMethod(MethodDesc method) {
updateMethod(method, codeBuilder -> {
Label nullLabel = codeBuilder.newLabel();
if (guardEventConfiguration) {
getEventConfiguration(codeBuilder);
codeBuilder.ifnull(nullLabel);
}
getEventConfiguration(codeBuilder);
invokevirtual(codeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_IS_ENABLED);
codeBuilder.ireturn();
if (guardEventConfiguration) {
codeBuilder.labelBinding(nullLabel);
codeBuilder.iconst_0();
codeBuilder.ireturn();
}
});
private static boolean isStatic(MethodModel method) {
return (method.flags().flagsMask() & ClassFile.ACC_STATIC) != 0;
}
private void updateIfStaticMethodExists(MethodDesc method, Consumer<CodeBuilder> code) {
if (hasStaticMethod(method)) {
updateMethod(method, code);
}
private static boolean isMethod(MethodModel m, MethodDesc desc) {
return desc.matches(m);
}
private boolean hasStaticMethod(MethodDesc method) {
for (MethodModel m : classModel.methods()) {
if (m.methodName().equalsString(method.name()) && m.methodTypeSymbol().equals(method.descriptor())) {
return Modifier.isStatic(m.flags().flagsMask());
}
}
return false;
}
private void getEventWriter(CodeBuilder codeBuilder) {
private static void getEventWriter(CodeBuilder codeBuilder) {
invokestatic(codeBuilder, TYPE_EVENT_WRITER, METHOD_GET_EVENT_WRITER);
}
private void getEventConfiguration(CodeBuilder codeBuilder) {
if (untypedEventConfiguration) {
codeBuilder.getstatic(getEventClassDesc(), FIELD_EVENT_CONFIGURATION.name(), TYPE_OBJECT);
codeBuilder.getstatic(eventClassDesc, FIELD_EVENT_CONFIGURATION.name(), TYPE_OBJECT);
codeBuilder.checkcast(TYPE_EVENT_CONFIGURATION);
} else {
codeBuilder.getstatic(getEventClassDesc(), FIELD_EVENT_CONFIGURATION.name(), TYPE_EVENT_CONFIGURATION);
codeBuilder.getstatic(eventClassDesc, FIELD_EVENT_CONFIGURATION.name(), TYPE_EVENT_CONFIGURATION);
}
}
private void makeUninstrumented() {
updateExistingWithReturnFalse(METHOD_EVENT_SHOULD_COMMIT);
updateExistingWithReturnFalse(METHOD_IS_ENABLED);
updateExistingWithEmptyVoidMethod(METHOD_COMMIT);
if (staticCommitMethod != null) {
updateExistingWithEmptyVoidMethod(staticCommitMethod);
private boolean hasUntypedConfiguration() {
for (FieldModel f : inspector.getClassModel().fields()) {
if (f.fieldName().equalsString(FIELD_EVENT_CONFIGURATION.name())) {
return f.fieldType().equalsString(TYPE_OBJECT.descriptorString());
}
}
updateExistingWithEmptyVoidMethod(METHOD_BEGIN);
updateExistingWithEmptyVoidMethod(METHOD_END);
}
private final void updateExistingWithEmptyVoidMethod(MethodDesc voidMethod) {
updateMethod(voidMethod, codeBuilder -> {
codeBuilder.return_();
});
}
private final void updateExistingWithReturnFalse(MethodDesc voidMethod) {
updateMethod(voidMethod, codeBuilder -> {
codeBuilder.iconst_0();
codeBuilder.ireturn();
});
}
private Consumer<CodeBuilder> findMethodUpdate(MethodModel mm) {
MethodDesc m = MethodDesc.of(mm.methodName().stringValue(), mm.methodType().stringValue());
return methodUpdates.get(m);
}
private void updateMethod(MethodDesc method, Consumer<CodeBuilder> codeBuilder) {
methodUpdates.put(method, codeBuilder);
}
private ClassDesc getEventClassDesc() {
return classModel.thisClass().asSymbol();
}
public String getEventName() {
return eventName;
throw new InternalError("Class missing configuration field");
}
}

View File

@ -72,7 +72,8 @@ final class JVMUpcalls {
}
boolean jdkClass = Utils.isJDKClass(clazz);
Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "Adding instrumentation to event class " + clazz.getName() + " using retransform");
EventInstrumentation ei = new EventInstrumentation(clazz.getSuperclass(), oldBytes, traceId, jdkClass, false);
ClassInspector c = new ClassInspector(clazz.getSuperclass(), oldBytes, jdkClass);
EventInstrumentation ei = new EventInstrumentation(c, traceId, false);
byte[] bytes = ei.buildInstrumented();
Bytecode.log(clazz.getName(), bytes);
return bytes;
@ -105,9 +106,9 @@ final class JVMUpcalls {
}
String eventName = "<Unknown>";
try {
EventInstrumentation ei = new EventInstrumentation(superClass, oldBytes, traceId, bootClassLoader, true);
eventName = ei.getEventName();
if (!JVMSupport.shouldInstrument(bootClassLoader, ei.getEventName())) {
ClassInspector c = new ClassInspector(superClass, oldBytes, bootClassLoader);
eventName = c.getEventName();
if (!JVMSupport.shouldInstrument(bootClassLoader, c.getEventName())) {
Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "Skipping instrumentation for " + eventName + " since container support is missing");
return oldBytes;
}
@ -118,14 +119,15 @@ final class JVMUpcalls {
// No need to generate bytecode if:
// 1) Event class is disabled, and there is not an external configuration that overrides.
// 2) Event class has @Registered(false)
if (!mr.isEnabled(ei.getEventName()) && !ei.isEnabled() || !ei.isRegistered()) {
if (!mr.isEnabled(c.getEventName()) && !c.isEnabled() || !c.isRegistered()) {
Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "Skipping instrumentation for event type " + eventName + " since event was disabled on class load");
return oldBytes;
}
}
Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "Adding " + (forceInstrumentation ? "forced " : "") + "instrumentation for event type " + eventName + " during initial class load");
EventInstrumentation ei = new EventInstrumentation(c, traceId, true);
byte[] bytes = ei.buildInstrumented();
Bytecode.log(ei.getClassName() + "(" + traceId + ")", bytes);
Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "Adding " + (forceInstrumentation ? "forced " : "") + "instrumentation for event type " + eventName + " during initial class load");
Bytecode.log(c.getClassName() + "(" + traceId + ")", bytes);
return bytes;
} catch (Throwable t) {
Logger.log(LogTag.JFR_SYSTEM, LogLevel.WARN, "Unexpected error when adding instrumentation for event type " + eventName + ". " + t.getMessage());

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. 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
@ -33,6 +33,7 @@ import jdk.jfr.internal.Logger;
import jdk.jfr.internal.LogLevel;
import jdk.jfr.internal.LogTag;
import java.lang.classfile.CodeBuilder;
import java.lang.classfile.MethodModel;
import java.lang.classfile.ClassModel;
import java.lang.classfile.ClassFile;
import jdk.internal.classfile.components.ClassPrinter;
@ -74,6 +75,12 @@ public final class Bytecode {
MethodTypeDesc mtd = MethodTypeDesc.of(returnDesc, parameterDesc);
return new MethodDesc(methodName, mtd);
}
public boolean matches(MethodModel m) {
return this.descriptor().equals(m.methodTypeSymbol()) && m.methodName().equalsString(this.name());
}
}
public record SettingDesc(ClassDesc paramType, String methodName) {
}
public static ClassDesc classDesc(ValueDescriptor v) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. 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
@ -28,6 +28,7 @@ import java.util.ArrayList;
import java.util.List;
import jdk.jfr.internal.RemoveFields;
import jdk.jfr.internal.util.Bytecode.FieldDesc;
/**
* Class that describes fields that was not directly named
* in the event definition.
@ -37,6 +38,8 @@ public final class ImplicitFields {
public static final String DURATION = "duration";
public static final String EVENT_THREAD = "eventThread";
public static final String STACK_TRACE = "stackTrace";
public static final FieldDesc FIELD_DURATION = FieldDesc.of(long.class, DURATION);
public static final FieldDesc FIELD_START_TIME = FieldDesc.of(long.class, START_TIME);
private final List<String> fields = new ArrayList<>(4);