diff options
author | Jean-Philippe Lesot <jplesot@google.com> | 2014-04-10 11:21:17 +0200 |
---|---|---|
committer | Jean-Philippe Lesot <jplesot@google.com> | 2014-04-10 11:23:34 +0200 |
commit | cddf2a3c40925d77a7e3d7b22801037d957cae3b (patch) | |
tree | ca0a044223b08d5937f16e5548b3fca0a4479e83 /java-allocation-instrumenter | |
parent | f19253a8e55f9ea06c225b7682620f4b1b8182f5 (diff) | |
download | toolchain_jack-cddf2a3c40925d77a7e3d7b22801037d957cae3b.zip toolchain_jack-cddf2a3c40925d77a7e3d7b22801037d957cae3b.tar.gz toolchain_jack-cddf2a3c40925d77a7e3d7b22801037d957cae3b.tar.bz2 |
Remove unused asm library from allocation-jack.jar.
This is not necessary because allocation-jack.jar is only a library
in order to compile jack with java-allocation-instrumenter API.
The runtime version of allocation.jar is a java agent loaded by the
JRE, and need to be retrieved from external source.
Change-Id: I8b16ba18597e2aba6a647f42c511a711bb2fa016
Diffstat (limited to 'java-allocation-instrumenter')
8 files changed, 0 insertions, 1534 deletions
diff --git a/java-allocation-instrumenter/Android.mk b/java-allocation-instrumenter/Android.mk index 51654a9..26dfbdc 100644 --- a/java-allocation-instrumenter/Android.mk +++ b/java-allocation-instrumenter/Android.mk @@ -33,7 +33,6 @@ LOCAL_JAVA_LIBRARIES := \ guava-jack LOCAL_STATIC_JAVA_LIBRARIES := \ - asm-all-4.1-jack \ guava-collect-jack include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationClassAdapter.java b/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationClassAdapter.java deleted file mode 100644 index d66bd15..0000000 --- a/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationClassAdapter.java +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2009 Google Inc. All Rights Reserved. - -package com.google.monitoring.runtime.instrumentation; - -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.commons.LocalVariablesSorter; -import org.objectweb.asm.commons.JSRInlinerAdapter; - -/** - * Instruments bytecodes that allocate heap memory to call a recording hook. - * A <code>ClassVisitor</code> that processes methods with a - * <code>AllocationMethodAdapter</code> to instrument heap allocations. - * - * @author jeremymanson@google.com (Jeremy Manson) - * @author fischman@google.com (Ami Fischman) (Original Author) - */ -class AllocationClassAdapter extends ClassVisitor { - private final String recorderClass; - private final String recorderMethod; - - public AllocationClassAdapter(ClassVisitor cv, String recorderClass, - String recorderMethod) { - super(Opcodes.ASM4, cv); - this.recorderClass = recorderClass; - this.recorderMethod = recorderMethod; - } - - /** - * For each method in the class being instrumented, <code>visitMethod</code> - * is called and the returned MethodVisitor is used to visit the method. - * Note that a new MethodVisitor is constructed for each method. - */ - @Override - public MethodVisitor visitMethod(int access, String base, String desc, - String signature, String[] exceptions) { - MethodVisitor mv = - cv.visitMethod(access, base, desc, signature, exceptions); - - if (mv != null) { - // We need to compute stackmaps (see - // AllocationInstrumenter#instrument). This can't really be - // done for old bytecode that contains JSR and RET instructions. - // So, we remove JSRs and RETs. - JSRInlinerAdapter jsria = new JSRInlinerAdapter( - mv, access, base, desc, signature, exceptions); - AllocationMethodAdapter aimv = - new AllocationMethodAdapter(jsria, recorderClass, recorderMethod); - LocalVariablesSorter lvs = new LocalVariablesSorter(access, desc, aimv); - aimv.lvs = lvs; - mv = lvs; - } - return mv; - } -} diff --git a/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationInstrumenter.java b/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationInstrumenter.java deleted file mode 100644 index c4d6ab6..0000000 --- a/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationInstrumenter.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (C) 2007 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.monitoring.runtime.instrumentation; - -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassWriter; - -import java.lang.instrument.ClassFileTransformer; -import java.lang.instrument.Instrumentation; -import java.lang.instrument.UnmodifiableClassException; -import java.security.ProtectionDomain; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Instruments bytecodes that allocate heap memory to call a recording hook. - * This will add a static invocation to a recorder function to any bytecode that - * looks like it will be allocating heap memory allowing users to implement heap - * profiling schemes. - * - * @author Ami Fischman - * @author Jeremy Manson - */ -public class AllocationInstrumenter implements ClassFileTransformer { - static final Logger logger = - Logger.getLogger(AllocationInstrumenter.class.getName()); - - // We can rewrite classes loaded by the bootstrap class loader - // iff the agent is loaded by the bootstrap class loader. It is - // always *supposed* to be loaded by the bootstrap class loader, but - // this relies on the Boot-Class-Path attribute in the JAR file always being - // set to the name of the JAR file that contains this agent, which we cannot - // guarantee programmatically. - private static volatile boolean canRewriteBootstrap; - - static boolean canRewriteClass(String className, ClassLoader loader) { - // There are two conditions under which we don't rewrite: - // 1. If className was loaded by the bootstrap class loader and - // the agent wasn't (in which case the class being rewritten - // won't be able to call agent methods). - // 2. If it is java.lang.ThreadLocal, which can't be rewritten because the - // JVM depends on its structure. - if (((loader == null) && !canRewriteBootstrap) || - className.startsWith("java/lang/ThreadLocal")) { - return false; - } - // third_party/java/webwork/*/ognl.jar contains bad class files. Ugh. - if (className.startsWith("ognl/")) { - return false; - } - - return true; - } - - // No instantiating me except in premain() or in {@link JarClassTransformer}. - AllocationInstrumenter() { } - - public static void premain(String agentArgs, Instrumentation inst) { - AllocationRecorder.setInstrumentation(inst); - - // Force eager class loading here; we need this class to do - // instrumentation, so if we don't do the eager class loading, we - // get a ClassCircularityError when trying to load and instrument - // this class. - try { - Class.forName("sun.security.provider.PolicyFile"); - } catch (Throwable t) { - // NOP - } - - if (!inst.isRetransformClassesSupported()) { - System.err.println("Some JDK classes are already loaded and " + - "will not be instrumented."); - } - - // Don't try to rewrite classes loaded by the bootstrap class - // loader if this class wasn't loaded by the bootstrap class - // loader. - if (AllocationRecorder.class.getClassLoader() != null) { - canRewriteBootstrap = false; - // The loggers aren't installed yet, so we use println. - System.err.println("Class loading breakage: " + - "Will not be able to instrument JDK classes"); - return; - } - - canRewriteBootstrap = true; - - inst.addTransformer(new ConstructorInstrumenter(), - inst.isRetransformClassesSupported()); - - List<String> args = Arrays.asList( - agentArgs == null ? new String[0] : agentArgs.split(",")); - if (!args.contains("manualOnly")) { - bootstrap(inst); - } - } - - private static void bootstrap(Instrumentation inst) { - inst.addTransformer(new AllocationInstrumenter(), - inst.isRetransformClassesSupported()); - - if (!canRewriteBootstrap) { - return; - } - - // Get the set of already loaded classes that can be rewritten. - Class<?>[] classes = inst.getAllLoadedClasses(); - ArrayList<Class<?>> classList = new ArrayList<Class<?>>(); - for (int i = 0; i < classes.length; i++) { - if (inst.isModifiableClass(classes[i])) { - classList.add(classes[i]); - } - } - - // Reload classes, if possible. - Class<?>[] workaround = new Class<?>[classList.size()]; - try { - inst.retransformClasses(classList.toArray(workaround)); - } catch (UnmodifiableClassException e) { - System.err.println("AllocationInstrumenter was unable to " + - "retransform early loaded classes."); - } - - - } - - @Override public byte[] transform( - ClassLoader loader, String className, Class<?> classBeingRedefined, - ProtectionDomain protectionDomain, byte[] origBytes) { - if (!canRewriteClass(className, loader)) { - return null; - } - - return instrument(origBytes, loader); - } - - /** - * Given the bytes representing a class, go through all the bytecode in it and - * instrument any occurences of new/newarray/anewarray/multianewarray with - * pre- and post-allocation hooks. Even more fun, intercept calls to the - * reflection API's Array.newInstance() and instrument those too. - * - * @param originalBytes the original <code>byte[]</code> code. - * @param recorderClass the <code>String</code> internal name of the class - * containing the recorder method to run. - * @param recorderMethod the <code>String</code> name of the recorder method - * to run. - * @return the instrumented <code>byte[]</code> code. - */ - public static byte[] instrument(byte[] originalBytes, String recorderClass, - String recorderMethod, ClassLoader loader) { - try { - ClassReader cr = new ClassReader(originalBytes); - // The verifier in JDK7 requires accurate stackmaps, so we use - // COMPUTE_FRAMES. - ClassWriter cw = - new StaticClassWriter(cr, ClassWriter.COMPUTE_FRAMES, loader); - - VerifyingClassAdapter vcw = - new VerifyingClassAdapter(cw, originalBytes, cr.getClassName()); - ClassVisitor adapter = - new AllocationClassAdapter(vcw, recorderClass, recorderMethod); - - cr.accept(adapter, ClassReader.SKIP_FRAMES); - - return vcw.toByteArray(); - } catch (RuntimeException e) { - logger.log(Level.WARNING, "Failed to instrument class.", e); - throw e; - } catch (Error e) { - logger.log(Level.WARNING, "Failed to instrument class.", e); - throw e; - } - } - - - /** - * @see #instrument(byte[], String, String, ClassLoader) - * documentation for the 4-arg version. This is a convenience - * version that uses the recorder in this class. - */ - public static byte[] instrument(byte[] originalBytes, ClassLoader loader) { - return instrument( - originalBytes, - "com/google/monitoring/runtime/instrumentation/AllocationRecorder", - "recordAllocation", - loader); - } -} diff --git a/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationMethodAdapter.java b/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationMethodAdapter.java deleted file mode 100644 index eaa0bad..0000000 --- a/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationMethodAdapter.java +++ /dev/null @@ -1,589 +0,0 @@ -/* - * Copyright (C) 2009 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.monitoring.runtime.instrumentation; - -import org.objectweb.asm.Label; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; -import org.objectweb.asm.commons.LocalVariablesSorter; - -import java.util.LinkedList; -import java.util.List; - -/** - * A <code>MethodAdapter</code> that instruments all heap allocation bytecodes - * to record the allocation being done for profiling. - * Instruments bytecodes that allocate heap memory to call a recording hook. - * - * @author Ami Fischman - */ -class AllocationMethodAdapter extends MethodVisitor { - /** - * The signature string the recorder method must have. The method must be - * static, return void, and take as arguments: - * <ol> - * <li>an int count of how many instances are being allocated. -1 means a - * simple new to distinguish from a 1-element array. 0 shows up as a value - * here sometimes; one reason is toArray()-type methods that require an array - * type argument (see ArrayList.toArray() for example).</li> - * <li>a String descriptor of the class/primitive type being allocated.</li> - * <li>an Object reference to the just-allocated Object.</li> - * </ol> - */ - public static final String RECORDER_SIGNATURE = - "(ILjava/lang/String;Ljava/lang/Object;)V"; - - /** - * Like RECORDER_SIGNATURE, but for a method that extracts all of - * the information dynamically from a class. - */ - public static final String CLASS_RECORDER_SIG = - "(Ljava/lang/Class;Ljava/lang/Object;)V"; - - // A helper struct for describing the scope of temporary local variables we - // create as part of the instrumentation. - private static class VariableScope { - public final int index; - public final Label start; - public final Label end; - public final String desc; - public VariableScope(int index, Label start, Label end, String desc) { - this.index = index; this.start = start; this.end = end; this.desc = desc; - } - } - - // Dictionary of primitive type opcode to English name. - private static final String[] primitiveTypeNames = new String[] { - "INVALID0", "INVALID1", "INVALID2", "INVALID3", - "boolean", "char", "float", "double", - "byte", "short", "int", "long" - }; - - // To track the difference between <init>'s called as the result of a NEW - // and <init>'s called because of superclass initialization, we track the - // number of NEWs that still need to have their <init>'s called. - private int outstandingAllocs = 0; - - // We need to set the scope of any local variables we materialize; - // accumulate the scopes here and set them all at the end of the visit to - // ensure all labels have been resolved. Allocated on-demand. - private List<VariableScope> localScopes = null; - - private List<VariableScope> getLocalScopes() { - if (localScopes == null) { - localScopes = new LinkedList<VariableScope>(); - } - return localScopes; - } - - private final String recorderClass; - private final String recorderMethod; - - /** - * The LocalVariablesSorter used in this adapter. Lame that it's public but - * the ASM architecture requires setting it from the outside after this - * AllocationMethodAdapter is fully constructed and the LocalVariablesSorter - * constructor requires a reference to this adapter. The only setter of - * this should be AllocationClassAdapter.visitMethod(). - */ - public LocalVariablesSorter lvs = null; - - /** - * A new AllocationMethodAdapter is created for each method that gets visited. - */ - public AllocationMethodAdapter(MethodVisitor mv, String recorderClass, - String recorderMethod) { - super(Opcodes.ASM4, mv); - this.recorderClass = recorderClass; - this.recorderMethod = recorderMethod; - } - - /** - * newarray shows up as an instruction taking an int operand (the primitive - * element type of the array) so we hook it here. - */ - @Override - public void visitIntInsn(int opcode, int operand) { - if (opcode == Opcodes.NEWARRAY) { - // instack: ... count - // outstack: ... aref - if (operand >= 4 && operand <= 11) { - super.visitInsn(Opcodes.DUP); // -> stack: ... count count - super.visitIntInsn(opcode, operand); // -> stack: ... count aref - invokeRecordAllocation(primitiveTypeNames[operand]); - // -> stack: ... aref - } else { - AllocationInstrumenter.logger.severe("NEWARRAY called with an invalid operand " + - operand + ". Not instrumenting this allocation!"); - super.visitIntInsn(opcode, operand); - } - } else { - super.visitIntInsn(opcode, operand); - } - } - - // Helper method to compute class name as a String and push it on the stack. - // pre: stack: ... class - // post: stack: ... class className - private void pushClassNameOnStack() { - super.visitInsn(Opcodes.DUP); - // -> stack: ... class class - super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", - "getName", "()Ljava/lang/String;"); - // -> stack: ... class classNameDotted - super.visitLdcInsn('.'); - // -> stack: ... class classNameDotted '.' - super.visitLdcInsn('/'); - // -> stack: ... class classNameDotted '.' '/' - super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", - "replace", "(CC)Ljava/lang/String;"); - // -> stack: ... class className - } - - // Helper method to compute the product of an integer array and push it on - // the stack. - // pre: stack: ... intArray - // post: stack: ... intArray product - private void pushProductOfIntArrayOnStack() { - Label beginScopeLabel = new Label(); - Label endScopeLabel = new Label(); - - int dimsArrayIndex = newLocal("[I", beginScopeLabel, endScopeLabel); - int counterIndex = newLocal("I", beginScopeLabel, endScopeLabel); - int productIndex = newLocal("I", beginScopeLabel, endScopeLabel); - Label loopLabel = new Label(); - Label endLabel = new Label(); - - super.visitLabel(beginScopeLabel); - - // stack: ... intArray - super.visitVarInsn(Opcodes.ASTORE, dimsArrayIndex); - // -> stack: ... - - // counter = 0 - super.visitInsn(Opcodes.ICONST_0); - super.visitVarInsn(Opcodes.ISTORE, counterIndex); - // product = 1 - super.visitInsn(Opcodes.ICONST_1); - super.visitVarInsn(Opcodes.ISTORE, productIndex); - // loop: - super.visitLabel(loopLabel); - // if index >= arraylength goto end: - super.visitVarInsn(Opcodes.ILOAD, counterIndex); - super.visitVarInsn(Opcodes.ALOAD, dimsArrayIndex); - super.visitInsn(Opcodes.ARRAYLENGTH); - super.visitJumpInsn(Opcodes.IF_ICMPGE, endLabel); - // product = product * max(array[counter],1) - super.visitVarInsn(Opcodes.ALOAD, dimsArrayIndex); - super.visitVarInsn(Opcodes.ILOAD, counterIndex); - super.visitInsn(Opcodes.IALOAD); - super.visitInsn(Opcodes.DUP); - Label nonZeroDimension = new Label(); - super.visitJumpInsn(Opcodes.IFNE, nonZeroDimension); - super.visitInsn(Opcodes.POP); - super.visitInsn(Opcodes.ICONST_1); - super.visitLabel(nonZeroDimension); - super.visitVarInsn(Opcodes.ILOAD, productIndex); - super.visitInsn(Opcodes.IMUL); // if overflow happens it happens. - super.visitVarInsn(Opcodes.ISTORE, productIndex); - // iinc counter 1 - super.visitIincInsn(counterIndex, 1); - // goto loop - super.visitJumpInsn(Opcodes.GOTO, loopLabel); - // end: - super.visitLabel(endLabel); - // re-push dimensions array - super.visitVarInsn(Opcodes.ALOAD, dimsArrayIndex); - // push product - super.visitVarInsn(Opcodes.ILOAD, productIndex); - - super.visitLabel(endScopeLabel); - } - - /** - * Reflection-based allocation (@see java.lang.reflect.Array#newInstance) is - * triggered with a static method call (INVOKESTATIC), so we hook it here. - * Class initialization is triggered with a constructor call (INVOKESPECIAL) - * so we hook that here too as a proxy for the new bytecode which leaves an - * uninitialized object on the stack that we're not allowed to touch. - * {@link java.lang.Object#clone} is also a call to INVOKESPECIAL, - * and is hooked here. {@link java.lang.Class#newInstance} and - * {@link java.lang.reflect.Constructor#newInstance} are both - * INVOKEVIRTUAL calls, so they are hooked here, as well. - */ - @Override - public void visitMethodInsn(int opcode, String owner, String name, - String signature) { - if (opcode == Opcodes.INVOKESTATIC && - // Array does its own native allocation. Grr. - owner.equals("java/lang/reflect/Array") && - name.equals("newInstance")) { - if (signature.equals("(Ljava/lang/Class;I)Ljava/lang/Object;")) { - - Label beginScopeLabel = new Label(); - Label endScopeLabel = new Label(); - super.visitLabel(beginScopeLabel); - - // stack: ... class count - int countIndex = newLocal("I", beginScopeLabel, endScopeLabel); - super.visitVarInsn(Opcodes.ISTORE, countIndex); - // -> stack: ... class - pushClassNameOnStack(); - // -> stack: ... class className - int typeNameIndex = - newLocal("Ljava/lang/String;", beginScopeLabel, endScopeLabel); - super.visitVarInsn(Opcodes.ASTORE, typeNameIndex); - // -> stack: ... class - super.visitVarInsn(Opcodes.ILOAD, countIndex); - // -> stack: ... class count - super.visitMethodInsn(opcode, owner, name, signature); - // -> stack: ... newobj - super.visitInsn(Opcodes.DUP); - // -> stack: ... newobj newobj - super.visitVarInsn(Opcodes.ILOAD, countIndex); - // -> stack: ... newobj newobj count - super.visitInsn(Opcodes.SWAP); - // -> stack: ... newobj count newobj - super.visitVarInsn(Opcodes.ALOAD, typeNameIndex); - super.visitLabel(endScopeLabel); - // -> stack: ... newobj count newobj className - super.visitInsn(Opcodes.SWAP); - // -> stack: ... newobj count className newobj - super.visitMethodInsn(Opcodes.INVOKESTATIC, recorderClass, - recorderMethod, RECORDER_SIGNATURE); - // -> stack: ... newobj - return; - } else if (signature.equals("(Ljava/lang/Class;[I)Ljava/lang/Object;")){ - Label beginScopeLabel = new Label(); - Label endScopeLabel = new Label(); - super.visitLabel(beginScopeLabel); - - int dimsArrayIndex = newLocal("[I", beginScopeLabel, endScopeLabel); - // stack: ... class dimsArray - pushProductOfIntArrayOnStack(); - // -> stack: ... class dimsArray product - int productIndex = newLocal("I", beginScopeLabel, endScopeLabel); - super.visitVarInsn(Opcodes.ISTORE, productIndex); - // -> stack: ... class dimsArray - - super.visitVarInsn(Opcodes.ASTORE, dimsArrayIndex); - // -> stack: ... class - pushClassNameOnStack(); - // -> stack: ... class className - int typeNameIndex = - newLocal("Ljava/lang/String;", beginScopeLabel, endScopeLabel); - super.visitVarInsn(Opcodes.ASTORE, typeNameIndex); - // -> stack: ... class - super.visitVarInsn(Opcodes.ALOAD, dimsArrayIndex); - // -> stack: ... class dimsArray - super.visitMethodInsn(opcode, owner, name, signature); - // -> stack: ... newobj - - super.visitInsn(Opcodes.DUP); - // -> stack: ... newobj newobj - super.visitVarInsn(Opcodes.ILOAD, productIndex); - // -> stack: ... newobj newobj product - super.visitInsn(Opcodes.SWAP); - // -> stack: ... newobj product newobj - super.visitVarInsn(Opcodes.ALOAD, typeNameIndex); - super.visitLabel(endScopeLabel); - // -> stack: ... newobj product newobj className - super.visitInsn(Opcodes.SWAP); - // -> stack: ... newobj product className newobj - super.visitMethodInsn(Opcodes.INVOKESTATIC, recorderClass, - recorderMethod, RECORDER_SIGNATURE); - // -> stack: ... newobj - return; - } - } - - if (opcode == Opcodes.INVOKEVIRTUAL) { - if ("clone".equals(name) && owner.startsWith("[")) { - super.visitMethodInsn(opcode, owner, name, signature); - - int i = 0; - while (i < owner.length()) { - if (owner.charAt(i) != '[') { - break; - } - i++; - } - if (i > 1) { - // -> stack: ... newobj - super.visitTypeInsn(Opcodes.CHECKCAST, owner); - // -> stack: ... arrayref - calculateArrayLengthAndDispatch(owner.substring(i), i); - } else { - // -> stack: ... newobj - super.visitInsn(Opcodes.DUP); - // -> stack: ... newobj newobj - super.visitTypeInsn(Opcodes.CHECKCAST, owner); - // -> stack: ... newobj arrayref - super.visitInsn(Opcodes.ARRAYLENGTH); - // -> stack: ... newobj length - super.visitInsn(Opcodes.SWAP); - // -> stack: ... length newobj - invokeRecordAllocation(owner.substring(i)); - } - return; - } else if ("newInstance".equals(name)) { - if ("java/lang/Class".equals(owner) && - "()Ljava/lang/Object;".equals(signature)) { - super.visitInsn(Opcodes.DUP); - // -> stack: ... Class Class - super.visitMethodInsn(opcode, owner, name, signature); - // -> stack: ... Class newobj - super.visitInsn(Opcodes.DUP_X1); - // -> stack: ... newobj Class newobj - super.visitMethodInsn(Opcodes.INVOKESTATIC, - recorderClass, recorderMethod, - CLASS_RECORDER_SIG); - // -> stack: ... newobj - return; - } else if ("java/lang/reflect/Constructor".equals(owner) && - "([Ljava/lang/Object;)Ljava/lang/Object;".equals(signature)) { - buildRecorderFromObject(opcode, owner, name, signature); - return; - } - } - } - - if (opcode == Opcodes.INVOKESPECIAL) { - if ("clone".equals(name) && "java/lang/Object".equals(owner)) { - buildRecorderFromObject(opcode, owner, name, signature); - return; - } else if ("<init>".equals(name) && outstandingAllocs > 0) { - // Tricky because superclass initializers mean there can be more calls - // to <init> than calls to NEW; hence outstandingAllocs. - --outstandingAllocs; - - // Most of the time (i.e. in bytecode generated by javac) it is the case - // that following an <init> call the top of the stack has a reference ot - // the newly-initialized object. But nothing in the JVM Spec requires - // this, so we need to play games with the stack to make an explicit - // extra copy (and then discard it). - - dupStackElementBeforeSignatureArgs(signature); - super.visitMethodInsn(opcode, owner, name, signature); - super.visitLdcInsn(-1); - super.visitInsn(Opcodes.SWAP); - invokeRecordAllocation(owner); - super.visitInsn(Opcodes.POP); - return; - } - } - - super.visitMethodInsn(opcode, owner, name, signature); - } - - // This is the instrumentation that occurs when there is no static - // information about the class we are instantiating. First we build the - // object, then we get the class and invoke the recorder. - private void buildRecorderFromObject( - int opcode, String owner, String name, String signature) { - super.visitMethodInsn(opcode, owner, name, signature); - // -> stack: ... newobj - super.visitInsn(Opcodes.DUP); - // -> stack: ... newobj newobj - super.visitInsn(Opcodes.DUP); - // -> stack: ... newobj newobj newobj - // We could be instantiating this class or a subclass, so we - // have to get the class the hard way. - super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, - "java/lang/Object", - "getClass", - "()Ljava/lang/Class;"); - // -> stack: ... newobj newobj Class - super.visitInsn(Opcodes.SWAP); - // -> stack: ... newobj Class newobj - super.visitMethodInsn(Opcodes.INVOKESTATIC, - recorderClass, recorderMethod, - CLASS_RECORDER_SIG); - // -> stack: ... newobj - } - - // Given a method signature interpret the top of the stack as the arguments - // to the method, dup the top-most element preceding these arguments, and - // leave the arguments alone. This is done by inspecting each parameter - // type, popping off the stack elements using the type information, - // duplicating the target element, and pushing the arguments back on the - // stack. - private void dupStackElementBeforeSignatureArgs(final String sig) { - final Label beginScopeLabel = new Label(); - final Label endScopeLabel = new Label(); - super.visitLabel(beginScopeLabel); - - Type[] argTypes = Type.getArgumentTypes(sig); - int[] args = new int[argTypes.length]; - - for (int i = argTypes.length - 1; i >= 0; --i) { - args[i] = newLocal(argTypes[i], beginScopeLabel, endScopeLabel); - super.visitVarInsn(argTypes[i].getOpcode(Opcodes.ISTORE), args[i]); - } - super.visitInsn(Opcodes.DUP); - for (int i = 0; i < argTypes.length; ++i) { - super.visitVarInsn(argTypes[i].getOpcode(Opcodes.ILOAD), args[i]); - } - super.visitLabel(endScopeLabel); - } - - /** - * new and anewarray bytecodes take a String operand for the type of - * the object or array element so we hook them here. Note that new doesn't - * actually result in any instrumentation here; we just do a bit of - * book-keeping and do the instrumentation following the constructor call - * (because we're not allowed to touch the object until it is initialized). - */ - @Override - public void visitTypeInsn(int opcode, String typeName) { - if (opcode == Opcodes.NEW) { - // We can't actually tag this object right after allocation because it - // must be initialized with a ctor before we can touch it (Verifier - // enforces this). Instead, we just note it and tag following - // initialization. - super.visitTypeInsn(opcode, typeName); - ++outstandingAllocs; - } else if (opcode == Opcodes.ANEWARRAY) { - super.visitInsn(Opcodes.DUP); - super.visitTypeInsn(opcode, typeName); - invokeRecordAllocation(typeName); - } else { - super.visitTypeInsn(opcode, typeName); - } - } - - /** - * Called by the ASM framework once the class is done being visited to - * compute stack & local variable count maximums. - */ - @Override - public void visitMaxs(int maxStack, int maxLocals) { - if (localScopes != null) { - for (VariableScope scope : localScopes) { - super.visitLocalVariable("xxxxx$" + scope.index, scope.desc, null, - scope.start, scope.end, scope.index); - } - } - super.visitMaxs(maxStack, maxLocals); - } - - // Helper method to allocate a new local variable and account for its scope. - private int newLocal(Type type, String typeDesc, - Label begin, Label end) { - int newVar = lvs.newLocal(type); - getLocalScopes().add(new VariableScope(newVar, begin, end, typeDesc)); - return newVar; - } - - // Sometimes I happen to have a string descriptor and sometimes a type; - // these alternate versions let me avoid recomputing whatever I already - // know. - private int newLocal(String typeDescriptor, Label begin, Label end) { - return newLocal(Type.getType(typeDescriptor), typeDescriptor, begin, end); - } - private int newLocal(Type type, Label begin, Label end) { - return newLocal(type, type.getDescriptor(), begin, end); - } - - // Helper method to actually invoke the recorder function for an allocation - // event. - // pre: stack: ... count newobj - // post: stack: ... newobj - private void invokeRecordAllocation(String typeName) { - typeName = typeName.replaceAll("^\\[*L", "").replaceAll(";$", ""); - // stack: ... count newobj - super.visitInsn(Opcodes.DUP_X1); - // -> stack: ... newobj count newobj - super.visitLdcInsn(typeName); - // -> stack: ... newobj count newobj typename - super.visitInsn(Opcodes.SWAP); - // -> stack: ... newobj count typename newobj - super.visitMethodInsn(Opcodes.INVOKESTATIC, - recorderClass, recorderMethod, RECORDER_SIGNATURE); - // -> stack: ... newobj - } - - /** - * multianewarray gets its very own visit method in the ASM framework, so we - * hook it here. This bytecode is different from most in that it consumes a - * variable number of stack elements during execution. The number of stack - * elements consumed is specified by the dimCount operand. - */ - @Override - public void visitMultiANewArrayInsn(String typeName, int dimCount) { - // stack: ... dim1 dim2 dim3 ... dimN - super.visitMultiANewArrayInsn(typeName, dimCount); - // -> stack: ... aref - calculateArrayLengthAndDispatch(typeName, dimCount); - } - - void calculateArrayLengthAndDispatch(String typeName, int dimCount) { - // Since the dimensions of the array are not known at instrumentation - // time, we take the created multi-dimensional array and peel off nesting - // levels from the left. For each nesting layer we probe the array length - // and accumulate a partial product which we can then feed the recording - // function. - - // below we note the partial product of dimensions 1 to X-1 as productToX - // (so productTo1 == 1 == no dimensions yet). We denote by aref0 the - // array reference at the current nesting level (the containing aref's [0] - // element). If we hit a level whose arraylength is 0 there's no point - // continuing so we shortcut out. - Label zeroDimension = new Label(); - super.visitInsn(Opcodes.DUP); // -> stack: ... origaref aref0 - super.visitLdcInsn(1); // -> stack: ... origaref aref0 productTo1 - for (int i = 0; i < dimCount; ++i) { - // pre: stack: ... origaref aref0 productToI - super.visitInsn(Opcodes.SWAP); // -> stack: ... origaref productToI aref - super.visitInsn(Opcodes.DUP_X1); - // -> stack: ... origaref aref0 productToI aref - super.visitInsn(Opcodes.ARRAYLENGTH); - // -> stack: ... origaref aref0 productToI dimI - - Label nonZeroDimension = new Label(); - super.visitInsn(Opcodes.DUP); - // -> stack: ... origaref aref0 productToI dimI dimI - super.visitJumpInsn(Opcodes.IFNE, nonZeroDimension); - // -> stack: ... origaref aref0 productToI dimI - super.visitInsn(Opcodes.POP); - // -> stack: ... origaref aref0 productToI - super.visitJumpInsn(Opcodes.GOTO, zeroDimension); - super.visitLabel(nonZeroDimension); - // -> stack: ... origaref aref0 productToI max(dimI,1) - - super.visitInsn(Opcodes.IMUL); - // -> stack: ... origaref aref0 productTo{I+1} - if (i < dimCount - 1) { - super.visitInsn(Opcodes.SWAP); - // -> stack: ... origaref productTo{I+1} aref0 - super.visitInsn(Opcodes.ICONST_0); - // -> stack: ... origaref productTo{I+1} aref0 0 - super.visitInsn(Opcodes.AALOAD); - // -> stack: ... origaref productTo{I+1} aref0' - super.visitInsn(Opcodes.SWAP); - } - // post: stack: ... origaref aref0 productTo{I+1} - } - super.visitLabel(zeroDimension); - - super.visitInsn(Opcodes.SWAP); // -> stack: ... origaref product aref0 - super.visitInsn(Opcodes.POP); // -> stack: ... origaref product - super.visitInsn(Opcodes.SWAP); // -> stack: ... product origaref - invokeRecordAllocation(typeName); - } -} diff --git a/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/ConstructorCallback.java b/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/ConstructorCallback.java deleted file mode 100644 index e9fabd3..0000000 --- a/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/ConstructorCallback.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.monitoring.runtime.instrumentation; - -/** - * This interface describes a function that is used to sample a - * constructor. It is intended to be invoked every time a constructor - * for class T is invoked. This will not be invoked when subclasses of - * T are instantiated. - * - * This mechanism works independently of whether the class is part of the - * JDK core library. - * - * @param <T> The class that will be sampled with this ConstructorCallback - * - * @author Jeremy Manson - */ -public interface ConstructorCallback<T> { - /** - * When an object implementing interface - * <code>ConstructorCallback</code> is passed to {@link - * com.google.monitoring.runtime.allocation.AllocationInspector# - * addConstructorCallback(Class, ConstructorCallback)}, it will get executed - * whenever a constructor for type T is invoked. - * - * @param newObj the new <code>Object</code> whose construction - * we're recording. The object is not fully constructed; any - * references to this object that are stored in this callback are - * subject to the memory model constraints related to such - * objects. - */ - public void sample(T newObj); -} diff --git a/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/ConstructorInstrumenter.java b/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/ConstructorInstrumenter.java deleted file mode 100644 index f84e151..0000000 --- a/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/ConstructorInstrumenter.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.monitoring.runtime.instrumentation; - -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.commons.LocalVariablesSorter; - -import java.lang.instrument.ClassFileTransformer; -import java.lang.instrument.Instrumentation; -import java.lang.instrument.UnmodifiableClassException; -import java.security.ProtectionDomain; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Instruments bytecode by inserting a specified call in the - * constructor of a given class. This class is intended to be loaded - * by a javaagent; end-users will want to add {@link ConstructorCallback}s by - * invoking {@link #instrumentClass(Class, ConstructorCallback)}. - * - * @author Jeremy Manson - */ -public class ConstructorInstrumenter implements ClassFileTransformer { - // Implementation details: uses the java.lang.instrument API to - // insert an INVOKESTATIC call to a specified method directly prior to - // constructor return for the given class. - - private static final Logger logger = - Logger.getLogger(ConstructorInstrumenter.class.getName()); - private static ConcurrentHashMap<Class<?>, List<ConstructorCallback<?>>> - samplerMap = - new ConcurrentHashMap<Class<?>, List<ConstructorCallback<?>>>(); - - /** - * We have a read-modify-write operation when doing a put in samplerMap - * (above) and retransforming the class. This lock protects multiple threads - * from performing that operation concurrently. - */ - private static final Object samplerPutAtomicityLock = new Object(); - - // Only for package access (specifically, AllocationInstrumenter) - ConstructorInstrumenter() { } - - /** - * Ensures that the given sampler will be invoked every time a constructor - * for class c is invoked. - * - * @param c The class to be tracked - * @param sampler the code to be invoked when an instance of c is constructed - */ - public static void instrumentClass(Class<?> c, ConstructorCallback<?> sampler) - throws UnmodifiableClassException { - // IMPORTANT: Don't forget that other threads may be accessing this - // class while this code is running. Specifically, the class may be - // executed directly after the retransformClasses is called. Thus, we need - // to be careful about what happens after the retransformClasses call. - synchronized (samplerPutAtomicityLock) { - List<ConstructorCallback<?>> list = samplerMap.get(c); - if (list == null) { - CopyOnWriteArrayList<ConstructorCallback<?>> samplerList = - new CopyOnWriteArrayList<ConstructorCallback<?>>(); - samplerList.add(sampler); - samplerMap.put(c, samplerList); - Instrumentation inst = AllocationRecorder.getInstrumentation(); - Class<?>[] cs = new Class<?>[1]; - cs[0] = c; - inst.retransformClasses(c); - } else { - list.add(sampler); - } - } - } - - /** - * {@inheritDoc} - */ - @Override public byte[] transform( - ClassLoader loader, String className, Class<?> classBeingRedefined, - ProtectionDomain protectionDomain, byte[] classfileBuffer) { - if ((classBeingRedefined == null) || - (!samplerMap.containsKey(classBeingRedefined))) { - return null; - } - if (!AllocationInstrumenter.canRewriteClass(className, loader)) { - throw new RuntimeException( - new UnmodifiableClassException("cannot instrument " + className)); - } - return instrument(classfileBuffer, classBeingRedefined); - } - - /** - * Given the bytes representing a class, add invocations of the - * ConstructorCallback method to the constructor. - * - * @param originalBytes the original <code>byte[]</code> code. - * @return the instrumented <code>byte[]</code> code. - */ - public static byte[] instrument( - byte[] originalBytes, Class<?> classBeingRedefined) { - try { - ClassReader cr = new ClassReader(originalBytes); - ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS); - VerifyingClassAdapter vcw = - new VerifyingClassAdapter(cw, originalBytes, cr.getClassName()); - ClassVisitor adapter = - new ConstructorClassAdapter(vcw, classBeingRedefined); - - cr.accept(adapter, ClassReader.SKIP_FRAMES); - - return vcw.toByteArray(); - } catch (RuntimeException e) { - logger.log(Level.WARNING, "Failed to instrument class.", e); - throw e; - } catch (Error e) { - logger.log(Level.WARNING, "Failed to instrument class.", e); - throw e; - } - } - - /** - * The per-method transformations to make. Really only affects the - * <init> methods. - */ - static class ConstructorMethodAdapter extends MethodVisitor { - /** - * The LocalVariablesSorter used in this adapter. Lame that it's public but - * the ASM architecture requires setting it from the outside after this - * AllocationMethodAdapter is fully constructed and the LocalVariablesSorter - * constructor requires a reference to this adapter. The only setter of - * this should be AllocationClassAdapter.visitMethod(). - */ - public LocalVariablesSorter lvs = null; - Class<?> cl; - ConstructorMethodAdapter(MethodVisitor mv, Class<?> cl) { - super(Opcodes.ASM4, mv); - this.cl = cl; - } - - /** - * Inserts the appropriate INVOKESTATIC call - */ - @Override public void visitInsn(int opcode) { - if ((opcode == Opcodes.ARETURN) || - (opcode == Opcodes.IRETURN) || - (opcode == Opcodes.LRETURN) || - (opcode == Opcodes.FRETURN) || - (opcode == Opcodes.DRETURN)) { - throw new RuntimeException(new UnmodifiableClassException( - "Constructors are supposed to return void")); - } - if (opcode == Opcodes.RETURN) { - super.visitVarInsn(Opcodes.ALOAD, 0); - super.visitMethodInsn( - Opcodes.INVOKESTATIC, - "com/google/monitoring/runtime/instrumentation/ConstructorInstrumenter", - "invokeSamplers", - "(Ljava/lang/Object;)V"); - } - super.visitInsn(opcode); - } - } - - /** - * Bytecode is rewritten to invoke this method; it calls the sampler for - * the given class. Note that it won't do anything if o is a subclass - * of the class that was supposed to be tracked. - */ - @SuppressWarnings("unchecked") - public static void invokeSamplers(Object o) { - List<ConstructorCallback<?>> samplers = samplerMap.get(o.getClass()); - if (samplers != null) { - for (@SuppressWarnings("rawtypes") ConstructorCallback sampler : samplers) { - sampler.sample(o); - } - } - } - - /** - * The class that deals with per-class transformations. Basically, invokes - * the per-method transformer above if the method is an {@code <init>} method. - */ - static class ConstructorClassAdapter extends ClassVisitor { - Class<?> cl; - public ConstructorClassAdapter(ClassVisitor cv, Class<?> cl) { - super(Opcodes.ASM4, cv); - this.cl = cl; - } - - /** - * For each method in the class being instrumented, - * <code>visitMethod</code> is called and the returned - * MethodVisitor is used to visit the method. Note that a new - * MethodVisitor is constructed for each method. - */ - @Override - public MethodVisitor visitMethod(int access, String name, String desc, - String signature, String[] exceptions) { - MethodVisitor mv = - cv.visitMethod(access, name, desc, signature, exceptions); - - if ((mv != null) && "<init>".equals(name)){ - ConstructorMethodAdapter aimv = new ConstructorMethodAdapter(mv, cl); - LocalVariablesSorter lvs = new LocalVariablesSorter(access, desc, aimv); - aimv.lvs = lvs; - mv = lvs; - } - return mv; - } - } -} diff --git a/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/StaticClassWriter.java b/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/StaticClassWriter.java deleted file mode 100644 index 1e26d4c..0000000 --- a/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/StaticClassWriter.java +++ /dev/null @@ -1,273 +0,0 @@ -/*** - * ASM tests - * Copyright (c) 2002-2005 France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -// Portions Copyright 2011 Google, Inc. -// -// This is an extracted version of the ClassInfo and ClassWriter -// portions of ClassWriterComputeFramesTest in the set of ASM tests. -// We have done a fair bit of rewriting for readability, and changed -// the comments. The original author is Eric Bruneton. - - -package com.google.monitoring.runtime.instrumentation; - -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; - -import java.io.InputStream; -import java.io.IOException; - -/** - * A {@link ClassWriter} that looks for static class data in the - * classpath when the classes are not available at runtime. - * - * <p>ClassWriter uses class hierarchy information, which it gets by - * looking at loaded classes, to make some decisions about the best - * way to write classes. The problem with this is that it fails if - * the superclass hasn't been loaded yet. StaticClassWriter fails - * over to looking for the class hierarchy information in the - * ClassLoader's resources (usually the classpath) if the class it - * needs hasn't been loaded yet. - * - * <p>This class was heavily influenced by ASM's - * org.objectweb.asm.util.ClassWriterComputeFramesTest, which contains - * the same logic in a subclass. The code here has been slightly - * cleaned up for readability. - * - * @author jeremymanson@google.com (Jeremy Manson) - */ -class StaticClassWriter extends ClassWriter { - - /* The classloader that we use to look for the unloaded class */ - private final ClassLoader classLoader; - - /** - * {@inheritDoc} - * @param classLoader the class loader that loaded this class - */ - public StaticClassWriter( - ClassReader classReader, int flags, ClassLoader classLoader) { - super(classReader, flags); - this.classLoader = classLoader; - } - - /** - * {@inheritDoc} - */ - @Override protected String getCommonSuperClass( - final String type1, final String type2) { - try { - return super.getCommonSuperClass(type1, type2); - } catch (Throwable e) { - // Try something else... - } - // Exactly the same as in ClassWriter, but gets the superclass - // directly from the class file. - ClassInfo ci1, ci2; - try { - ci1 = new ClassInfo(type1, classLoader); - ci2 = new ClassInfo(type2, classLoader); - } catch (Throwable e) { - throw new RuntimeException(e); - } - if (ci1.isAssignableFrom(ci2)) { - return type1; - } - if (ci2.isAssignableFrom(ci1)) { - return type2; - } - if (ci1.isInterface() || ci2.isInterface()) { - return "java/lang/Object"; - } - - do { - // Should never be null, because if ci1 were the Object class - // or an interface, it would have been caught above. - ci1 = ci1.getSuperclass(); - } while (!ci1.isAssignableFrom(ci2)); - return ci1.getType().getInternalName(); - } - - /** - * For a given class, this stores the information needed by the - * getCommonSuperClass test. This determines if the class is - * available at runtime, and then, if it isn't, it tries to get the - * class file, and extract the appropriate information from that. - */ - static class ClassInfo { - - private final Type type; - private final ClassLoader loader; - private final boolean isInterface; - private final String superClass; - private final String[] interfaces; - - public ClassInfo(String type, ClassLoader loader) { - Class cls = null; - // First, see if we can extract the information from the class... - try { - cls = Class.forName(type); - } catch (Exception e) { - // failover... - } - - if (cls != null) { - this.type = Type.getType(cls); - this.loader = loader; - this.isInterface = cls.isInterface(); - this.superClass = cls.getSuperclass().getName(); - Class[] ifs = cls.getInterfaces(); - this.interfaces = new String[ifs.length]; - for (int i = 0; i < ifs.length; i++) { - this.interfaces[i] = ifs[i].getName(); - } - return; - } - - // The class isn't loaded. Try to get the class file, and - // extract the information from that. - this.loader = loader; - this.type = Type.getObjectType(type); - String fileName = type.replace('.', '/') + ".class"; - InputStream is = null; - ClassReader cr; - try { - is = (loader == null) ? - ClassLoader.getSystemResourceAsStream(fileName) : - loader.getResourceAsStream(fileName); - cr = new ClassReader(is); - } catch (IOException e) { - throw new RuntimeException(e); - } finally { - if (is != null) { - try { - is.close(); - } catch (Exception e) { - } - } - } - - int offset = cr.header; - isInterface = (cr.readUnsignedShort(offset) & Opcodes.ACC_INTERFACE) != 0; - char[] buf = new char[2048]; - - // Read the superclass - offset += 4; - superClass = readConstantPoolString(cr, offset, buf); - - // Read the interfaces - offset += 2; - int numInterfaces = cr.readUnsignedShort(offset); - interfaces = new String[numInterfaces]; - offset += 2; - for (int i = 0; i < numInterfaces; i++) { - interfaces[i] = readConstantPoolString(cr, offset, buf); - offset += 2; - } - } - - String readConstantPoolString(ClassReader cr, int offset, char[] buf) { - int cpIndex = cr.getItem(cr.readUnsignedShort(offset)); - if (cpIndex == 0) { - return null; - // throw new RuntimeException("Bad constant pool index"); - } - return cr.readUTF8(cpIndex, buf); - } - - Type getType() { - return type; - } - - ClassInfo getSuperclass() { - if (superClass == null) { - return null; - } - return new ClassInfo(superClass, loader); - } - - /** - * Same as {@link Class#getInterfaces()} - */ - ClassInfo[] getInterfaces() { - if (interfaces == null) { - return new ClassInfo[0]; - } - ClassInfo[] result = new ClassInfo[interfaces.length]; - for (int i = 0; i < result.length; ++i) { - result[i] = new ClassInfo(interfaces[i], loader); - } - return result; - } - - /** - * Same as {@link Class#isInterface} - */ - boolean isInterface() { - return isInterface; - } - - private boolean implementsInterface(ClassInfo that) { - for (ClassInfo c = this; c != null; c = c.getSuperclass()) { - for (ClassInfo iface : c.getInterfaces()) { - if (iface.type.equals(that.type) || - iface.implementsInterface(that)) { - return true; - } - } - } - return false; - } - - private boolean isSubclassOf(ClassInfo that) { - for (ClassInfo ci = this; ci != null; ci = ci.getSuperclass()) { - if (ci.getSuperclass() != null && - ci.getSuperclass().type.equals(that.type)) { - return true; - } - } - return false; - } - - /** - * Same as {@link Class#isAssignableFrom(Class)} - */ - boolean isAssignableFrom(ClassInfo that) { - return (this == that || - that.isSubclassOf(this) || - that.implementsInterface(this) || - (that.isInterface() - && getType().getDescriptor().equals("Ljava/lang/Object;"))); - } - } - -} diff --git a/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/VerifyingClassAdapter.java b/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/VerifyingClassAdapter.java deleted file mode 100644 index 8e5ca6c..0000000 --- a/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/VerifyingClassAdapter.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.monitoring.runtime.instrumentation; - -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.commons.CodeSizeEvaluator; - -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * This is a class writer that gets used in place of the existing - * {@link ClassWriter}, and verifies properties of the class getting written. - * - * Currently, it only checks to see if the methods are of the correct length - * for Java methods (<64K). - * - * @author jeremymanson@google.com (Jeremy Manson) - */ -public class VerifyingClassAdapter extends ClassVisitor { - private static final Logger logger = - Logger.getLogger(VerifyingClassAdapter.class.getName()); - - /** - * An enum which indicates whether the class in question is verified. - */ - public enum State { - PASS, UNKNOWN, FAIL_TOO_LONG; - } - - final ClassWriter cw; - final byte [] original; - final String className; - String message; - State state; - - /** - * @param cw A class writer that is wrapped by this class adapter - * @param original the original bytecode - * @param className the name of the class being examined. - */ - public VerifyingClassAdapter(ClassWriter cw, byte [] original, - String className) { - super(Opcodes.ASM4, cw); - state = State.UNKNOWN; - message = "The class has not finished being examined"; - this.cw = cw; - this.original = original; - this.className = className.replace('/', '.'); - } - - /** - * {@inheritDoc} - * - * In addition, the returned {@link MethodVisitor} will throw an exception - * if the method is greater than 64K in length. - */ - @Override - public MethodVisitor visitMethod( - final int access, - final String name, - final String desc, - final String signature, - final String[] exceptions) { - MethodVisitor mv = - super.visitMethod(access, name, desc, signature, exceptions); - return new CodeSizeEvaluator(mv) { - @Override - public void visitEnd() { - super.visitEnd(); - if (getMaxSize() > 64 * 1024) { - state = State.FAIL_TOO_LONG; - message = "the method " + name + " was too long."; - } - } - }; - } - - /** - * {@inheritDoc} - */ - @Override - public void visitEnd() { - super.visitEnd(); - if (state == State.UNKNOWN) { - state = State.PASS; - } - } - - /** - * Gets the verification state of this class. - * - * @return true iff the class passed inspection. - */ - public boolean isVerified() { - return state == State.PASS; - } - - /** - * Returns the byte array that contains the byte code for this class. - * - * @return a byte array. - */ - public byte[] toByteArray() { - if (state != State.PASS) { - logger.log(Level.WARNING, - "Failed to instrument class " + className + " because " + message); - return original; - } - return cw.toByteArray(); - } -} |