GlobalSlowMethodTransformer.kt 6.0 KB
Newer Older
1
package com.didichuxing.doraemonkit.plugin.classtransformer
J
jackjintai 已提交
2 3 4

import com.didichuxing.doraemonkit.plugin.DoKitExtUtil
import com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
5
import com.didichuxing.doraemonkit.plugin.getMethodExitInsnNodes
J
jackjintai 已提交
6 7 8 9 10 11 12 13 14 15 16 17 18 19
import com.didiglobal.booster.annotations.Priority
import com.didiglobal.booster.transform.TransformContext
import com.didiglobal.booster.transform.asm.ClassTransformer
import com.didiglobal.booster.transform.asm.asIterable
import com.didiglobal.booster.transform.asm.className
import com.google.auto.service.AutoService
import org.objectweb.asm.Opcodes.*
import org.objectweb.asm.tree.*

/**
 * ================================================
 * 作    者:jint(金台)
 * 版    本:1.0
 * 创建日期:2020/5/14-18:07
20
 * 描    述:全局业务代码慢函数  wiki:https://juejin.im/post/5e8d87c4f265da47ad218e6b
J
jackjintai 已提交
21 22 23 24 25 26 27
 * 修订历史:
 * ================================================
 */
@Priority(3)
@AutoService(ClassTransformer::class)
class GlobalSlowMethodTransformer : ClassTransformer {
    val thresholdTime = DoKitExtUtil.slowMethodExt.normalMethod.thresholdTime
28

J
jackjintai 已提交
29
    override fun transform(context: TransformContext, klass: ClassNode): ClassNode {
30 31 32
//        if(klass.className == "com.didichuxing.doraemondemo.App"){
//            println("===GlobalSlowMethodTransformer====transform===")
//        }
J
jackjintai 已提交
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
        if (!DoKitExtUtil.dokitPluginSwitchOpen()) {
            return klass
        }

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

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

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


        val className = klass.className
        //没有自定义设置插装包名 默认是以applicationId为包名 即全局业务代码插桩
        DoKitExtUtil.slowMethodExt.normalMethod.packageNames.forEach { packageName ->
            //包含在白名单中且不在黑名单中
            if (className.contains(packageName) && notMatchedBlackList(className)) {
                klass.methods.filter { methodNode ->
                    methodNode.name != "<init>"
                }.forEach { methodNode ->
                    methodNode.instructions.asIterable().filterIsInstance(MethodInsnNode::class.java).let { methodInsnNodes ->
                        if (methodInsnNodes.isNotEmpty()) {
                            //方法入口插入
                            methodNode.instructions.insert(createMethodEnterInsnList(className, methodNode.name, methodNode.access))
                            //方法出口插入
63
                            methodNode.instructions.getMethodExitInsnNodes()?.forEach { methodExitInsnNode ->
J
jackjintai 已提交
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
                                methodNode.instructions.insertBefore(methodExitInsnNode, createMethodExitInsnList(className, methodNode.name, methodNode.access))
                            }
                        }
                    }
                }
            }
        }
        return klass
    }


    private fun notMatchedBlackList(className: String): Boolean {
        for (strBlack in DoKitExtUtil.slowMethodExt.normalMethod.methodBlacklist) {
            if (className.contains(strBlack)) {
                return false
            }
        }

        return true
    }

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

        return insnList
    }


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


}