/******************************************************************************* * Copyright 2014, * Luis Pina <luis@luispina.me>, * Michael Hicks <mwh@cs.umd.edu> * * This file is part of Rubah. * * Rubah is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Rubah is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Rubah. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package rubah.tools; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Modifier; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.commons.AdviceAdapter; public class MethodTracer extends ReadWriteTool implements Opcodes { public static final String TOOL_NAME = "tracer"; private String className; private String methodName; @Override protected void foundClassFile(String name, InputStream inputStream) throws IOException { ClassReader reader = new ClassReader(inputStream); ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); ClassVisitor visitor = writer; visitor = new MethodTracerClassVisitor(writer); reader.accept(visitor, ClassReader.EXPAND_FRAMES); this.addFileToOutJar(name, writer.toByteArray()); } private class MethodTracerClassVisitor extends ClassVisitor { public MethodTracerClassVisitor(ClassVisitor cv) { super(ASM5, cv); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { MethodTracer.this.className = name; super.visit(version, access, name, signature, superName, interfaces); } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodTracer.this.methodName = name + desc; MethodVisitor ret = super.visitMethod(access, name, desc, signature, exceptions); if (Modifier.isAbstract(access)) { return ret; } return new MethodTracerMethodVisitor(access, name, desc, ret); } } private class MethodTracerMethodVisitor extends AdviceAdapter { private Label startFinally = new Label(); public MethodTracerMethodVisitor(int access, String name, String desc, MethodVisitor mv) { super(ASM5, mv, access, name, desc); } @Override public void visitCode() { super.visitCode(); this.mv.visitLabel(this.startFinally); this.mv.visitLdcInsn(MethodTracer.this.className); this.mv.visitLdcInsn(MethodTracer.this.methodName); this.mv.visitMethodInsn(INVOKESTATIC, "rubah/tools/MethodTracer", "print", "(Ljava/lang/String;Ljava/lang/String;)V", false); } @Override public void visitMaxs(int maxStack, int maxLocals) { Label endFinally = new Label(); this.visitTryCatchBlock(this.startFinally, endFinally, endFinally, null); this.mv.visitLabel(endFinally); this.onFinally(ATHROW); this.visitInsn(ATHROW); super.visitMaxs(maxStack, maxLocals); } @Override protected void onMethodExit(int opcode) { if (opcode != ATHROW) { this.onFinally(opcode); } } private void onFinally(int opcode) { this.mv.visitFieldInsn(GETSTATIC, "rubah/tools/MethodTracer", "level", "I"); this.mv.visitInsn(ICONST_1); this.mv.visitInsn(ISUB); this.mv.visitFieldInsn(PUTSTATIC, "rubah/tools/MethodTracer", "level", "I"); } } public static int level = 0; public static void print(String className, String methodName) { level++; StringBuffer buf = new StringBuffer(); for (int i = 0 ; i < level ; i++) { buf.append("|-"); } buf.append(className); buf.append(" - "); buf.append(methodName); System.out.println(buf.toString()); } }