1 /* 2 * Copyright (c) 2013, 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.instrument; 27 28 import java.io.ByteArrayOutputStream; 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.lang.reflect.Method; 32 import java.util.ArrayList; 33 import java.util.List; 34 35 import jdk.internal.org.objectweb.asm.ClassReader; 36 import jdk.internal.org.objectweb.asm.ClassVisitor; 37 import jdk.internal.org.objectweb.asm.ClassWriter; 38 import jdk.internal.org.objectweb.asm.Opcodes; 39 import jdk.internal.org.objectweb.asm.tree.ClassNode; 40 import jdk.jfr.internal.SecuritySupport; 41 import jdk.jfr.internal.Utils; 42 43 /** 44 * This class will perform byte code instrumentation given an "instrumentor" class. 45 * 46 * @see JITracer 47 * 48 * @author Staffan Larsen 49 */ 50 @Deprecated 51 final class JIClassInstrumentation { 52 private final Class<?> instrumentor; 53 private final String targetName; 54 private final String instrumentorName; 55 private final byte[] newBytes; 56 private final ClassReader targetClassReader; 57 private final ClassReader instrClassReader; 58 59 /** 60 * Creates an instance and performs the instrumentation. 61 * 62 * @param instrumentor instrumentor class 63 * @param target target class 64 * @param old_target_bytes bytes in target 65 * 66 * @throws ClassNotFoundException 67 * @throws IOException 68 */ 69 JIClassInstrumentation(Class<?> instrumentor, Class<?> target, byte[] old_target_bytes) throws ClassNotFoundException, IOException { 70 instrumentorName = instrumentor.getName(); 71 this.targetName = target.getName(); 72 this.instrumentor = instrumentor; 73 this.targetClassReader = new ClassReader(old_target_bytes); 74 this.instrClassReader = new ClassReader(getOriginalClassBytes(instrumentor)); 75 this.newBytes = makeBytecode(); 76 Utils.writeGeneratedASM(target.getName(), newBytes); 77 } 78 79 private static byte[] getOriginalClassBytes(Class<?> clazz) throws IOException { 80 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 81 String name = "/" + clazz.getName().replace(".", "/") + ".class"; 82 InputStream is = SecuritySupport.getResourceAsStream(name); 83 int bytesRead; 84 byte[] buffer = new byte[16384]; 85 while ((bytesRead = is.read(buffer, 0, buffer.length)) != -1) { 86 baos.write(buffer, 0, bytesRead); 87 } 88 baos.flush(); 89 is.close(); 90 return baos.toByteArray(); 91 } 92 93 private byte[] makeBytecode() throws IOException, ClassNotFoundException { 94 95 // Find the methods to instrument and inline 96 97 final List<Method> instrumentationMethods = new ArrayList<>(); 98 for (final Method m : instrumentor.getDeclaredMethods()) { 99 JIInstrumentationMethod im = m.getAnnotation(JIInstrumentationMethod.class); 100 if (im != null) { 101 instrumentationMethods.add(m); 102 } 103 } 104 105 // We begin by inlining the target's methods into the instrumentor 106 107 ClassNode temporary = new ClassNode(); 108 ClassVisitor inliner = new JIInliner( 109 Opcodes.ASM5, 110 temporary, 111 targetName, 112 instrumentorName, 113 targetClassReader, 114 instrumentationMethods); 115 instrClassReader.accept(inliner, ClassReader.EXPAND_FRAMES); 116 117 // Now we have the target's methods inlined into the instrumentation code (in 'temporary'). 118 // We now need to replace the target's method with the code in the 119 // instrumentation method. 120 121 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 122 JIMethodMergeAdapter ma = new JIMethodMergeAdapter( 123 cw, 124 temporary, 125 instrumentationMethods, 126 instrumentor.getAnnotationsByType(JITypeMapping.class)); 127 targetClassReader.accept(ma, ClassReader.EXPAND_FRAMES); 128 129 return cw.toByteArray(); 130 } 131 132 /** 133 * Get the instrumented byte codes that can be used to retransform the class. 134 * 135 * @return bytes 136 */ 137 public byte[] getNewBytes() { 138 return newBytes.clone(); 139 } 140 }