Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
DiDi
DoraemonKit
提交
b8041bb4
D
DoraemonKit
项目概览
DiDi
/
DoraemonKit
10 个月 前同步成功
通知
166
Star
19623
Fork
3062
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
D
DoraemonKit
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
b8041bb4
编写于
6月 03, 2020
作者:
J
jackjintai
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
android:优化插件功能
上级
e8776162
变更
61
隐藏空白更改
内联
并排
Showing
61 changed file
with
3081 addition
and
537 deletion
+3081
-537
Android/java/app/src/debug/java/com/didichuxing/doraemondemo/App.kt
...va/app/src/debug/java/com/didichuxing/doraemondemo/App.kt
+8
-0
Android/java/app/src/debug/java/com/didichuxing/doraemondemo/MainDebugActivity.kt
...ug/java/com/didichuxing/doraemondemo/MainDebugActivity.kt
+2
-0
Android/java/app/src/main/java/com/didichuxing/doraemondemo/AopTest.java
...p/src/main/java/com/didichuxing/doraemondemo/AopTest.java
+15
-1
Android/java/build.gradle
Android/java/build.gradle
+10
-13
Android/java/buildSrc/.gitignore
Android/java/buildSrc/.gitignore
+1
-0
Android/java/buildSrc/build.gradle
Android/java/buildSrc/build.gradle
+59
-0
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitExt.kt
...ain/kotlin/com/didichuxing/doraemonkit/plugin/DoKitExt.kt
+108
-0
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitExtUtil.kt
...kotlin/com/didichuxing/doraemonkit/plugin/DoKitExtUtil.kt
+173
-0
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitPlugin.kt
.../kotlin/com/didichuxing/doraemonkit/plugin/DoKitPlugin.kt
+143
-0
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitTransformInvocation.kt
...idichuxing/doraemonkit/plugin/DoKitTransformInvocation.kt
+195
-0
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitTransformTaskExecutionListener.kt
...doraemonkit/plugin/DoKitTransformTaskExecutionListener.kt
+23
-0
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/asmtransformer/BaseDoKitAsmTransformer.kt
...aemonkit/plugin/asmtransformer/BaseDoKitAsmTransformer.kt
+82
-0
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/asmtransformer/DoKitAsmTransformer.kt
.../doraemonkit/plugin/asmtransformer/DoKitAsmTransformer.kt
+16
-0
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/BigImgTransformer.kt
.../doraemonkit/plugin/classtransformer/BigImgTransformer.kt
+155
-0
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/CommTransformer.kt
...ng/doraemonkit/plugin/classtransformer/CommTransformer.kt
+355
-0
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/EnterMethodStackTransformer.kt
...it/plugin/classtransformer/EnterMethodStackTransformer.kt
+169
-0
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/GlobalSlowMethodTransformer.kt
...it/plugin/classtransformer/GlobalSlowMethodTransformer.kt
+137
-0
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/MethodStackDepTransformer.kt
...nkit/plugin/classtransformer/MethodStackDepTransformer.kt
+149
-0
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/UrlConnectionTransformer.kt
...onkit/plugin/classtransformer/UrlConnectionTransformer.kt
+65
-0
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/extension/CommExt.kt
...n/com/didichuxing/doraemonkit/plugin/extension/CommExt.kt
+34
-0
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/extension/DoKitExt.kt
.../com/didichuxing/doraemonkit/plugin/extension/DoKitExt.kt
+47
-0
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/extension/SlowMethodExt.kt
...didichuxing/doraemonkit/plugin/extension/SlowMethodExt.kt
+120
-0
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/processor/DoKitPluginConfigProcessor.kt
...oraemonkit/plugin/processor/DoKitPluginConfigProcessor.kt
+65
-0
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/stack_method/MethodStackNode.kt
...huxing/doraemonkit/plugin/stack_method/MethodStackNode.kt
+18
-0
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/stack_method/MethodStackNodeUtil.kt
...ng/doraemonkit/plugin/stack_method/MethodStackNodeUtil.kt
+30
-0
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/transform/DoKitBaseTransform.kt
...huxing/doraemonkit/plugin/transform/DoKitBaseTransform.kt
+84
-0
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/transform/DoKitCommTransform.kt
...huxing/doraemonkit/plugin/transform/DoKitCommTransform.kt
+12
-0
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/transform/DoKitDependTransform.kt
...xing/doraemonkit/plugin/transform/DoKitDependTransform.kt
+19
-0
Android/java/buildSrc/src/main/resources/META-INF/gradle-plugins/com.didi.dokit.debug.properties
...s/META-INF/gradle-plugins/com.didi.dokit.debug.properties
+3
-0
Android/java/config.gradle
Android/java/config.gradle
+2
-2
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitExt.kt
...ain/kotlin/com/didichuxing/doraemonkit/plugin/DoKitExt.kt
+53
-2
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitExtUtil.kt
...kotlin/com/didichuxing/doraemonkit/plugin/DoKitExtUtil.kt
+40
-23
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitPlugin.kt
.../kotlin/com/didichuxing/doraemonkit/plugin/DoKitPlugin.kt
+6
-3
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/BigImgTransformer.kt
.../doraemonkit/plugin/classtransformer/BigImgTransformer.kt
+1
-1
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/CommTransformer.kt
...ng/doraemonkit/plugin/classtransformer/CommTransformer.kt
+16
-6
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/EnterMethodStackTransformer.kt
...it/plugin/classtransformer/EnterMethodStackTransformer.kt
+6
-6
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/GlobalSlowMethodTransformer.kt
...it/plugin/classtransformer/GlobalSlowMethodTransformer.kt
+11
-10
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/MethodStackDepTransformer.kt
...nkit/plugin/classtransformer/MethodStackDepTransformer.kt
+11
-6
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/UrlConnectionTransformer.kt
...onkit/plugin/classtransformer/UrlConnectionTransformer.kt
+2
-2
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/extension/SlowMethodExt.kt
...didichuxing/doraemonkit/plugin/extension/SlowMethodExt.kt
+2
-1
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/transform/DoKitBaseTransform.kt
...huxing/doraemonkit/plugin/transform/DoKitBaseTransform.kt
+9
-7
Android/java/doraemonkit-test/build.gradle
Android/java/doraemonkit-test/build.gradle
+1
-1
Android/java/doraemonkit/build.gradle
Android/java/doraemonkit/build.gradle
+0
-3
Android/java/doraemonkit/src/main/java/com/didichuxing/doraemonkit/aop/MethodCostUtil.kt
...in/java/com/didichuxing/doraemonkit/aop/MethodCostUtil.kt
+169
-0
Android/java/doraemonkit/src/main/java/com/didichuxing/doraemonkit/aop/method_stack/MethodInvokNode.java
...chuxing/doraemonkit/aop/method_stack/MethodInvokNode.java
+0
-108
Android/java/doraemonkit/src/main/java/com/didichuxing/doraemonkit/aop/method_stack/MethodInvokNode.kt
...dichuxing/doraemonkit/aop/method_stack/MethodInvokNode.kt
+47
-0
Android/java/doraemonkit/src/main/java/com/didichuxing/doraemonkit/aop/method_stack/MethodStackBean.java
...chuxing/doraemonkit/aop/method_stack/MethodStackBean.java
+0
-51
Android/java/doraemonkit/src/main/java/com/didichuxing/doraemonkit/aop/method_stack/MethodStackBean.kt
...dichuxing/doraemonkit/aop/method_stack/MethodStackBean.kt
+20
-0
Android/java/doraemonkit/src/main/java/com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil.java
...chuxing/doraemonkit/aop/method_stack/MethodStackUtil.java
+0
-271
Android/java/doraemonkit/src/main/java/com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil.kt
...dichuxing/doraemonkit/aop/method_stack/MethodStackUtil.kt
+248
-0
Android/java/doraemonkit/src/main/java/com/didichuxing/doraemonkit/aop/method_stack/StaticMethodObject.kt
...huxing/doraemonkit/aop/method_stack/StaticMethodObject.kt
+2
-3
Android/java/gradle.properties
Android/java/gradle.properties
+5
-2
Android/java/gradle/wrapper/gradle-wrapper.properties
Android/java/gradle/wrapper/gradle-wrapper.properties
+1
-1
Android/java/settings.gradle
Android/java/settings.gradle
+3
-3
Android/kotlin/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitExt.kt
...ain/kotlin/com/didichuxing/doraemonkit/plugin/DoKitExt.kt
+53
-0
Android/kotlin/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/GlobalSlowMethodTransformer.kt
...it/plugin/classtransformer/GlobalSlowMethodTransformer.kt
+5
-4
Android/kotlin/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/MethodStackDepTransformer.kt
...nkit/plugin/classtransformer/MethodStackDepTransformer.kt
+6
-1
Android/kotlin/config.gradle
Android/kotlin/config.gradle
+1
-1
Android/kotlin/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitExt.kt
...ain/kotlin/com/didichuxing/doraemonkit/plugin/DoKitExt.kt
+53
-0
Android/kotlin/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/GlobalSlowMethodTransformer.kt
...it/plugin/classtransformer/GlobalSlowMethodTransformer.kt
+5
-4
Android/kotlin/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/MethodStackDepTransformer.kt
...nkit/plugin/classtransformer/MethodStackDepTransformer.kt
+6
-1
未找到文件。
Android/java/app/src/debug/java/com/didichuxing/doraemondemo/App.kt
浏览文件 @
b8041bb4
...
...
@@ -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
())
...
...
Android/java/app/src/debug/java/com/didichuxing/doraemondemo/MainDebugActivity.kt
浏览文件 @
b8041bb4
...
...
@@ -151,6 +151,8 @@ class MainDebugActivity : BaseActivity(), View.OnClickListener {
.
addConverterFactory
(
GsonConverterFactory
.
create
())
.
build
()
githubService
=
retrofit
.
create
(
GithubService
::
class
.
java
)
AopTest
().
test
()
}
private
fun
test1
()
{
...
...
Android/java/app/src/main/java/com/didichuxing/doraemondemo/AopTest.java
浏览文件 @
b8041bb4
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
);
}
...
...
Android/java/build.gradle
浏览文件 @
b8041bb4
// 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"])
//
}
}
}
...
...
Android/java/buildSrc/.gitignore
0 → 100644
浏览文件 @
b8041bb4
/build
Android/java/buildSrc/build.gradle
0 → 100644
浏览文件 @
b8041bb4
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
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitExt.kt
0 → 100644
浏览文件 @
b8041bb4
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
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitExtUtil.kt
0 → 100644
浏览文件 @
b8041bb4
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
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitPlugin.kt
0 → 100644
浏览文件 @
b8041bb4
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
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitTransformInvocation.kt
0 → 100644
浏览文件 @
b8041bb4
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
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitTransformTaskExecutionListener.kt
0 → 100644
浏览文件 @
b8041bb4
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
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/asmtransformer/BaseDoKitAsmTransformer.kt
0 → 100644
浏览文件 @
b8041bb4
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
}
}
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/asmtransformer/DoKitAsmTransformer.kt
0 → 100644
浏览文件 @
b8041bb4
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
))
{
}
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/BigImgTransformer.kt
0 → 100644
浏览文件 @
b8041bb4
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
}
}
}
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/CommTransformer.kt
0 → 100644
浏览文件 @
b8041bb4
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
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/EnterMethodStackTransformer.kt
0 → 100644
浏览文件 @
b8041bb4
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
}
}
}
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/GlobalSlowMethodTransformer.kt
0 → 100644
浏览文件 @
b8041bb4
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
}
}
}
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/MethodStackDepTransformer.kt
0 → 100644
浏览文件 @
b8041bb4
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
}
}
}
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/UrlConnectionTransformer.kt
0 → 100644
浏览文件 @
b8041bb4
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
}
}
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/extension/CommExt.kt
0 → 100644
浏览文件 @
b8041bb4
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
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/extension/DoKitExt.kt
0 → 100644
浏览文件 @
b8041bb4
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
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/extension/SlowMethodExt.kt
0 → 100644
浏览文件 @
b8041bb4
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
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/processor/DoKitPluginConfigProcessor.kt
0 → 100644
浏览文件 @
b8041bb4
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
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/stack_method/MethodStackNode.kt
0 → 100644
浏览文件 @
b8041bb4
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
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/stack_method/MethodStackNodeUtil.kt
0 → 100644
浏览文件 @
b8041bb4
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
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/transform/DoKitBaseTransform.kt
0 → 100644
浏览文件 @
b8041bb4
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
()
}
}
}
}
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/transform/DoKitCommTransform.kt
0 → 100644
浏览文件 @
b8041bb4
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
)
{
}
Android/java/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/transform/DoKitDependTransform.kt
0 → 100644
浏览文件 @
b8041bb4
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"
}
}
Android/java/buildSrc/src/main/resources/META-INF/gradle-plugins/com.didi.dokit.debug.properties
0 → 100644
浏览文件 @
b8041bb4
#入口文件
#文件名为插件名 即 apply plugin "xxx"
implementation-class
=
com.didichuxing.doraemonkit.plugin.DoKitPlugin
\ No newline at end of file
Android/java/config.gradle
浏览文件 @
b8041bb4
...
...
@@ -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'
,
...
...
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitExt.kt
浏览文件 @
b8041bb4
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
||
...
...
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitExtUtil.kt
浏览文件 @
b8041bb4
...
...
@@ -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
)
{
...
...
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitPlugin.kt
浏览文件 @
b8041bb4
...
...
@@ -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
)
{
...
...
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/BigImgTransformer.kt
浏览文件 @
b8041bb4
...
...
@@ -24,7 +24,7 @@ import org.objectweb.asm.tree.VarInsnNode
* 修订历史:
* ================================================
*/
@Priority
(
2
)
@Priority
(
1
)
@AutoService
(
ClassTransformer
::
class
)
class
BigImgTransformer
:
ClassTransformer
{
...
...
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/CommTransformer.kt
浏览文件 @
b8041bb4
...
...
@@ -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
.
s
trategy
==
SlowMethodExt
.
STRATEGY_STACK
)
ICONST_0
else
ICONST_1
))
add
(
InsnNode
(
if
(
DoKitExtUtil
.
mSlowMethodS
trategy
==
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
))
...
...
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/EnterMethodStackTransformer.kt
浏览文件 @
b8041bb4
...
...
@@ -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
.
s
trategy
==
SlowMethodExt
.
STRATEGY_NORMAL
)
{
if
(
DoKitExtUtil
.
mSlowMethodS
trategy
==
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
))
...
...
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/GlobalSlowMethodTransformer.kt
浏览文件 @
b8041bb4
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
.
s
trategy
==
SlowMethodExt
.
STRATEGY_STACK
)
{
if
(
DoKitExtUtil
.
mSlowMethodS
trategy
==
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
))
...
...
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/MethodStackDepTransformer.kt
浏览文件 @
b8041bb4
...
...
@@ -37,7 +37,7 @@ class MethodStackDepTransformer(private val level: Int = 1) : ClassTransformer {
return
klass
}
if
(
DoKitExtUtil
.
slowMethodExt
.
s
trategy
==
SlowMethodExt
.
STRATEGY_NORMAL
)
{
if
(
DoKitExtUtil
.
mSlowMethodS
trategy
==
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
))
...
...
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/UrlConnectionTransformer.kt
浏览文件 @
b8041bb4
...
...
@@ -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;"
...
...
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/extension/SlowMethodExt.kt
浏览文件 @
b8041bb4
...
...
@@ -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
(),
...
...
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/transform/DoKitBaseTransform.kt
浏览文件 @
b8041bb4
...
...
@@ -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
{
...
...
Android/java/doraemonkit-test/build.gradle
浏览文件 @
b8041bb4
...
...
@@ -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'
...
...
Android/java/doraemonkit/build.gradle
浏览文件 @
b8041bb4
...
...
@@ -42,9 +42,6 @@ android {
enabled
=
true
}
viewBinding
{
enabled
=
true
}
kotlinOptions
{
jvmTarget
=
"1.8"
...
...
Android/java/doraemonkit/src/main/java/com/didichuxing/doraemonkit/aop/MethodCostUtil.
java
→
Android/java/doraemonkit/src/main/java/com/didichuxing/doraemonkit/aop/MethodCostUtil.
kt
浏览文件 @
b8041bb4
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
i
nstanceof
Application
)
{
String
[]
methods
=
methodName
.
split
(
"&"
);
if
(
methods
.
length
==
2
)
{
if
(
methods
[
1
]
.
equals
(
"onCreate"
)
)
{
TimeCounterManager
.
get
().
onAppCreateStart
()
;
METHOD_COSTS
[
methodName
]
=
System
.
currentTimeMillis
()
if
(
classObj
i
s
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
i
nstanceof
Application
)
{
val
startTime
=
METHOD_COSTS
[
methodName
]
!!
val
costTime
=
(
System
.
currentTimeMillis
()
-
startTime
).
toInt
()
METHOD_COSTS
.
remove
(
methodName
)
if
(
classObj
i
s
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
i
nstanceof
Activity
)
{
}
else
if
(
classObj
i
s
Activity
)
{
//Activity 启动时间统计
//printActivityStartTime(methodName);
}
else
if
(
classObj
i
nstanceof
Service
)
{
}
else
if
(
classObj
i
s
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
Android/java/doraemonkit/src/main/java/com/didichuxing/doraemonkit/aop/method_stack/MethodInvokNode.java
已删除
100644 → 0
浏览文件 @
e8776162
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
;
}
}
Android/java/doraemonkit/src/main/java/com/didichuxing/doraemonkit/aop/method_stack/MethodInvokNode.kt
0 → 100644
浏览文件 @
b8041bb4
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
Android/java/doraemonkit/src/main/java/com/didichuxing/doraemonkit/aop/method_stack/MethodStackBean.java
已删除
100644 → 0
浏览文件 @
e8776162
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
+
'}'
;
}
}
Android/java/doraemonkit/src/main/java/com/didichuxing/doraemonkit/aop/method_stack/MethodStackBean.kt
0 → 100644
浏览文件 @
b8041bb4
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
Android/java/doraemonkit/src/main/java/com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil.java
已删除
100644 → 0
浏览文件 @
e8776162
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
;
}
}
Android/java/doraemonkit/src/main/java/com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil.kt
0 → 100644
浏览文件 @
b8041bb4
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
Android/java/doraemonkit/src/main/java/com/didichuxing/doraemonkit/aop/method_stack/Sta
icMethodObject.java
→
Android/java/doraemonkit/src/main/java/com/didichuxing/doraemonkit/aop/method_stack/Sta
ticMethodObject.kt
浏览文件 @
b8041bb4
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
Android/java/gradle.properties
浏览文件 @
b8041bb4
...
...
@@ -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
Android/java/gradle/wrapper/gradle-wrapper.properties
浏览文件 @
b8041bb4
...
...
@@ -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
Android/java/settings.gradle
浏览文件 @
b8041bb4
...
...
@@ -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'
Android/kotlin/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitExt.kt
浏览文件 @
b8041bb4
...
...
@@ -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
||
...
...
Android/kotlin/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/GlobalSlowMethodTransformer.kt
浏览文件 @
b8041bb4
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
())
{
...
...
Android/kotlin/buildSrc/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/MethodStackDepTransformer.kt
浏览文件 @
b8041bb4
...
...
@@ -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
()
...
...
Android/kotlin/config.gradle
浏览文件 @
b8041bb4
...
...
@@ -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'
,
...
...
Android/kotlin/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitExt.kt
浏览文件 @
b8041bb4
...
...
@@ -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
||
...
...
Android/kotlin/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/GlobalSlowMethodTransformer.kt
浏览文件 @
b8041bb4
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
())
{
...
...
Android/kotlin/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/MethodStackDepTransformer.kt
浏览文件 @
b8041bb4
...
...
@@ -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.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录