src/jdk.jfr/share/classes/jdk/jfr/internal/EventHandlerCreator.java
changeset 50113 caf115bb98ad
child 52015 821bfc24d750
equal deleted inserted replaced
50112:7a2a740815b7 50113:caf115bb98ad
       
     1 /*
       
     2  * Copyright (c) 2016, 2018, 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 
       
    26 package jdk.jfr.internal;
       
    27 
       
    28 import java.lang.reflect.Constructor;
       
    29 import java.lang.reflect.Field;
       
    30 import java.lang.reflect.InvocationTargetException;
       
    31 import java.lang.reflect.Modifier;
       
    32 import java.util.ArrayList;
       
    33 import java.util.List;
       
    34 import java.util.StringJoiner;
       
    35 
       
    36 import jdk.internal.org.objectweb.asm.ClassWriter;
       
    37 import jdk.internal.org.objectweb.asm.Label;
       
    38 import jdk.internal.org.objectweb.asm.MethodVisitor;
       
    39 import jdk.internal.org.objectweb.asm.Opcodes;
       
    40 import jdk.internal.org.objectweb.asm.Type;
       
    41 import jdk.internal.org.objectweb.asm.commons.Method;
       
    42 import jdk.jfr.Event;
       
    43 import jdk.jfr.EventType;
       
    44 import jdk.jfr.SettingControl;
       
    45 import jdk.jfr.ValueDescriptor;
       
    46 import jdk.jfr.internal.EventInstrumentation.FieldInfo;
       
    47 import jdk.jfr.internal.EventInstrumentation.SettingInfo;
       
    48 import jdk.jfr.internal.handlers.EventHandler;
       
    49 
       
    50 final class EventHandlerCreator {
       
    51     // TODO:
       
    52     // How can we find out class version without loading a
       
    53     // class as resource in a privileged block and use ASM to inspect
       
    54     // the contents. Using '52' even though we know later versions
       
    55     // are available. The reason for this is compatibility aspects
       
    56     // with for example WLS.
       
    57     private static final int CLASS_VERSION = 52;
       
    58 
       
    59     // This is needed so a new EventHandler is automatically generated in MetadataRespoistory
       
    60     // if a user Event class is loaded using APPCDS/CDS.
       
    61     private static final String SUFFIX  = "_" + System.currentTimeMillis() + "-" + JVM.getJVM().getPid();
       
    62 
       
    63     private static final String FIELD_EVENT_TYPE = "platformEventType";
       
    64     private static final String FIELD_PREFIX_STRING_POOL = "stringPool";
       
    65 
       
    66     private final static Type TYPE_STRING_POOL = Type.getType(StringPool.class);
       
    67     private final static Type TYPE_EVENT_WRITER = Type.getType(EventWriter.class);
       
    68     private final static Type TYPE_PLATFORM_EVENT_TYPE = Type.getType(PlatformEventType.class);
       
    69     private final static Type TYPE_EVENT_HANDLER = Type.getType(EventHandler.class);
       
    70     private final static Type TYPE_SETTING_CONTROL = Type.getType(SettingControl.class);
       
    71     private final static Type TYPE_EVENT_TYPE = Type.getType(EventType.class);
       
    72     private final static Type TYPE_EVENT_CONTROL = Type.getType(EventControl.class);
       
    73     private final static String DESCRIPTOR_EVENT_HANDLER = "(" + Type.BOOLEAN_TYPE.getDescriptor() + TYPE_EVENT_TYPE.getDescriptor() + TYPE_EVENT_CONTROL.getDescriptor() + ")V";
       
    74     private final static Method METHOD_GET_EVENT_WRITER = new Method("getEventWriter", "()" + TYPE_EVENT_WRITER.getDescriptor());
       
    75     private final static Method METHOD_EVENT_HANDLER_CONSTRUCTOR = new Method("<init>", DESCRIPTOR_EVENT_HANDLER);
       
    76     private final static Method METHOD_RESET = new Method("reset", "()V");
       
    77 
       
    78     private final ClassWriter classWriter;
       
    79     private final String className;
       
    80     private final String internalClassName;
       
    81     private final List<SettingInfo> settingInfos;
       
    82     private final List<FieldInfo> fields;
       
    83 
       
    84     public EventHandlerCreator(long id, List<SettingInfo> settingInfos, List<FieldInfo> fields) {
       
    85         this.classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
       
    86         this.className = makeEventHandlerName(id);
       
    87         this.internalClassName = ASMToolkit.getInternalName(className);
       
    88         this.settingInfos = settingInfos;
       
    89         this.fields = fields;
       
    90     }
       
    91 
       
    92     public static String makeEventHandlerName(long id) {
       
    93         return EventHandler.class.getName() + id + SUFFIX;
       
    94     }
       
    95 
       
    96     public EventHandlerCreator(long id, List<SettingInfo> settingInfos, EventType type, Class<? extends Event> eventClass) {
       
    97         this(id, settingInfos, createFieldInfos(eventClass, type));
       
    98     }
       
    99 
       
   100     private static List<FieldInfo> createFieldInfos(Class<? extends Event> eventClass, EventType type) throws Error {
       
   101         List<FieldInfo> fieldInfos = new ArrayList<>();
       
   102         for (ValueDescriptor v : type.getFields()) {
       
   103             // Only value descriptors that are not fields on the event class.
       
   104             if (v != TypeLibrary.STACK_TRACE_FIELD && v != TypeLibrary.THREAD_FIELD) {
       
   105                 String fieldName = PrivateAccess.getInstance().getFieldName(v);
       
   106                 String fieldDescriptor = ASMToolkit.getDescriptor(v.getTypeName());
       
   107                 Class<?> c = eventClass;
       
   108                 String internalName = null;
       
   109                 while (c != Event.class) {
       
   110                     try {
       
   111                         Field field = c.getDeclaredField(fieldName);
       
   112                         if (c == eventClass || !Modifier.isPrivate(field.getModifiers())) {
       
   113                             internalName = ASMToolkit.getInternalName(c.getName());
       
   114                             break;
       
   115                         }
       
   116                     } catch (NoSuchFieldException | SecurityException e) {
       
   117                         // ignore
       
   118                     }
       
   119                     c = c.getSuperclass();
       
   120                 }
       
   121                 if (internalName != null) {
       
   122                     fieldInfos.add(new FieldInfo(fieldName, fieldDescriptor, internalName));
       
   123                 } else {
       
   124                     throw new InternalError("Could not locate field " + fieldName + " for event type" + type.getName());
       
   125                 }
       
   126             }
       
   127         }
       
   128         return fieldInfos;
       
   129     }
       
   130 
       
   131     public Class<? extends EventHandler> makeEventHandlerClass() {
       
   132         buildClassInfo();
       
   133         buildConstructor();
       
   134         buildWriteMethod();
       
   135         byte[] bytes = classWriter.toByteArray();
       
   136         ASMToolkit.logASM(className, bytes);
       
   137         return SecuritySupport.defineClass(className, bytes, Event.class.getClassLoader()).asSubclass(EventHandler.class);
       
   138     }
       
   139 
       
   140     public static EventHandler instantiateEventHandler(Class<? extends EventHandler> handlerClass, boolean registered, EventType eventType, EventControl eventControl) throws Error {
       
   141         final Constructor<?> cc;
       
   142         try {
       
   143             cc = handlerClass.getDeclaredConstructors()[0];
       
   144         } catch (Exception e) {
       
   145             throw (Error) new InternalError("Could not get handler constructor for " + eventType.getName()).initCause(e);
       
   146         }
       
   147         // Users should not be allowed to create instances of the event handler
       
   148         // so we need to unlock it here.
       
   149         SecuritySupport.setAccessible(cc);
       
   150         try {
       
   151             List<SettingInfo> settingInfos = eventControl.getSettingInfos();
       
   152             Object[] arguments = new Object[3 + settingInfos.size()];
       
   153             arguments[0] = registered;
       
   154             arguments[1] = eventType;
       
   155             arguments[2] = eventControl;
       
   156             for (SettingInfo si : settingInfos) {
       
   157                 arguments[si.index + 3] = si.settingControl;
       
   158             }
       
   159             return (EventHandler) cc.newInstance(arguments);
       
   160         } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
       
   161             throw (Error) new InternalError("Could not instantiate event handler for " + eventType.getName() + ". " + e.getMessage()).initCause(e);
       
   162         }
       
   163     }
       
   164 
       
   165     private void buildConstructor() {
       
   166         MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PRIVATE, METHOD_EVENT_HANDLER_CONSTRUCTOR.getName(), makeConstructorDescriptor(settingInfos), null, null);
       
   167         mv.visitVarInsn(Opcodes.ALOAD, 0); // this
       
   168         mv.visitVarInsn(Opcodes.ILOAD, 1); // registered
       
   169         mv.visitVarInsn(Opcodes.ALOAD, 2); // event type
       
   170         mv.visitVarInsn(Opcodes.ALOAD, 3); // event control
       
   171         mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(EventHandler.class), METHOD_EVENT_HANDLER_CONSTRUCTOR.getName(), METHOD_EVENT_HANDLER_CONSTRUCTOR.getDescriptor(), false);
       
   172         for (SettingInfo si : settingInfos) {
       
   173             mv.visitVarInsn(Opcodes.ALOAD, 0); // this
       
   174             mv.visitVarInsn(Opcodes.ALOAD, si.index + 4); // Setting Control
       
   175             mv.visitFieldInsn(Opcodes.PUTFIELD, internalClassName, si.fieldName, TYPE_SETTING_CONTROL.getDescriptor());
       
   176         }
       
   177         // initialized string field writers
       
   178         int fieldIndex = 0;
       
   179         for (FieldInfo field : fields) {
       
   180             if (field.isString()) {
       
   181                 mv.visitVarInsn(Opcodes.ALOAD, 0);
       
   182                 mv.visitVarInsn(Opcodes.ALOAD, 0);
       
   183                 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(EventHandler.class), "createStringFieldWriter", "()" + TYPE_STRING_POOL.getDescriptor(), false);
       
   184                 mv.visitFieldInsn(Opcodes.PUTFIELD, internalClassName, FIELD_PREFIX_STRING_POOL + fieldIndex, TYPE_STRING_POOL.getDescriptor());
       
   185             }
       
   186             fieldIndex++;
       
   187         }
       
   188         mv.visitInsn(Opcodes.RETURN);
       
   189         mv.visitMaxs(0, 0);
       
   190         mv.visitEnd();
       
   191     }
       
   192 
       
   193     private void buildClassInfo() {
       
   194         String internalSuperName = ASMToolkit.getInternalName(EventHandler.class.getName());
       
   195         classWriter.visit(CLASS_VERSION, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, internalClassName, null, internalSuperName, null);
       
   196         for (SettingInfo si : settingInfos) {
       
   197             classWriter.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL, si.fieldName, TYPE_SETTING_CONTROL.getDescriptor(), null, null);
       
   198         }
       
   199         int fieldIndex = 0;
       
   200         for (FieldInfo field : fields) {
       
   201             if (field.isString()) {
       
   202                 classWriter.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL, FIELD_PREFIX_STRING_POOL+ fieldIndex, TYPE_STRING_POOL.getDescriptor(), null, null);
       
   203             }
       
   204             fieldIndex++;
       
   205         }
       
   206     }
       
   207 
       
   208     private void visitMethod(final MethodVisitor mv, final int opcode, final Type type, final Method method) {
       
   209         mv.visitMethodInsn(opcode, type.getInternalName(), method.getName(), method.getDescriptor(), false);
       
   210     }
       
   211 
       
   212     private void buildWriteMethod() {
       
   213         int argIndex = 0; // // indexes the argument type array, the argument type array does not include 'this'
       
   214         int slotIndex = 1; // indexes the proper slot in the local variable table, takes type size into account, therefore sometimes argIndex != slotIndex
       
   215         int fieldIndex = 0;
       
   216         Method desc = ASMToolkit.makeWriteMethod(fields);
       
   217         Type[] argumentTypes = Type.getArgumentTypes(desc.getDescriptor());
       
   218         MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC, desc.getName(), desc.getDescriptor(), null, null);
       
   219         mv.visitCode();
       
   220         Label start = new Label();
       
   221         Label endTryBlock = new Label();
       
   222         Label exceptionHandler = new Label();
       
   223         mv.visitTryCatchBlock(start, endTryBlock, exceptionHandler, "java/lang/Throwable");
       
   224         mv.visitLabel(start);
       
   225         visitMethod(mv, Opcodes.INVOKESTATIC, TYPE_EVENT_WRITER, METHOD_GET_EVENT_WRITER);
       
   226         // stack: [BW]
       
   227         mv.visitInsn(Opcodes.DUP);
       
   228         // stack: [BW], [BW]
       
   229         // write begin event
       
   230         mv.visitVarInsn(Opcodes.ALOAD, 0);
       
   231         // stack: [BW], [BW], [this]
       
   232         mv.visitFieldInsn(Opcodes.GETFIELD, TYPE_EVENT_HANDLER.getInternalName(), FIELD_EVENT_TYPE, TYPE_PLATFORM_EVENT_TYPE.getDescriptor());
       
   233         // stack: [BW], [BW], [BS]
       
   234         visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, EventWriterMethod.BEGIN_EVENT.asASM());
       
   235         // stack: [BW], [integer]
       
   236         Label recursive = new Label();
       
   237         mv.visitJumpInsn(Opcodes.IFEQ, recursive);
       
   238         // stack: [BW]
       
   239         // write startTime
       
   240         mv.visitInsn(Opcodes.DUP);
       
   241         // stack: [BW], [BW]
       
   242         mv.visitVarInsn(argumentTypes[argIndex].getOpcode(Opcodes.ILOAD), slotIndex);
       
   243         // stack: [BW], [BW], [long]
       
   244         slotIndex += argumentTypes[argIndex++].getSize();
       
   245         visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, EventWriterMethod.PUT_LONG.asASM());
       
   246         // stack: [BW]
       
   247         fieldIndex++;
       
   248         // write duration
       
   249         mv.visitInsn(Opcodes.DUP);
       
   250         // stack: [BW], [BW]
       
   251         mv.visitVarInsn(argumentTypes[argIndex].getOpcode(Opcodes.ILOAD), slotIndex);
       
   252         // stack: [BW], [BW], [long]
       
   253         slotIndex += argumentTypes[argIndex++].getSize();
       
   254         visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, EventWriterMethod.PUT_LONG.asASM());
       
   255         // stack: [BW]
       
   256         fieldIndex++;
       
   257         // write eventThread
       
   258         mv.visitInsn(Opcodes.DUP);
       
   259         // stack: [BW], [BW]
       
   260         visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, EventWriterMethod.PUT_EVENT_THREAD.asASM());
       
   261         // stack: [BW]
       
   262         // write stackTrace
       
   263         mv.visitInsn(Opcodes.DUP);
       
   264         // stack: [BW], [BW]
       
   265         visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, EventWriterMethod.PUT_STACK_TRACE.asASM());
       
   266         // stack: [BW]
       
   267         // write custom fields
       
   268         while (fieldIndex < fields.size()) {
       
   269             mv.visitInsn(Opcodes.DUP);
       
   270             // stack: [BW], [BW]
       
   271             mv.visitVarInsn(argumentTypes[argIndex].getOpcode(Opcodes.ILOAD), slotIndex);
       
   272             // stack:[BW], [BW], [field]
       
   273             slotIndex += argumentTypes[argIndex++].getSize();
       
   274             FieldInfo field = fields.get(fieldIndex);
       
   275             if (field.isString()) {
       
   276                 mv.visitVarInsn(Opcodes.ALOAD, 0);
       
   277                 // stack:[BW], [BW], [field], [this]
       
   278                 mv.visitFieldInsn(Opcodes.GETFIELD, this.internalClassName, FIELD_PREFIX_STRING_POOL + fieldIndex, TYPE_STRING_POOL.getDescriptor());
       
   279                 // stack:[BW], [BW], [field], [string]
       
   280             }
       
   281             EventWriterMethod eventMethod = EventWriterMethod.lookupMethod(field);
       
   282             visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, eventMethod.asASM());
       
   283             // stack: [BW]
       
   284             fieldIndex++;
       
   285         }
       
   286         // stack: [BW]
       
   287         // write end event (writer already on stack)
       
   288         visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, EventWriterMethod.END_EVENT.asASM());
       
   289         // stack [integer]
       
   290         // notified -> restart event write attempt
       
   291         mv.visitJumpInsn(Opcodes.IFEQ, start);
       
   292         // stack:
       
   293         mv.visitLabel(endTryBlock);
       
   294         Label end = new Label();
       
   295         mv.visitJumpInsn(Opcodes.GOTO, end);
       
   296         mv.visitLabel(exceptionHandler);
       
   297         // stack: [ex]
       
   298         mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/lang/Throwable"});
       
   299         visitMethod(mv, Opcodes.INVOKESTATIC, TYPE_EVENT_WRITER, METHOD_GET_EVENT_WRITER);
       
   300         // stack: [ex] [BW]
       
   301         mv.visitInsn(Opcodes.DUP);
       
   302         // stack: [ex] [BW] [BW]
       
   303         Label rethrow = new Label();
       
   304         mv.visitJumpInsn(Opcodes.IFNULL, rethrow);
       
   305         // stack: [ex] [BW]
       
   306         mv.visitInsn(Opcodes.DUP);
       
   307         // stack: [ex] [BW] [BW]
       
   308         visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, METHOD_RESET);
       
   309         mv.visitLabel(rethrow);
       
   310         // stack:[ex] [BW]
       
   311         mv.visitFrame(Opcodes.F_SAME, 0, null, 2, new Object[] {"java/lang/Throwable", TYPE_EVENT_WRITER.getInternalName()});
       
   312         mv.visitInsn(Opcodes.POP);
       
   313         // stack:[ex]
       
   314         mv.visitInsn(Opcodes.ATHROW);
       
   315         mv.visitLabel(recursive);
       
   316         // stack: [BW]
       
   317         mv.visitFrame(Opcodes.F_SAME, 0, null, 1, new Object[] { TYPE_EVENT_WRITER.getInternalName()} );
       
   318         mv.visitInsn(Opcodes.POP);
       
   319         mv.visitLabel(end);
       
   320         // stack:
       
   321         mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
       
   322         mv.visitInsn(Opcodes.RETURN);
       
   323         mv.visitMaxs(0, 0);
       
   324         mv.visitEnd();
       
   325     }
       
   326 
       
   327     private static String makeConstructorDescriptor(List<SettingInfo> settingsInfos) {
       
   328         StringJoiner constructordescriptor = new StringJoiner("", "(", ")V");
       
   329         constructordescriptor.add(Type.BOOLEAN_TYPE.getDescriptor());
       
   330         constructordescriptor.add(Type.getType(EventType.class).getDescriptor());
       
   331         constructordescriptor.add(Type.getType(EventControl.class).getDescriptor());
       
   332         for (int i = 0; i < settingsInfos.size(); i++) {
       
   333             constructordescriptor.add(TYPE_SETTING_CONTROL.getDescriptor());
       
   334         }
       
   335         return constructordescriptor.toString();
       
   336     }
       
   337 }