summaryrefslogtreecommitdiffstats
path: root/java-allocation-instrumenter
diff options
context:
space:
mode:
authorJean-Philippe Lesot <jplesot@google.com>2014-04-10 11:21:17 +0200
committerJean-Philippe Lesot <jplesot@google.com>2014-04-10 11:23:34 +0200
commitcddf2a3c40925d77a7e3d7b22801037d957cae3b (patch)
treeca0a044223b08d5937f16e5548b3fca0a4479e83 /java-allocation-instrumenter
parentf19253a8e55f9ea06c225b7682620f4b1b8182f5 (diff)
downloadtoolchain_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')
-rw-r--r--java-allocation-instrumenter/Android.mk1
-rw-r--r--java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationClassAdapter.java56
-rw-r--r--java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationInstrumenter.java208
-rw-r--r--java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationMethodAdapter.java589
-rw-r--r--java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/ConstructorCallback.java47
-rw-r--r--java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/ConstructorInstrumenter.java231
-rw-r--r--java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/StaticClassWriter.java273
-rw-r--r--java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/VerifyingClassAdapter.java129
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();
- }
-}