MethodStackDepTransformer.kt 7.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
package com.didichuxing.doraemonkit.plugin.asmclasstransformer

import com.didichuxing.doraemonkit.plugin.DoKitExtUtil
import com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
import com.didichuxing.doraemonkit.plugin.getMethodExitInsnNodes
import com.didichuxing.doraemonkit.plugin.ownerClassName
import com.didichuxing.doraemonkit.plugin.stack_method.MethodStackNode
import com.didichuxing.doraemonkit.plugin.stack_method.MethodStackNodeUtil
import com.didiglobal.booster.transform.TransformContext
import com.didiglobal.booster.transform.asm.asIterable
import com.didiglobal.booster.transform.asm.className
import org.objectweb.asm.Opcodes.*
import org.objectweb.asm.tree.*

/**
 * ================================================
 * 作    者:jint(金台)
 * 版    本:1.0
 * 创建日期:2020/5/14-18:07
 * 描    述:入口函数 慢函数调用栈 wiki:https://juejin.im/post/5e8d87c4f265da47ad218e6b
 * 修订历史:不要指定自动注入 需要手动在DoKitAsmTransformer中通过配置创建
 * 原理:transform()方法的调用是无序的  原因:哪一个class会先被transformer执行是不确定的  但是每一个class被transformer执行顺序是遵循transformer的Priority规则的
 * ================================================
 */
class MethodStackDepTransformer(private val level: Int = 1) : BaseDoKitClassTransformer() {

    private val thresholdTime = DoKitExtUtil.slowMethodExt.stackMethod.thresholdTime
    override fun transform(context: TransformContext, klass: ClassNode): ClassNode {
        if (!DoKitExtUtil.dokitPluginSwitchOpen()) {
            return klass
        }

        if (!DoKitExtUtil.slowMethodExt.methodSwitch) {
            return klass
        }

        if (DoKitExtUtil.slowMethodExt.strategy == SlowMethodExt.STRATEGY_NORMAL) {
            return klass
        }

        if (DoKitExtUtil.ignorePackageNames(klass.className)) {
            return klass
        }


        val methodStackKeys: MutableSet<String> = MethodStackNodeUtil.METHOD_STACK_KEYS[level - 1]

        klass.methods.forEach { methodNode ->
            val key = "${klass.className}&${methodNode.name}&${methodNode.desc}"
            if (methodStackKeys.contains(key)) {
                println("level===>$level   mathched key===>$key")
                operateMethodInsn(klass, methodNode)
            }

        }

        return klass
    }


    private fun operateMethodInsn(klass: ClassNode, methodNode: MethodNode) {
        //读取全是函数调用的指令
        methodNode.instructions.asIterable().filterIsInstance(MethodInsnNode::class.java).filter { methodInsnNode ->
            methodInsnNode.name != "<init>"
        }.forEach { methodInsnNode ->
            val methodStackNode = MethodStackNode(level, methodInsnNode.ownerClassName, methodInsnNode.name, methodInsnNode.desc, klass.className, methodNode.name, methodNode.desc)
            MethodStackNodeUtil.addMethodStackNode(level, methodStackNode)
        }
        //函数出入口插入耗时统计代码
        //方法入口插入
        methodNode.instructions.insert(createMethodEnterInsnList(level, klass.className, methodNode.name, methodNode.desc, methodNode.access))
        //方法出口插入
        methodNode.instructions.getMethodExitInsnNodes()?.forEach { methodExitInsnNode ->
            methodNode.instructions.insertBefore(methodExitInsnNode, createMethodExitInsnList(level, klass.className, methodNode.name, methodNode.desc, methodNode.access))
        }
    }


    /**
     * 创建慢函数入口指令集
     */
    private fun createMethodEnterInsnList(level: Int, className: String, methodName: String, desc: String, access: Int): InsnList {
        val isStaticMethod = access and ACC_STATIC != 0
        val insnList = InsnList()
        if (isStaticMethod) {
            insnList.add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "getInstance", "()Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;", false))
            insnList.add(IntInsnNode(BIPUSH, DoKitExtUtil.mStackMethodLevel))
            insnList.add(IntInsnNode(BIPUSH, thresholdTime))
            insnList.add(IntInsnNode(BIPUSH, level))
            insnList.add(LdcInsnNode(className))
            insnList.add(LdcInsnNode(methodName))
            insnList.add(LdcInsnNode(desc))
            insnList.add(MethodInsnNode(INVOKEVIRTUAL, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "recodeStaticMethodCostStart", "(IIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", false))
        } else {
            insnList.add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "getInstance", "()Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;", false))
            insnList.add(IntInsnNode(BIPUSH, DoKitExtUtil.mStackMethodLevel))
            insnList.add(IntInsnNode(BIPUSH, thresholdTime))
            insnList.add(IntInsnNode(BIPUSH, level))
            insnList.add(LdcInsnNode(className))
            insnList.add(LdcInsnNode(methodName))
            insnList.add(LdcInsnNode(desc))
            insnList.add(VarInsnNode(ALOAD, 0))
            insnList.add(MethodInsnNode(INVOKEVIRTUAL, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "recodeObjectMethodCostStart", "(IIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V", false))
        }

        return insnList
    }


    /**
     * 创建慢函数退出时的指令集
     */
    private fun createMethodExitInsnList(level: Int, className: String, methodName: String, desc: String, access: Int): InsnList {
        val isStaticMethod = access and ACC_STATIC != 0
        val insnList = InsnList()
        if (isStaticMethod) {
            insnList.add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "getInstance", "()Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;", false))
            insnList.add(IntInsnNode(BIPUSH, thresholdTime))
            insnList.add(IntInsnNode(BIPUSH, level))
            insnList.add(LdcInsnNode(className))
            insnList.add(LdcInsnNode(methodName))
            insnList.add(LdcInsnNode(desc))
            insnList.add(MethodInsnNode(INVOKEVIRTUAL, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "recodeStaticMethodCostEnd", "(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", false))
        } else {
            insnList.add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "getInstance", "()Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;", false))
            insnList.add(IntInsnNode(BIPUSH, thresholdTime))
            insnList.add(IntInsnNode(BIPUSH, level))
            insnList.add(LdcInsnNode(className))
            insnList.add(LdcInsnNode(methodName))
            insnList.add(LdcInsnNode(desc))
            insnList.add(VarInsnNode(ALOAD, 0))
            insnList.add(MethodInsnNode(INVOKEVIRTUAL, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "recodeObjectMethodCostEnd", "(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V", false))
        }
        return insnList
    }

}