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 }