From 8628b72c87cc2d6dfe6d182016e2ebb80376aa38 Mon Sep 17 00:00:00 2001 From: jackjintai Date: Wed, 20 May 2020 15:44:47 +0800 Subject: [PATCH] android:buildSrc module --- Android/app/build.gradle | 3 +- Android/build.gradle | 2 +- .../doraemonkit/plugin/DoKitExtUtil.kt | 2 +- .../plugin/extension/SlowMethodExt.kt | 12 +- .../plugin/transform/BigImgTransformer.kt | 143 ++++++++++++++++++ .../transform/GlobalSlowMethodTransformer.kt | 127 ++++++++++++++++ .../transform/UrlConnectionTransformer.kt | 35 +++-- Android/doraemonkit/build.gradle | 1 + 8 files changed, 297 insertions(+), 28 deletions(-) create mode 100644 Android/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/transform/BigImgTransformer.kt create mode 100644 Android/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/transform/GlobalSlowMethodTransformer.kt diff --git a/Android/app/build.gradle b/Android/app/build.gradle index fc176b02..f0ec90e4 100644 --- a/Android/app/build.gradle +++ b/Android/app/build.gradle @@ -61,7 +61,6 @@ android { } - } @@ -84,7 +83,7 @@ dokitExt { slowMethod { //0:默认模式 打印函数调用栈 需添加指定入口 默认为application onCreate 和attachBaseContext //1:普通模式 运行时打印某个函数的耗时 全局业务代码函数插入 - strategy 0 + strategy 1 //函数功能开关 methodSwitch true diff --git a/Android/build.gradle b/Android/build.gradle index cf48416c..aba1a81e 100644 --- a/Android/build.gradle +++ b/Android/build.gradle @@ -13,7 +13,7 @@ buildscript { // } } dependencies { - classpath 'com.android.tools.build:gradle:3.4.2' + classpath 'com.android.tools.build:gradle:3.6.3' classpath 'com.novoda:bintray-release:0.9.2' // classpath "com.didichuxing.doraemonkit:doraemonkit-plugin:${rootProject.ext.android["pluginVersionName"]}" //classpath "com.didichuxing.doraemonkit:doraemonkit-plugin:3.1.5" diff --git a/Android/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitExtUtil.kt b/Android/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitExtUtil.kt index de8bdd4a..ebac863b 100644 --- a/Android/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitExtUtil.kt +++ b/Android/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitExtUtil.kt @@ -22,7 +22,7 @@ object DoKitExtUtil { private var mDokitPluginSwitch = true private var mDokitLogSwitch = false private var mUsefulInRelease = false - private val applications: MutableList = mutableListOf() + private val applications: MutableSet = mutableSetOf() var commExt = CommExt() private set val slowMethodExt = SlowMethodExt() diff --git a/Android/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/extension/SlowMethodExt.kt b/Android/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/extension/SlowMethodExt.kt index 7e07a70c..15a36085 100644 --- a/Android/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/extension/SlowMethodExt.kt +++ b/Android/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/extension/SlowMethodExt.kt @@ -48,7 +48,7 @@ open class SlowMethodExt( //默认阈值为5ms var thresholdTime: Int = 5, //入口函集合 - var enterMethods: MutableList = mutableListOf()) { + var enterMethods: MutableSet = mutableSetOf()) { /** * 默认值为5ms */ @@ -56,7 +56,7 @@ open class SlowMethodExt( this.thresholdTime = thresholdTime } - fun normalMethod(enterMethods: MutableList) { + fun normalMethod(enterMethods: MutableSet) { this.enterMethods = enterMethods } @@ -72,9 +72,9 @@ open class SlowMethodExt( //默认阈值为500ms var thresholdTime: Int = 500, //普通函数的插装包名集合 - var packageNames: MutableList = mutableListOf(), + var packageNames: MutableSet = mutableSetOf(), //插桩黑名单 - var methodBlacklist: MutableList = mutableListOf()) { + var methodBlacklist: MutableSet = mutableSetOf()) { /** * 默认值为500ms */ @@ -83,11 +83,11 @@ open class SlowMethodExt( this.thresholdTime = thresholdTime } - fun packageNames(packageNames: MutableList) { + fun packageNames(packageNames: MutableSet) { this.packageNames = packageNames } - fun methodBlacklist(methodBlacklist: MutableList) { + fun methodBlacklist(methodBlacklist: MutableSet) { this.methodBlacklist = methodBlacklist } diff --git a/Android/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/transform/BigImgTransformer.kt b/Android/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/transform/BigImgTransformer.kt new file mode 100644 index 00000000..e64125be --- /dev/null +++ b/Android/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/transform/BigImgTransformer.kt @@ -0,0 +1,143 @@ +package com.didichuxing.doraemonkit.plugin.transform + +import com.didichuxing.doraemonkit.plugin.DoKitExtUtil +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.className +import com.google.auto.service.AutoService +import org.objectweb.asm.Opcodes.* +import org.objectweb.asm.tree.ClassNode +import org.objectweb.asm.tree.InsnList +import org.objectweb.asm.tree.MethodInsnNode +import org.objectweb.asm.tree.VarInsnNode + +/** + * ================================================ + * 作 者:jint(金台) + * 版 本:1.0 + * 创建日期:2020/5/14-18:07 + * 描 述:wiki:https://juejin.im/post/5e8d87c4f265da47ad218e6b + * 修订历史: + * ================================================ + */ +@Priority(2) +@AutoService(ClassTransformer::class) +class BigImgTransformer : ClassTransformer { + private val SHADOW_URL = "com/didichuxing/doraemonkit/aop/urlconnection/HttpUrlConnectionProxyUtil" + private val DESC = "(Ljava/net/URLConnection;)Ljava/net/URLConnection;" + + override fun transform(context: TransformContext, klass: ClassNode): ClassNode { + if (!DoKitExtUtil.dokitPluginSwitchOpen()) { + return klass + } + + if (!DoKitExtUtil.commExt.bigImgSwitch) { + return klass + } + + if (DoKitExtUtil.ignorePackageNames(klass.className)) { + return klass + } + + + val className = klass.className + //glide + if (className == "com.bumptech.glide.request.SingleRequest") { + klass.methods.find { methodNode -> + (methodNode.name == "init" || methodNode.name == "") && methodNode.desc != null + }.let { methodNode -> + //函数结束的地方插入 + methodNode?.instructions?.methodExitInsnNode().let { + methodNode?.instructions?.insertBefore(it, createGlideInsnList()) + } + } + } + + //picasso + if (className == "com.squareup.picasso.Request") { + klass.methods.find { methodNode -> + methodNode.name == "" && methodNode.desc != null + }.let { methodNode -> + //函数结束的地方插入 + methodNode?.instructions?.methodExitInsnNode().let { + methodNode?.instructions?.insertBefore(it, createPicassoInsnList()) + } + } + } + + //Fresco + if (className == "com.facebook.imagepipeline.request.ImageRequest") { + klass.methods.find { methodNode -> + methodNode.name == "" && methodNode.desc != null + }.let { methodNode -> + //函数开始的地方插入 + methodNode?.instructions?.insert(createFrescoInsnList()) + } + } + + //ImageLoader + if (className == "com.nostra13.universalimageloader.core.ImageLoadingInfo") { + klass.methods.find { methodNode -> + methodNode.name == "" && methodNode.desc != null + }.let { methodNode -> + methodNode?.instructions?.insert(createImageLoaderInsnList()) + } + } + + + return klass + } + + /** + * 创建Glide Aop代码指令 + */ + private fun createGlideInsnList(): InsnList { + val insnList = InsnList() + insnList.add(VarInsnNode(ALOAD, 0)) + insnList.add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/bigimg/glide/GlideHook", "proxy", "(Ljava/lang/Object;)V", false)) + return insnList + } + + /** + * 创建Picasso Aop代码指令 + */ + private fun createPicassoInsnList(): InsnList { + val insnList = InsnList() + insnList.add(VarInsnNode(ALOAD, 0)) + insnList.add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/bigimg/picasso/PicassoHook", "proxy", "(Ljava/lang/Object;)V", false)) + return insnList + } + + + /** + * 创建Fresco Aop代码指令 + */ + private fun createFrescoInsnList(): InsnList { + val insnList = InsnList() + insnList.add(VarInsnNode(ALOAD, 1)) + insnList.add(VarInsnNode(ALOAD, 1)) + insnList.add(MethodInsnNode(INVOKEVIRTUAL, "com/facebook/imagepipeline/request/ImageRequestBuilder", "getSourceUri", "()Landroid/net/Uri;", false)) + insnList.add(VarInsnNode(ALOAD, 1)) + insnList.add(MethodInsnNode(INVOKEVIRTUAL, "com/facebook/imagepipeline/request/ImageRequestBuilder", "getPostprocessor", "()Lcom/facebook/imagepipeline/request/Postprocessor;", false)) + insnList.add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/bigimg/fresco/FrescoHook", "proxy", "(Landroid/net/Uri;Lcom/facebook/imagepipeline/request/Postprocessor;)Lcom/facebook/imagepipeline/request/Postprocessor;", false)) + insnList.add(MethodInsnNode(INVOKEVIRTUAL, "com/facebook/imagepipeline/request/ImageRequestBuilder", "setPostprocessor", "(Lcom/facebook/imagepipeline/request/Postprocessor;)Lcom/facebook/imagepipeline/request/ImageRequestBuilder;", false)) + return insnList + } + + + /** + * 创建ImageLoader Aop代码指令 + */ + private fun createImageLoaderInsnList(): InsnList { + val insnList = InsnList() + insnList.add(VarInsnNode(ALOAD, 6)) + insnList.add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/bigimg/imageloader/ImageLoaderHook", "proxy", "(Lcom/nostra13/universalimageloader/core/listener/ImageLoadingListener;)Lcom/nostra13/universalimageloader/core/listener/ImageLoadingListener;", false)) + insnList.add(VarInsnNode(ASTORE, 6)) + return insnList + } + + +} + diff --git a/Android/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/transform/GlobalSlowMethodTransformer.kt b/Android/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/transform/GlobalSlowMethodTransformer.kt new file mode 100644 index 00000000..e9496955 --- /dev/null +++ b/Android/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/transform/GlobalSlowMethodTransformer.kt @@ -0,0 +1,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 != "" + }.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 + } + + +} + diff --git a/Android/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/transform/UrlConnectionTransformer.kt b/Android/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/transform/UrlConnectionTransformer.kt index b488d750..dd5bf6a3 100644 --- a/Android/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/transform/UrlConnectionTransformer.kt +++ b/Android/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/transform/UrlConnectionTransformer.kt @@ -3,8 +3,6 @@ package com.didichuxing.doraemonkit.plugin.transform import com.didichuxing.doraemonkit.plugin.DoKitExtUtil import com.didiglobal.booster.annotations.Priority import com.didiglobal.booster.kotlinx.asIterable -import com.didiglobal.booster.kotlinx.file -import com.didiglobal.booster.kotlinx.touch import com.didiglobal.booster.transform.TransformContext import com.didiglobal.booster.transform.asm.ClassTransformer import com.didiglobal.booster.transform.asm.className @@ -13,8 +11,6 @@ import org.objectweb.asm.Opcodes.INVOKESTATIC import org.objectweb.asm.Opcodes.INVOKEVIRTUAL import org.objectweb.asm.tree.ClassNode import org.objectweb.asm.tree.MethodInsnNode -import org.objectweb.asm.tree.MethodNode -import java.io.PrintWriter /** * ================================================ @@ -28,33 +24,36 @@ import java.io.PrintWriter @Priority(1) @AutoService(ClassTransformer::class) class UrlConnectionTransformer : ClassTransformer { - + private val SHADOW_URL = "com/didichuxing/doraemonkit/aop/urlconnection/HttpUrlConnectionProxyUtil" + private val DESC = "(Ljava/net/URLConnection;)Ljava/net/URLConnection;" override fun transform(context: TransformContext, klass: ClassNode): ClassNode { if (!DoKitExtUtil.dokitPluginSwitchOpen()) { return klass } - if (DoKitExtUtil.ignorePackageNames(klass.className)) { + if (!DoKitExtUtil.commExt.networkSwitch) { return klass } + if (DoKitExtUtil.ignorePackageNames(klass.className)) { + return klass + } - if (DoKitExtUtil.commExt.networkSwitch) { - klass.methods.forEach { method -> - method.instructions?.iterator()?.asIterable()?.filterIsInstance(MethodInsnNode::class.java)?.filter { - it.opcode == INVOKEVIRTUAL && - it.owner == "java/net/URL" && - it.name == "openConnection" && - it.desc == "()Ljava/net/URLConnection;" - }?.forEach { - method.instructions.insert(it, MethodInsnNode(INVOKESTATIC, SHADOW_URL, "proxy", "(Ljava/net/URLConnection;)Ljava/net/URLConnection;", false)) - } + klass.methods.forEach { method -> + method.instructions?.iterator()?.asIterable()?.filterIsInstance(MethodInsnNode::class.java)?.filter { + it.opcode == INVOKEVIRTUAL && + it.owner == "java/net/URL" && + it.name == "openConnection" && + it.desc == "()Ljava/net/URLConnection;" + }?.forEach { + method.instructions.insert(it, MethodInsnNode(INVOKESTATIC, SHADOW_URL, "proxy", DESC, false)) } } + return klass } -} -private const val SHADOW_URL = "com/didichuxing/doraemonkit/aop/urlconnection/HttpUrlConnectionProxyUtil" + +} diff --git a/Android/doraemonkit/build.gradle b/Android/doraemonkit/build.gradle index 2e4b8ce7..22e456a5 100644 --- a/Android/doraemonkit/build.gradle +++ b/Android/doraemonkit/build.gradle @@ -1,6 +1,7 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' +apply plugin: 'kotlin-kapt' apply from: 'upload.gradle' android { -- GitLab