使用ASM编写 打印方法运行的时间

    技术2022-07-17  75

    编写ASM 代码操作字节码 并打印方法运行时间

    package grg.app.buildsrc.method import org.objectweb.asm.* import org.objectweb.asm.commons.AdviceAdapter /** * 方法运行时间计时 * @param customTAG 自定义的打印tag */ class AddTimeListener( val customTAG: String, methodVisitor: MethodVisitor?, access: Int, name: String?, descriptor: String? ) : AdviceAdapter(Opcodes.ASM7, methodVisitor, access, name, descriptor) { val startTimeLabel = newLabel() val endTimeLabel = newLabel() var startTimeIndex: Int = 0 override fun onMethodEnter() { super.onMethodEnter() startTimeIndex = newLocal(Type.DOUBLE_TYPE) startTimeLabel.let { visitLabel(it) } mv.visitLocalVariable( "startTime", "J", null, startTimeLabel, endTimeLabel, startTimeIndex ) mv.visitMethodInsn( Opcodes.INVOKESTATIC, "android/os/SystemClock", "currentThreadTimeMillis", "()J", false ) println("插入本地变量表index=${startTimeIndex}") mv.visitVarInsn(Opcodes.LSTORE, startTimeIndex) } override fun onMethodExit(opcode: Int) { //求时间差 并存入 本地变量表 mv.visitMethodInsn( Opcodes.INVOKESTATIC, "android/os/SystemClock", "currentThreadTimeMillis", "()J", false ) mv.visitVarInsn(Opcodes.LLOAD, startTimeIndex) mv.visitInsn(Opcodes.LSUB) mv.visitVarInsn(Opcodes.LSTORE, startTimeIndex) //StringBuilder 构建str 并存入本地变量表 val strVarIndex = newLocal(Type.getType(String::class.java)) val strBeginLabel = newLabel() val strEndLabel = newLabel() mv.visitLocalVariable( "__tempStr", "Ljava/lang/String;", null, strBeginLabel, strEndLabel, strVarIndex ) strBeginLabel.let { visitLabel(it) } //NEW java/lang/StringBuilder mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder") // DUP mv.visitInsn(Opcodes.DUP) // INVOKESPECIAL java/lang/StringBuilder.<init> ()V mv.visitMethodInsn( Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false ) // LDC "Wtf" mv.visitLdcInsn(customTAG) // INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false ) // ILOAD 2 mv.visitLdcInsn(",方法${name}消耗时间:") // INVOKEVIRTUAL java/lang/StringBuilder.append (I)Ljava/lang/StringBuilder; mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false ) //求得的时间 入栈 mv.visitVarInsn(Opcodes.LLOAD, startTimeIndex) //append 到 StringBuilder上 mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(J)Ljava/lang/StringBuilder;", false ) // ILOAD 2 mv.visitLdcInsn("毫秒") // INVOKEVIRTUAL java/lang/StringBuilder.append (I)Ljava/lang/StringBuilder; mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false ) //toString // INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String; mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false ) mv.visitVarInsn(Opcodes.ASTORE, strVarIndex) mv.visitFieldInsn( Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;" ) mv.visitVarInsn(Opcodes.ALOAD, strVarIndex) mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false ) endTimeLabel.let { visitLabel(it) } strEndLabel.let { visitLabel(it) } super.onMethodExit(opcode) } }

    然后在ClassVisitor的visitMethod方法中对你想要的方法进行操作就行

    eg:

    class TestClassVisitor(classVisitor: ClassVisitor) : ClassVisitor(Opcodes.ASM6, classVisitor) { val monitorMethodList = arrayOf<String>( "onClick", "onCreate", "onDestroy", "onResume" ) override fun visitMethod( access: Int, name: String?, descriptor: String?, signature: String?, exceptions: Array<out String>? ): MethodVisitor { val visitMethod = super.visitMethod(access, name, descriptor, signature, exceptions) return when (name) { in monitorMethodList -> { AddTimeListener("TIME_TAG",visitMethod, access, name, descriptor) } else -> { visitMethod } } } }

    运行结果:

    敲代码不易,加个收藏再走啦!!! 下一篇博客 我会逐一分析上述代码

    代码分析已出炉 点这里立马看

    Processed: 0.019, SQL: 9