package com.ikokoon.serenity.instrumentation.profiling; import org.apache.log4j.Logger; import org.objectweb.asm.ClassAdapter; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.Label; import org.objectweb.asm.MethodAdapter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import com.ikokoon.serenity.IConstants; import com.ikokoon.toolkit.Toolkit; /** * @author Michael Couck * @since 06.06.10 * @version 01.00 */ public class ProfilingClassAdviceAdapter extends ClassAdapter { private Logger logger; private String className; /** * Constructor takes the class that will be profiled and the original visitor. * * @param visitor * the visitor from ASM * @param className * the name of the class to be profiled */ public ProfilingClassAdviceAdapter(ClassVisitor visitor, String className) { super(visitor); this.className = Toolkit.slashToDot(className); this.logger = Logger.getLogger(this.getClass()); } /** * {@inheritDoc} */ public MethodVisitor visitMethod(int access, final String methodName, final String methodDescription, String methodSignature, String[] exceptions) { MethodVisitor methodVisitor = super.visitMethod(access, methodName, methodDescription, methodSignature, exceptions); MethodVisitor methodAdapter = getMethodAdapter(methodVisitor, access, methodName, methodDescription); return methodAdapter; } private MethodVisitor getMethodAdapter(MethodVisitor methodVisitor, int access, final String methodName, final String methodDescription) { // logger.warn("Access : " + access + ", " + Opcodes.ACC_ABSTRACT + ", " + Opcodes.ACC_INTERFACE); // We test for interfaces and abstract classes, of course these methods do // not have bodies so we can't add instructions to these methods or the Jvm // will not like it, class format exceptions boolean isAbstract = false; switch (access) { case Opcodes.ACC_ABSTRACT: case Opcodes.ACC_ABSTRACT + Opcodes.ACC_PUBLIC: case Opcodes.ACC_ABSTRACT + Opcodes.ACC_PRIVATE: case Opcodes.ACC_ABSTRACT + Opcodes.ACC_PROTECTED: case Opcodes.ACC_INTERFACE: case Opcodes.ACC_INTERFACE + Opcodes.ACC_PUBLIC: case Opcodes.ACC_INTERFACE + Opcodes.ACC_PRIVATE: case Opcodes.ACC_INTERFACE + Opcodes.ACC_PROTECTED: isAbstract = true; break; default: break; } if (isAbstract) { logger.info("Abstract method : " + access + " : " + methodName); return methodVisitor; } MethodAdapter methodAdapter = new ProfilingMethodAdviceAdapter(methodVisitor, access, methodName, methodDescription) { private Label[] catchBlockLabels = new Label[0]; @Override protected void onMethodEnter() { if (methodName.equals("<init>") || methodName.equals("<clinit>")) { insertInstruction(IConstants.COLLECTOR_CLASS_NAME, IConstants.COLLECT_ALLOCATION, IConstants.PROFILING_METHOD_DESCRIPTION); } insertInstruction(IConstants.COLLECTOR_CLASS_NAME, IConstants.COLLECT_START, IConstants.PROFILING_METHOD_DESCRIPTION); } @Override protected void onMethodExit(int inst) { insertInstruction(IConstants.COLLECTOR_CLASS_NAME, IConstants.COLLECT_END, IConstants.PROFILING_METHOD_DESCRIPTION); } @Override public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { super.visitTryCatchBlock(start, end, handler, type); if (type != null) { // We have to end the wait regardless of the exception type Label[] copyCatchBlockLabels = new Label[catchBlockLabels.length + 1]; System.arraycopy(catchBlockLabels, 0, copyCatchBlockLabels, 0, catchBlockLabels.length); copyCatchBlockLabels[copyCatchBlockLabels.length - 1] = handler; catchBlockLabels = copyCatchBlockLabels; } } @Override public void visitLabel(Label label) { super.visitLabel(label); for (Label interruptedCatchBlockLabel : catchBlockLabels) { if (label == interruptedCatchBlockLabel) { // This is an exception label, handler, so stop the wait, we don't know what the // caught exception was so to be safe just call the stop wait insertInstruction(IConstants.COLLECTOR_CLASS_NAME, IConstants.COLLECT_END_WAIT, IConstants.PROFILING_METHOD_DESCRIPTION); } } } @Override public void visitMethodInsn(int opcode, String owner, String name, String desc) { if (isWaitInsn(opcode, owner, name, desc)) { insertInstruction(IConstants.COLLECTOR_CLASS_NAME, IConstants.COLLECT_START_WAIT, IConstants.PROFILING_METHOD_DESCRIPTION); super.visitMethodInsn(opcode, owner, name, desc); insertInstruction(IConstants.COLLECTOR_CLASS_NAME, IConstants.COLLECT_END_WAIT, IConstants.PROFILING_METHOD_DESCRIPTION); } else { super.visitMethodInsn(opcode, owner, name, desc); } } private void insertInstruction(String collectorClassName, String collectorMethodName, String collectorMethodDescription) { super.visitLdcInsn(className); super.visitLdcInsn(methodName); super.visitLdcInsn(methodDesc); super.visitMethodInsn(Opcodes.INVOKESTATIC, collectorClassName, collectorMethodName, collectorMethodDescription); } }; return methodAdapter; } }