mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 23:04:50 +02:00
8318124: JFR: Rewrite instrumentation to use Class-File API
Reviewed-by: mgronlun
This commit is contained in:
parent
c1aeac79ba
commit
69c0ae23a3
12 changed files with 781 additions and 751 deletions
|
@ -187,15 +187,20 @@ module java.base {
|
||||||
exports jdk.internal.classfile to
|
exports jdk.internal.classfile to
|
||||||
jdk.jartool,
|
jdk.jartool,
|
||||||
jdk.jdeps,
|
jdk.jdeps,
|
||||||
|
jdk.jfr,
|
||||||
jdk.jlink,
|
jdk.jlink,
|
||||||
jdk.jshell;
|
jdk.jshell;
|
||||||
exports jdk.internal.classfile.attribute to
|
exports jdk.internal.classfile.attribute to
|
||||||
jdk.jartool,
|
jdk.jartool,
|
||||||
jdk.jdeps,
|
jdk.jdeps,
|
||||||
|
jdk.jfr,
|
||||||
jdk.jlink;
|
jdk.jlink;
|
||||||
|
exports jdk.internal.classfile.components to
|
||||||
|
jdk.jfr;
|
||||||
exports jdk.internal.classfile.constantpool to
|
exports jdk.internal.classfile.constantpool to
|
||||||
jdk.jartool,
|
jdk.jartool,
|
||||||
jdk.jdeps,
|
jdk.jdeps,
|
||||||
|
jdk.jfr,
|
||||||
jdk.jlink;
|
jdk.jlink;
|
||||||
exports jdk.internal.classfile.instruction to
|
exports jdk.internal.classfile.instruction to
|
||||||
jdk.jdeps,
|
jdk.jdeps,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -31,7 +31,7 @@ import java.util.List;
|
||||||
|
|
||||||
import jdk.jfr.EventType;
|
import jdk.jfr.EventType;
|
||||||
import jdk.jfr.ValueDescriptor;
|
import jdk.jfr.ValueDescriptor;
|
||||||
import jdk.jfr.internal.EventInstrumentation;
|
import jdk.jfr.internal.util.Utils;
|
||||||
import jdk.jfr.internal.consumer.ObjectContext;
|
import jdk.jfr.internal.consumer.ObjectContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -57,7 +57,7 @@ public final class RecordedEvent extends RecordedObject {
|
||||||
* @return stack trace, or {@code null} if doesn't exist for the event
|
* @return stack trace, or {@code null} if doesn't exist for the event
|
||||||
*/
|
*/
|
||||||
public RecordedStackTrace getStackTrace() {
|
public RecordedStackTrace getStackTrace() {
|
||||||
return getTyped(EventInstrumentation.FIELD_STACK_TRACE, RecordedStackTrace.class, null);
|
return getTyped(Utils.FIELD_STACK_TRACE, RecordedStackTrace.class, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -67,7 +67,7 @@ public final class RecordedEvent extends RecordedObject {
|
||||||
* @return thread, or {@code null} if doesn't exist for the event
|
* @return thread, or {@code null} if doesn't exist for the event
|
||||||
*/
|
*/
|
||||||
public RecordedThread getThread() {
|
public RecordedThread getThread() {
|
||||||
return getTyped(EventInstrumentation.FIELD_EVENT_THREAD, RecordedThread.class, null);
|
return getTyped(Utils.FIELD_EVENT_THREAD, RecordedThread.class, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,102 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2016, 2022, 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 java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
|
|
||||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
|
||||||
import jdk.internal.org.objectweb.asm.Type;
|
|
||||||
import jdk.internal.org.objectweb.asm.util.TraceClassVisitor;
|
|
||||||
import jdk.jfr.ValueDescriptor;
|
|
||||||
|
|
||||||
final class ASMToolkit {
|
|
||||||
public static final Type TYPE_STRING = Type.getType(String.class);
|
|
||||||
private static final Type TYPE_THREAD = Type.getType(Thread.class);
|
|
||||||
private static final Type TYPE_CLASS = Type.getType(Class.class);
|
|
||||||
|
|
||||||
public static Type toType(ValueDescriptor v) {
|
|
||||||
return switch (v.getTypeName()) {
|
|
||||||
case "byte" -> Type.BYTE_TYPE;
|
|
||||||
case "short" -> Type.SHORT_TYPE;
|
|
||||||
case "int" -> Type.INT_TYPE;
|
|
||||||
case "long" ->Type.LONG_TYPE;
|
|
||||||
case "double" -> Type.DOUBLE_TYPE;
|
|
||||||
case "float" -> Type.FLOAT_TYPE;
|
|
||||||
case "char" -> Type.CHAR_TYPE;
|
|
||||||
case "boolean" -> Type.BOOLEAN_TYPE;
|
|
||||||
case "java.lang.String" -> TYPE_STRING;
|
|
||||||
case "java.lang.Thread" -> TYPE_THREAD;
|
|
||||||
case "java.lang.Class" -> TYPE_CLASS;
|
|
||||||
default -> throw new Error("Not a valid type " + v.getTypeName());
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts "int" into "I" and "java.lang.String" into "Ljava/lang/String;"
|
|
||||||
*
|
|
||||||
* @param typeName
|
|
||||||
* type
|
|
||||||
*
|
|
||||||
* @return descriptor
|
|
||||||
*/
|
|
||||||
public static String getDescriptor(String typeName) {
|
|
||||||
return switch (typeName) {
|
|
||||||
case "int" -> "I";
|
|
||||||
case "long" -> "J";
|
|
||||||
case "boolean" -> "Z";
|
|
||||||
case "float" -> "F";
|
|
||||||
case "double" -> "D";
|
|
||||||
case "short" -> "S";
|
|
||||||
case "char" -> "C";
|
|
||||||
case "byte" -> "B";
|
|
||||||
default -> Type.getObjectType(getInternalName(typeName)).getDescriptor();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts java.lang.String into java/lang/String
|
|
||||||
*
|
|
||||||
* @param className
|
|
||||||
*
|
|
||||||
* @return internal name
|
|
||||||
*/
|
|
||||||
public static String getInternalName(String className) {
|
|
||||||
return className.replace(".", "/");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void logASM(String className, byte[] bytes) {
|
|
||||||
Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.INFO, "Generated bytecode for class " + className);
|
|
||||||
if (Logger.shouldLog(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.TRACE)) {
|
|
||||||
ClassReader cr = new ClassReader(bytes);
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
||||||
PrintWriter w = new PrintWriter(baos);
|
|
||||||
w.println("Bytecode:");
|
|
||||||
cr.accept(new TraceClassVisitor(w), 0);
|
|
||||||
Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.TRACE, baos.toString());
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -25,117 +25,112 @@
|
||||||
|
|
||||||
package jdk.jfr.internal;
|
package jdk.jfr.internal;
|
||||||
|
|
||||||
|
import static jdk.jfr.internal.util.Bytecode.invokespecial;
|
||||||
|
|
||||||
|
import java.lang.constant.ClassDesc;
|
||||||
|
import java.lang.constant.ConstantDescs;
|
||||||
|
import java.lang.reflect.AccessFlag;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
import jdk.internal.org.objectweb.asm.AnnotationVisitor;
|
import jdk.internal.classfile.AnnotationValue;
|
||||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
import jdk.internal.classfile.ClassBuilder;
|
||||||
import jdk.internal.org.objectweb.asm.Label;
|
import jdk.internal.classfile.Classfile;
|
||||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
import jdk.internal.classfile.Label;
|
||||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
import jdk.internal.classfile.attribute.RuntimeVisibleAnnotationsAttribute;
|
||||||
import jdk.internal.org.objectweb.asm.Type;
|
|
||||||
import jdk.internal.org.objectweb.asm.commons.GeneratorAdapter;
|
|
||||||
import jdk.internal.org.objectweb.asm.commons.Method;
|
|
||||||
import jdk.jfr.AnnotationElement;
|
import jdk.jfr.AnnotationElement;
|
||||||
import jdk.jfr.Event;
|
import jdk.jfr.Event;
|
||||||
import jdk.jfr.ValueDescriptor;
|
import jdk.jfr.ValueDescriptor;
|
||||||
|
import jdk.jfr.internal.util.Bytecode;
|
||||||
|
import jdk.jfr.internal.util.Bytecode.MethodDesc;
|
||||||
|
|
||||||
// Helper class for building dynamic events
|
// Helper class for building dynamic events
|
||||||
public final class EventClassBuilder {
|
public final class EventClassBuilder {
|
||||||
|
private static final ClassDesc TYPE_EVENT = Bytecode.classDesc(Event.class);
|
||||||
private static final Type TYPE_EVENT = Type.getType(Event.class);
|
private static final ClassDesc TYPE_IOBE = Bytecode.classDesc(IndexOutOfBoundsException.class);
|
||||||
private static final Type TYPE_IOBE = Type.getType(IndexOutOfBoundsException.class);
|
private static final MethodDesc DEFAULT_CONSTRUCTOR = MethodDesc.of("<init>", "()V");
|
||||||
private static final Method DEFAULT_CONSTRUCTOR = Method.getMethod("void <init> ()");
|
private static final MethodDesc SET_METHOD = MethodDesc.of("set", "(ILjava/lang/Object;)V");
|
||||||
private static final Method SET_METHOD = Method.getMethod("void set (int, java.lang.Object)");
|
|
||||||
private static final AtomicLong idCounter = new AtomicLong();
|
private static final AtomicLong idCounter = new AtomicLong();
|
||||||
private final ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
|
|
||||||
private final String fullClassName;
|
private final String fullClassName;
|
||||||
private final Type type;
|
private final ClassDesc type;
|
||||||
private final List<ValueDescriptor> fields;
|
private final List<ValueDescriptor> fields;
|
||||||
private final List<AnnotationElement> annotationElements;
|
private final List<AnnotationElement> annotationElements;
|
||||||
|
|
||||||
public EventClassBuilder(List<AnnotationElement> annotationElements, List<ValueDescriptor> fields) {
|
public EventClassBuilder(List<AnnotationElement> annotationElements, List<ValueDescriptor> fields) {
|
||||||
this.fullClassName = "jdk.jfr.DynamicEvent" + idCounter.incrementAndGet();
|
this.fullClassName = "jdk.jfr.DynamicEvent" + idCounter.incrementAndGet();
|
||||||
this.type = Type.getType("L" + fullClassName.replace(".", "/") + ";");
|
this.type = ClassDesc.of(fullClassName);
|
||||||
this.fields = fields;
|
this.fields = fields;
|
||||||
this.annotationElements = annotationElements;
|
this.annotationElements = annotationElements;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Class<? extends Event> build() {
|
public Class<? extends Event> build() {
|
||||||
buildClassInfo();
|
byte[] bytes = Classfile.of().build(ClassDesc.of(fullClassName), cb -> build(cb));
|
||||||
buildConstructor();
|
Bytecode.log(fullClassName, bytes);
|
||||||
buildFields();
|
|
||||||
buildSetMethod();
|
|
||||||
endClass();
|
|
||||||
byte[] bytes = classWriter.toByteArray();
|
|
||||||
ASMToolkit.logASM(fullClassName, bytes);
|
|
||||||
return SecuritySupport.defineClass(Event.class, bytes).asSubclass(Event.class);
|
return SecuritySupport.defineClass(Event.class, bytes).asSubclass(Event.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void endClass() {
|
void build(ClassBuilder builder) {
|
||||||
classWriter.visitEnd();
|
buildClassInfo(builder);
|
||||||
|
buildConstructor(builder);
|
||||||
|
buildFields(builder);
|
||||||
|
buildSetMethod(builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildSetMethod() {
|
private void buildSetMethod(ClassBuilder builder) {
|
||||||
GeneratorAdapter ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC, SET_METHOD, null, null, classWriter);
|
// void Event::set(int index, Object value);
|
||||||
|
builder.withMethod(SET_METHOD.name(), SET_METHOD.descriptor(), Classfile.ACC_PUBLIC, methodBuilder -> methodBuilder.withCode(codeBuilder -> {
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (ValueDescriptor v : fields) {
|
for (ValueDescriptor v : fields) {
|
||||||
ga.loadArg(0);
|
codeBuilder.iload(1);
|
||||||
ga.visitLdcInsn(index);
|
codeBuilder.ldc(index);
|
||||||
Label notEqual = new Label();
|
Label notEqual = codeBuilder.newLabel();
|
||||||
ga.ifICmp(GeneratorAdapter.NE, notEqual);
|
codeBuilder.if_icmpne(notEqual);
|
||||||
ga.loadThis();
|
codeBuilder.aload(0); // this
|
||||||
ga.loadArg(1);
|
codeBuilder.aload(2); // value
|
||||||
Type fieldType = ASMToolkit.toType(v);
|
ClassDesc cd = Bytecode.classDesc(v);
|
||||||
ga.unbox(ASMToolkit.toType(v));
|
Bytecode.unbox(codeBuilder, cd);
|
||||||
ga.putField(type, v.getName(), fieldType);
|
codeBuilder.putfield(type, v.getName(), cd);
|
||||||
ga.visitInsn(Opcodes.RETURN);
|
codeBuilder.return_();
|
||||||
ga.visitLabel(notEqual);
|
codeBuilder.labelBinding(notEqual);
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
ga.throwException(TYPE_IOBE, "Index must between 0 and " + fields.size());
|
Bytecode.throwException(codeBuilder, TYPE_IOBE, "Index must between 0 and " + fields.size());
|
||||||
ga.endMethod();
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildConstructor() {
|
private void buildConstructor(ClassBuilder builder) {
|
||||||
MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC, DEFAULT_CONSTRUCTOR.getName(), DEFAULT_CONSTRUCTOR.getDescriptor(), null, null);
|
builder.withMethod(ConstantDescs.INIT_NAME, ConstantDescs.MTD_void, Classfile.ACC_PUBLIC, methodBuilder -> methodBuilder.withCode(codeBuilder -> {
|
||||||
mv.visitIntInsn(Opcodes.ALOAD, 0);
|
codeBuilder.aload(0);
|
||||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, TYPE_EVENT.getInternalName(), DEFAULT_CONSTRUCTOR.getName(), DEFAULT_CONSTRUCTOR.getDescriptor(), false);
|
invokespecial(codeBuilder, TYPE_EVENT, DEFAULT_CONSTRUCTOR);
|
||||||
mv.visitInsn(Opcodes.RETURN);
|
codeBuilder.return_();
|
||||||
mv.visitMaxs(0, 0);
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildClassInfo() {
|
private void buildClassInfo(ClassBuilder builder) {
|
||||||
String internalSuperName = ASMToolkit.getInternalName(Event.class.getName());
|
builder.withSuperclass(Bytecode.classDesc(Event.class));
|
||||||
String internalClassName = type.getInternalName();
|
builder.withFlags(AccessFlag.FINAL, AccessFlag.PUBLIC, AccessFlag.SUPER);
|
||||||
classWriter.visit(52, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, internalClassName, null, internalSuperName, null);
|
List<jdk.internal.classfile.Annotation> annotations = new ArrayList<>();
|
||||||
|
for (jdk.jfr.AnnotationElement a : annotationElements) {
|
||||||
for (AnnotationElement a : annotationElements) {
|
List<jdk.internal.classfile.AnnotationElement> list = new ArrayList<>();
|
||||||
String descriptor = ASMToolkit.getDescriptor(a.getTypeName());
|
|
||||||
AnnotationVisitor av = classWriter.visitAnnotation(descriptor, true);
|
|
||||||
for (ValueDescriptor v : a.getValueDescriptors()) {
|
for (ValueDescriptor v : a.getValueDescriptors()) {
|
||||||
Object value = a.getValue(v.getName());
|
// ValueDescriptor can only hold primitive
|
||||||
String name = v.getName();
|
// No need to care about classes/enums
|
||||||
if (v.isArray()) {
|
var value = a.getValue(v.getName());
|
||||||
AnnotationVisitor arrayVisitor = av.visitArray(name);
|
var av = AnnotationValue.of(value);
|
||||||
Object[] array = (Object[]) value;
|
var ae = jdk.internal.classfile.AnnotationElement.of(v.getName(), av);
|
||||||
for (int i = 0; i < array.length; i++) {
|
list.add(ae);
|
||||||
arrayVisitor.visit(null, array[i]);
|
|
||||||
}
|
}
|
||||||
arrayVisitor.visitEnd();
|
ClassDesc cd = ClassDesc.of(a.getTypeName());
|
||||||
} else {
|
annotations.add(jdk.internal.classfile.Annotation.of(cd, list));
|
||||||
av.visit(name, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
av.visitEnd();
|
|
||||||
}
|
}
|
||||||
|
builder.with(RuntimeVisibleAnnotationsAttribute.of(annotations));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildFields() {
|
private void buildFields(ClassBuilder builder) {
|
||||||
for (ValueDescriptor v : fields) {
|
for (ValueDescriptor v : fields) {
|
||||||
String internal = ASMToolkit.getDescriptor(v.getTypeName());
|
builder.withField(v.getName(), Bytecode.classDesc(v), Classfile.ACC_PRIVATE);
|
||||||
classWriter.visitField(Opcodes.ACC_PRIVATE, v.getName(), internal, null, null);
|
|
||||||
// No need to store annotations on field since they will be replaced anyway.
|
// No need to store annotations on field since they will be replaced anyway.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -25,38 +25,38 @@
|
||||||
|
|
||||||
package jdk.jfr.internal;
|
package jdk.jfr.internal;
|
||||||
|
|
||||||
import jdk.internal.org.objectweb.asm.commons.Method;
|
import jdk.jfr.internal.util.Bytecode.FieldDesc;
|
||||||
import jdk.jfr.internal.EventInstrumentation.FieldInfo;
|
import jdk.jfr.internal.util.Bytecode.MethodDesc;
|
||||||
import jdk.jfr.internal.event.EventConfiguration;
|
import jdk.jfr.internal.util.Utils;
|
||||||
|
|
||||||
public enum EventWriterMethod {
|
public enum EventWriterMethod {
|
||||||
|
|
||||||
BEGIN_EVENT("(" + jdk.internal.org.objectweb.asm.Type.getType(EventConfiguration.class).getDescriptor() + "J)Z", "???", "beginEvent"),
|
BEGIN_EVENT("beginEvent", "(Ljdk/jfr/internal/event/EventConfiguration;J)Z", "???"),
|
||||||
END_EVENT("()Z", "???", "endEvent"),
|
END_EVENT("endEvent", "()Z", "???"),
|
||||||
PUT_BYTE("(B)V", "byte", "putByte"),
|
PUT_BYTE("putByte", "(B)V", "B"),
|
||||||
PUT_SHORT("(S)V", "short", "putShort"),
|
PUT_SHORT("putShort", "(S)V", "S"),
|
||||||
PUT_INT("(I)V", "int", "putInt"),
|
PUT_INT("putInt", "(I)V", "I"),
|
||||||
PUT_LONG("(J)V", "long", "putLong"),
|
PUT_LONG("putLong", "(J)V", "J"),
|
||||||
PUT_FLOAT("(F)V", "float", "putFloat"),
|
PUT_FLOAT("putFloat", "(F)V", "F"),
|
||||||
PUT_DOUBLE("(D)V", "double", "putDouble"),
|
PUT_DOUBLE("putDouble", "(D)V", "D"),
|
||||||
PUT_CHAR("(C)V", "char", "putChar"),
|
PUT_CHAR("putChar", "(C)V", "C"),
|
||||||
PUT_BOOLEAN("(Z)V", "boolean", "putBoolean"),
|
PUT_BOOLEAN("putBoolean", "(Z)V", "Z"),
|
||||||
PUT_THREAD("(Ljava/lang/Thread;)V", Type.THREAD.getName(), "putThread"),
|
PUT_THREAD("putThread", "(Ljava/lang/Thread;)V", "Ljava/lang/Thread;"),
|
||||||
PUT_CLASS("(Ljava/lang/Class;)V", Type.CLASS.getName(), "putClass"),
|
PUT_CLASS("putClass", "(Ljava/lang/Class;)V", "Ljava/lang/Class;"),
|
||||||
PUT_STRING("(Ljava/lang/String;)V", Type.STRING.getName(), "putString"),
|
PUT_STRING("putString", "(Ljava/lang/String;)V", "Ljava/lang/String;"),
|
||||||
PUT_EVENT_THREAD("()V", Type.THREAD.getName(), "putEventThread"),
|
PUT_EVENT_THREAD("putEventThread", "()V", "???"),
|
||||||
PUT_STACK_TRACE("()V", Type.TYPES_PREFIX + "StackTrace", "putStackTrace");
|
PUT_STACK_TRACE("putStackTrace", "()V", "???");
|
||||||
|
|
||||||
final Method asmMethod;
|
final MethodDesc method;
|
||||||
final String typeDescriptor;
|
final String fieldType;
|
||||||
|
|
||||||
EventWriterMethod(String paramSignature, String typeName, String methodName) {
|
EventWriterMethod(String methodName, String paramType, String fieldType) {
|
||||||
this.typeDescriptor = ASMToolkit.getDescriptor(typeName);
|
this.fieldType = fieldType;
|
||||||
this.asmMethod = new Method(methodName, paramSignature);
|
this.method = MethodDesc.of(methodName, paramType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Method asASM() {
|
public MethodDesc method() {
|
||||||
return asmMethod;
|
return method;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -67,16 +67,16 @@ public enum EventWriterMethod {
|
||||||
*
|
*
|
||||||
* @return the method
|
* @return the method
|
||||||
*/
|
*/
|
||||||
public static EventWriterMethod lookupMethod(FieldInfo field) {
|
public static EventWriterMethod lookupMethod(FieldDesc field) {
|
||||||
// event thread
|
// event thread
|
||||||
if (field.name().equals(EventInstrumentation.FIELD_EVENT_THREAD)) {
|
if (field.name().equals(Utils.FIELD_EVENT_THREAD)) {
|
||||||
return EventWriterMethod.PUT_EVENT_THREAD;
|
return EventWriterMethod.PUT_EVENT_THREAD;
|
||||||
}
|
}
|
||||||
for (EventWriterMethod m : EventWriterMethod.values()) {
|
for (EventWriterMethod m : EventWriterMethod.values()) {
|
||||||
if (field.descriptor().equals(m.typeDescriptor)) {
|
if (field.type().descriptorString().equals(m.fieldType)) {
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new Error("Unknown type " + field.descriptor());
|
throw new Error("Unknown field type " + field.type());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ import java.lang.reflect.Modifier;
|
||||||
|
|
||||||
import jdk.jfr.internal.event.EventConfiguration;
|
import jdk.jfr.internal.event.EventConfiguration;
|
||||||
import jdk.jfr.internal.instrument.JDKEvents;
|
import jdk.jfr.internal.instrument.JDKEvents;
|
||||||
import jdk.jfr.internal.util.Utils;
|
import jdk.jfr.internal.util.Bytecode;
|
||||||
/**
|
/**
|
||||||
* All upcalls from the JVM should go through this class.
|
* All upcalls from the JVM should go through this class.
|
||||||
*
|
*
|
||||||
|
@ -75,7 +75,7 @@ final class JVMUpcalls {
|
||||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "Adding instrumentation to event class " + clazz.getName() + " using retransform");
|
Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "Adding instrumentation to event class " + clazz.getName() + " using retransform");
|
||||||
EventInstrumentation ei = new EventInstrumentation(clazz.getSuperclass(), oldBytes, traceId, bootClassLoader, false);
|
EventInstrumentation ei = new EventInstrumentation(clazz.getSuperclass(), oldBytes, traceId, bootClassLoader, false);
|
||||||
byte[] bytes = ei.buildInstrumented();
|
byte[] bytes = ei.buildInstrumented();
|
||||||
ASMToolkit.logASM(clazz.getName(), bytes);
|
Bytecode.log(clazz.getName(), bytes);
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
return JDKEvents.retransformCallback(clazz, oldBytes);
|
return JDKEvents.retransformCallback(clazz, oldBytes);
|
||||||
|
@ -126,7 +126,7 @@ final class JVMUpcalls {
|
||||||
EventWriterKey.ensureEventWriterFactory();
|
EventWriterKey.ensureEventWriterFactory();
|
||||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "Adding " + (forceInstrumentation ? "forced " : "") + "instrumentation for event type " + eventName + " during initial class load");
|
Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "Adding " + (forceInstrumentation ? "forced " : "") + "instrumentation for event type " + eventName + " during initial class load");
|
||||||
byte[] bytes = ei.buildInstrumented();
|
byte[] bytes = ei.buildInstrumented();
|
||||||
ASMToolkit.logASM(ei.getClassName() + "(" + traceId + ")", bytes);
|
Bytecode.log(ei.getClassName() + "(" + traceId + ")", bytes);
|
||||||
return bytes;
|
return bytes;
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.WARN, "Unexpected error when adding instrumentation for event type " + eventName);
|
Logger.log(LogTag.JFR_SYSTEM, LogLevel.WARN, "Unexpected error when adding instrumentation for event type " + eventName);
|
||||||
|
|
|
@ -76,26 +76,26 @@ public final class TypeLibrary {
|
||||||
private static ValueDescriptor createStartTimeField() {
|
private static ValueDescriptor createStartTimeField() {
|
||||||
var annos = createStandardAnnotations("Start Time", null);
|
var annos = createStandardAnnotations("Start Time", null);
|
||||||
annos.add(new jdk.jfr.AnnotationElement(Timestamp.class, Timestamp.TICKS));
|
annos.add(new jdk.jfr.AnnotationElement(Timestamp.class, Timestamp.TICKS));
|
||||||
return PrivateAccess.getInstance().newValueDescriptor(EventInstrumentation.FIELD_START_TIME, Type.LONG, annos, 0, false,
|
return PrivateAccess.getInstance().newValueDescriptor(Utils.FIELD_START_TIME, Type.LONG, annos, 0, false,
|
||||||
EventInstrumentation.FIELD_START_TIME);
|
Utils.FIELD_START_TIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ValueDescriptor createStackTraceField() {
|
private static ValueDescriptor createStackTraceField() {
|
||||||
var annos = createStandardAnnotations("Stack Trace", "Stack Trace starting from the method the event was committed in");
|
var annos = createStandardAnnotations("Stack Trace", "Stack Trace starting from the method the event was committed in");
|
||||||
return PrivateAccess.getInstance().newValueDescriptor(EventInstrumentation.FIELD_STACK_TRACE, Type.STACK_TRACE, annos, 0, true,
|
return PrivateAccess.getInstance().newValueDescriptor(Utils.FIELD_STACK_TRACE, Type.STACK_TRACE, annos, 0, true,
|
||||||
EventInstrumentation.FIELD_STACK_TRACE);
|
Utils.FIELD_STACK_TRACE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ValueDescriptor createThreadField() {
|
private static ValueDescriptor createThreadField() {
|
||||||
var annos = createStandardAnnotations("Event Thread", "Thread in which event was committed in");
|
var annos = createStandardAnnotations("Event Thread", "Thread in which event was committed in");
|
||||||
return PrivateAccess.getInstance().newValueDescriptor(EventInstrumentation.FIELD_EVENT_THREAD, Type.THREAD, annos, 0, true,
|
return PrivateAccess.getInstance().newValueDescriptor(Utils.FIELD_EVENT_THREAD, Type.THREAD, annos, 0, true,
|
||||||
EventInstrumentation.FIELD_EVENT_THREAD);
|
Utils.FIELD_EVENT_THREAD);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ValueDescriptor createDurationField() {
|
private static ValueDescriptor createDurationField() {
|
||||||
var annos = createStandardAnnotations("Duration", null);
|
var annos = createStandardAnnotations("Duration", null);
|
||||||
annos.add(new jdk.jfr.AnnotationElement(Timespan.class, Timespan.TICKS));
|
annos.add(new jdk.jfr.AnnotationElement(Timespan.class, Timespan.TICKS));
|
||||||
return PrivateAccess.getInstance().newValueDescriptor(EventInstrumentation.FIELD_DURATION, Type.LONG, annos, 0, false, EventInstrumentation.FIELD_DURATION);
|
return PrivateAccess.getInstance().newValueDescriptor(Utils.FIELD_DURATION, Type.LONG, annos, 0, false, Utils.FIELD_DURATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static synchronized void initialize() {
|
public static synchronized void initialize() {
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
package jdk.jfr.internal.consumer;
|
package jdk.jfr.internal.consumer;
|
||||||
|
|
||||||
import static jdk.jfr.internal.EventInstrumentation.FIELD_DURATION;
|
import static jdk.jfr.internal.util.Utils.FIELD_DURATION;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
166
src/jdk.jfr/share/classes/jdk/jfr/internal/util/Bytecode.java
Normal file
166
src/jdk.jfr/share/classes/jdk/jfr/internal/util/Bytecode.java
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023, 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.util;
|
||||||
|
|
||||||
|
import jdk.jfr.ValueDescriptor;
|
||||||
|
import java.lang.constant.ClassDesc;
|
||||||
|
import java.lang.constant.ConstantDescs;
|
||||||
|
import java.lang.constant.MethodTypeDesc;
|
||||||
|
import java.util.Objects;
|
||||||
|
import jdk.jfr.internal.Logger;
|
||||||
|
import jdk.jfr.internal.LogLevel;
|
||||||
|
import jdk.jfr.internal.LogTag;
|
||||||
|
import jdk.internal.classfile.CodeBuilder;
|
||||||
|
import jdk.internal.classfile.ClassModel;
|
||||||
|
import jdk.internal.classfile.Classfile;
|
||||||
|
import jdk.internal.classfile.components.ClassPrinter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class when working with bytecode.
|
||||||
|
*/
|
||||||
|
public final class Bytecode {
|
||||||
|
|
||||||
|
private static final ClassDesc CD_Thread = classDesc(Thread.class);
|
||||||
|
|
||||||
|
public record ClassMethodDesc(ClassDesc type, MethodDesc method) {
|
||||||
|
public static ClassMethodDesc of(Class<?> clazz, String method, String desrciptor) {
|
||||||
|
return new ClassMethodDesc(classDesc(clazz), MethodDesc.of(method, desrciptor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public record FieldDesc(ClassDesc type, String name) {
|
||||||
|
public static FieldDesc of(ClassDesc type, String name) {
|
||||||
|
return new FieldDesc(type, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FieldDesc of(Class<?> type, String name) {
|
||||||
|
return of(classDesc(type), name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public record MethodDesc(String name, MethodTypeDesc descriptor) {
|
||||||
|
public static MethodDesc of(String methodName, String descriptor) {
|
||||||
|
return new MethodDesc(methodName, MethodTypeDesc.ofDescriptor(descriptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MethodDesc of(String methodName, Class<?> returnType, Class<?>... parameters) {
|
||||||
|
ClassDesc[] parameterDesc = new ClassDesc[parameters.length];
|
||||||
|
for (int i = 0; i < parameterDesc.length; i++) {
|
||||||
|
parameterDesc[i] = classDesc(parameters[i]);
|
||||||
|
}
|
||||||
|
ClassDesc returnDesc = classDesc(returnType);
|
||||||
|
MethodTypeDesc mtd = MethodTypeDesc.of(returnDesc, parameterDesc);
|
||||||
|
return new MethodDesc(methodName, mtd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ClassDesc classDesc(ValueDescriptor v) {
|
||||||
|
String typeName = v.getTypeName();
|
||||||
|
return switch (typeName) {
|
||||||
|
case "boolean" -> ConstantDescs.CD_boolean;
|
||||||
|
case "byte" -> ConstantDescs.CD_byte;
|
||||||
|
case "short" -> ConstantDescs.CD_short;
|
||||||
|
case "char" -> ConstantDescs.CD_char;
|
||||||
|
case "int" -> ConstantDescs.CD_int;
|
||||||
|
case "long" -> ConstantDescs.CD_long;
|
||||||
|
case "double" -> ConstantDescs.CD_double;
|
||||||
|
case "float" -> ConstantDescs.CD_float;
|
||||||
|
case "java.lang.String" -> ConstantDescs.CD_String;
|
||||||
|
case "java.lang.Class" -> ConstantDescs.CD_Class;
|
||||||
|
case "java.lang.Thread" -> CD_Thread;
|
||||||
|
default -> throw new InternalError("Unsupported JFR type " + v.getTypeName());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ClassDesc classDesc(Class<?> clazz) {
|
||||||
|
return ClassDesc.ofDescriptor(clazz.descriptorString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void getfield(CodeBuilder codeBuilder, ClassDesc owner, FieldDesc field) {
|
||||||
|
codeBuilder.getfield(owner, field.name(), field.type());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void putfield(CodeBuilder codeBuilder, ClassDesc owner, FieldDesc field) {
|
||||||
|
codeBuilder.putfield(owner, field.name(), field.type());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void invokestatic(CodeBuilder codeBuilder, ClassDesc owner, MethodDesc method) {
|
||||||
|
codeBuilder.invokestatic(owner, method.name(), method.descriptor());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void invokespecial(CodeBuilder codeBuilder, ClassDesc owner, MethodDesc method) {
|
||||||
|
codeBuilder.invokespecial(owner, method.name(), method.descriptor());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void invokevirtual(CodeBuilder codeBuilder, ClassDesc owner, MethodDesc method) {
|
||||||
|
codeBuilder.invokevirtual(owner, method.name(), method.descriptor());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void invokevirtual(CodeBuilder codeBuilder, ClassMethodDesc cmd) {
|
||||||
|
invokevirtual(codeBuilder, cmd.type(), cmd.method());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void unbox(CodeBuilder codeBuilder, ClassDesc type) {
|
||||||
|
if (!type.isPrimitive()) {
|
||||||
|
codeBuilder.checkcast(type);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ClassMethodDesc unboxer = switch (type.descriptorString()) {
|
||||||
|
case "B" -> ClassMethodDesc.of(Byte.class, "byteValue", "()B");
|
||||||
|
case "S" -> ClassMethodDesc.of(Short.class, "shortValue", "()S");
|
||||||
|
case "C" -> ClassMethodDesc.of(Character.class, "charValue", "()C");
|
||||||
|
case "I" -> ClassMethodDesc.of(Integer.class, "intValue", "()I");
|
||||||
|
case "J" -> ClassMethodDesc.of(Long.class, "longValue", "()J");
|
||||||
|
case "F" -> ClassMethodDesc.of(Float.class, "floatValue", "()F");
|
||||||
|
case "D" -> ClassMethodDesc.of(Double.class, "doubleValue", "()D");
|
||||||
|
case "Z" -> ClassMethodDesc.of(Boolean.class, "booleanValue", "()Z");
|
||||||
|
default -> throw new InternalError("Unsupported JFR type " + type.descriptorString());
|
||||||
|
};
|
||||||
|
codeBuilder.checkcast(unboxer.type());
|
||||||
|
invokevirtual(codeBuilder, unboxer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void throwException(CodeBuilder cb, ClassDesc type, String message) {
|
||||||
|
Objects.requireNonNull(message);
|
||||||
|
cb.new_(type);
|
||||||
|
cb.dup();
|
||||||
|
cb.ldc(message);
|
||||||
|
MethodDesc md = MethodDesc.of("<init>", void.class, String.class);
|
||||||
|
invokespecial(cb, type, md);
|
||||||
|
cb.athrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void log(String className, byte[] bytes) {
|
||||||
|
Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.INFO, "Generated bytecode for class " + className);
|
||||||
|
if (Logger.shouldLog(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.TRACE)) {
|
||||||
|
StringBuilder out = new StringBuilder();
|
||||||
|
out.append("Bytecode:");
|
||||||
|
out.append(System.lineSeparator());
|
||||||
|
ClassModel classModel = Classfile.of().parse(bytes);
|
||||||
|
ClassPrinter.toYaml(classModel, ClassPrinter.Verbosity.TRACE_ALL, out::append);
|
||||||
|
Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.TRACE, out.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -56,6 +56,11 @@ import jdk.jfr.internal.settings.StackTraceSetting;
|
||||||
import jdk.jfr.internal.settings.ThresholdSetting;
|
import jdk.jfr.internal.settings.ThresholdSetting;
|
||||||
|
|
||||||
public final class Utils {
|
public final class Utils {
|
||||||
|
public static final String FIELD_DURATION = "duration";
|
||||||
|
public static final String FIELD_STACK_TRACE = "stackTrace";
|
||||||
|
public static final String FIELD_START_TIME = "startTime";
|
||||||
|
public static final String FIELD_EVENT_THREAD = "eventThread";
|
||||||
|
|
||||||
private static final Object flushObject = new Object();
|
private static final Object flushObject = new Object();
|
||||||
private static final String LEGACY_EVENT_NAME_PREFIX = "com.oracle.jdk.";
|
private static final String LEGACY_EVENT_NAME_PREFIX = "com.oracle.jdk.";
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -37,6 +37,6 @@ public class TestEventWriterLog {
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-Xlog:jfr+system+bytecode=trace", "-XX:StartFlightRecording", "-version");
|
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-Xlog:jfr+system+bytecode=trace", "-XX:StartFlightRecording", "-version");
|
||||||
OutputAnalyzer output = new OutputAnalyzer(pb.start());
|
OutputAnalyzer output = new OutputAnalyzer(pb.start());
|
||||||
output.shouldContain("extends jdk/jfr/events/AbstractJDKEvent");
|
output.shouldContain("superclass: jdk/jfr/events/AbstractJDKEvent");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue