package com.ikokoon.serenity.instrumentation; import static org.junit.Assert.*; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.util.Arrays; import org.junit.Test; import org.mockito.asm.Type; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import com.ikokoon.serenity.ATest; import com.ikokoon.serenity.IConstants; import com.ikokoon.serenity.instrumentation.profiling.ProfilingClassAdapter; import com.ikokoon.serenity.instrumentation.profiling.ProfilingClassAdviceAdapter; import com.ikokoon.serenity.model.Class; import com.ikokoon.serenity.model.Line; import com.ikokoon.serenity.model.Method; import com.ikokoon.serenity.persistence.DataBaseToolkit; import com.ikokoon.toolkit.Toolkit; public class InstrumentationTest extends ATest { @Test(expected = RuntimeException.class) public void checkNoProfilingInstructions() throws IOException { byte[] classBytes = getClassBytes(this.className); byte[] sourceBytes = getSourceBytes(this.className); // Verify that the profiling instructions are not in the byte code ByteArrayOutputStream source = new ByteArrayOutputStream(); source.write(sourceBytes); ClassReader reader = new ClassReader(classBytes); ClassWriter writer = new ClassWriter(reader, true); ClassVisitor visitor = new InstrumentationClassAdapterChecker(writer, IConstants.COLLECT_START, IConstants.PROFILING_METHOD_DESCRIPTION); logger.debug("Adding class visitor : " + visitor); reader.accept(visitor, false); } @Test @SuppressWarnings("unchecked") public void checkProfilingAdviceInstructions() throws Exception { byte[] classBytes = getClassBytes(this.className); byte[] sourceBytes = getSourceBytes(this.className); // Add the profiling instructions ByteArrayOutputStream source = new ByteArrayOutputStream(); source.write(sourceBytes); ClassReader reader = new ClassReader(classBytes); ClassWriter writer = new ClassWriter(reader, true); ProfilingClassAdviceAdapter profilingClassAdviceAdapter = new ProfilingClassAdviceAdapter(writer, className); reader.accept(profilingClassAdviceAdapter, false); classBytes = writer.toByteArray(); String classPath = Toolkit.dotToSlash(this.className); File file = new File("./" + classPath + ".class"); if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } file.createNewFile(); Toolkit.setContents(file, classBytes); logger.warn("Wrote class to : " + file.getAbsolutePath()); // Verify that the profiling instructions are in the byte code reader = new ClassReader(classBytes); writer = new ClassWriter(reader, true); InstrumentationClassAdapterChecker instrumentationClassAdapterChecker = new InstrumentationClassAdapterChecker(writer, IConstants.COLLECT_START, IConstants.PROFILING_METHOD_DESCRIPTION); reader.accept(instrumentationClassAdapterChecker, false); final byte[] finalClassBytes = classBytes; final String finalClassName = this.className; // Call methods on the target and verify that the various methods // were called in the correct order including the wait, join and so on ClassLoader loader = new ClassLoader() { public java.lang.Class<?> loadClass(String className) throws ClassNotFoundException { if (className == finalClassName) { return this.defineClass(className, finalClassBytes, 0, finalClassBytes.length); } return super.loadClass(className); } }; String joinMethodName = "join"; String joinMethodDescription = Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] { Type.getType(Long.class), Type.getType(Integer.class) }); Thread.currentThread().setContextClassLoader(loader); Object target = loader.loadClass(className).newInstance(); Long joinTime = 1000l; Toolkit.executeMethod(target, joinMethodName, new Object[] { joinTime, 0 }); DataBaseToolkit.dump(dataBase, null, "Dump after profiling"); Method<Class<?, ?>, Line<?, ?>> method = dataBase.find(Method.class, Arrays.asList(className, joinMethodName, joinMethodDescription)); logger.warn("Method : " + method); assertNotNull(method); logger.warn("Invocations : " + method.getInvocations()); logger.warn("Start time : " + method.getStartTime()); logger.warn("End time : " + method.getEndTime()); logger.warn("Start wait : " + method.getStartWait()); logger.warn("End wait : " + method.getEndWait()); logger.warn("Wait time : " + method.getWaitTime()); logger.warn("Net time : " + method.getNetTime()); logger.warn("Total time : " + method.getTotalTime()); assertTrue(method.getInvocations() > 0); assertTrue(method.getStartTime() > 0); assertTrue(method.getEndTime() > 0); assertTrue(method.getStartWait() > 0); assertTrue(method.getEndWait() > 0); assertTrue(method.getWaitTime() > 0); // assertTrue(method.getNetTime() > 0); assertTrue(method.getTotalTime() > 0); } interface Interface { public void method(); } @Test(expected = RuntimeException.class) public void interfaceTest() throws Exception { String className = Interface.class.getName(); byte[] classBytes = getClassBytes(className); byte[] sourceBytes = getSourceBytes(className); // Add the profiling instructions ByteArrayOutputStream source = new ByteArrayOutputStream(); source.write(sourceBytes); ClassReader reader = new ClassReader(classBytes); ClassWriter writer = new ClassWriter(reader, true); ProfilingClassAdviceAdapter profilingClassAdviceAdapter = new ProfilingClassAdviceAdapter(writer, className); reader.accept(profilingClassAdviceAdapter, false); classBytes = writer.toByteArray(); String classPath = Toolkit.dotToSlash(className); File file = new File("./" + classPath + ".class"); if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } file.createNewFile(); Toolkit.setContents(file, classBytes); logger.warn("Wrote class to : " + file.getAbsolutePath()); // Verify that the profiling instructions are not in the byte code reader = new ClassReader(classBytes); writer = new ClassWriter(reader, true); InstrumentationClassAdapterChecker instrumentationClassAdapterChecker = new InstrumentationClassAdapterChecker(writer, IConstants.COLLECT_START, IConstants.PROFILING_METHOD_DESCRIPTION); reader.accept(instrumentationClassAdapterChecker, false); } // @Test @SuppressWarnings("unchecked") public void checkProfilingInstructions() throws Exception { byte[] classBytes = getClassBytes(this.className); byte[] sourceBytes = getSourceBytes(this.className); // Add the profiling instructions ByteArrayOutputStream source = new ByteArrayOutputStream(); source.write(sourceBytes); ClassReader reader = new ClassReader(classBytes); ClassWriter writer = new ClassWriter(reader, true); ProfilingClassAdapter profilingClassAdapter = new ProfilingClassAdapter(writer, className); reader.accept(profilingClassAdapter, false); classBytes = writer.toByteArray(); // OutputStream outputStream = new ByteArrayOutputStream(); // PrintWriter printWriter = new PrintWriter(outputStream); // ClassVisitor asMifierClassVisitor = (ClassVisitor) new ASMifierClassVisitor(printWriter); // reader = new ClassReader(classBytes); // reader.accept((ClassVisitor) asMifierClassVisitor, false); String classPath = Toolkit.dotToSlash(this.className); File file = new File("./" + classPath + ".class"); if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } file.createNewFile(); Toolkit.setContents(file, classBytes); logger.warn("Wrote class to : " + file.getAbsolutePath()); // Verify that the profiling instructions are not in the byte code reader = new ClassReader(classBytes); writer = new ClassWriter(reader, true); // ClassVisitor classVisitor, Class<?> methodVisitorClass, String collectorMethodName, String collectorMethodDescription InstrumentationClassAdapterChecker instrumentationClassAdapterChecker = new InstrumentationClassAdapterChecker(writer, IConstants.COLLECT_START, IConstants.PROFILING_METHOD_DESCRIPTION); reader.accept(instrumentationClassAdapterChecker, false); final byte[] finalClassBytes = classBytes; final String finalClassName = this.className; // Call the complex method on the target and verify that the Collector was called with the new instructions ClassLoader loader = new ClassLoader() { public java.lang.Class<?> loadClass(String className) throws ClassNotFoundException { if (className == finalClassName) { return this.defineClass(className, finalClassBytes, 0, finalClassBytes.length); } return super.loadClass(className); } }; Thread.currentThread().setContextClassLoader(loader); Object target = loader.loadClass(className).newInstance(); Toolkit.executeMethod(target, methodName, new Object[] { "", "", "", 1, 2 }); DataBaseToolkit.dump(dataBase, null, "Dump after profiling"); Method<Class<?, ?>, Line<?, ?>> method = dataBase.find(Method.class, Arrays.asList(className, methodName, methodDescription)); logger.warn("Method : " + method); } }