JVM插码之五:Java agent+ASM实战--监控所有方法执行时间

本文建立在对instrumentation和agent有初步的了解的前提下阅读,关于这2个类的讲解在其它文章中。

这是一个maven项目,pom中需要的配置,lib中有asm的jar包

pom.xml文件:

4.0.0com.dxzchama0.0.1-SNAPSHOTjarchamahttp://maven.apache.orgUTF-8org.ow2.asmasm-test6.2testorg.ow2.asmasm-analysis6.2org.ow2.asmasm-commons6.2org.ow2.asmasm-tree6.2org.ow2.asmasm-util6.2javassistjavassist3.12.1.GAcglibcglib3.2.5orooro2.0.8junitjunit3.8.1testorg.apache.maven.pluginsmaven-compiler-plugin1.81.8utf-8org.apache.maven.pluginsmaven-shade-plugin3.0.0packageshadecom.dxz.chama.javaagent.asm.UdAgent

打包后的agent的jar包中manifest属性如下:

agent类,只有一个方法,就是把自定义的类修改器添加到instrumentation中。

package com.dxz.chama.javaagent.asm;import java.lang.instrument.Instrumentation;public class UdAgent {public static void premain(String agentArgs, Instrumentation instrumentation){instrumentation.addTransformer(new LogTransformer());}
}

类转换器实现:

package com.dxz.chama.javaagent.asm;import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;public class LogTransformer implements ClassFileTransformer {@Overridepublic byte[] transform(ClassLoader loader, String className, Class classBeingRedefined,ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {try {ClassReader cr = new ClassReader(className);ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);TimeCountAdpter timeCountAdpter = new TimeCountAdpter(cw);cr.accept(timeCountAdpter, ClassReader.EXPAND_FRAMES);return cw.toByteArray();} catch (IOException e) {e.printStackTrace();}return null;}
}

实际修改字节码的方法,这里给每个类添加了一个字段UDASMCN,用于记录当前类的名字(方便打印信息)。同时记录每个方法的名字,以及执行时间。

package com.dxz.chama.javaagent.asm;import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.AnalyzerAdapter;
import org.objectweb.asm.commons.LocalVariablesSorter;public class TimeCountAdpter extends ClassVisitor implements Opcodes {private String owner;private boolean isInterface;private String filedName = "UDASMCN";private int acc = Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL;private boolean isPresent = false;private String methodName;public TimeCountAdpter(ClassVisitor classVisitor) {super(ASM6, classVisitor);}@Overridepublic void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {super.visit(version, access, name, signature, superName, interfaces);owner = name;isInterface = (access & ACC_INTERFACE) != 0;}@Overridepublic FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {if (name.equals(filedName)) {isPresent = true;}return super.visitField(access, name, descriptor, signature, value);}@Overridepublic MethodVisitor visitMethod(int access, String name, String descriptor, String signature,String[] exceptions) {MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions);if (!isInterface && mv != null && !name.equals("") && !name.equals("")) {methodName = name;AddTimerMethodAdapter at = new AddTimerMethodAdapter(mv);at.aa = new AnalyzerAdapter(owner, access, name, descriptor, at);at.lvs = new LocalVariablesSorter(access, descriptor, at.aa);return at.lvs;}return mv;}public void visitEnd() {if (!isInterface) {FieldVisitor fv = cv.visitField(acc, filedName, "Ljava/lang/String;", null, owner);if (fv != null) {fv.visitEnd();}}cv.visitEnd();}class AddTimerMethodAdapter extends MethodVisitor {private int time;private int maxStack;public LocalVariablesSorter lvs;public AnalyzerAdapter aa;public AddTimerMethodAdapter(MethodVisitor methodVisitor) {super(ASM6, methodVisitor);}@Overridepublic void visitCode() {mv.visitCode();mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "nanoTime", "()J", false);time = lvs.newLocal(Type.LONG_TYPE);mv.visitVarInsn(LSTORE, time);maxStack = 4;}@Overridepublic void visitInsn(int opcode) {if (((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) && !isPresent) {mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "nanoTime", "()J", false);mv.visitVarInsn(LLOAD, time);mv.visitInsn(LSUB);mv.visitVarInsn(LSTORE, time);mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");mv.visitTypeInsn(NEW, "java/lang/StringBuilder");mv.visitInsn(DUP);mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "", "()V", false);mv.visitFieldInsn(GETSTATIC, owner, filedName, "Ljava/lang/String;");mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append","(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);mv.visitLdcInsn("  " + methodName + ":");mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append","(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);mv.visitVarInsn(LLOAD, time);mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(J)Ljava/lang/StringBuilder;",false);mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);maxStack = Math.max(aa.stack.size() + 4, maxStack);}mv.visitInsn(opcode);}@Overridepublic void visitMaxs(int maxStack, int maxLocals) {super.visitMaxs(Math.max(maxStack, this.maxStack), maxLocals);}}}

打包成jar包后,在另一个程序启动时调用,启动参数如下:

-javaagent:D:\study\chama\target\chama-0.0.1-SNAPSHOT.jar
执行效果:
时间单位是纳秒,可以看到每个方法执行完时,都会打印这个方法 的执行时间,以com/Main main:11457636为例,说明类com/Main的main方法执行力11毫秒。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部