diff --git a/java/test/org/broadinstitute/sting/BaseTest.java b/java/test/org/broadinstitute/sting/BaseTest.java index 7b9abce55..58c41ca47 100755 --- a/java/test/org/broadinstitute/sting/BaseTest.java +++ b/java/test/org/broadinstitute/sting/BaseTest.java @@ -43,15 +43,14 @@ public abstract class BaseTest { protected static String seqLocation = "/seq"; protected static String oneKGLocation = "/broad/1KG"; protected static String testDir = "testdata/"; - protected static boolean alreadySetup = false; - private static long startTime; + /** before the class starts up */ @BeforeClass public static void baseStartup() { if (!alreadySetup) { - + alreadySetup = true; // setup a basic log configuration BasicConfigurator.configure(); @@ -78,7 +77,7 @@ public abstract class BaseTest { } } } - + public static void findFileLocations() throws IOException { // if either doesn't exist if (!fileExist(seqLocation) || !fileExist(oneKGLocation)) { @@ -132,29 +131,4 @@ public abstract class BaseTest { return temp.exists(); } - - - /** - * this test is here so that we can always pass when this test is run - */ - @Test - public void basicTest() { - - } - - /** after the class runs */ - @AfterClass - public static void baseShutdown() { - - } - - // pass through to the junit 3 calls, which are not annotated - @Before - public void baseSetup() throws Exception { - } - - @After - public void baseTearDown() throws Exception { - } - } diff --git a/java/test/org/broadinstitute/sting/TrivialInstrumenter.java b/java/test/org/broadinstitute/sting/TrivialInstrumenter.java index 9a98c1be7..a95564a48 100755 --- a/java/test/org/broadinstitute/sting/TrivialInstrumenter.java +++ b/java/test/org/broadinstitute/sting/TrivialInstrumenter.java @@ -1,11 +1,9 @@ package org.broadinstitute.sting; -import org.apache.bcel.Constants; -import org.apache.bcel.Repository; -import org.apache.bcel.classfile.JavaClass; -import org.apache.bcel.generic.*; +import javassist.*; import org.junit.Ignore; +import java.io.IOException; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.lang.instrument.Instrumentation; @@ -40,7 +38,6 @@ import java.lang.instrument.Instrumentation; /** A trivial example program that basically just says hello! */ @Ignore public class TrivialInstrumenter implements ClassFileTransformer { - public static void premain(String options, Instrumentation ins) { if (options != null) { System.out.printf(" I've been called with options: \"%s\"\n", options); @@ -53,223 +50,104 @@ public class TrivialInstrumenter implements ClassFileTransformer { Class cBR, java.security.ProtectionDomain pD, byte[] classfileBuffer) throws IllegalClassFormatException { - if (className.contains("broadinstitute") && !(className.endsWith("BaseTest"))) { + int size = classfileBuffer.length; + + if (className.contains("broadinstitute") && + className.endsWith("Test") && + !(className.endsWith("BaseTest"))) { + ClassPool pool = ClassPool.getDefault(); + CtClass cl = null; - JavaClass jclas = null; try { - jclas = Repository.lookupClass(className); - if (jclas == null) { - return null; + cl = pool.makeClass(new java.io.ByteArrayInputStream(classfileBuffer)); + if (cl.isInterface() == false) { + for (CtBehavior meth : cl.getDeclaredMethods()) { + + if (meth.isEmpty() == false) { + Object anns[] = meth.getAvailableAnnotations(); + boolean weAreAJunitTest = false; + for (Object obj : anns) { + if (obj instanceof org.junit.Test) { + weAreAJunitTest = true; + } + } + if (weAreAJunitTest) { + addAnnouncement(meth, cl); + } + } + } + classfileBuffer = cl.toBytecode(); + return classfileBuffer; } - } catch (Exception e) { - return null; - } - if (!(jclas.getSuperClass().getClassName().contains("BaseTest"))) { - return null; - } - System.err.println("looking at " + className); - ClassGen cgen = new ClassGen(jclas); - ConstantPoolGen pgen = cgen.getConstantPool(); - InstructionFactory fact = new InstructionFactory(cgen, pgen); - createFields(cgen, pgen); - /*for (Method m : cgen.getMethods()) { - System.err.println("looking at " + m.getName()); - addStringOutputToMethod(jclas, cgen, pgen, m, fact); - }*/ - createBeforeMethod(cgen, pgen, fact); - createAfterMethod(cgen, pgen, fact); - - return cgen.getJavaClass().getBytes(); - } - return null; // No transformation required - } - - /** - * create any fields we need here - * - * @param cgen our classgen for the class to add to - * @param pgen our constant pool generator, check out the JVM spec about this - */ - private void createFields(ClassGen cgen, ConstantPoolGen pgen) { - FieldGen field; - - field = new FieldGen(Constants.ACC_PRIVATE | Constants.ACC_STATIC, Type.LONG, "startTime", pgen); - cgen.addField(field.getField()); - field = new FieldGen(Constants.ACC_PRIVATE, Type.STRING, "currentTestName", pgen); - cgen.addField(field.getField()); - } - - /* - private void addStringOutputToMethod(JavaClass classname, ClassGen cgen, ConstantPoolGen pgen, Method meth, InstructionFactory fact) { - - if(true) {return;} - if (meth.getName().contains("<")) { - System.err.println("Nope -> " + meth.getName()); - return; - } - //if (meth.isPublic()) { - boolean outputInstead = true; - MethodGen g = new MethodGen(meth, cgen.getClassName(), pgen); - InstructionList il = g.getInstructionList(); - //if (outputInstead) { - BufferedWriter outputStream = null; - BufferedWriter outputStream2 = null; - //} - Instruction returnInstruction = null; - InstructionHandle[] iHandles = il.getInstructionHandles(); - for (int f = 0; f < iHandles.length; f++) { - if (iHandles[f].getInstruction() instanceof ReturnInstruction) { - returnInstruction = iHandles[f].getInstruction(); - //System.out.println("found the invoke virtual"); - break; - } - } - if (outputInstead) { - try { - outputStream = - new BufferedWriter(new FileWriter("one.txt")); - outputStream2 = - new BufferedWriter(new FileWriter("two.txt")); - - outputStream.write(meth.getName() + " of " + meth.getClass()); - - for (Instruction i : il.getInstructions()) { - - outputStream.write(i.getName() + " " + i.getOpcode() + " " + i.toString() + "\n"); - - } - outputStream.close(); - } - catch (IOException e) { + // baseTearDown + } catch (NotFoundException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. - } - } - //InstructionHandle handle = il.getEnd(); - //il.s - //il.insert(getFieldInstruction, fact.createLoad(Type.OBJECT, 0)); - //il.insert(getFieldInstruction, fact.createNew("java.lang.String")); - //il.insert(getFieldInstruction, InstructionConstants.DUP); - //il.insert(getFieldInstruction, new PUSH(pgen, meth.getName())); - //il.insert(getFieldInstruction, fact.createInvoke("java.lang.String", "", Type.VOID, new Type[]{Type.STRING}, Constants.INVOKESPECIAL)); - //il.insert(getFieldInstruction, fact.createFieldAccess(cgen.getClassName(), "currentTestName", Type.STRING, Constants.PUTFIELD)); - //il.insert(getFieldInstruction,fact.createPrintln("Hello World")); - /*il.insert(returnInstruction, new ALOAD(0)); - il.insert(returnInstruction, fact.createNew("java.lang.String")); - il.insert(returnInstruction, InstructionConstants.DUP); - il.insert(returnInstruction, new PUSH(pgen, meth.getName())); - il.insert(returnInstruction, fact.createInvoke("java.lang.String", "", Type.VOID, new Type[]{Type.STRING}, Constants.INVOKESPECIAL)); - il.insert(returnInstruction, fact.createFieldAccess(classname.replace("/","."), "currentTestName", Type.STRING, Constants.PUTFIELD));*/ - /*il.setPositions(); - g.setMaxStack(); - g.setMaxLocals(); - g.removeLineNumbers(); - //org.apache.bcel.classfile.LocalVariableTypeTable table; - InstructionList inst = g.getInstructionList(); - if (outputInstead) { - try { - outputStream2.write(meth.getName() + " of " + meth.getClass() + " classname: " + classname.getClassName() + "\n"); - - for (Instruction i : inst.getInstructions()) { - - outputStream2.write(i.getName() + " " + i.getOpcode() + " " + i.toString() + "\n"); - - } - outputStream2.close(); - } - catch (IOException e) { + } catch (IOException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } catch (CannotCompileException e) { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } finally { + if (cl != null) { + cl.detach(); + } } + } - //. - cgen.replaceMethod(meth, g.getMethod()); - il.dispose();*/ - //} - /*if (meth.isPublic()) { - InstructionList il = new InstructionList(); - MethodGen method = new MethodGen(Constants.ACC_PUBLIC, Type.VOID, Type.NO_ARGS, new String[]{}, meth.getName(), cgen.getClassName(), il, pgen); - - InstructionHandle ih_0 = il.append(fact.createLoad(Type.OBJECT, 0)); - il.append(fact.createNew("java.lang.String")); - il.append(InstructionConstants.DUP); - il.append(new PUSH(pgen, "grrr")); - il.append(fact.createInvoke("java.lang.String", "", Type.VOID, new Type[]{Type.STRING}, Constants.INVOKESPECIAL)); - il.append(fact.createFieldAccess(cgen.getClassName(), "currentTestName", Type.STRING, Constants.PUTFIELD)); - InstructionHandle ih_13 = il.append(fact.createReturn(Type.VOID)); - method.setMaxStack(); - method.setMaxLocals(); - cgen.removeMethod(meth); - cgen.addMethod(method.getMethod()); - il.dispose(); - } - - }*/ - - - /** - * create the before method - * - * @param cgen our classgen for the class to add to - * @param pgen our constant pool generator - * @param fact the instruction factory we're using - */ - - private void createBeforeMethod(ClassGen cgen, ConstantPoolGen pgen, InstructionFactory fact) { - InstructionList il = new InstructionList(); - MethodGen method = new MethodGen(Constants.ACC_PUBLIC | Constants.ACC_FINAL, Type.VOID, Type.NO_ARGS, new String[]{}, "baseSetup", cgen.getClassName(), il, pgen); - - InstructionHandle ih_0 = il.append(fact.createInvoke("java.lang.System", "currentTimeMillis", Type.LONG, Type.NO_ARGS, Constants.INVOKESTATIC)); - il.append(fact.createFieldAccess(cgen.getClassName(), "startTime", Type.LONG, Constants.PUTSTATIC)); - InstructionHandle ih_6 = il.append(fact.createReturn(Type.VOID)); - - method.setMaxStack(); - method.setMaxLocals(); - cgen.addMethod(method.getMethod()); - il.dispose(); + return null; } - /** - * create the after method - * - * @param cgen our classgen for the class to add to - * @param pgen our constant pool generator - * @param fact the instruction factory we're using - */ - private void createAfterMethod(ClassGen cgen, ConstantPoolGen pgen, InstructionFactory fact) { - InstructionList il = new InstructionList(); - MethodGen method = new MethodGen(Constants.ACC_PUBLIC, Type.VOID, Type.NO_ARGS, new String[]{}, "baseTearDown", cgen.getClassName(), il, pgen); + private static void addTiming(CtClass clas, String mname) + throws NotFoundException, CannotCompileException { - InstructionHandle ih_0 = il.append(fact.createInvoke("java.lang.System", "currentTimeMillis", Type.LONG, Type.NO_ARGS, Constants.INVOKESTATIC)); - il.append(fact.createStore(Type.LONG, 1)); - InstructionHandle ih_4 = il.append(fact.createFieldAccess(cgen.getClassName(), "logger", new ObjectType("org.apache.log4j.Logger"), Constants.GETSTATIC)); - il.append(fact.createNew("java.lang.StringBuilder")); - il.append(InstructionConstants.DUP); - il.append(fact.createInvoke("java.lang.StringBuilder", "", Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL)); - il.append(new PUSH(pgen, "Test Name: ")); - il.append(fact.createInvoke("java.lang.StringBuilder", "append", new ObjectType("java.lang.StringBuilder"), new Type[]{Type.STRING}, Constants.INVOKEVIRTUAL)); - il.append(fact.createLoad(Type.OBJECT, 0)); - il.append(fact.createFieldAccess(cgen.getClassName(), "currentTestName", Type.STRING, Constants.GETFIELD)); - il.append(fact.createInvoke("java.lang.StringBuilder", "append", new ObjectType("java.lang.StringBuilder"), new Type[]{Type.STRING}, Constants.INVOKEVIRTUAL)); - il.append(new PUSH(pgen, " runtime: %dms")); - il.append(fact.createInvoke("java.lang.StringBuilder", "append", new ObjectType("java.lang.StringBuilder"), new Type[]{Type.STRING}, Constants.INVOKEVIRTUAL)); - il.append(fact.createInvoke("java.lang.StringBuilder", "toString", Type.STRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); - il.append(new PUSH(pgen, 1)); - il.append(fact.createNewArray(Type.OBJECT, (short) 1)); - il.append(InstructionConstants.DUP); - il.append(new PUSH(pgen, 0)); - il.append(fact.createLoad(Type.LONG, 1)); - il.append(fact.createFieldAccess(cgen.getClassName(), "startTime", Type.LONG, Constants.GETSTATIC)); - il.append(InstructionConstants.LSUB); - il.append(fact.createInvoke("java.lang.Long", "valueOf", new ObjectType("java.lang.Long"), new Type[]{Type.LONG}, Constants.INVOKESTATIC)); - il.append(InstructionConstants.AASTORE); - il.append(fact.createInvoke("java.lang.String", "format", Type.STRING, new Type[]{Type.STRING, new ArrayType(Type.OBJECT, 1)}, Constants.INVOKESTATIC)); - il.append(fact.createInvoke("org.apache.log4j.Logger", "warn", Type.VOID, new Type[]{Type.OBJECT}, Constants.INVOKEVIRTUAL)); - InstructionHandle ih_55 = il.append(fact.createReturn(Type.VOID)); - method.setMaxStack(); - method.setMaxLocals(); - cgen.addMethod(method.getMethod()); - il.dispose(); + // get the method information (throws exception if method with + // given name is not declared directly by this class, returns + // arbitrary choice if more than one with the given name) + CtMethod mold = clas.getDeclaredMethod(mname); + + // rename old method to synthetic name, then duplicate the + // method with original name for use as interceptor + String nname = mname + "$impl"; + mold.setName(nname); + CtMethod mnew = CtNewMethod.copy(mold, mname, clas, null); + + // start the body text generation by saving the start time + // to a local variable, then call the timed method; the + // actual code generated needs to depend on whether the + // timed method returns a value + String type = mold.getReturnType().getName(); + StringBuffer body = new StringBuffer(); + body.append("{\nlong start = System.currentTimeMillis();\n"); + if (!"void".equals(type)) { + body.append(type + " result = "); + } + body.append(nname + "($$);\n"); + + // finish body text generation with call to print the timing + // information, and return saved value (if not void) + body.append("System.out.println(\"Call to method " + mname + + " took \" +\n (System.currentTimeMillis()-start) + " + + "\" ms.\");\n"); + if (!"void".equals(type)) { + body.append("return result;\n"); + } + body.append("}"); + + // replace the body of the interceptor method with generated + // code block and add it to class + mnew.setBody(body.toString()); + clas.addMethod(mnew); + + // print the generated code block just to show what was done + //System.out.println("Interceptor method body:"); + //System.out.println(body.toString()); } - + private void addAnnouncement(CtBehavior method, CtClass cl) + throws NotFoundException, CannotCompileException { + String name = method.getName(); + method.insertAfter("logger.warn(\"\");"); + } } + +