提交 b8041bb4 编写于 作者: J jackjintai

android:优化插件功能

上级 e8776162
......@@ -3,6 +3,7 @@ package com.didichuxing.doraemondemo
import android.app.Activity
import android.app.Application
import android.content.Context
import android.util.Log
import androidx.multidex.MultiDex
import com.didichuxing.doraemondemo.dokit.DemoKit
import com.didichuxing.doraemonkit.DoraemonKit
......@@ -16,6 +17,8 @@ import java.util.*
* @mail 704167880@qq.com
*/
class App : Application() {
var name = "init"
override fun onCreate() {
super.onCreate()
......@@ -31,6 +34,11 @@ class App : Application() {
// kits.add(DemoKit())
// kits.add(DemoKit())
// kits.add(DemoKit())
name = "jint"
var newName = name
Log.i("TAG", "newName====>$newName")
val mapKits: LinkedHashMap<String, MutableList<AbstractKit>> = linkedMapOf()
mapKits["业务专区1"] = mutableListOf<AbstractKit>(DemoKit())
mapKits["业务专区2"] = mutableListOf<AbstractKit>(DemoKit())
......
......@@ -151,6 +151,8 @@ class MainDebugActivity : BaseActivity(), View.OnClickListener {
.addConverterFactory(GsonConverterFactory.create())
.build()
githubService = retrofit.create(GithubService::class.java)
AopTest().test()
}
private fun test1() {
......
package com.didichuxing.doraemondemo;
import android.util.Log;
/**
* ================================================
* 作 者:jint(金台)
......@@ -11,9 +13,21 @@ package com.didichuxing.doraemondemo;
*/
public class AopTest {
private static final String TAG = "AopTest";
private String name;
public void test() {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void test() {
setName("jint");
String newName = getName();
Log.i("TAG", "newName====>" + newName);
}
......
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
apply from: "config.gradle"
ext {
kotlin_version = '1.3.72'
}
repositories {
google()
jcenter()
maven {
//本地插件地址
url uri(rootProject.ext.config["localRepoURL"])
}
// maven {
// //本地插件地址
// url uri(rootProject.ext.config["localRepoURL"])
// }
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.3'
classpath 'com.android.tools.build:gradle:3.3.0'
classpath 'com.novoda:bintray-release:0.9.2'
classpath "com.didichuxing.doraemonkit:doraemonkit-plugin:${rootProject.ext.android["pluginVersionName"]}"
//classpath "com.didichuxing.doraemonkit:doraemonkit-plugin:${rootProject.ext.android["pluginVersionName"]}"
//classpath "com.didichuxing.doraemonkit:doraemonkit-plugin:3.1.5"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${rootProject.ext.android["kotlin_version"]}"
}
......@@ -25,10 +22,10 @@ allprojects {
repositories {
google()
jcenter()
maven {
//本地插件地址
url uri(rootProject.ext.config["localRepoURL"])
}
// maven {
// //本地插件地址
// url uri(rootProject.ext.config["localRepoURL"])
// }
}
}
......
buildscript {
apply from: "../config.gradle"
repositories {
mavenLocal()
mavenCentral()
google()
jcenter()
maven { url 'https://oss.sonatype.org/content/repositories/public/' }
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
}
dependencies {
//classpath 'com.android.tools.build:gradle:3.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${rootProject.ext.android["kotlin_version"]}"
}
}
//apply plugin: 'java-library'
apply plugin: 'kotlin'
apply plugin: 'kotlin-kapt'
/**
* 由于 buildSrc 的执行时机要早于任何一个 project,因此需要⾃⼰添加仓库
*/
repositories {
mavenLocal()
mavenCentral()
google()
jcenter()
maven { url 'https://oss.sonatype.org/content/repositories/public/' }
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
}
sourceSets {
main {
java {
srcDirs += []
}
kotlin {
srcDirs += ['src/main/kotlin', 'src/main/java']
}
}
}
compileKotlin {
kotlinOptions.jvmTarget = JavaVersion.VERSION_1_8
}
dependencies {
compileOnly localGroovy()
compileOnly gradleApi()
implementation 'com.android.tools.build:gradle:3.3.0'
/* 👇👇👇👇 引用这两个模块 👇👇👇👇 */
kapt "com.google.auto.service:auto-service:1.0-rc6"
api "com.didiglobal.booster:booster-api:${rootProject.ext.android["booster_version"]}"
api "com.didiglobal.booster:booster-transform-asm:${rootProject.ext.android["booster_version"]}"
}
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin
import com.android.build.gradle.api.BaseVariant
import com.didiglobal.booster.transform.TransformContext
import org.objectweb.asm.Opcodes.*
import org.objectweb.asm.tree.InsnList
import org.objectweb.asm.tree.InsnNode
import org.objectweb.asm.tree.MethodInsnNode
import org.objectweb.asm.tree.MethodNode
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/19-18:00
* 描 述:dokit 对象扩展
* 修订历史:
* ================================================
*/
fun MethodNode.isGetSetMethod(): Boolean {
var ignoreCount = 0
val iterator = instructions.iterator()
while (iterator.hasNext()) {
val insnNode = iterator.next()
val opcode = insnNode.opcode
if (-1 == opcode) {
continue
}
if (opcode != GETFIELD && opcode != GETSTATIC && opcode != H_GETFIELD && opcode != H_GETSTATIC && opcode != RETURN && opcode != ARETURN && opcode != DRETURN && opcode != FRETURN && opcode != LRETURN && opcode != IRETURN && opcode != PUTFIELD && opcode != PUTSTATIC && opcode != H_PUTFIELD && opcode != H_PUTSTATIC && opcode > SALOAD) {
if (name.equals("<init>") && opcode == INVOKESPECIAL) {
ignoreCount++
if (ignoreCount > 1) {
return false
}
continue
}
return false
}
}
return true
}
fun MethodNode.isSingleMethod(): Boolean {
val iterator = instructions.iterator()
while (iterator.hasNext()) {
val insnNode = iterator.next()
val opcode = insnNode.opcode
if (-1 == opcode) {
continue
} else if (INVOKEVIRTUAL <= opcode && opcode <= INVOKEDYNAMIC) {
return false
}
}
return true
}
fun MethodNode.isEmptyMethod(): Boolean {
val iterator = instructions.iterator()
while (iterator.hasNext()) {
val insnNode = iterator.next()
val opcode = insnNode.opcode
return if (-1 == opcode) {
continue
} else {
false
}
}
return true
}
fun InsnList.getMethodExitInsnNodes(): Sequence<InsnNode>? {
return this.iterator()?.asSequence()?.filterIsInstance(InsnNode::class.java)?.filter {
it.opcode == RETURN ||
it.opcode == IRETURN ||
it.opcode == FRETURN ||
it.opcode == ARETURN ||
it.opcode == LRETURN ||
it.opcode == DRETURN ||
it.opcode == ATHROW
}
}
fun BaseVariant.isRelease(): Boolean {
if (this.name.contains("release") || this.name.contains("Release")) {
return true
}
return false
}
fun TransformContext.isRelease(): Boolean {
if (this.name.contains("release") || this.name.contains("Release")) {
return true
}
return false
}
fun String.println() {
if (DoKitExtUtil.dokitLogSwitchOpen()) {
print(this)
}
}
val MethodInsnNode.ownerClassName: String
get() = owner.replace('/', '.')
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin
import com.didichuxing.doraemonkit.plugin.extension.CommExt
import com.didichuxing.doraemonkit.plugin.extension.DoKitExt
import com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/3/24-14:58
* 描 述:
* 修订历史:
* ================================================
*/
object DoKitExtUtil {
//private var mApplicationId: String = ""
/**
* dokit 插件开关 字段权限必须为public 否则无法进行赋值
*/
private var mDokitPluginSwitch = true
private var mDokitLogSwitch = false
/**
* 默认函数调用为5级
*/
public var mStackMethodLevel = 5
/**
* 慢函数默认关闭
*/
public var mSlowMethodSwitch = false
/**
* 慢函数策略 默认为函数调用栈策略
*/
public var mSlowMethodStrategy = SlowMethodExt.STRATEGY_STACK
private val applications: MutableSet<String> = mutableSetOf()
var commExt = CommExt()
private set
val slowMethodExt = SlowMethodExt()
fun dokitPluginSwitchOpen(): Boolean {
return mDokitPluginSwitch
}
fun dokitLogSwitchOpen(): Boolean {
return mDokitLogSwitch
}
fun dokitSlowMethodSwitchOpen(): Boolean {
return mSlowMethodSwitch
}
/**
* 初始化
*
* @param dokitEx dokitExtension
* @param appExtension appExtension
*/
fun init(dokitEx: DoKitExt, applicationId: String) {
mDokitPluginSwitch = dokitEx.dokitPluginSwitch
mDokitLogSwitch = dokitEx.dokitLogSwitch
//设置普通的配置
commExt = dokitEx.comm
//slowMethodExt.strategy = dokitEx.slowMethod.strategy
//slowMethodExt.methodSwitch = dokitEx.slowMethod.methodSwitch
/**
* ============慢函数普通策略的配置 start==========
*/
slowMethodExt.normalMethod.thresholdTime = dokitEx.slowMethod.normalMethod.thresholdTime
//设置慢函数普通策略插装包名
slowMethodExt.normalMethod.packageNames.clear()
for (packageName in dokitEx.slowMethod.normalMethod.packageNames) {
slowMethodExt.normalMethod.packageNames.add(packageName)
}
//添加默认的包名
if (applicationId.isNotEmpty()) {
if (slowMethodExt.normalMethod.packageNames.isEmpty()) {
slowMethodExt.normalMethod.packageNames.add(applicationId)
}
}
//设置慢函数普通策略插装包名黑名单
slowMethodExt.normalMethod.methodBlacklist.clear()
for (blackStr in dokitEx.slowMethod.normalMethod.methodBlacklist) {
slowMethodExt.normalMethod.methodBlacklist.add(blackStr)
}
/**
* ============慢函数普通策略的配置end==========
*/
/**
* ============慢函数stack策略的配置 start==========
*/
slowMethodExt.stackMethod.thresholdTime = dokitEx.slowMethod.stackMethod.thresholdTime
slowMethodExt.stackMethod.enterMethods.clear()
//添加默认的入口函数
for (application in applications) {
val attachBaseContextMethodName = "$application.attachBaseContext"
val onCreateMethodName = "$application.onCreate"
slowMethodExt.stackMethod.enterMethods.add(attachBaseContextMethodName)
slowMethodExt.stackMethod.enterMethods.add(onCreateMethodName)
}
for (methodName in dokitEx.slowMethod.stackMethod.enterMethods) {
slowMethodExt.stackMethod.enterMethods.add(methodName)
}
/**
* ============慢函数stack策略的配置 end==========
*/
}
fun setApplications(applications: MutableSet<String>) {
if (applications.isEmpty()) {
return
}
this.applications.clear()
for (application in applications) {
this.applications.add(application)
}
}
fun ignorePackageNames(className: String): Boolean {
//命中白名单返回false
for (packageName in whitePackageNames) {
if (className.startsWith(packageName, true)) {
return false
}
}
//命中黑名单返回true
for (packageName in blackPackageNames) {
if (className.startsWith(packageName, true)) {
return true
}
}
return false
}
/**
* 白名单
*/
private val whitePackageNames = arrayOf(
"com.didichuxing.doraemonkit.DoraemonKit",
"com.didichuxing.doraemonkit.DoraemonKitReal"
)
/**
* 黑名单
*/
private val blackPackageNames = arrayOf(
"com.didichuxing.doraemonkit.",
"kotlin.",
"java.",
"android.",
"androidx."
)
fun log(tag: String, className: String, methodName: String, access: Int, desc: String, signature: String, thresholdTime: Int) {
if (mDokitLogSwitch) {
println("$tag===matched====> className===$className methodName===$methodName access===$access desc===$desc signature===$signature thresholdTime===$thresholdTime")
}
}
}
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin
import com.android.build.gradle.AppExtension
import com.android.build.gradle.LibraryExtension
import com.didichuxing.doraemonkit.plugin.extension.DoKitExt
import com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
import com.didichuxing.doraemonkit.plugin.stack_method.MethodStackNodeUtil
import com.didichuxing.doraemonkit.plugin.transform.DoKitCommTransform
import com.didichuxing.doraemonkit.plugin.transform.DoKitDependTransform
import com.didiglobal.booster.annotations.Priority
import com.didiglobal.booster.gradle.getAndroid
import com.didiglobal.booster.gradle.getProperty
import com.didiglobal.booster.task.spi.VariantProcessor
import org.gradle.api.Plugin
import org.gradle.api.Project
import java.util.*
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/14-10:01
* 描 述:
* 修订历史:
* ================================================
*/
class DoKitPlugin : Plugin<Project> {
private lateinit var mProject: Project
override fun apply(project: Project) {
mProject = project
//创建指定扩展 并将project 传入构造函数
//创建指定扩展 并将project 传入构造函数
val doKitExt = project.extensions.create("dokitExt", DoKitExt::class.java)
// val debug = project.gradle.startParameter.taskNames.any {
// it.contains("debug") || it.contains("Debug")
// }
// if (!debug) {
// return
// }
project.gradle.addListener(DoKitTransformTaskExecutionListener(project))
//println("project.plugins===>${project.plugins}")
/**
* when 也可以用来取代 if-else if链。
* 如果不提供参数,所有的分支条件都是简单的布尔表达式,而当一个分支的条件为真时则执行该分支:
*/
/**
* 作用域函数:let、run、with、apply 以及 also
* 它们的唯一目的是在对象的上下文中执行代码块
* 由于作用域函数本质上都非常相似,因此了解它们之间的区别很重要。每个作用域函数之间有两个主要区别:
* 引用上下文对象的方式:
* 作为 lambda 表达式的接收者(this)或者作为 lambda 表达式的参数(it)
* run、with 以及 apply 通过关键字 this 引用上下文对象
* let 及 also 将上下文对象作为 lambda 表达式参数
*
* 返回值:
* apply 及 also 返回上下文对象。
* let、run 及 with 返回 lambda 表达式结果.
*/
/**
* 函数 对象引用 返回值 是否是扩展函数
* let it Lambda 表达式结果 是
* run this Lambda 表达式结果 是
* run - Lambda 表达式结果 不是:调用无需上下文对象
* with this Lambda 表达式结果 不是:把上下文对象当做参数
* apply this 上下文对象 是
* also it 上下文对象 是
*/
/**
*对一个非空(non-null)对象执行 lambda 表达式:let
*将表达式作为变量引入为局部作用域中:let
*对象配置:apply
*对象配置并且计算结果:run
*在需要表达式的地方运行语句:非扩展的 run
*附加效果:also
*一个对象的一组函数调用:with
*/
when {
project.plugins.hasPlugin("com.android.application") || project.plugins.hasPlugin("com.android.dynamic-feature") -> {
project.getAndroid<AppExtension>().let { androidExt ->
val slowMethodSwitch = project.getProperty("DOKIT_METHOD_SWITCH", false)
val slowMethodStrategy = project.getProperty("DOKIT_METHOD_STRATEGY", 0)
val methodStackLevel = project.getProperty("DOKIT_METHOD_STACK_LEVEL", 5)
DoKitExtUtil.mSlowMethodSwitch = slowMethodSwitch
DoKitExtUtil.mSlowMethodStrategy = slowMethodStrategy
DoKitExtUtil.mStackMethodLevel = methodStackLevel
MethodStackNodeUtil.METHOD_STACK_KEYS.clear()
//注册transform
androidExt.registerTransform(DoKitCommTransform(project))
if (slowMethodSwitch && slowMethodStrategy == SlowMethodExt.STRATEGY_STACK) {
MethodStackNodeUtil.METHOD_STACK_KEYS.add(0, mutableSetOf<String>())
val methodStackRange = 1 until methodStackLevel
if (methodStackLevel > 1) {
for (index in methodStackRange) {
MethodStackNodeUtil.METHOD_STACK_KEYS.add(index, mutableSetOf<String>())
androidExt.registerTransform(DoKitDependTransform(project, index))
}
}
}
//项目评估完毕回调
project.afterEvaluate {
this.variantProcessors.let { processors ->
androidExt.applicationVariants.forEach { variant ->
processors.forEach { processor ->
processor.process(variant)
}
}
}
}
}
}
project.plugins.hasPlugin("com.android.library") -> {
project.getAndroid<LibraryExtension>().let { libraryExt ->
libraryExt.registerTransform(DoKitCommTransform(project))
project.afterEvaluate {
this.variantProcessors.let { processors ->
libraryExt.libraryVariants.forEach { variant ->
processors.forEach { processor ->
processor.process(variant)
}
}
}
}
}
}
}
}
private val variantProcessors: Collection<VariantProcessor>
get() = ServiceLoader.load(VariantProcessor::class.java, javaClass.classLoader).sortedBy {
it.javaClass.getAnnotation(Priority::class.java)?.value ?: 0
}
}
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin
import com.android.build.api.transform.Context
import com.android.build.api.transform.DirectoryInput
import com.android.build.api.transform.Format
import com.android.build.api.transform.JarInput
import com.android.build.api.transform.QualifiedContent
import com.android.build.api.transform.SecondaryInput
import com.android.build.api.transform.Status.ADDED
import com.android.build.api.transform.Status.CHANGED
import com.android.build.api.transform.Status.NOTCHANGED
import com.android.build.api.transform.Status.REMOVED
import com.android.build.api.transform.TransformInput
import com.android.build.api.transform.TransformInvocation
import com.android.build.api.transform.TransformOutputProvider
import com.didichuxing.doraemonkit.plugin.transform.DoKitBaseTransform
import com.didiglobal.booster.gradle.*
import com.didiglobal.booster.kotlinx.NCPU
import com.didiglobal.booster.transform.AbstractKlassPool
import com.didiglobal.booster.transform.ArtifactManager
import com.didiglobal.booster.transform.TransformContext
import com.didiglobal.booster.transform.artifacts
import com.didiglobal.booster.transform.util.transform
import java.io.File
import java.net.URI
import java.util.concurrent.Executors
import java.util.concurrent.Future
/**
* Represents a delegate of TransformInvocation
*
* @author johnsonlee
*/
internal class DoKitTransformInvocation(private val delegate: TransformInvocation, internal val transform: DoKitBaseTransform) : TransformInvocation, TransformContext, ArtifactManager {
private val executor = Executors.newWorkStealingPool(NCPU)
override val name: String = delegate.context.variantName
override val projectDir: File = delegate.project.projectDir
override val buildDir: File = delegate.project.buildDir
override val temporaryDir: File = delegate.context.temporaryDir
override val reportsDir: File = File(buildDir, "reports").also { it.mkdirs() }
override val bootClasspath = delegate.bootClasspath
override val compileClasspath = delegate.compileClasspath
override val runtimeClasspath = delegate.runtimeClasspath
override val artifacts = this
override val klassPool: AbstractKlassPool = object : AbstractKlassPool(compileClasspath, transform.bootKlassPool) {}
override val applicationId = delegate.applicationId
override val originalApplicationId = delegate.originalApplicationId
override val isDebuggable = variant.buildType.isDebuggable
override val isDataBindingEnabled = delegate.isDataBindingEnabled
override fun hasProperty(name: String) = project.hasProperty(name)
@Suppress("UNCHECKED_CAST")
override fun <T> getProperty(name: String, default: T): T = project.properties[name] as? T ?: default
override fun getInputs(): MutableCollection<TransformInput> = delegate.inputs
override fun getSecondaryInputs(): MutableCollection<SecondaryInput> = delegate.secondaryInputs
override fun getReferencedInputs(): MutableCollection<TransformInput> = delegate.referencedInputs
override fun isIncremental() = delegate.isIncremental
override fun getOutputProvider(): TransformOutputProvider? = delegate.outputProvider
override fun getContext(): Context = delegate.context
override fun get(type: String) = variant.artifacts.get(type)
internal fun doFullTransform() = doTransform(this::transformFully)
internal fun doIncrementalTransform() = doTransform(this::transformIncrementally)
private val tasks = mutableListOf<Future<*>>()
private fun onPreTransform() {
transform.transformers.forEach {
it.onPreTransform(this)
}
}
private fun onPostTransform() {
tasks.forEach {
it.get()
}
transform.transformers.forEach {
it.onPostTransform(this)
}
}
private fun doTransform(block: () -> Unit) {
this.onPreTransform()
block()
this.onPostTransform()
}
private fun transformFully() {
this.inputs.map {
it.jarInputs + it.directoryInputs
}.flatten().forEach { input ->
tasks += executor.submit {
val format = if (input is DirectoryInput) Format.DIRECTORY else Format.JAR
outputProvider?.let { provider ->
project.logger.info("Transforming ${input.file}")
input.transform(provider.getContentLocation(input.name, input.contentTypes, input.scopes, format), this)
}
}
}
}
private fun transformIncrementally() {
this.inputs.parallelStream().forEach { input ->
input.jarInputs.parallelStream().filter { it.status != NOTCHANGED }.forEach { jarInput ->
tasks += executor.submit {
doIncrementalTransform(jarInput)
}
}
input.directoryInputs.parallelStream().filter { it.changedFiles.isNotEmpty() }.forEach { dirInput ->
val base = dirInput.file.toURI()
tasks += executor.submit {
doIncrementalTransform(dirInput, base)
}
}
}
}
@Suppress("NON_EXHAUSTIVE_WHEN")
private fun doIncrementalTransform(jarInput: JarInput) {
when (jarInput.status) {
REMOVED -> jarInput.file.delete()
CHANGED, ADDED -> {
project.logger.info("Transforming ${jarInput.file}")
outputProvider?.let { provider ->
jarInput.transform(provider.getContentLocation(jarInput.name, jarInput.contentTypes, jarInput.scopes, Format.JAR), this)
}
}
}
}
@Suppress("NON_EXHAUSTIVE_WHEN")
private fun doIncrementalTransform(dirInput: DirectoryInput, base: URI) {
dirInput.changedFiles.forEach { (file, status) ->
when (status) {
REMOVED -> {
project.logger.info("Deleting $file")
outputProvider?.let { provider ->
provider.getContentLocation(dirInput.name, dirInput.contentTypes, dirInput.scopes, Format.DIRECTORY).parentFile.listFiles()?.asSequence()
?.filter { it.isDirectory }
?.map { File(it, dirInput.file.toURI().relativize(file.toURI()).path) }
?.filter { it.exists() }
?.forEach { it.delete() }
}
file.delete()
}
ADDED, CHANGED -> {
project.logger.info("Transforming $file")
outputProvider?.let { provider ->
val root = provider.getContentLocation(dirInput.name, dirInput.contentTypes, dirInput.scopes, Format.DIRECTORY)
val output = File(root, base.relativize(file.toURI()).path)
file.transform(output) { bytecode ->
bytecode.transform(this)
}
}
}
}
}
}
}
private fun ByteArray.transform(invocation: DoKitTransformInvocation): ByteArray {
return invocation.transform.transformers.fold(this) { bytes, transformer ->
transformer.transform(invocation, bytes)
}
}
private fun QualifiedContent.transform(output: File, invocation: DoKitTransformInvocation) {
this.file.transform(output) { bytecode ->
bytecode.transform(invocation)
}
}
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin
import com.android.build.gradle.internal.pipeline.TransformTask
import com.didiglobal.booster.kotlinx.call
import com.didiglobal.booster.kotlinx.get
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.execution.TaskExecutionAdapter
/**
* @author neighbWang
*/
class DoKitTransformTaskExecutionListener(private val project: Project) : TaskExecutionAdapter() {
override fun beforeExecute(task: Task) {
task.takeIf {
it.project == project && it is TransformTask && it.transform.scopes.isNotEmpty()
}?.run {
task["outputStream"]?.call<Unit>("init")
}
}
}
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin.asmtransformer
import com.didiglobal.booster.annotations.Priority
import com.didiglobal.booster.transform.TransformContext
import com.didiglobal.booster.transform.Transformer
import com.didiglobal.booster.transform.asm.ClassTransformer
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.tree.ClassNode
import java.lang.management.ManagementFactory
import java.lang.management.ThreadMXBean
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/21-16:44
* 描 述:
* 修订历史:
* ================================================
*/
open class BaseDoKitAsmTransformer : Transformer {
private val threadMxBean = ManagementFactory.getThreadMXBean()
private val durations = mutableMapOf<ClassTransformer, Long>()
internal val transformers: Collection<ClassTransformer>
/**
* For unit test only
*/
constructor(vararg transformers: ClassTransformer) {
this.transformers = transformers.sortedBy {
it.javaClass.getAnnotation(Priority::class.java)?.value ?: 0
}
}
override fun onPreTransform(context: TransformContext) {
this.transformers.forEach { transformer ->
this.threadMxBean.sumCpuTime(transformer) {
transformer.onPreTransform(context)
}
}
}
override fun transform(context: TransformContext, bytecode: ByteArray): ByteArray {
return ClassWriter(ClassWriter.COMPUTE_MAXS).also { writer ->
this.transformers.fold(ClassNode().also { klass ->
ClassReader(bytecode).accept(klass, 0)
}) { klass, transformer ->
this.threadMxBean.sumCpuTime(transformer) {
transformer.transform(context, klass)
}
}.accept(writer)
}.toByteArray()
}
override fun onPostTransform(context: TransformContext) {
this.transformers.forEach { transformer ->
this.threadMxBean.sumCpuTime(transformer) {
transformer.onPostTransform(context)
}
}
val w1 = this.durations.keys.map {
it.javaClass.name.length
}.max() ?: 20
this.durations.forEach { (transformer, ns) ->
println("${transformer.javaClass.name.padEnd(w1 + 1)}: ${ns / 1000000} ms")
}
}
private fun <R> ThreadMXBean.sumCpuTime(transformer: ClassTransformer, action: () -> R): R {
val ct0 = this.currentThreadCpuTime
val result = action()
val ct1 = this.currentThreadCpuTime
durations[transformer] = durations.getOrDefault(transformer, 0) + (ct1 - ct0)
return result
}
}
package com.didichuxing.doraemonkit.plugin.asmtransformer
import com.didichuxing.doraemonkit.plugin.classtransformer.MethodStackDepTransformer
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/21-16:44
* 描 述:
* 修订历史:
* ================================================
*/
class DoKitAsmTransformer(val level: Int) : BaseDoKitAsmTransformer(MethodStackDepTransformer(level)) {
}
package com.didichuxing.doraemonkit.plugin.classtransformer
import com.didichuxing.doraemonkit.plugin.DoKitExtUtil
import com.didichuxing.doraemonkit.plugin.getMethodExitInsnNodes
import com.didichuxing.doraemonkit.plugin.isRelease
import com.didichuxing.doraemonkit.plugin.println
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(1)
@AutoService(ClassTransformer::class)
class BigImgTransformer : ClassTransformer {
override fun transform(context: TransformContext, klass: ClassNode): ClassNode {
if (context.isRelease()) {
return klass
}
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 == "<init>") && methodNode.desc != null
}.let { methodNode ->
//函数结束的地方插入
methodNode?.instructions?.getMethodExitInsnNodes()?.forEach {
"hook glide succeed: ${className}_${methodNode.name}_${methodNode.desc}".println()
methodNode.instructions?.insertBefore(it, createGlideInsnList())
}
}
}
//picasso
if (className == "com.squareup.picasso.Request") {
klass.methods.find { methodNode ->
methodNode.name == "<init>" && methodNode.desc != null
}.let { methodNode ->
//函数结束的地方插入
methodNode?.instructions?.getMethodExitInsnNodes()?.forEach {
"hook picasso succeed: ${className}_${methodNode.name}_${methodNode.desc}".println()
methodNode.instructions?.insertBefore(it, createPicassoInsnList())
}
}
}
//Fresco
if (className == "com.facebook.imagepipeline.request.ImageRequest") {
klass.methods.find { methodNode ->
methodNode.name == "<init>" && methodNode.desc != null
}.let { methodNode ->
"hook Fresco succeed: ${className}_${methodNode?.name}_${methodNode?.desc}".println()
//函数开始的地方插入
methodNode?.instructions?.insert(createFrescoInsnList())
}
}
//ImageLoader
if (className == "com.nostra13.universalimageloader.core.ImageLoadingInfo") {
klass.methods.find { methodNode ->
methodNode.name == "<init>" && methodNode.desc != null
}.let { methodNode ->
"hook ImageLoader succeed: ${className}_${methodNode?.name}_${methodNode?.desc}".println()
methodNode?.instructions?.insert(createImageLoaderInsnList())
}
}
return klass
}
/**
* 创建Glide Aop代码指令
*/
private fun createGlideInsnList(): InsnList {
return with(InsnList()) {
add(VarInsnNode(ALOAD, 0))
add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/bigimg/glide/GlideHook", "proxy", "(Ljava/lang/Object;)V", false))
this
}
}
/**
* 创建Picasso Aop代码指令
*/
private fun createPicassoInsnList(): InsnList {
return with(InsnList()) {
add(VarInsnNode(ALOAD, 0))
add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/bigimg/picasso/PicassoHook", "proxy", "(Ljava/lang/Object;)V", false))
this
}
}
/**
* 创建Fresco Aop代码指令
*/
private fun createFrescoInsnList(): InsnList {
return with(InsnList()) {
add(VarInsnNode(ALOAD, 1))
add(VarInsnNode(ALOAD, 1))
add(MethodInsnNode(INVOKEVIRTUAL, "com/facebook/imagepipeline/request/ImageRequestBuilder", "getSourceUri", "()Landroid/net/Uri;", false))
add(VarInsnNode(ALOAD, 1))
add(MethodInsnNode(INVOKEVIRTUAL, "com/facebook/imagepipeline/request/ImageRequestBuilder", "getPostprocessor", "()Lcom/facebook/imagepipeline/request/Postprocessor;", false))
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))
add(MethodInsnNode(INVOKEVIRTUAL, "com/facebook/imagepipeline/request/ImageRequestBuilder", "setPostprocessor", "(Lcom/facebook/imagepipeline/request/Postprocessor;)Lcom/facebook/imagepipeline/request/ImageRequestBuilder;", false))
this
}
}
/**
* 创建ImageLoader Aop代码指令
*/
private fun createImageLoaderInsnList(): InsnList {
return with(InsnList()) {
add(VarInsnNode(ALOAD, 6))
add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/bigimg/imageloader/ImageLoaderHook", "proxy", "(Lcom/nostra13/universalimageloader/core/listener/ImageLoadingListener;)Lcom/nostra13/universalimageloader/core/listener/ImageLoadingListener;", false))
add(VarInsnNode(ASTORE, 6))
this
}
}
}
package com.didichuxing.doraemonkit.plugin.classtransformer
import com.didichuxing.doraemonkit.plugin.DoKitExtUtil
import com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
import com.didichuxing.doraemonkit.plugin.getMethodExitInsnNodes
import com.didichuxing.doraemonkit.plugin.isRelease
import com.didichuxing.doraemonkit.plugin.println
import com.didiglobal.booster.annotations.Priority
import com.didiglobal.booster.kotlinx.asIterable
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.*
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/14-18:07
* 描 述:wiki:https://juejin.im/post/5e8d87c4f265da47ad218e6b
* 修订历史:
* ================================================
*/
@Priority(0)
@AutoService(ClassTransformer::class)
class CommTransformer : 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 (context.isRelease()) {
return klass
}
if (!DoKitExtUtil.dokitPluginSwitchOpen()) {
return klass
}
val className = klass.className
//查找DoraemonKitReal&pluginConfig方法并插入指定字节码
if (className == "com.didichuxing.doraemonkit.DoraemonKitReal") {
klass.methods?.find {
it.name == "pluginConfig"
}.let { methodNode ->
"insert map to the DoraemonKitReal pluginConfig succeed".println()
methodNode?.instructions?.insert(createPluginConfigInsnList())
}
}
//gps字节码操作
if (DoKitExtUtil.commExt.gpsSwitch) {
//插入高德地图相关字节码
if (className == "com.amap.api.location.AMapLocationClient") {
klass.methods?.find {
it.name == "setLocationListener"
}.let { methodNode ->
"hook amap succeed: ${className}_${methodNode?.name}_${methodNode?.desc}".println()
methodNode?.instructions?.insert(createAmapLocationInsnList())
}
}
//插入腾讯地图相关字节码
if (className == "com.tencent.map.geolocation.TencentLocationManager") {
//持续定位和单次定位
klass.methods?.filter {
it.name == "requestSingleFreshLocation" || it.name == "requestLocationUpdates"
}?.forEach { methodNode ->
"hook tencent map succeed: ${className}_${methodNode?.name}_${methodNode?.desc}".println()
methodNode?.instructions?.insert(createTencentLocationInsnList())
}
}
//插入百度地图相关字节码
klass.methods?.find {
it.name == "onReceiveLocation" && it.desc == "(Lcom/baidu/location/BDLocation;)V"
}.let { methodNode ->
"hook baidu map succeed: ${className}_${methodNode?.name}_${methodNode?.desc}".println()
methodNode?.instructions?.insert(createBaiduLocationInsnList())
}
}
//网络 OkHttp&didi platform aop
if (DoKitExtUtil.commExt.networkSwitch) {
//okhttp
if (className == "okhttp3.OkHttpClient\$Builder") {
//空参数的构造方法
klass.methods?.find {
it.name == "<init>" && it.desc == "()V"
}.let { zeroConsMethodNode ->
"hook OkHttp succeed: ${className}_${zeroConsMethodNode?.name}_${zeroConsMethodNode?.desc}".println()
zeroConsMethodNode?.instructions?.getMethodExitInsnNodes()?.forEach {
zeroConsMethodNode.instructions.insertBefore(it, createOkHttpZeroConsInsnList())
}
}
//一个参数的构造方法
klass.methods?.find {
it.name == "<init>" && it.desc == "(Lokhttp3/OkHttpClient;)V"
}.let { oneConsMethodNode ->
"hook OkHttp succeed: ${className}_${oneConsMethodNode?.name}_${oneConsMethodNode?.desc}".println()
oneConsMethodNode?.instructions?.getMethodExitInsnNodes()?.forEach {
oneConsMethodNode.instructions.insertBefore(it, createOkHttpOneConsInsnList())
}
}
}
//didi platform
if (className == "didihttp/DidiHttpClient\$Builder") {
//空参数的构造方法
klass.methods?.find {
it.name == "<init>" && it.desc == "()V"
}.let { zeroConsMethodNode ->
"hook didi http succeed: ${className}_${zeroConsMethodNode?.name}_${zeroConsMethodNode?.desc}".println()
zeroConsMethodNode?.instructions?.getMethodExitInsnNodes()?.forEach {
zeroConsMethodNode.instructions.insertBefore(it, createDidiHttpZeroConsInsnList())
}
}
//一个参数的构造方法
klass.methods?.find {
it.name == "<init>" && it.desc == "(Ldidihttp/DidiHttpClient;)V"
}.let { oneConsMethodNode ->
"hook didi http succeed: ${className}_${oneConsMethodNode?.name}_${oneConsMethodNode?.desc}".println()
oneConsMethodNode?.instructions?.getMethodExitInsnNodes()?.forEach {
oneConsMethodNode.instructions.insertBefore(it, createDidiHttpOneConsInsnList())
}
}
}
// url connection
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
}
/**
* 创建pluginConfig代码指令
*/
private fun createPluginConfigInsnList(): InsnList {
//val insnList = InsnList()
return with(InsnList()) {
//new HashMap
add(TypeInsnNode(NEW, "java/util/HashMap"))
add(InsnNode(DUP))
add(MethodInsnNode(INVOKESPECIAL, "java/util/HashMap", "<init>", "()V", false))
//保存变量
add(VarInsnNode(ASTORE, 0))
//获取第一个变量
//put("dokitPluginSwitch",true)
add(VarInsnNode(ALOAD, 0))
add(LdcInsnNode("dokitPluginSwitch"))
add(InsnNode(if (DoKitExtUtil.dokitPluginSwitchOpen()) ICONST_1 else ICONST_0))
add(MethodInsnNode(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false))
add(MethodInsnNode(INVOKEINTERFACE, "java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true))
add(InsnNode(POP))
//put("gpsSwitch",true)
add(VarInsnNode(ALOAD, 0))
add(LdcInsnNode("gpsSwitch"))
add(InsnNode(if (DoKitExtUtil.commExt.gpsSwitch) ICONST_1 else ICONST_0))
add(MethodInsnNode(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false))
add(MethodInsnNode(INVOKEINTERFACE, "java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true))
add(InsnNode(POP))
//put("networkSwitch",true)
add(VarInsnNode(ALOAD, 0))
add(LdcInsnNode("networkSwitch"))
add(InsnNode(if (DoKitExtUtil.commExt.networkSwitch) ICONST_1 else ICONST_0))
add(MethodInsnNode(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false))
add(MethodInsnNode(INVOKEINTERFACE, "java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true))
add(InsnNode(POP))
//put("bigImgSwitch",true)
add(VarInsnNode(ALOAD, 0))
add(LdcInsnNode("bigImgSwitch"))
add(InsnNode(if (DoKitExtUtil.commExt.bigImgSwitch) ICONST_1 else ICONST_0))
add(MethodInsnNode(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false))
add(MethodInsnNode(INVOKEINTERFACE, "java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true))
add(InsnNode(POP))
//put("methodSwitch",true)
add(VarInsnNode(ALOAD, 0))
add(LdcInsnNode("methodSwitch"))
add(InsnNode(if (DoKitExtUtil.dokitSlowMethodSwitchOpen()) ICONST_1 else ICONST_0))
add(MethodInsnNode(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false))
add(MethodInsnNode(INVOKEINTERFACE, "java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true))
add(InsnNode(POP))
//put("methodStrategy",0)
add(VarInsnNode(ALOAD, 0))
add(LdcInsnNode("methodStrategy"))
add(InsnNode(if (DoKitExtUtil.mSlowMethodStrategy == SlowMethodExt.STRATEGY_STACK) ICONST_0 else ICONST_1))
add(MethodInsnNode(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false))
add(MethodInsnNode(INVOKEINTERFACE, "java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true))
add(InsnNode(POP))
//将HashMap注入到DokitPluginConfig中
add(VarInsnNode(ALOAD, 0))
add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/DokitPluginConfig", "inject", "(Ljava/util/Map;)V", false))
this
}
//return insnList
}
/**
* 创建Amap地图代码指令
*/
private fun createAmapLocationInsnList(): InsnList {
return with(InsnList()) {
//在AMapLocationClient的setLocationListener方法之中插入自定义代理回调类
add(TypeInsnNode(NEW, "com/didichuxing/doraemonkit/aop/AMapLocationListenerProxy"))
add(InsnNode(DUP))
//访问第一个参数
add(VarInsnNode(ALOAD, 1))
add(MethodInsnNode(INVOKESPECIAL, "com/didichuxing/doraemonkit/aop/AMapLocationListenerProxy", "<init>", "(Lcom/amap/api/location/AMapLocationListener;)V", false))
//对第一个参数进行重新赋值
add(VarInsnNode(ASTORE, 1))
this
}
}
/**
* 创建tencent地图代码指令
*/
private fun createTencentLocationInsnList(): InsnList {
return with(InsnList()) {
//在AMapLocationClient的setLocationListener方法之中插入自定义代理回调类
add(TypeInsnNode(NEW, "com/didichuxing/doraemonkit/aop/TencentLocationListenerProxy"))
add(InsnNode(DUP))
//访问第一个参数
add(VarInsnNode(ALOAD, 2))
add(MethodInsnNode(INVOKESPECIAL, "com/didichuxing/doraemonkit/aop/TencentLocationListenerProxy", "<init>", "(Lcom/tencent/map/geolocation/TencentLocationListener;)V", false))
//对第一个参数进行重新赋值
add(VarInsnNode(ASTORE, 2))
this
}
}
/**
* 创建百度地图代码指令
*/
private fun createBaiduLocationInsnList(): InsnList {
return with(InsnList()) {
//在AMapLocationClient的setLocationListener方法之中插入自定义代理回调类
add(VarInsnNode(ALOAD, 1))
add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/BDLocationUtil", "proxy", "(Lcom/baidu/location/BDLocation;)Lcom/baidu/location/BDLocation;", false))
//对第一个参数进行重新赋值
add(VarInsnNode(ASTORE, 1))
this
}
}
/**
* 创建Okhttp Build 空参数构造函数指令
*/
private fun createOkHttpZeroConsInsnList(): InsnList {
return with(InsnList()) {
//插入application 拦截器
add(VarInsnNode(ALOAD, 0))
add(FieldInsnNode(GETFIELD, "okhttp3/OkHttpClient\$Builder", "interceptors", "Ljava/util/List;"))
add(FieldInsnNode(GETSTATIC, "com/didichuxing/doraemonkit/aop/OkHttpHook", "globalInterceptors", "Ljava/util/List;"))
add(MethodInsnNode(INVOKEINTERFACE, "java/util/List", "addAll", "(Ljava/util/Collection;)Z", true))
add(InsnNode(POP))
//插入NetworkInterceptor 拦截器
add(VarInsnNode(ALOAD, 0))
add(FieldInsnNode(GETFIELD, "okhttp3/OkHttpClient\$Builder", "networkInterceptors", "Ljava/util/List;"))
add(FieldInsnNode(GETSTATIC, "com/didichuxing/doraemonkit/aop/OkHttpHook", "globalNetworkInterceptors", "Ljava/util/List;"))
add(MethodInsnNode(INVOKEINTERFACE, "java/util/List", "addAll", "(Ljava/util/Collection;)Z", true))
add(InsnNode(POP))
this
}
}
/**
* 创建Okhttp Build 一个参数构造函数指令
*/
private fun createOkHttpOneConsInsnList(): InsnList {
return with(InsnList()) {
add(VarInsnNode(ALOAD, 0))
add(VarInsnNode(ALOAD, 1))
add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/OkHttpHook", "performOkhttpOneParamBuilderInit", "(Ljava/lang/Object;Ljava/lang/Object;)V", false))
this
}
}
/**
* 创建didiClient Build 空参数构造函数指令
*/
private fun createDidiHttpZeroConsInsnList(): InsnList {
return with(InsnList()) {
//插入application 拦截器
add(VarInsnNode(ALOAD, 0))
add(FieldInsnNode(GETFIELD, "didihttp/DidiHttpClient\$Builder", "interceptors", "Ljava/util/List;"))
add(FieldInsnNode(GETSTATIC, "com/didichuxing/foundation/net/rpc/http/PlatformHttpHook", "globalInterceptors", "Ljava/util/List;"))
add(MethodInsnNode(INVOKEINTERFACE, "java/util/List", "addAll", "(Ljava/util/Collection;)Z", true))
add(InsnNode(POP))
//插入NetworkInterceptor 拦截器
add(VarInsnNode(ALOAD, 0))
add(FieldInsnNode(GETFIELD, "didihttp/DidiHttpClient\$Builder", "networkInterceptors", "Ljava/util/List;"))
add(FieldInsnNode(GETSTATIC, "com/didichuxing/foundation/net/rpc/http/PlatformHttpHook", "globalNetworkInterceptors", "Ljava/util/List;"))
add(MethodInsnNode(INVOKEINTERFACE, "java/util/List", "addAll", "(Ljava/util/Collection;)Z", true))
add(InsnNode(POP))
this
}
}
/**
* 创建didiClient Build 一个参数构造函数指令
*/
private fun createDidiHttpOneConsInsnList(): InsnList {
return with(InsnList()) {
add(VarInsnNode(ALOAD, 0))
add(VarInsnNode(ALOAD, 1))
add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/foundation/net/rpc/http/PlatformHttpHook", "performDidiHttpOneParamBuilderInit", "(Ljava/lang/Object;Ljava/lang/Object;)V", false))
this
}
}
}
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin.classtransformer
import com.didichuxing.doraemonkit.plugin.*
import com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
import com.didichuxing.doraemonkit.plugin.stack_method.MethodStackNode
import com.didichuxing.doraemonkit.plugin.stack_method.MethodStackNodeUtil
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
* 修订历史:不要指定自动注入 需要手动在DoKitAsmTransformer中通过配置创建
* 原理:transform()方法的调用是无序的 原因:哪一个class会先被transformer执行是不确定的 但是每一个class被transformer执行顺序是遵循transformer的Priority规则的
* ================================================
*/
@Priority(3)
@AutoService(ClassTransformer::class)
class EnterMethodStackTransformer : ClassTransformer {
private val thresholdTime = DoKitExtUtil.slowMethodExt.stackMethod.thresholdTime
private val level = 0
override fun transform(context: TransformContext, klass: ClassNode): ClassNode {
if (context.isRelease()) {
return klass
}
if (!DoKitExtUtil.dokitPluginSwitchOpen()) {
return klass
}
if (!DoKitExtUtil.dokitSlowMethodSwitchOpen()) {
return klass
}
if (DoKitExtUtil.mSlowMethodStrategy == SlowMethodExt.STRATEGY_NORMAL) {
return klass
}
if (DoKitExtUtil.ignorePackageNames(klass.className)) {
return klass
}
//默认为Application onCreate 和attachBaseContext
val enterMethods = DoKitExtUtil.slowMethodExt.stackMethod.enterMethods
//找不到配置的Application
if (enterMethods.isEmpty()) {
val superName = klass.superName
//先判断父类
if (superName.isNotEmpty() && (superName == "android/app/Application" ||
superName == "android/support/multidex/MultiDexApplication" ||
superName == "androidx/multidex/MultiDexApplication")) {
klass.methods.filter { methodNode ->
(methodNode.name == "onCreate" && methodNode.desc == "()V") || (methodNode.name == "attachBaseContext" && methodNode.desc == "(Landroid/content/Context;)V")
}.let { methodNodes ->
//读取全是函数调用的Insn
methodNodes.forEach { methodNode ->
operateMethodInsn(klass, methodNode)
}
}
}
} else {
enterMethods.forEach { enterMethodName ->
klass.methods.forEach { methodNode ->
val allMethodName = "${klass.className}.${methodNode.name}"
if (allMethodName == enterMethodName) {
"level===>$level mathched enterMethod===>$allMethodName".println()
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
return with(InsnList()) {
if (isStaticMethod) {
add(FieldInsnNode(GETSTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "INSTANCE", "Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"))
add(IntInsnNode(BIPUSH, DoKitExtUtil.mStackMethodLevel))
add(IntInsnNode(BIPUSH, thresholdTime))
add(IntInsnNode(BIPUSH, level))
add(LdcInsnNode(className))
add(LdcInsnNode(methodName))
add(LdcInsnNode(desc))
add(MethodInsnNode(INVOKEVIRTUAL, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "recodeStaticMethodCostStart", "(IIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", false))
} else {
add(FieldInsnNode(GETSTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "INSTANCE", "Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"))
add(IntInsnNode(BIPUSH, DoKitExtUtil.mStackMethodLevel))
add(IntInsnNode(BIPUSH, thresholdTime))
add(IntInsnNode(BIPUSH, level))
add(LdcInsnNode(className))
add(LdcInsnNode(methodName))
add(LdcInsnNode(desc))
add(VarInsnNode(ALOAD, 0))
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))
}
this
}
}
/**
* 创建慢函数退出时的指令集
*/
private fun createMethodExitInsnList(level: Int, className: String, methodName: String, desc: String, access: Int): InsnList {
val isStaticMethod = access and ACC_STATIC != 0
return with(InsnList()) {
if (isStaticMethod) {
add(FieldInsnNode(GETSTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "INSTANCE", "Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"))
add(IntInsnNode(BIPUSH, thresholdTime))
add(IntInsnNode(BIPUSH, level))
add(LdcInsnNode(className))
add(LdcInsnNode(methodName))
add(LdcInsnNode(desc))
add(MethodInsnNode(INVOKEVIRTUAL, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "recodeStaticMethodCostEnd", "(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", false))
} else {
add(FieldInsnNode(GETSTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "INSTANCE", "Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"))
add(IntInsnNode(BIPUSH, thresholdTime))
add(IntInsnNode(BIPUSH, level))
add(LdcInsnNode(className))
add(LdcInsnNode(methodName))
add(LdcInsnNode(desc))
add(VarInsnNode(ALOAD, 0))
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))
}
this
}
}
}
package com.didichuxing.doraemonkit.plugin.classtransformer
import com.didichuxing.doraemonkit.plugin.*
import com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
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(2)
@AutoService(ClassTransformer::class)
class GlobalSlowMethodTransformer : ClassTransformer {
val thresholdTime = DoKitExtUtil.slowMethodExt.normalMethod.thresholdTime
override fun transform(context: TransformContext, klass: ClassNode): ClassNode {
if (context.isRelease()) {
return klass
}
if (!DoKitExtUtil.dokitPluginSwitchOpen()) {
return klass
}
if (!DoKitExtUtil.dokitSlowMethodSwitchOpen()) {
return klass
}
if (DoKitExtUtil.mSlowMethodStrategy == 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>" &&
!methodNode.isEmptyMethod() &&
!methodNode.isSingleMethod() &&
!methodNode.isGetSetMethod()
}.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.getMethodExitInsnNodes()?.forEach { 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
return with(InsnList()) {
if (isStaticMethod) {
add(FieldInsnNode(GETSTATIC, "com/didichuxing/doraemonkit/aop/MethodCostUtil", "INSTANCE", "Lcom/didichuxing/doraemonkit/aop/MethodCostUtil;"))
add(IntInsnNode(SIPUSH, thresholdTime))
add(LdcInsnNode("$className&$methodName"))
add(MethodInsnNode(INVOKEVIRTUAL, "com/didichuxing/doraemonkit/aop/MethodCostUtil", "recodeStaticMethodCostStart", "(ILjava/lang/String;)V", false))
} else {
add(FieldInsnNode(GETSTATIC, "com/didichuxing/doraemonkit/aop/MethodCostUtil", "INSTANCE", "Lcom/didichuxing/doraemonkit/aop/MethodCostUtil;"))
add(IntInsnNode(SIPUSH, thresholdTime))
add(LdcInsnNode("$className&$methodName"))
add(VarInsnNode(ALOAD, 0))
add(MethodInsnNode(INVOKEVIRTUAL, "com/didichuxing/doraemonkit/aop/MethodCostUtil", "recodeObjectMethodCostStart", "(ILjava/lang/String;Ljava/lang/Object;)V", false))
}
this
}
}
/**
* 创建慢函数退出时的指令集
*/
private fun createMethodExitInsnList(className: String, methodName: String, access: Int): InsnList {
val isStaticMethod = access and ACC_STATIC != 0
return with(InsnList()) {
if (isStaticMethod) {
add(FieldInsnNode(GETSTATIC, "com/didichuxing/doraemonkit/aop/MethodCostUtil", "INSTANCE", "Lcom/didichuxing/doraemonkit/aop/MethodCostUtil;"))
add(IntInsnNode(SIPUSH, thresholdTime))
add(LdcInsnNode("$className&$methodName"))
add(MethodInsnNode(INVOKEVIRTUAL, "com/didichuxing/doraemonkit/aop/MethodCostUtil", "recodeStaticMethodCostEnd", "(ILjava/lang/String;)V", false))
} else {
add(FieldInsnNode(GETSTATIC, "com/didichuxing/doraemonkit/aop/MethodCostUtil", "INSTANCE", "Lcom/didichuxing/doraemonkit/aop/MethodCostUtil;"))
add(IntInsnNode(SIPUSH, thresholdTime))
add(LdcInsnNode("$className&$methodName"))
add(VarInsnNode(ALOAD, 0))
add(MethodInsnNode(INVOKEVIRTUAL, "com/didichuxing/doraemonkit/aop/MethodCostUtil", "recodeObjectMethodCostEnd", "(ILjava/lang/String;Ljava/lang/Object;)V", false))
}
this
}
}
}
package com.didichuxing.doraemonkit.plugin.classtransformer
import com.didichuxing.doraemonkit.plugin.*
import com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
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.ClassTransformer
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) : ClassTransformer {
private val thresholdTime = DoKitExtUtil.slowMethodExt.stackMethod.thresholdTime
override fun transform(context: TransformContext, klass: ClassNode): ClassNode {
if (context.isRelease()) {
return klass
}
if (!DoKitExtUtil.dokitPluginSwitchOpen()) {
return klass
}
if (!DoKitExtUtil.dokitSlowMethodSwitchOpen()) {
return klass
}
if (DoKitExtUtil.mSlowMethodStrategy == SlowMethodExt.STRATEGY_NORMAL) {
return klass
}
if (DoKitExtUtil.ignorePackageNames(klass.className)) {
return klass
}
val methodStackKeys: MutableSet<String> = MethodStackNodeUtil.METHOD_STACK_KEYS[level - 1]
klass.methods.filter { methodNode ->
methodNode.name != "<init>" &&
!methodNode.isEmptyMethod() &&
!methodNode.isSingleMethod() &&
!methodNode.isGetSetMethod()
}.forEach { methodNode ->
val key = "${klass.className}&${methodNode.name}&${methodNode.desc}"
if (methodStackKeys.contains(key)) {
"level===>$level mathched key===>$key".println()
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
return with(InsnList()) {
if (isStaticMethod) {
add(FieldInsnNode(GETSTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "INSTANCE", "Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"))
add(IntInsnNode(BIPUSH, DoKitExtUtil.mStackMethodLevel))
add(IntInsnNode(BIPUSH, thresholdTime))
add(IntInsnNode(BIPUSH, level))
add(LdcInsnNode(className))
add(LdcInsnNode(methodName))
add(LdcInsnNode(desc))
add(MethodInsnNode(INVOKEVIRTUAL, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "recodeStaticMethodCostStart", "(IIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", false))
} else {
add(FieldInsnNode(GETSTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "INSTANCE", "Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"))
add(IntInsnNode(BIPUSH, DoKitExtUtil.mStackMethodLevel))
add(IntInsnNode(BIPUSH, thresholdTime))
add(IntInsnNode(BIPUSH, level))
add(LdcInsnNode(className))
add(LdcInsnNode(methodName))
add(LdcInsnNode(desc))
add(VarInsnNode(ALOAD, 0))
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))
}
this
}
}
/**
* 创建慢函数退出时的指令集
*/
private fun createMethodExitInsnList(level: Int, className: String, methodName: String, desc: String, access: Int): InsnList {
val isStaticMethod = access and ACC_STATIC != 0
return with(InsnList()) {
if (isStaticMethod) {
add(FieldInsnNode(GETSTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "INSTANCE", "Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"))
add(IntInsnNode(BIPUSH, thresholdTime))
add(IntInsnNode(BIPUSH, level))
add(LdcInsnNode(className))
add(LdcInsnNode(methodName))
add(LdcInsnNode(desc))
add(MethodInsnNode(INVOKEVIRTUAL, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "recodeStaticMethodCostEnd", "(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", false))
} else {
add(FieldInsnNode(GETSTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "INSTANCE", "Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"))
add(IntInsnNode(BIPUSH, thresholdTime))
add(IntInsnNode(BIPUSH, level))
add(LdcInsnNode(className))
add(LdcInsnNode(methodName))
add(LdcInsnNode(desc))
add(VarInsnNode(ALOAD, 0))
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))
}
this
}
}
}
package com.didichuxing.doraemonkit.plugin.classtransformer
import com.didichuxing.doraemonkit.plugin.DoKitExtUtil
import com.didichuxing.doraemonkit.plugin.isRelease
import com.didichuxing.doraemonkit.plugin.println
import com.didiglobal.booster.annotations.Priority
import com.didiglobal.booster.kotlinx.asIterable
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.INVOKESTATIC
import org.objectweb.asm.Opcodes.INVOKEVIRTUAL
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.MethodInsnNode
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/14-18:07
* 描 述:wiki:https://juejin.im/post/5e8d87c4f265da47ad218e6b
* 修订历史:
* ================================================
*/
//@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 (context.isRelease()) {
return klass
}
if (!DoKitExtUtil.dokitPluginSwitchOpen()) {
return klass
}
if (!DoKitExtUtil.commExt.networkSwitch) {
return klass
}
if (DoKitExtUtil.ignorePackageNames(klass.className)) {
return klass
}
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
}
}
package com.didichuxing.doraemonkit.plugin.extension
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/4/28-14:56
* 描 述:
* 修订历史:
* ================================================
*/
open class CommExt(var gpsSwitch: Boolean = true,
var networkSwitch: Boolean = true,
var bigImgSwitch: Boolean = true) {
fun gpsSwitch(gpsSwitch: Boolean) {
this.gpsSwitch = gpsSwitch
}
fun networkSwitch(networkSwitch: Boolean) {
this.networkSwitch = networkSwitch
}
fun bigImgSwitch(bigImgSwitch: Boolean) {
this.bigImgSwitch = bigImgSwitch
}
override fun toString(): String {
return "CommExt(gpsSwitch=$gpsSwitch, networkSwitch=$networkSwitch, bigImgSwitch=$bigImgSwitch)"
}
}
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin.extension
import org.gradle.api.Action
/**
* Created by jint on 07/10/2018.
*/
open class DoKitExt(var dokitPluginSwitch: Boolean = true,
var dokitLogSwitch: Boolean = false,
var comm: CommExt = CommExt(),
var slowMethod: SlowMethodExt = SlowMethodExt()) {
//方法名必须和插件配置一直才能进行反射注入
fun dokitPluginSwitch(dokitPluginSwitch: Boolean) {
this.dokitPluginSwitch = dokitPluginSwitch
}
fun dokitLogSwitch(dokitLogSwitch: Boolean) {
this.dokitLogSwitch = dokitLogSwitch
}
/**
* 让comm 支持 DSL 语法
*
* @param action
*/
fun comm(action: Action<CommExt>) {
action.execute(comm)
}
/**
* 让slowMethod 支持 DSL 语法
*
* @param action
*/
fun slowMethod(action: Action<SlowMethodExt>) {
action.execute(slowMethod)
}
override fun toString(): String {
return "DoKitExt(dokitPluginSwitch=$dokitPluginSwitch, dokitLogSwitch=$dokitLogSwitch, comm=$comm, slowMethod=$slowMethod)"
}
}
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin.extension
import groovy.lang.Closure
import org.gradle.util.ConfigureUtil
import java.util.*
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/4/28-14:56
* 描 述:
* 修订历史:
* ================================================
*/
open class SlowMethodExt(
//0:打印函数调用栈 1:普通模式 运行时打印某个函数的耗时 全局业务代码函数插入
@Deprecated("已弃用,请在项目根目录的gradle.properties中通过DOKIT_METHOD_STRATEGY=0|1 来控制")
var strategy: Int = STRATEGY_STACK,
//函数功能开关
@Deprecated("已弃用,请在项目根目录的gradle.properties中通过DoKit_METHOD_SWITCH=true|false 来控制")
var methodSwitch: Boolean = false,
//函数调用栈模式
var stackMethod: StackMethodExt = StackMethodExt(),
//普通模式
var normalMethod: NormalMethodExt = NormalMethodExt()) {
/**
* 函数功能开关
*/
fun strategy(strategy: Int) {
this.strategy = strategy
}
fun methodSwitch(methodSwitch: Boolean) {
this.methodSwitch = methodSwitch
}
fun stackMethod(closure: Closure<StackMethodExt?>?) {
ConfigureUtil.configure(closure, stackMethod)
}
fun normalMethod(closure: Closure<NormalMethodExt?>?) {
ConfigureUtil.configure(closure, normalMethod)
}
class StackMethodExt(
//默认阈值为5ms
var thresholdTime: Int = 5,
//入口函集合
var enterMethods: MutableSet<String> = mutableSetOf()) {
/**
* 默认值为5ms
*/
fun thresholdTime(thresholdTime: Int) {
this.thresholdTime = thresholdTime
}
fun normalMethod(enterMethods: MutableSet<String>) {
this.enterMethods = enterMethods
}
override fun toString(): String {
return "StackMethodExt(thresholdTime=$thresholdTime, enterMethods=$enterMethods)"
}
}
class NormalMethodExt(
//默认阈值为500ms
var thresholdTime: Int = 500,
//普通函数的插装包名集合
var packageNames: MutableSet<String> = mutableSetOf(),
//插桩黑名单
var methodBlacklist: MutableSet<String> = mutableSetOf()) {
/**
* 默认值为500ms
*/
fun thresholdTime(thresholdTime: Int) {
this.thresholdTime = thresholdTime
}
fun packageNames(packageNames: MutableSet<String>) {
this.packageNames = packageNames
}
fun methodBlacklist(methodBlacklist: MutableSet<String>) {
this.methodBlacklist = methodBlacklist
}
override fun toString(): String {
return "NormalMethodExt{" +
"thresholdTime=" + thresholdTime +
", packageNames=" + packageNames +
", methodBlacklist=" + methodBlacklist +
'}'
}
}
override fun toString(): String {
return "SlowMethodExt{" +
"strategy=" + strategy +
", methodSwitch=" + methodSwitch +
", stackMethod=" + stackMethod +
", normalMethod=" + normalMethod +
'}'
}
companion object {
const val STRATEGY_STACK = 0
const val STRATEGY_NORMAL = 1
}
}
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin.processor
import com.android.build.gradle.AppExtension
import com.android.build.gradle.api.BaseVariant
import com.android.build.gradle.api.LibraryVariant
import com.didichuxing.doraemonkit.plugin.DoKitExtUtil
import com.didichuxing.doraemonkit.plugin.extension.DoKitExt
import com.didichuxing.doraemonkit.plugin.isRelease
import com.didichuxing.doraemonkit.plugin.println
import com.didichuxing.doraemonkit.plugin.transform.*
import com.didiglobal.booster.gradle.getAndroid
import com.didiglobal.booster.gradle.isDynamicFeature
import com.didiglobal.booster.gradle.project
import com.didiglobal.booster.gradle.variantData
import com.didiglobal.booster.task.spi.VariantProcessor
import com.didiglobal.booster.transform.ArtifactManager
import com.didiglobal.booster.transform.artifacts
import com.didiglobal.booster.transform.util.ComponentHandler
import com.google.auto.service.AutoService
import org.gradle.api.Project
import javax.xml.parsers.SAXParserFactory
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/15-11:28
* 描 述:
* 修订历史:
* ================================================
*/
@AutoService(VariantProcessor::class)
class DoKitPluginConfigProcessor : VariantProcessor {
override fun process(variant: BaseVariant) {
if (variant is LibraryVariant || variant.variantData.isDynamicFeature()) {
"error====>dokit plugin config must in the app module".println()
return
}
if (variant.isRelease()) {
return
}
//查找AndroidManifest.xml 文件路径
variant.artifacts.get(ArtifactManager.MERGED_MANIFESTS).forEach { manifest ->
val parser = SAXParserFactory.newInstance().newSAXParser()
val handler = ComponentHandler()
parser.parse(manifest, handler)
DoKitExtUtil.setApplications(handler.applications)
"applications path====>${handler.applications}".println()
}
//读取插件配置
variant.project.getAndroid<AppExtension>().let { appExt ->
//查找Application路径
val doKitExt = variant.project.extensions.getByType(DoKitExt::class.java)
DoKitExtUtil.init(doKitExt, appExt.defaultConfig.applicationId)
}
}
}
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin.stack_method
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/20-16:50
* 描 述:
* 修订历史:
* ================================================
*/
data class MethodStackNode(var level: Int,
var className: String,
var methodName: String,
var desc: String,
var parentClassName: String,
var parentMethodName: String,
var parentDesc: String)
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin.stack_method
import org.gradle.internal.impldep.org.apache.commons.lang.mutable.Mutable
import java.util.*
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/20-16:58
* 描 述:
* 修订历史:
* ================================================
*/
object MethodStackNodeUtil {
val METHOD_STACK_KEYS: MutableList<MutableSet<String>> by lazy {
Collections.synchronizedList(mutableListOf<MutableSet<String>>())
}
fun addMethodStackNode(level: Int, methodStackNode: MethodStackNode) {
val key = "${methodStackNode.className}&${methodStackNode.methodName}&${methodStackNode.desc}"
METHOD_STACK_KEYS[level].add(key)
}
}
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin.transform
import com.android.build.api.transform.QualifiedContent
import com.android.build.api.transform.Transform
import com.android.build.api.transform.TransformInvocation
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.internal.pipeline.TransformManager
import com.android.build.gradle.internal.pipeline.TransformManager.SCOPE_FULL_PROJECT
import com.didichuxing.doraemonkit.plugin.DoKitTransformInvocation
import com.didiglobal.booster.gradle.SCOPE_FULL_WITH_FEATURES
import com.didiglobal.booster.gradle.SCOPE_PROJECT
import com.didiglobal.booster.gradle.getAndroid
import com.didiglobal.booster.transform.AbstractKlassPool
import com.didiglobal.booster.transform.Transformer
import com.google.common.collect.ImmutableSet
import org.gradle.api.Project
import java.util.*
/**
* Represents the transform base
* DoKitCommTransform 作用于 CommTransformer、BigImgTransformer、UrlConnectionTransformer、GlobalSlowMethodTransformer
* @author johnsonlee
*/
open class DoKitBaseTransform(val project: Project) : Transform() {
/*
* Preload transformers as List to fix NoSuchElementException caused by ServiceLoader in parallel mode
*/
internal open val transformers = ServiceLoader.load(Transformer::class.java, javaClass.classLoader).toList()
private val androidExt: BaseExtension = project.getAndroid()
private lateinit var androidKlassPool: AbstractKlassPool
init {
project.afterEvaluate {
androidKlassPool = object : AbstractKlassPool(androidExt.bootClasspath) {}
}
}
val bootKlassPool: AbstractKlassPool
get() = androidKlassPool
override fun getName() = this.javaClass.simpleName
override fun isIncremental() = true
override fun isCacheable() = true
override fun getInputTypes(): MutableSet<QualifiedContent.ContentType> = TransformManager.CONTENT_CLASS
override fun getScopes(): MutableSet<in QualifiedContent.Scope> = when {
transformers.isEmpty() -> mutableSetOf()
project.plugins.hasPlugin("com.android.library") -> SCOPE_PROJECT
project.plugins.hasPlugin("com.android.application") -> SCOPE_FULL_PROJECT
project.plugins.hasPlugin("com.android.dynamic-feature") -> SCOPE_FULL_WITH_FEATURES
else -> TODO("Not an Android project")
}
override fun getReferencedScopes(): MutableSet<in QualifiedContent.Scope> {
if (transformers.isEmpty()) {
return when {
project.plugins.hasPlugin("com.android.library") -> SCOPE_PROJECT
project.plugins.hasPlugin("com.android.application") -> SCOPE_FULL_PROJECT
project.plugins.hasPlugin("com.android.dynamic-feature") -> SCOPE_FULL_WITH_FEATURES
else -> TODO("Not an Android project")
}
}
return super.getReferencedScopes()
}
final override fun transform(invocation: TransformInvocation) {
DoKitTransformInvocation(invocation, this).apply {
if (isIncremental) {
doIncrementalTransform()
} else {
outputProvider?.deleteAll()
doFullTransform()
}
}
}
}
package com.didichuxing.doraemonkit.plugin.transform
import org.gradle.api.Project
/**
* Represents the transform base
* DoKitCommTransform 作用于 CommTransformer、BigImgTransformer、UrlConnectionTransformer、GlobalSlowMethodTransformer、EnterMethodStackTransformer
* @author johnsonlee
*/
class DoKitCommTransform(androidProject: Project) : DoKitBaseTransform(androidProject) {
}
package com.didichuxing.doraemonkit.plugin.transform
import com.didichuxing.doraemonkit.plugin.asmtransformer.DoKitAsmTransformer
import com.didiglobal.booster.transform.Transformer
import org.gradle.api.Project
/**
* Represents the transform base
* DoKitCommTransform 作用于 CommTransformer、BigImgTransformer、UrlConnectionTransformer、GlobalSlowMethodTransformer
* @author johnsonlee
*/
open class DoKitDependTransform(androidProject: Project, val level: Int) : DoKitBaseTransform(androidProject) {
internal override val transformers = mutableListOf<Transformer>(DoKitAsmTransformer(level))
override fun getName(): String {
return "${this.javaClass.simpleName}_$level"
}
}
#入口文件
#文件名为插件名 即 apply plugin "xxx"
implementation-class=com.didichuxing.doraemonkit.plugin.DoKitPlugin
\ No newline at end of file
......@@ -11,7 +11,7 @@ ext {
]
android = [compileSdkVersion : 29,
suppotrSdkVersion : "28.0.0",
applicationId : "com.didichuxing.doraemondemo",
applicationId : "com.didichuxing.doraemondemo.java",
minSdkVersion : 16,
targetSdkVersion : 29,
//app版本号
......@@ -26,7 +26,7 @@ ext {
versionName : "3.1.6",
glide_version : "4.9.0",
kotlin_version : "1.3.72",
booster_version : "1.6.0"
booster_version : "1.7.0"
]
dependencies = [// ###### android library start ######
"multidex" : 'androidx.multidex:multidex:2.0.0',
......
package com.didichuxing.doraemonkit.plugin
import com.android.build.gradle.api.BaseVariant
import com.didiglobal.booster.kotlinx.boolean
import com.didiglobal.booster.transform.TransformContext
import org.objectweb.asm.Opcodes.*
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.InsnList
import org.objectweb.asm.tree.InsnNode
import org.objectweb.asm.tree.MethodInsnNode
import org.objectweb.asm.tree.MethodNode
/**
* ================================================
......@@ -19,6 +18,58 @@ import org.objectweb.asm.tree.MethodInsnNode
* ================================================
*/
fun MethodNode.isGetSetMethod(): Boolean {
var ignoreCount = 0
val iterator = instructions.iterator()
while (iterator.hasNext()) {
val insnNode = iterator.next()
val opcode = insnNode.opcode
if (-1 == opcode) {
continue
}
if (opcode != GETFIELD && opcode != GETSTATIC && opcode != H_GETFIELD && opcode != H_GETSTATIC && opcode != RETURN && opcode != ARETURN && opcode != DRETURN && opcode != FRETURN && opcode != LRETURN && opcode != IRETURN && opcode != PUTFIELD && opcode != PUTSTATIC && opcode != H_PUTFIELD && opcode != H_PUTSTATIC && opcode > SALOAD) {
if (name.equals("<init>") && opcode == INVOKESPECIAL) {
ignoreCount++
if (ignoreCount > 1) {
return false
}
continue
}
return false
}
}
return true
}
fun MethodNode.isSingleMethod(): Boolean {
val iterator = instructions.iterator()
while (iterator.hasNext()) {
val insnNode = iterator.next()
val opcode = insnNode.opcode
if (-1 == opcode) {
continue
} else if (INVOKEVIRTUAL <= opcode && opcode <= INVOKEDYNAMIC) {
return false
}
}
return true
}
fun MethodNode.isEmptyMethod(): Boolean {
val iterator = instructions.iterator()
while (iterator.hasNext()) {
val insnNode = iterator.next()
val opcode = insnNode.opcode
return if (-1 == opcode) {
continue
} else {
false
}
}
return true
}
fun InsnList.getMethodExitInsnNodes(): Sequence<InsnNode>? {
return this.iterator()?.asSequence()?.filterIsInstance(InsnNode::class.java)?.filter {
it.opcode == RETURN ||
......
......@@ -3,8 +3,6 @@ package com.didichuxing.doraemonkit.plugin
import com.didichuxing.doraemonkit.plugin.extension.CommExt
import com.didichuxing.doraemonkit.plugin.extension.DoKitExt
import com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
import com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt.Companion.STRATEGY_NORMAL
import com.didichuxing.doraemonkit.plugin.stack_method.MethodStackNodeUtil
/**
* ================================================
......@@ -34,6 +32,12 @@ object DoKitExtUtil {
*/
public var mSlowMethodSwitch = false
/**
* 慢函数策略 默认为函数调用栈策略
*/
public var mSlowMethodStrategy = SlowMethodExt.STRATEGY_STACK
private val applications: MutableSet<String> = mutableSetOf()
var commExt = CommExt()
private set
......@@ -62,8 +66,8 @@ object DoKitExtUtil {
mDokitLogSwitch = dokitEx.dokitLogSwitch
//设置普通的配置
commExt = dokitEx.comm
slowMethodExt.strategy = dokitEx.slowMethod.strategy
slowMethodExt.methodSwitch = dokitEx.slowMethod.methodSwitch
//slowMethodExt.strategy = dokitEx.slowMethod.strategy
//slowMethodExt.methodSwitch = dokitEx.slowMethod.methodSwitch
/**
* ============慢函数普通策略的配置 start==========
*/
......@@ -122,29 +126,42 @@ object DoKitExtUtil {
}
fun ignorePackageNames(className: String): Boolean {
var isMatched = false
for (packageName in ignorePackageNames) {
if (className.contains(packageName)) {
isMatched = true
break
//命中白名单返回false
for (packageName in whitePackageNames) {
if (className.startsWith(packageName, true)) {
return false
}
}
return isMatched
//命中黑名单返回true
for (packageName in blackPackageNames) {
if (className.startsWith(packageName, true)) {
return true
}
}
return false
}
private val ignorePackageNames = arrayOf(
"com.didichuxing.doraemonkit.aop",
"com.didichuxing.doraemonkit.config",
"com.didichuxing.doraemonkit.constant",
"com.didichuxing.doraemonkit.datapick",
"com.didichuxing.doraemonkit.kit",
"com.didichuxing.doraemonkit.model",
"com.didichuxing.doraemonkit.okgo",
"com.didichuxing.doraemonkit.picasso",
"com.didichuxing.doraemonkit.reflection",
"com.didichuxing.doraemonkit.util",
"com.didichuxing.doraemonkit.widget",
"com.didichuxing.doraemonkit.zxing"
/**
* 白名单
*/
private val whitePackageNames = arrayOf(
"com.didichuxing.doraemonkit.DoraemonKit",
"com.didichuxing.doraemonkit.DoraemonKitReal"
)
/**
* 黑名单
*/
private val blackPackageNames = arrayOf(
"com.didichuxing.doraemonkit.",
"kotlin.",
"java.",
"android.",
"androidx."
)
fun log(tag: String, className: String, methodName: String, access: Int, desc: String, signature: String, thresholdTime: Int) {
......
......@@ -3,6 +3,7 @@ package com.didichuxing.doraemonkit.plugin
import com.android.build.gradle.AppExtension
import com.android.build.gradle.LibraryExtension
import com.didichuxing.doraemonkit.plugin.extension.DoKitExt
import com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
import com.didichuxing.doraemonkit.plugin.stack_method.MethodStackNodeUtil
import com.didichuxing.doraemonkit.plugin.transform.DoKitCommTransform
import com.didichuxing.doraemonkit.plugin.transform.DoKitDependTransform
......@@ -81,16 +82,18 @@ class DoKitPlugin : Plugin<Project> {
when {
project.plugins.hasPlugin("com.android.application") || project.plugins.hasPlugin("com.android.dynamic-feature") -> {
project.getAndroid<AppExtension>().let { androidExt ->
val slowMethodSwitch = project.getProperty("DOKIT_SLOW_METHOD_SWITCH", false)
val slowMethodSwitch = project.getProperty("DOKIT_METHOD_SWITCH", false)
val slowMethodStrategy = project.getProperty("DOKIT_METHOD_STRATEGY", 0)
val methodStackLevel = project.getProperty("DOKIT_METHOD_STACK_LEVEL", 5)
DoKitExtUtil.mSlowMethodSwitch = slowMethodSwitch
DoKitExtUtil.mSlowMethodStrategy = slowMethodStrategy
DoKitExtUtil.mStackMethodLevel = methodStackLevel
MethodStackNodeUtil.METHOD_STACK_KEYS.clear()
//注册transform
androidExt.registerTransform(DoKitCommTransform(project))
MethodStackNodeUtil.METHOD_STACK_KEYS.add(0, mutableSetOf<String>())
if (slowMethodSwitch) {
if (slowMethodSwitch && slowMethodStrategy == SlowMethodExt.STRATEGY_STACK) {
MethodStackNodeUtil.METHOD_STACK_KEYS.add(0, mutableSetOf<String>())
val methodStackRange = 1 until methodStackLevel
if (methodStackLevel > 1) {
for (index in methodStackRange) {
......
......@@ -24,7 +24,7 @@ import org.objectweb.asm.tree.VarInsnNode
* 修订历史:
* ================================================
*/
@Priority(2)
@Priority(1)
@AutoService(ClassTransformer::class)
class BigImgTransformer : ClassTransformer {
......
......@@ -6,6 +6,7 @@ import com.didichuxing.doraemonkit.plugin.getMethodExitInsnNodes
import com.didichuxing.doraemonkit.plugin.isRelease
import com.didichuxing.doraemonkit.plugin.println
import com.didiglobal.booster.annotations.Priority
import com.didiglobal.booster.kotlinx.asIterable
import com.didiglobal.booster.transform.TransformContext
import com.didiglobal.booster.transform.asm.ClassTransformer
import com.didiglobal.booster.transform.asm.className
......@@ -25,11 +26,8 @@ import org.objectweb.asm.tree.*
@Priority(0)
@AutoService(ClassTransformer::class)
class CommTransformer : ClassTransformer {
override fun onPreTransform(context: TransformContext) {
super.onPreTransform(context)
//println("CommTransformer===>${context.name}")
}
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 (context.isRelease()) {
......@@ -135,6 +133,18 @@ class CommTransformer : ClassTransformer {
}
}
// url connection
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
......@@ -198,7 +208,7 @@ class CommTransformer : ClassTransformer {
//put("methodStrategy",0)
add(VarInsnNode(ALOAD, 0))
add(LdcInsnNode("methodStrategy"))
add(InsnNode(if (DoKitExtUtil.slowMethodExt.strategy == SlowMethodExt.STRATEGY_STACK) ICONST_0 else ICONST_1))
add(InsnNode(if (DoKitExtUtil.mSlowMethodStrategy == SlowMethodExt.STRATEGY_STACK) ICONST_0 else ICONST_1))
add(MethodInsnNode(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false))
add(MethodInsnNode(INVOKEINTERFACE, "java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true))
add(InsnNode(POP))
......
......@@ -23,7 +23,7 @@ import org.objectweb.asm.tree.*
* 原理:transform()方法的调用是无序的 原因:哪一个class会先被transformer执行是不确定的 但是每一个class被transformer执行顺序是遵循transformer的Priority规则的
* ================================================
*/
@Priority(4)
@Priority(3)
@AutoService(ClassTransformer::class)
class EnterMethodStackTransformer : ClassTransformer {
......@@ -42,7 +42,7 @@ class EnterMethodStackTransformer : ClassTransformer {
return klass
}
if (DoKitExtUtil.slowMethodExt.strategy == SlowMethodExt.STRATEGY_NORMAL) {
if (DoKitExtUtil.mSlowMethodStrategy == SlowMethodExt.STRATEGY_NORMAL) {
return klass
}
......@@ -109,7 +109,7 @@ class EnterMethodStackTransformer : ClassTransformer {
val isStaticMethod = access and ACC_STATIC != 0
return with(InsnList()) {
if (isStaticMethod) {
add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "getInstance", "()Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;", false))
add(FieldInsnNode(GETSTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "INSTANCE", "Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"))
add(IntInsnNode(BIPUSH, DoKitExtUtil.mStackMethodLevel))
add(IntInsnNode(BIPUSH, thresholdTime))
add(IntInsnNode(BIPUSH, level))
......@@ -118,7 +118,7 @@ class EnterMethodStackTransformer : ClassTransformer {
add(LdcInsnNode(desc))
add(MethodInsnNode(INVOKEVIRTUAL, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "recodeStaticMethodCostStart", "(IIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", false))
} else {
add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "getInstance", "()Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;", false))
add(FieldInsnNode(GETSTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "INSTANCE", "Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"))
add(IntInsnNode(BIPUSH, DoKitExtUtil.mStackMethodLevel))
add(IntInsnNode(BIPUSH, thresholdTime))
add(IntInsnNode(BIPUSH, level))
......@@ -143,7 +143,7 @@ class EnterMethodStackTransformer : ClassTransformer {
return with(InsnList()) {
if (isStaticMethod) {
add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "getInstance", "()Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;", false))
add(FieldInsnNode(GETSTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "INSTANCE", "Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"))
add(IntInsnNode(BIPUSH, thresholdTime))
add(IntInsnNode(BIPUSH, level))
add(LdcInsnNode(className))
......@@ -151,7 +151,7 @@ class EnterMethodStackTransformer : ClassTransformer {
add(LdcInsnNode(desc))
add(MethodInsnNode(INVOKEVIRTUAL, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "recodeStaticMethodCostEnd", "(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", false))
} else {
add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "getInstance", "()Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;", false))
add(FieldInsnNode(GETSTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "INSTANCE", "Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"))
add(IntInsnNode(BIPUSH, thresholdTime))
add(IntInsnNode(BIPUSH, level))
add(LdcInsnNode(className))
......
package com.didichuxing.doraemonkit.plugin.classtransformer
import com.didichuxing.doraemonkit.plugin.DoKitExtUtil
import com.didichuxing.doraemonkit.plugin.*
import com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
import com.didichuxing.doraemonkit.plugin.getMethodExitInsnNodes
import com.didichuxing.doraemonkit.plugin.isRelease
import com.didiglobal.booster.annotations.Priority
import com.didiglobal.booster.transform.TransformContext
import com.didiglobal.booster.transform.asm.ClassTransformer
......@@ -22,7 +20,7 @@ import org.objectweb.asm.tree.*
* 修订历史:
* ================================================
*/
@Priority(3)
@Priority(2)
@AutoService(ClassTransformer::class)
class GlobalSlowMethodTransformer : ClassTransformer {
val thresholdTime = DoKitExtUtil.slowMethodExt.normalMethod.thresholdTime
......@@ -40,7 +38,7 @@ class GlobalSlowMethodTransformer : ClassTransformer {
return klass
}
if (DoKitExtUtil.slowMethodExt.strategy == SlowMethodExt.STRATEGY_STACK) {
if (DoKitExtUtil.mSlowMethodStrategy == SlowMethodExt.STRATEGY_STACK) {
return klass
}
......@@ -55,7 +53,10 @@ class GlobalSlowMethodTransformer : ClassTransformer {
//包含在白名单中且不在黑名单中
if (className.contains(packageName) && notMatchedBlackList(className)) {
klass.methods.filter { methodNode ->
methodNode.name != "<init>"
methodNode.name != "<init>" &&
!methodNode.isEmptyMethod() &&
!methodNode.isSingleMethod() &&
!methodNode.isGetSetMethod()
}.forEach { methodNode ->
methodNode.instructions.asIterable().filterIsInstance(MethodInsnNode::class.java).let { methodInsnNodes ->
if (methodInsnNodes.isNotEmpty()) {
......@@ -91,12 +92,12 @@ class GlobalSlowMethodTransformer : ClassTransformer {
val isStaticMethod = access and ACC_STATIC != 0
return with(InsnList()) {
if (isStaticMethod) {
add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/MethodCostUtil", "getInstance", "()Lcom/didichuxing/doraemonkit/aop/MethodCostUtil;", false))
add(FieldInsnNode(GETSTATIC, "com/didichuxing/doraemonkit/aop/MethodCostUtil", "INSTANCE", "Lcom/didichuxing/doraemonkit/aop/MethodCostUtil;"))
add(IntInsnNode(SIPUSH, thresholdTime))
add(LdcInsnNode("$className&$methodName"))
add(MethodInsnNode(INVOKEVIRTUAL, "com/didichuxing/doraemonkit/aop/MethodCostUtil", "recodeStaticMethodCostStart", "(ILjava/lang/String;)V", false))
} else {
add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/MethodCostUtil", "getInstance", "()Lcom/didichuxing/doraemonkit/aop/MethodCostUtil;", false))
add(FieldInsnNode(GETSTATIC, "com/didichuxing/doraemonkit/aop/MethodCostUtil", "INSTANCE", "Lcom/didichuxing/doraemonkit/aop/MethodCostUtil;"))
add(IntInsnNode(SIPUSH, thresholdTime))
add(LdcInsnNode("$className&$methodName"))
add(VarInsnNode(ALOAD, 0))
......@@ -116,12 +117,12 @@ class GlobalSlowMethodTransformer : ClassTransformer {
val isStaticMethod = access and ACC_STATIC != 0
return with(InsnList()) {
if (isStaticMethod) {
add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/MethodCostUtil", "getInstance", "()Lcom/didichuxing/doraemonkit/aop/MethodCostUtil;", false))
add(FieldInsnNode(GETSTATIC, "com/didichuxing/doraemonkit/aop/MethodCostUtil", "INSTANCE", "Lcom/didichuxing/doraemonkit/aop/MethodCostUtil;"))
add(IntInsnNode(SIPUSH, thresholdTime))
add(LdcInsnNode("$className&$methodName"))
add(MethodInsnNode(INVOKEVIRTUAL, "com/didichuxing/doraemonkit/aop/MethodCostUtil", "recodeStaticMethodCostEnd", "(ILjava/lang/String;)V", false))
} else {
add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/MethodCostUtil", "getInstance", "()Lcom/didichuxing/doraemonkit/aop/MethodCostUtil;", false))
add(FieldInsnNode(GETSTATIC, "com/didichuxing/doraemonkit/aop/MethodCostUtil", "INSTANCE", "Lcom/didichuxing/doraemonkit/aop/MethodCostUtil;"))
add(IntInsnNode(SIPUSH, thresholdTime))
add(LdcInsnNode("$className&$methodName"))
add(VarInsnNode(ALOAD, 0))
......
......@@ -37,7 +37,7 @@ class MethodStackDepTransformer(private val level: Int = 1) : ClassTransformer {
return klass
}
if (DoKitExtUtil.slowMethodExt.strategy == SlowMethodExt.STRATEGY_NORMAL) {
if (DoKitExtUtil.mSlowMethodStrategy == SlowMethodExt.STRATEGY_NORMAL) {
return klass
}
......@@ -48,7 +48,12 @@ class MethodStackDepTransformer(private val level: Int = 1) : ClassTransformer {
val methodStackKeys: MutableSet<String> = MethodStackNodeUtil.METHOD_STACK_KEYS[level - 1]
klass.methods.forEach { methodNode ->
klass.methods.filter { methodNode ->
methodNode.name != "<init>" &&
!methodNode.isEmptyMethod() &&
!methodNode.isSingleMethod() &&
!methodNode.isGetSetMethod()
}.forEach { methodNode ->
val key = "${klass.className}&${methodNode.name}&${methodNode.desc}"
if (methodStackKeys.contains(key)) {
"level===>$level mathched key===>$key".println()
......@@ -86,7 +91,7 @@ class MethodStackDepTransformer(private val level: Int = 1) : ClassTransformer {
val isStaticMethod = access and ACC_STATIC != 0
return with(InsnList()) {
if (isStaticMethod) {
add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "getInstance", "()Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;", false))
add(FieldInsnNode(GETSTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "INSTANCE", "Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"))
add(IntInsnNode(BIPUSH, DoKitExtUtil.mStackMethodLevel))
add(IntInsnNode(BIPUSH, thresholdTime))
add(IntInsnNode(BIPUSH, level))
......@@ -95,7 +100,7 @@ class MethodStackDepTransformer(private val level: Int = 1) : ClassTransformer {
add(LdcInsnNode(desc))
add(MethodInsnNode(INVOKEVIRTUAL, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "recodeStaticMethodCostStart", "(IIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", false))
} else {
add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "getInstance", "()Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;", false))
add(FieldInsnNode(GETSTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "INSTANCE", "Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"))
add(IntInsnNode(BIPUSH, DoKitExtUtil.mStackMethodLevel))
add(IntInsnNode(BIPUSH, thresholdTime))
add(IntInsnNode(BIPUSH, level))
......@@ -118,7 +123,7 @@ class MethodStackDepTransformer(private val level: Int = 1) : ClassTransformer {
val isStaticMethod = access and ACC_STATIC != 0
return with(InsnList()) {
if (isStaticMethod) {
add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "getInstance", "()Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;", false))
add(FieldInsnNode(GETSTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "INSTANCE", "Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"))
add(IntInsnNode(BIPUSH, thresholdTime))
add(IntInsnNode(BIPUSH, level))
add(LdcInsnNode(className))
......@@ -126,7 +131,7 @@ class MethodStackDepTransformer(private val level: Int = 1) : ClassTransformer {
add(LdcInsnNode(desc))
add(MethodInsnNode(INVOKEVIRTUAL, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "recodeStaticMethodCostEnd", "(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", false))
} else {
add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "getInstance", "()Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;", false))
add(FieldInsnNode(GETSTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "INSTANCE", "Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"))
add(IntInsnNode(BIPUSH, thresholdTime))
add(IntInsnNode(BIPUSH, level))
add(LdcInsnNode(className))
......
......@@ -23,8 +23,8 @@ import org.objectweb.asm.tree.MethodInsnNode
* 修订历史:
* ================================================
*/
@Priority(1)
@AutoService(ClassTransformer::class)
//@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;"
......
......@@ -16,9 +16,10 @@ import java.util.*
open class SlowMethodExt(
//0:打印函数调用栈 1:普通模式 运行时打印某个函数的耗时 全局业务代码函数插入
@Deprecated("已弃用,请在项目根目录的gradle.properties中通过DOKIT_METHOD_STRATEGY=0|1 来控制")
var strategy: Int = STRATEGY_STACK,
//函数功能开关
@Deprecated("已弃用,请在项目根目录的gradle.properties中通过DoKit_SLOW_METHOD_SWITCH=true|false 来控制")
@Deprecated("已弃用,请在项目根目录的gradle.properties中通过DoKit_METHOD_SWITCH=true|false 来控制")
var methodSwitch: Boolean = false,
//函数调用栈模式
var stackMethod: StackMethodExt = StackMethodExt(),
......
......@@ -12,8 +12,9 @@ import com.didiglobal.booster.gradle.SCOPE_PROJECT
import com.didiglobal.booster.gradle.getAndroid
import com.didiglobal.booster.transform.AbstractKlassPool
import com.didiglobal.booster.transform.Transformer
import com.google.common.collect.ImmutableSet
import org.gradle.api.Project
import java.util.ServiceLoader
import java.util.*
/**
* Represents the transform base
......@@ -56,17 +57,18 @@ open class DoKitBaseTransform(val project: Project) : Transform() {
else -> TODO("Not an Android project")
}
override fun getReferencedScopes(): MutableSet<in QualifiedContent.Scope> = when {
transformers.isEmpty() ->
when {
override fun getReferencedScopes(): MutableSet<in QualifiedContent.Scope> {
if (transformers.isEmpty()) {
return when {
project.plugins.hasPlugin("com.android.library") -> SCOPE_PROJECT
project.plugins.hasPlugin("com.android.application") -> SCOPE_FULL_PROJECT
project.plugins.hasPlugin("com.android.dynamic-feature") -> SCOPE_FULL_WITH_FEATURES
else -> TODO("Not an Android project")
}
else -> super.getReferencedScopes()
} as MutableSet<in QualifiedContent.Scope>
}
return super.getReferencedScopes()
}
final override fun transform(invocation: TransformInvocation) {
DoKitTransformInvocation(invocation, this).apply {
......
......@@ -30,7 +30,7 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${rootProject.ext.android['kotlin_version']}"
implementation 'androidx.appcompat:appcompat:1.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
......
......@@ -42,9 +42,6 @@ android {
enabled = true
}
viewBinding {
enabled = true
}
kotlinOptions {
jvmTarget = "1.8"
......
package com.didichuxing.doraemonkit.aop;
package com.didichuxing.doraemonkit.aop
import android.app.Activity;
import android.app.Application;
import android.app.Service;
import android.util.Log;
import com.didichuxing.doraemonkit.kit.timecounter.TimeCounterManager;
import java.util.concurrent.ConcurrentHashMap;
import android.app.Activity
import android.app.Application
import android.app.Service
import android.util.Log
import com.didichuxing.doraemonkit.aop.method_stack.StaticMethodObject
import com.didichuxing.doraemonkit.kit.timecounter.TimeCounterManager
import java.util.concurrent.ConcurrentHashMap
/**
* ================================================
......@@ -18,58 +17,44 @@ import java.util.concurrent.ConcurrentHashMap;
* 修订历史:
* ================================================
*/
public class MethodCostUtil {
private static final String TAG = "DOKIT_SLOW_METHOD";
/**
* key className&method
*/
private static ConcurrentHashMap<String, Long> METHOD_COSTS = new ConcurrentHashMap<>();
object MethodCostUtil {
private const val TAG = "DOKIT_SLOW_METHOD"
/**
* 静态内部类单例
* 用来标识是静态函数对象
*/
private static class Holder {
private static MethodCostUtil INSTANCE = new MethodCostUtil();
private val staticMethodObject: StaticMethodObject by lazy {
StaticMethodObject()
}
public static MethodCostUtil getInstance() {
return MethodCostUtil.Holder.INSTANCE;
}
/**
* key className&method
*/
private val METHOD_COSTS: ConcurrentHashMap<String, Long?> by lazy { ConcurrentHashMap<String, Long?>() }
public synchronized void recodeObjectMethodCostStart(int thresholdTime, String methodName, Object classObj) {
if (METHOD_COSTS == null) {
return;
}
@Synchronized
fun recodeObjectMethodCostStart(thresholdTime: Int, methodName: String, classObj: Any?) {
try {
METHOD_COSTS.put(methodName, System.currentTimeMillis());
if (classObj instanceof Application) {
String[] methods = methodName.split("&");
if (methods.length == 2) {
if (methods[1].equals("onCreate")) {
TimeCounterManager.get().onAppCreateStart();
METHOD_COSTS[methodName] = System.currentTimeMillis()
if (classObj is Application) {
val methods = methodName.split("&".toRegex()).toTypedArray()
if (methods.size == 2) {
if (methods[1] == "onCreate") {
TimeCounterManager.get().onAppCreateStart()
}
if (methods[1].equals("attachBaseContext")) {
TimeCounterManager.get().onAppAttachBaseContextStart();
if (methods[1] == "attachBaseContext") {
TimeCounterManager.get().onAppAttachBaseContextStart()
}
}
}
} catch (Exception e) {
e.printStackTrace();
} catch (e: Exception) {
e.printStackTrace()
}
}
fun recodeStaticMethodCostStart(thresholdTime: Int, methodName: String) {
recodeObjectMethodCostStart(thresholdTime, methodName, staticMethodObject)
public synchronized void recodeStaticMethodCostStart(int thresholdTime, String methodName) {
if (METHOD_COSTS == null) {
return;
}
try {
METHOD_COSTS.put(methodName, System.currentTimeMillis());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
......@@ -79,106 +64,95 @@ public class MethodCostUtil {
* @param methodName
* @param classObj 调用该函数的对象
*/
public void recodeObjectMethodCostEnd(int thresholdTime, String methodName, Object classObj) {
if (METHOD_COSTS == null) {
return;
}
synchronized (MethodCostUtil.class) {
fun recodeObjectMethodCostEnd(thresholdTime: Int, methodName: String, classObj: Any?) {
synchronized(MethodCostUtil::class.java) {
try {
if (METHOD_COSTS.containsKey(methodName)) {
long startTime = METHOD_COSTS.get(methodName);
int costTime = (int) (System.currentTimeMillis() - startTime);
METHOD_COSTS.remove(methodName);
if (classObj instanceof Application) {
val startTime = METHOD_COSTS[methodName]!!
val costTime = (System.currentTimeMillis() - startTime).toInt()
METHOD_COSTS.remove(methodName)
if (classObj is Application) {
//Application 启动时间统计
String[] methods = methodName.split("&");
if (methods.length == 2) {
if (methods[1].equals("onCreate")) {
TimeCounterManager.get().onAppCreateEnd();
val methods = methodName.split("&".toRegex()).toTypedArray()
if (methods.size == 2) {
if (methods[1] == "onCreate") {
TimeCounterManager.get().onAppCreateEnd()
}
if (methods[1].equals("attachBaseContext")) {
TimeCounterManager.get().onAppAttachBaseContextEnd();
if (methods[1] == "attachBaseContext") {
TimeCounterManager.get().onAppAttachBaseContextEnd()
}
}
//printApplicationStartTime(methodName);
} else if (classObj instanceof Activity) {
} else if (classObj is Activity) {
//Activity 启动时间统计
//printActivityStartTime(methodName);
} else if (classObj instanceof Service) {
} else if (classObj is Service) {
//service 启动时间统计
}
//如果该方法的执行时间大于1ms 则记录
if (costTime >= thresholdTime) {
String threadName = Thread.currentThread().getName();
Log.i(TAG, "================Dokit================");
Log.i(TAG, "\t methodName===>" + methodName + " threadName==>" + threadName + " thresholdTime===>" + thresholdTime + " costTime===>" + costTime);
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
for (StackTraceElement stackTraceElement : stackTraceElements) {
val threadName = Thread.currentThread().name
Log.i(TAG, "================Dokit================")
Log.i(TAG, "\t methodName===>$methodName threadName==>$threadName thresholdTime===>$thresholdTime costTime===>$costTime")
val stackTraceElements = Thread.currentThread().stackTrace
for (stackTraceElement in stackTraceElements) {
if (stackTraceElement.toString().contains("MethodCostUtil")) {
continue;
continue
}
if (stackTraceElement.toString().contains("dalvik.system.VMStack.getThreadStackTrace")) {
continue;
continue
}
if (stackTraceElement.toString().contains("java.lang.Thread.getStackTrace")) {
continue;
continue
}
Log.i(TAG, "\tat " + stackTraceElement.toString());
Log.i(TAG, "\tat $stackTraceElement")
}
}
}
} catch (Exception e) {
e.printStackTrace();
} catch (e: Exception) {
e.printStackTrace()
}
}
}
private void printApplicationStartTime(String methodName) {
Log.i(TAG, "================Dokit Application start================");
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
for (StackTraceElement stackTraceElement : stackTraceElements) {
private fun printApplicationStartTime(methodName: String) {
Log.i(TAG, "================Dokit Application start================")
val stackTraceElements = Thread.currentThread().stackTrace
for (stackTraceElement in stackTraceElements) {
if (stackTraceElement.toString().contains("MethodCostUtil")) {
continue;
continue
}
if (stackTraceElement.toString().contains("dalvik.system.VMStack.getThreadStackTrace")) {
continue;
continue
}
if (stackTraceElement.toString().contains("java.lang.Thread.getStackTrace")) {
continue;
continue
}
Log.i(TAG, "\tat " + stackTraceElement.toString());
Log.i(TAG, "\tat $stackTraceElement")
}
Log.i(TAG, "================Dokit Application end================");
Log.i(TAG, "\n");
Log.i(TAG, "================Dokit Application end================")
Log.i(TAG, "\n")
}
private void printActivityStartTime(String methodName) {
Log.i(TAG, "================Dokit Activity start================");
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
for (StackTraceElement stackTraceElement : stackTraceElements) {
private fun printActivityStartTime(methodName: String) {
Log.i(TAG, "================Dokit Activity start================")
val stackTraceElements = Thread.currentThread().stackTrace
for (stackTraceElement in stackTraceElements) {
if (stackTraceElement.toString().contains("MethodCostUtil")) {
continue;
continue
}
if (stackTraceElement.toString().contains("dalvik.system.VMStack.getThreadStackTrace")) {
continue;
continue
}
if (stackTraceElement.toString().contains("java.lang.Thread.getStackTrace")) {
continue;
continue
}
Log.i(TAG, "\tat " + stackTraceElement.toString());
Log.i(TAG, "\tat $stackTraceElement")
}
Log.i(TAG, "================Dokit Activity end================");
Log.i(TAG, "\n");
Log.i(TAG, "================Dokit Activity end================")
Log.i(TAG, "\n")
}
/**
......@@ -187,44 +161,9 @@ public class MethodCostUtil {
* @param thresholdTime 预设的值 单位为us 1000us = 1ms
* @param methodName
*/
public void recodeStaticMethodCostEnd(int thresholdTime, String methodName) {
if (METHOD_COSTS == null) {
return;
}
synchronized (MethodCostUtil.class) {
try {
if (METHOD_COSTS.containsKey(methodName)) {
long startTime = METHOD_COSTS.get(methodName);
int costTime = (int) (System.currentTimeMillis() - startTime);
METHOD_COSTS.remove(methodName);
//如果该方法的执行时间大于1ms 则记录
if (costTime >= thresholdTime) {
String threadName = Thread.currentThread().getName();
Log.i(TAG, "================Dokit================");
Log.i(TAG, "\t methodName===>" + methodName + " threadName==>" + threadName + " thresholdTime===>" + thresholdTime + " costTime===>" + costTime);
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
for (StackTraceElement stackTraceElement : stackTraceElements) {
if (stackTraceElement.toString().contains("MethodCostUtil")) {
continue;
}
if (stackTraceElement.toString().contains("dalvik.system.VMStack.getThreadStackTrace")) {
continue;
}
if (stackTraceElement.toString().contains("java.lang.Thread.getStackTrace")) {
continue;
}
Log.i(TAG, "\tat " + stackTraceElement.toString());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
fun recodeStaticMethodCostEnd(thresholdTime: Int, methodName: String) {
recodeObjectMethodCostEnd(thresholdTime, methodName, staticMethodObject)
}
}
}
\ No newline at end of file
package com.didichuxing.doraemonkit.aop.method_stack;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import java.util.ArrayList;
import java.util.List;
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/4/23-11:21
* 描 述:
* 修订历史:
* ================================================
*/
public class MethodInvokNode {
private MethodInvokNode parent;
private long startTimeMillis;
private long endTimeMillis;
private int costTimeMillis;
private String currentThreadName;
private String className;
private String methodName;
private int level;
private List<MethodInvokNode> children = new ArrayList<>();
public MethodInvokNode getParent() {
return parent;
}
public void setParent(MethodInvokNode parent) {
this.parent = parent;
}
public long getStartTimeMillis() {
return startTimeMillis;
}
public void setStartTimeMillis(long startTimeMillis) {
this.startTimeMillis = startTimeMillis;
}
public long getEndTimeMillis() {
return endTimeMillis;
}
public void setEndTimeMillis(long endTimeMillis) {
this.endTimeMillis = endTimeMillis;
this.costTimeMillis = (int) (endTimeMillis - startTimeMillis);
}
public int getCostTimeMillis() {
return (int) (endTimeMillis - startTimeMillis);
}
public String getCurrentThreadName() {
return currentThreadName;
}
public void setCurrentThreadName(String currentThreadName) {
this.currentThreadName = currentThreadName;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public void addChild(MethodInvokNode methodInvokNode) {
if (children != null) {
children.add(methodInvokNode);
}
}
public void setCostTimeMillis(int costTimeMillis) {
this.costTimeMillis = costTimeMillis;
}
public List<MethodInvokNode> getChildren() {
return children;
}
public void setChildren(List<MethodInvokNode> children) {
this.children = children;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
}
package com.didichuxing.doraemonkit.aop.method_stack
import java.util.*
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/4/23-11:21
* 描 述:
* 修订历史:
* ================================================
*/
class MethodInvokNode {
var parent: MethodInvokNode? = null
var startTimeMillis: Long = 0
private var endTimeMillis: Long = 0
private var costTimeMillis = 0
var currentThreadName: String? = null
var className: String? = null
var methodName: String? = null
var level = 0
var children: MutableList<MethodInvokNode> = mutableListOf()
fun getEndTimeMillis(): Long {
return endTimeMillis
}
fun setEndTimeMillis(endTimeMillis: Long) {
this.endTimeMillis = endTimeMillis
costTimeMillis = (endTimeMillis - startTimeMillis).toInt()
}
fun getCostTimeMillis(): Int {
return (endTimeMillis - startTimeMillis).toInt()
}
fun addChild(methodInvokNode: MethodInvokNode) {
children.add(methodInvokNode)
}
fun setCostTimeMillis(costTimeMillis: Int) {
this.costTimeMillis = costTimeMillis
}
}
\ No newline at end of file
package com.didichuxing.doraemonkit.aop.method_stack;
import java.util.List;
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/4/23-17:42
* 描 述:
* 修订历史:
* ================================================
*/
public class MethodStackBean {
String function;
String costTime;
List<MethodStackBean> children;
public String getFunction() {
return function;
}
public void setFunction(String function) {
this.function = function;
}
public String getCostTime() {
return costTime;
}
public void setCostTime(int costTime) {
this.costTime = costTime + "ms";
}
public List<MethodStackBean> getChildren() {
return children;
}
public void setChildren(List<MethodStackBean> children) {
this.children = children;
}
@Override
public String toString() {
return "MethodStackBean{" +
"function='" + function + '\'' +
", costTime=" + costTime +
", children=" + children +
'}';
}
}
package com.didichuxing.doraemonkit.aop.method_stack
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/4/23-17:42
* 描 述:
* 修订历史:
* ================================================
*/
class MethodStackBean {
var function: String? = null
var costTime: String? = null
var children: MutableList<MethodStackBean>? = null
fun setCostTime(costTime: Int) {
this.costTime = costTime.toString() + "ms"
}
}
\ No newline at end of file
package com.didichuxing.doraemonkit.aop.method_stack;
import android.app.Application;
import android.util.Log;
import com.blankj.utilcode.util.GsonUtils;
import com.blankj.utilcode.util.LogUtils;
import com.didichuxing.doraemonkit.aop.MethodCostUtil;
import com.didichuxing.doraemonkit.kit.timecounter.TimeCounterManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/4/22-15:44
* 描 述:
* 修订历史:
* ================================================
*/
public class MethodStackUtil {
private static final String TAG = "DOKIT_SLOW_METHOD";
/**
* key className&methodName
*/
// private ConcurrentHashMap<String, MethodInvokNode> ROOT_METHOD_STACKS = new ConcurrentHashMap<>();
// private ConcurrentHashMap<String, MethodInvokNode> LEVEL1_METHOD_STACKS = new ConcurrentHashMap<>();
// private ConcurrentHashMap<String, MethodInvokNode> LEVEL2_METHOD_STACKS = new ConcurrentHashMap<>();
// private ConcurrentHashMap<String, MethodInvokNode> LEVEL3_METHOD_STACKS = new ConcurrentHashMap<>();
// private ConcurrentHashMap<String, MethodInvokNode> LEVEL4_METHOD_STACKS = new ConcurrentHashMap<>();
private List<ConcurrentHashMap<String, MethodInvokNode>> METHOD_STACKS = Collections.synchronizedList(new ArrayList<ConcurrentHashMap<String, MethodInvokNode>>());
/**
* 静态内部类单例
*/
private static class Holder {
private static MethodStackUtil INSTANCE = new MethodStackUtil();
}
public static MethodStackUtil getInstance() {
return MethodStackUtil.Holder.INSTANCE;
}
private void createMethodStackList(int totalLevel) {
if (METHOD_STACKS.size() == totalLevel) {
return;
}
METHOD_STACKS.clear();
for (int index = 0; index < totalLevel; index++) {
METHOD_STACKS.add(index, new ConcurrentHashMap<String, MethodInvokNode>());
}
}
/**
* @param currentLevel
* @param methodName
* @param classObj null 代表静态函数
*/
public void recodeObjectMethodCostStart(int totalLevel, int thresholdTime, int currentLevel, String className, String methodName, String desc, Object classObj) {
try {
//先创建队列
createMethodStackList(totalLevel);
MethodInvokNode methodInvokNode = new MethodInvokNode();
methodInvokNode.setStartTimeMillis(System.currentTimeMillis());
methodInvokNode.setCurrentThreadName(Thread.currentThread().getName());
methodInvokNode.setClassName(className);
methodInvokNode.setMethodName(methodName);
methodInvokNode.setLevel(currentLevel);
METHOD_STACKS.get(currentLevel).put(String.format("%s&%s", className, methodName), methodInvokNode);
//特殊判定
if (currentLevel == 0) {
if (classObj instanceof Application) {
if (methodName.equals("onCreate")) {
TimeCounterManager.get().onAppCreateStart();
}
if (methodName.equals("attachBaseContext")) {
TimeCounterManager.get().onAppAttachBaseContextStart();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @param currentLevel
* @param className
* @param methodName
* @param desc
* @param classObj null 代表静态函数
*/
public void recodeObjectMethodCostEnd(int thresholdTime, int currentLevel, String className, String methodName, String desc, Object classObj) {
synchronized (MethodCostUtil.class) {
try {
MethodInvokNode methodInvokNode = METHOD_STACKS.get(currentLevel).get(String.format("%s&%s", className, methodName));
if (methodInvokNode != null) {
methodInvokNode.setEndTimeMillis(System.currentTimeMillis());
bindNode(thresholdTime, currentLevel, methodInvokNode);
}
//打印函数调用栈
if (currentLevel == 0) {
if (methodInvokNode != null) {
toStack(classObj instanceof Application, methodInvokNode);
}
if (classObj instanceof Application) {
//Application 启动时间统计
if (methodName.equals("onCreate")) {
TimeCounterManager.get().onAppCreateEnd();
}
if (methodName.equals("attachBaseContext")) {
TimeCounterManager.get().onAppAttachBaseContextEnd();
}
}
//移除对象
METHOD_STACKS.get(0).remove(className + "&" + methodName);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private String getParentMethod(String currentClassName, String currentMethodName) {
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
int index = 0;
for (int i = 0; i < stackTraceElements.length; i++) {
StackTraceElement stackTraceElement = stackTraceElements[i];
if (currentClassName.equals(stackTraceElement.getClassName()) && currentMethodName.equals(stackTraceElement.getMethodName())) {
index = i;
break;
}
}
StackTraceElement parentStackTraceElement = stackTraceElements[index + 1];
return String.format("%s&%s", parentStackTraceElement.getClassName(), parentStackTraceElement.getMethodName());
}
private void bindNode(int thresholdTime, int currentLevel, MethodInvokNode methodInvokNode) {
if (methodInvokNode == null) {
return;
}
//过滤掉小于10ms的函数
if (methodInvokNode.getCostTimeMillis() <= thresholdTime) {
return;
}
if (currentLevel >= 1) {
MethodInvokNode parentMethodNode = METHOD_STACKS.get(currentLevel - 1).get(getParentMethod(methodInvokNode.getClassName(), methodInvokNode.getMethodName()));
if (parentMethodNode != null) {
methodInvokNode.setParent(parentMethodNode);
parentMethodNode.addChild(methodInvokNode);
}
}
}
public void recodeStaticMethodCostStart(int totalLevel, int thresholdTime, int currentLevel, String className, String methodName, String desc) {
recodeObjectMethodCostStart(totalLevel, thresholdTime, currentLevel, className, methodName, desc, new StaicMethodObject());
}
public void recodeStaticMethodCostEnd(int thresholdTime, int currentLevel, String className, String methodName, String desc) {
recodeObjectMethodCostEnd(thresholdTime, currentLevel, className, methodName, desc, new StaicMethodObject());
}
private void jsonTravel(List<MethodStackBean> methodStackBeans, List<MethodInvokNode> methodInvokNodes) {
if (methodInvokNodes == null) {
return;
}
for (MethodInvokNode methodInvokNode : methodInvokNodes) {
MethodStackBean methodStackBean = new MethodStackBean();
methodStackBean.setCostTime(methodInvokNode.getCostTimeMillis());
methodStackBean.setFunction(methodInvokNode.getClassName() + "&" + methodInvokNode.getMethodName());
methodStackBean.setChildren(new ArrayList<MethodStackBean>());
jsonTravel(methodStackBean.getChildren(), methodInvokNode.getChildren());
methodStackBeans.add(methodStackBean);
}
}
private void stackTravel(StringBuilder stringBuilder, List<MethodInvokNode> methodInvokNodes) {
if (methodInvokNodes == null) {
return;
}
for (MethodInvokNode methodInvokNode : methodInvokNodes) {
stringBuilder.append(String.format("%s%s%s%s%s", methodInvokNode.getLevel(), SPACE_0, methodInvokNode.getCostTimeMillis() + "ms", getSpaceString(methodInvokNode.getLevel()), methodInvokNode.getClassName() + "&" + methodInvokNode.getMethodName())).append("\n");
stackTravel(stringBuilder, methodInvokNode.getChildren());
}
}
public void toJson() {
List<MethodStackBean> methodStackBeans = new ArrayList<>();
for (MethodInvokNode methodInvokNode : METHOD_STACKS.get(0).values()) {
MethodStackBean methodStackBean = new MethodStackBean();
methodStackBean.setCostTime(methodInvokNode.getCostTimeMillis());
methodStackBean.setFunction(methodInvokNode.getClassName() + "&" + methodInvokNode.getMethodName());
methodStackBean.setChildren(new ArrayList<MethodStackBean>());
jsonTravel(methodStackBean.getChildren(), methodInvokNode.getChildren());
methodStackBeans.add(methodStackBean);
}
String json = GsonUtils.toJson(methodStackBeans);
LogUtils.json(json);
}
private static final String SPACE_0 = "********";
private static final String SPACE_1 = "*************";
private static final String SPACE_2 = "*****************";
private static final String SPACE_3 = "*********************";
private static final String SPACE_4 = "*************************";
public void toStack(boolean isAppStart, MethodInvokNode methodInvokNode) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("=========DoKit函数调用栈==========").append("\n");
stringBuilder.append(String.format("%s %s %s", "level", "time", "function")).append("\n");
stringBuilder.append(String.format("%s%s%s%s%s", methodInvokNode.getLevel(), SPACE_0, methodInvokNode.getCostTimeMillis() + "ms", getSpaceString(methodInvokNode.getLevel()), methodInvokNode.getClassName() + "&" + methodInvokNode.getMethodName())).append("\n");
stackTravel(stringBuilder, methodInvokNode.getChildren());
Log.i(TAG, stringBuilder.toString());
if (isAppStart && methodInvokNode.getLevel() == 0) {
if (methodInvokNode.getMethodName().equals("onCreate")) {
STR_APP_ON_CREATE = stringBuilder.toString();
}
if (methodInvokNode.getMethodName().equals("attachBaseContext")) {
STR_APP_ATTACH_BASECONTEXT = stringBuilder.toString();
}
}
}
public static String STR_APP_ON_CREATE;
public static String STR_APP_ATTACH_BASECONTEXT;
private String getSpaceString(int level) {
if (level == 0) {
return SPACE_0;
} else if (level == 1) {
return SPACE_1;
} else if (level == 2) {
return SPACE_2;
} else if (level == 3) {
return SPACE_3;
} else if (level == 4) {
return SPACE_4;
}
return SPACE_0;
}
}
package com.didichuxing.doraemonkit.aop.method_stack
import android.app.Application
import android.util.Log
import com.blankj.utilcode.util.GsonUtils
import com.blankj.utilcode.util.LogUtils
import com.didichuxing.doraemonkit.aop.MethodCostUtil
import com.didichuxing.doraemonkit.kit.timecounter.TimeCounterManager
import java.util.*
import java.util.concurrent.ConcurrentHashMap
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/4/22-15:44
* 描 述:
* 修订历史:
* ================================================
*/
public object MethodStackUtil {
/**
* key className&methodName
*/
private val METHOD_STACKS: MutableList<ConcurrentHashMap<String, MethodInvokNode>> by lazy {
Collections.synchronizedList(mutableListOf<ConcurrentHashMap<String, MethodInvokNode>>())
}
/**
* 用来标识是静态函数对象
*/
private val staticMethodObject: StaticMethodObject by lazy {
StaticMethodObject()
}
private fun createMethodStackList(totalLevel: Int) {
if (METHOD_STACKS.size == totalLevel) {
return
}
METHOD_STACKS.clear()
for (index in 0 until totalLevel) {
METHOD_STACKS.add(index, ConcurrentHashMap())
}
}
/**
* @param currentLevel
* @param methodName
* @param classObj null 代表静态函数
*/
fun recodeObjectMethodCostStart(totalLevel: Int, thresholdTime: Int, currentLevel: Int, className: String?, methodName: String, desc: String?, classObj: Any?) {
try {
//先创建队列
createMethodStackList(totalLevel)
val methodInvokNode = MethodInvokNode()
methodInvokNode.startTimeMillis = System.currentTimeMillis()
methodInvokNode.currentThreadName = Thread.currentThread().name
methodInvokNode.className = className
methodInvokNode.methodName = methodName
methodInvokNode.level = currentLevel
METHOD_STACKS[currentLevel][String.format("%s&%s", className, methodName)] = methodInvokNode
//特殊判定
if (currentLevel == 0) {
if (classObj is Application) {
if (methodName == "onCreate") {
TimeCounterManager.get().onAppCreateStart()
}
if (methodName == "attachBaseContext") {
TimeCounterManager.get().onAppAttachBaseContextStart()
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* @param currentLevel
* @param className
* @param methodName
* @param desc
* @param classObj null 代表静态函数
*/
fun recodeObjectMethodCostEnd(thresholdTime: Int, currentLevel: Int, className: String, methodName: String, desc: String?, classObj: Any?) {
synchronized(MethodCostUtil::class.java) {
try {
val methodInvokNode = METHOD_STACKS[currentLevel][String.format("%s&%s", className, methodName)]
if (methodInvokNode != null) {
methodInvokNode.setEndTimeMillis(System.currentTimeMillis())
bindNode(thresholdTime, currentLevel, methodInvokNode)
}
//打印函数调用栈
if (currentLevel == 0) {
if (methodInvokNode != null) {
toStack(classObj is Application, methodInvokNode)
}
if (classObj is Application) {
//Application 启动时间统计
if (methodName == "onCreate") {
TimeCounterManager.get().onAppCreateEnd()
}
if (methodName == "attachBaseContext") {
TimeCounterManager.get().onAppAttachBaseContextEnd()
}
}
//移除对象
METHOD_STACKS[0].remove("$className&$methodName")
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
private fun getParentMethod(currentClassName: String?, currentMethodName: String?): String {
val stackTraceElements = Thread.currentThread().stackTrace
var index = 0
for (i in stackTraceElements.indices) {
val stackTraceElement = stackTraceElements[i]
if (currentClassName == stackTraceElement.className && currentMethodName == stackTraceElement.methodName) {
index = i
break
}
}
val parentStackTraceElement = stackTraceElements[index + 1]
return String.format("%s&%s", parentStackTraceElement.className, parentStackTraceElement.methodName)
}
private fun bindNode(thresholdTime: Int, currentLevel: Int, methodInvokNode: MethodInvokNode?) {
if (methodInvokNode == null) {
return
}
//过滤掉小于指定阈值的函数
if (methodInvokNode.getCostTimeMillis() <= thresholdTime) {
return
}
if (currentLevel >= 1) {
val parentMethodNode = METHOD_STACKS[currentLevel - 1][getParentMethod(methodInvokNode.className, methodInvokNode.methodName)]
if (parentMethodNode != null) {
methodInvokNode.parent = parentMethodNode
parentMethodNode.addChild(methodInvokNode)
}
}
}
fun recodeStaticMethodCostStart(totalLevel: Int, thresholdTime: Int, currentLevel: Int, className: String?, methodName: String, desc: String?) {
recodeObjectMethodCostStart(totalLevel, thresholdTime, currentLevel, className, methodName, desc, staticMethodObject)
}
fun recodeStaticMethodCostEnd(thresholdTime: Int, currentLevel: Int, className: String, methodName: String, desc: String?) {
recodeObjectMethodCostEnd(thresholdTime, currentLevel, className, methodName, desc, staticMethodObject)
}
private fun jsonTravel(methodStackBeans: MutableList<MethodStackBean>?, methodInvokNodes: List<MethodInvokNode>?) {
if (methodInvokNodes == null) {
return
}
for (methodInvokNode in methodInvokNodes) {
val methodStackBean = MethodStackBean()
methodStackBean.setCostTime(methodInvokNode.getCostTimeMillis())
methodStackBean.function = methodInvokNode.className + "&" + methodInvokNode.methodName
methodStackBean.children = ArrayList()
jsonTravel(methodStackBean.children, methodInvokNode.children)
methodStackBeans?.add(methodStackBean)
}
}
private fun stackTravel(stringBuilder: StringBuilder, methodInvokNodes: List<MethodInvokNode>?) {
if (methodInvokNodes == null) {
return
}
for (methodInvokNode in methodInvokNodes) {
stringBuilder.append(String.format("%s%s%s%s%s", methodInvokNode.level, SPACE_0, methodInvokNode.getCostTimeMillis().toString() + "ms", getSpaceString(methodInvokNode.level), methodInvokNode.className + "&" + methodInvokNode.methodName)).append("\n")
stackTravel(stringBuilder, methodInvokNode.children)
}
}
fun toJson() {
val methodStackBeans: MutableList<MethodStackBean> = ArrayList()
for (methodInvokNode in METHOD_STACKS[0].values) {
val methodStackBean = MethodStackBean()
methodStackBean.setCostTime(methodInvokNode.getCostTimeMillis())
methodStackBean.function = methodInvokNode.className + "&" + methodInvokNode.methodName
methodStackBean.children = ArrayList()
jsonTravel(methodStackBean.children, methodInvokNode.children)
methodStackBeans.add(methodStackBean)
}
val json = GsonUtils.toJson(methodStackBeans)
LogUtils.json(json)
}
fun toStack(isAppStart: Boolean, methodInvokNode: MethodInvokNode) {
val stringBuilder = StringBuilder()
stringBuilder.append("=========DoKit函数调用栈==========").append("\n")
stringBuilder.append(String.format("%s %s %s", "level", "time", "function")).append("\n")
stringBuilder.append(String.format("%s%s%s%s%s", methodInvokNode.level, SPACE_0, methodInvokNode.getCostTimeMillis().toString() + "ms", getSpaceString(methodInvokNode.level), methodInvokNode.className + "&" + methodInvokNode.methodName)).append("\n")
stackTravel(stringBuilder, methodInvokNode.children)
Log.i(TAG, stringBuilder.toString())
if (isAppStart && methodInvokNode.level == 0) {
if (methodInvokNode.methodName == "onCreate") {
STR_APP_ON_CREATE = stringBuilder.toString()
}
if (methodInvokNode.methodName == "attachBaseContext") {
STR_APP_ATTACH_BASECONTEXT = stringBuilder.toString()
}
}
}
private fun getSpaceString(level: Int): String {
return when (level) {
0 -> SPACE_0
1 -> SPACE_1
2 -> SPACE_2
3 -> SPACE_3
4 -> SPACE_4
5 -> SPACE_5
6 -> SPACE_6
7 -> SPACE_7
else -> SPACE_0
}
}
private const val TAG = "DOKIT_SLOW_METHOD"
private const val SPACE_0 = "********"
private const val SPACE_1 = "*************"
private const val SPACE_2 = "*****************"
private const val SPACE_3 = "*********************"
private const val SPACE_4 = "*************************"
private const val SPACE_5 = "*****************************"
private const val SPACE_6 = "*********************************"
private const val SPACE_7 = "*************************************"
@JvmField
var STR_APP_ON_CREATE: String? = null
@JvmField
var STR_APP_ATTACH_BASECONTEXT: String? = null
}
\ No newline at end of file
package com.didichuxing.doraemonkit.aop.method_stack;
package com.didichuxing.doraemonkit.aop.method_stack
/**
* ================================================
......@@ -9,5 +9,4 @@ package com.didichuxing.doraemonkit.aop.method_stack;
* 修订历史:
* ================================================
*/
public class StaicMethodObject {
}
class StaticMethodObject
\ No newline at end of file
......@@ -17,7 +17,10 @@ android.useAndroidX=true
android.enableJetifier=true
android.injected.testOnly=false
#dokit全局配置
#dokit 慢函数开关
DOKIT_METHOD_SWITCH=true
#dokit 函数调用栈层级
DOKIT_METHOD_STACK_LEVEL=4
#dokit 慢函数开关
DOKIT_SLOW_METHOD_SWITCH=false
#0:默认模式 打印函数调用栈 需添加指定入口 默认为application onCreate 和attachBaseContext
#1:普通模式 运行时打印某个函数的耗时 全局业务代码函数插入
DOKIT_METHOD_STRATEGY=1
......@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
......@@ -2,7 +2,7 @@ include ':app'
include ':doraemonkit'
//include ':doraemonkit-rpc'
include ':doraemonkit-no-op'
include ':doraemonkit-weex'
//include ':doraemonkit-weex'
//include ':doraemonkit-leakcanary'
include ':doraemonkit-plugin'
include ':doraemonkit-test'
//include ':doraemonkit-plugin'
//include ':doraemonkit-test'
......@@ -6,6 +6,7 @@ import org.objectweb.asm.Opcodes.*
import org.objectweb.asm.tree.InsnList
import org.objectweb.asm.tree.InsnNode
import org.objectweb.asm.tree.MethodInsnNode
import org.objectweb.asm.tree.MethodNode
/**
* ================================================
......@@ -17,6 +18,58 @@ import org.objectweb.asm.tree.MethodInsnNode
* ================================================
*/
fun MethodNode.isGetSetMethod(): Boolean {
var ignoreCount = 0
val iterator = instructions.iterator()
while (iterator.hasNext()) {
val insnNode = iterator.next()
val opcode = insnNode.opcode
if (-1 == opcode) {
continue
}
if (opcode != GETFIELD && opcode != GETSTATIC && opcode != H_GETFIELD && opcode != H_GETSTATIC && opcode != RETURN && opcode != ARETURN && opcode != DRETURN && opcode != FRETURN && opcode != LRETURN && opcode != IRETURN && opcode != PUTFIELD && opcode != PUTSTATIC && opcode != H_PUTFIELD && opcode != H_PUTSTATIC && opcode > SALOAD) {
if (name.equals("<init>") && opcode == INVOKESPECIAL) {
ignoreCount++
if (ignoreCount > 1) {
return false
}
continue
}
return false
}
}
return true
}
fun MethodNode.isSingleMethod(): Boolean {
val iterator = instructions.iterator()
while (iterator.hasNext()) {
val insnNode = iterator.next()
val opcode = insnNode.opcode
if (-1 == opcode) {
continue
} else if (INVOKEVIRTUAL <= opcode && opcode <= INVOKEDYNAMIC) {
return false
}
}
return true
}
fun MethodNode.isEmptyMethod(): Boolean {
val iterator = instructions.iterator()
while (iterator.hasNext()) {
val insnNode = iterator.next()
val opcode = insnNode.opcode
return if (-1 == opcode) {
continue
} else {
false
}
}
return true
}
fun InsnList.getMethodExitInsnNodes(): Sequence<InsnNode>? {
return this.iterator()?.asSequence()?.filterIsInstance(InsnNode::class.java)?.filter {
it.opcode == RETURN ||
......
package com.didichuxing.doraemonkit.plugin.classtransformer
import com.didichuxing.doraemonkit.plugin.DoKitExtUtil
import com.didichuxing.doraemonkit.plugin.*
import com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
import com.didichuxing.doraemonkit.plugin.getMethodExitInsnNodes
import com.didichuxing.doraemonkit.plugin.isRelease
import com.didiglobal.booster.annotations.Priority
import com.didiglobal.booster.transform.TransformContext
import com.didiglobal.booster.transform.asm.ClassTransformer
......@@ -55,7 +53,10 @@ class GlobalSlowMethodTransformer : ClassTransformer {
//包含在白名单中且不在黑名单中
if (className.contains(packageName) && notMatchedBlackList(className)) {
klass.methods.filter { methodNode ->
methodNode.name != "<init>"
methodNode.name != "<init>" &&
!methodNode.isEmptyMethod() &&
!methodNode.isSingleMethod() &&
!methodNode.isGetSetMethod()
}.forEach { methodNode ->
methodNode.instructions.asIterable().filterIsInstance(MethodInsnNode::class.java).let { methodInsnNodes ->
if (methodInsnNodes.isNotEmpty()) {
......
......@@ -48,7 +48,12 @@ class MethodStackDepTransformer(private val level: Int = 1) : ClassTransformer {
val methodStackKeys: MutableSet<String> = MethodStackNodeUtil.METHOD_STACK_KEYS[level - 1]
klass.methods.forEach { methodNode ->
klass.methods.filter { methodNode ->
methodNode.name != "<init>" &&
!methodNode.isEmptyMethod() &&
!methodNode.isSingleMethod() &&
!methodNode.isGetSetMethod()
}.forEach { methodNode ->
val key = "${klass.className}&${methodNode.name}&${methodNode.desc}"
if (methodStackKeys.contains(key)) {
"level===>$level mathched key===>$key".println()
......
......@@ -26,7 +26,7 @@ ext {
versionName : "3.1.5",
glide_version : "4.9.0",
kotlin_version : "1.3.72",
booster_version : "1.6.0"
booster_version : "1.7.0"
]
dependencies = [// ###### android library start ######
"multidex" : 'androidx.multidex:multidex:2.0.0',
......
......@@ -6,6 +6,7 @@ import org.objectweb.asm.Opcodes.*
import org.objectweb.asm.tree.InsnList
import org.objectweb.asm.tree.InsnNode
import org.objectweb.asm.tree.MethodInsnNode
import org.objectweb.asm.tree.MethodNode
/**
* ================================================
......@@ -17,6 +18,58 @@ import org.objectweb.asm.tree.MethodInsnNode
* ================================================
*/
fun MethodNode.isGetSetMethod(): Boolean {
var ignoreCount = 0
val iterator = instructions.iterator()
while (iterator.hasNext()) {
val insnNode = iterator.next()
val opcode = insnNode.opcode
if (-1 == opcode) {
continue
}
if (opcode != GETFIELD && opcode != GETSTATIC && opcode != H_GETFIELD && opcode != H_GETSTATIC && opcode != RETURN && opcode != ARETURN && opcode != DRETURN && opcode != FRETURN && opcode != LRETURN && opcode != IRETURN && opcode != PUTFIELD && opcode != PUTSTATIC && opcode != H_PUTFIELD && opcode != H_PUTSTATIC && opcode > SALOAD) {
if (name.equals("<init>") && opcode == INVOKESPECIAL) {
ignoreCount++
if (ignoreCount > 1) {
return false
}
continue
}
return false
}
}
return true
}
fun MethodNode.isSingleMethod(): Boolean {
val iterator = instructions.iterator()
while (iterator.hasNext()) {
val insnNode = iterator.next()
val opcode = insnNode.opcode
if (-1 == opcode) {
continue
} else if (INVOKEVIRTUAL <= opcode && opcode <= INVOKEDYNAMIC) {
return false
}
}
return true
}
fun MethodNode.isEmptyMethod(): Boolean {
val iterator = instructions.iterator()
while (iterator.hasNext()) {
val insnNode = iterator.next()
val opcode = insnNode.opcode
return if (-1 == opcode) {
continue
} else {
false
}
}
return true
}
fun InsnList.getMethodExitInsnNodes(): Sequence<InsnNode>? {
return this.iterator()?.asSequence()?.filterIsInstance(InsnNode::class.java)?.filter {
it.opcode == RETURN ||
......
package com.didichuxing.doraemonkit.plugin.classtransformer
import com.didichuxing.doraemonkit.plugin.DoKitExtUtil
import com.didichuxing.doraemonkit.plugin.*
import com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
import com.didichuxing.doraemonkit.plugin.getMethodExitInsnNodes
import com.didichuxing.doraemonkit.plugin.isRelease
import com.didiglobal.booster.annotations.Priority
import com.didiglobal.booster.transform.TransformContext
import com.didiglobal.booster.transform.asm.ClassTransformer
......@@ -55,7 +53,10 @@ class GlobalSlowMethodTransformer : ClassTransformer {
//包含在白名单中且不在黑名单中
if (className.contains(packageName) && notMatchedBlackList(className)) {
klass.methods.filter { methodNode ->
methodNode.name != "<init>"
methodNode.name != "<init>" &&
!methodNode.isEmptyMethod() &&
!methodNode.isSingleMethod() &&
!methodNode.isGetSetMethod()
}.forEach { methodNode ->
methodNode.instructions.asIterable().filterIsInstance(MethodInsnNode::class.java).let { methodInsnNodes ->
if (methodInsnNodes.isNotEmpty()) {
......
......@@ -48,7 +48,12 @@ class MethodStackDepTransformer(private val level: Int = 1) : ClassTransformer {
val methodStackKeys: MutableSet<String> = MethodStackNodeUtil.METHOD_STACK_KEYS[level - 1]
klass.methods.forEach { methodNode ->
klass.methods.filter { methodNode ->
methodNode.name != "<init>" &&
!methodNode.isEmptyMethod() &&
!methodNode.isSingleMethod() &&
!methodNode.isGetSetMethod()
}.forEach { methodNode ->
val key = "${klass.className}&${methodNode.name}&${methodNode.desc}"
if (methodStackKeys.contains(key)) {
"level===>$level mathched key===>$key".println()
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册