GlobalSlowMethodTransformer.kt 5.8 KB
Newer Older
J
jackjintai 已提交
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
package com.didichuxing.doraemonkit.plugin.transform

import com.didichuxing.doraemonkit.plugin.DoKitExtUtil
import com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
import com.didichuxing.doraemonkit.plugin.methodExitInsnNode
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
 * 描    述:全局业务代码 慢函数 wiki:https://juejin.im/post/5e8d87c4f265da47ad218e6b
 * 修订历史:
 * ================================================
 */
@Priority(3)
@AutoService(ClassTransformer::class)
class GlobalSlowMethodTransformer : ClassTransformer {
    val thresholdTime = DoKitExtUtil.slowMethodExt.normalMethod.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_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))
                            //方法出口插入
                            methodNode.instructions.methodExitInsnNode().let { methodExitInsnNode ->
                                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
    }


}