Skip to content

Commit f426f6b

Browse files
authored
Merge pull request didi#952 from Leifzhang/feature/pthread_hook_kit
[feat] 基于matrix开发了一个pthread hook的调试组件
2 parents c385a14 + f21e2a0 commit f426f6b

File tree

21 files changed

+632
-2
lines changed

21 files changed

+632
-2
lines changed

Android/app/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ dependencies {
209209
implementation rootProject.ext.dependencies["tbs"]
210210
debugImplementation rootProject.ext.dependencies["leakcanary_android"]
211211
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
212+
implementation project(":dokit-pthread-hook")
212213
}
213214

214215

Android/dokit-pthread-hook/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
apply plugin: 'com.android.library'
2+
apply plugin: 'kotlin-android'
3+
apply plugin: 'kotlin-kapt'
4+
apply from: '../upload.gradle'
5+
6+
android {
7+
compileSdkVersion rootProject.ext.android["compileSdkVersion"]
8+
9+
defaultConfig {
10+
minSdkVersion rootProject.ext.android["minSdkVersion_21"]
11+
targetSdkVersion rootProject.ext.android["targetSdkVersion"]
12+
versionCode rootProject.ext.android["versionCode"]
13+
versionName rootProject.ext.android["versionName"]
14+
15+
lintOptions {
16+
abortOnError false
17+
}
18+
}
19+
20+
buildTypes {
21+
debug {
22+
minifyEnabled false
23+
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
24+
}
25+
release {
26+
minifyEnabled false
27+
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
28+
}
29+
}
30+
31+
lintOptions {
32+
abortOnError false
33+
}
34+
35+
compileOptions {
36+
sourceCompatibility JavaVersion.VERSION_1_8
37+
targetCompatibility JavaVersion.VERSION_1_8
38+
}
39+
40+
kotlinOptions {
41+
jvmTarget = "1.8"
42+
}
43+
/**
44+
* 支持ViewBinding
45+
*/
46+
buildFeatures {
47+
viewBinding = true
48+
}
49+
50+
}
51+
52+
53+
dependencies {
54+
implementation fileTree(dir: 'libs', include: ['*.jar'])
55+
testImplementation 'junit:junit:4.12'
56+
androidTestImplementation 'junit:junit:4.12'
57+
compileOnly project(':dokit-util')
58+
compileOnly project(':dokit-okhttp-api')
59+
//此处需要使用api的形式 向上暴露内部api
60+
implementation project(':dokit')
61+
62+
if (needKotlinV14()) {
63+
implementation rootProject.ext.dependencies["kotlin_v14"]
64+
} else {
65+
implementation rootProject.ext.dependencies["kotlin_v13"]
66+
}
67+
def matrixVersion = "2.0.2"
68+
implementation "com.tencent.matrix:matrix-hooks:$matrixVersion"
69+
implementation "com.tencent.matrix:matrix-fd:$matrixVersion"
70+
implementation('com.github.kirich1409:viewbindingpropertydelegate-noreflection:1.5.2') {
71+
transitive = false
72+
}
73+
implementation rootProject.ext.dependencies["core-ktx"]
74+
implementation rootProject.ext.dependencies["fragment-ktx"]
75+
implementation rootProject.ext.dependencies["appcompat"]
76+
implementation rootProject.ext.dependencies["recyclerview"]
77+
//okhttp wrap
78+
//auto-service
79+
implementation rootProject.ext.dependencies["auto_service"]
80+
kapt rootProject.ext.dependencies["auto_service"]
81+
}

Android/dokit-pthread-hook/consumer-rules.pro

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ARTIFACT_ID=dokitx-pthread-hook
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Add project specific ProGuard rules here.
2+
# You can control the set of applied configuration files using the
3+
# proguardFiles setting in build.gradle.
4+
#
5+
# For more details, see
6+
# http://developer.android.com/guide/developing/tools/proguard.html
7+
8+
# If your project uses WebView with JS, uncomment the following
9+
# and specify the fully qualified class name to the JavaScript interface
10+
# class:
11+
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12+
# public *;
13+
#}
14+
15+
# Uncomment this to preserve the line number information for
16+
# debugging stack traces.
17+
#-keepattributes SourceFile,LineNumberTable
18+
19+
# If you keep the line number information, uncomment this to
20+
# hide the original source file name.
21+
#-renamesourcefileattribute SourceFile
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.kronos.dokit.pthread
2+
3+
import androidx.test.platform.app.InstrumentationRegistry
4+
import androidx.test.ext.junit.runners.AndroidJUnit4
5+
6+
import org.junit.Test
7+
import org.junit.runner.RunWith
8+
9+
import org.junit.Assert.*
10+
11+
/**
12+
* Instrumented test, which will execute on an Android device.
13+
*
14+
* See [testing documentation](http://d.android.com/tools/testing).
15+
*/
16+
@RunWith(AndroidJUnit4::class)
17+
class ExampleInstrumentedTest {
18+
@Test
19+
fun useAppContext() {
20+
// Context of the app under test.
21+
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22+
assertEquals("com.kronos.dokit.pthread.test", appContext.packageName)
23+
}
24+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
package="com.kronos.dokit.pthread">
4+
5+
<application>
6+
<activity
7+
android:name=".ui.PThreadHookUiActivity"
8+
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
9+
</application>
10+
</manifest>
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package com.kronos.dokit.pthread
2+
3+
import android.app.Activity
4+
import android.app.Application
5+
import android.os.Bundle
6+
import com.didichuxing.doraemonkit.DoKit
7+
8+
/**
9+
*
10+
* @Author LiABao
11+
* @Since 2021/11/17
12+
*
13+
*/
14+
class AutoDumpListener : Application.ActivityLifecycleCallbacks {
15+
16+
init {
17+
DoKit.APPLICATION.registerActivityLifecycleCallbacks(this)
18+
}
19+
20+
21+
private fun getThreadCount(): Int {
22+
return getAllThreads().size
23+
}
24+
25+
private var count = 0
26+
27+
/**
28+
* ps -p `self` -t
29+
* See http://man7.org/linux/man-pages/man1/ps.1.html
30+
*/
31+
private fun getAllThreads(): MutableList<Thread> {
32+
var group = Thread.currentThread().threadGroup
33+
var system: ThreadGroup?
34+
do {
35+
system = group
36+
group = group?.parent
37+
} while (group != null)
38+
val count = system?.activeCount() ?: 0
39+
val threads = arrayOfNulls<Thread>(count)
40+
system?.enumerate(threads)
41+
val list = mutableListOf<Thread>()
42+
threads.forEach {
43+
it?.let { it1 -> list.add(it1) }
44+
}
45+
return list
46+
}
47+
48+
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
49+
50+
}
51+
52+
override fun onActivityStarted(activity: Activity) {
53+
54+
}
55+
56+
override fun onActivityResumed(activity: Activity) {
57+
58+
}
59+
60+
override fun onActivityPaused(activity: Activity) {
61+
62+
}
63+
64+
override fun onActivityStopped(activity: Activity) {
65+
}
66+
67+
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
68+
69+
}
70+
71+
override fun onActivityDestroyed(activity: Activity) {
72+
if (count > 3) {
73+
return
74+
}
75+
if (getThreadCount() > Threshold) {
76+
DoKit.APPLICATION.dump {
77+
forEach {
78+
79+
}
80+
81+
}
82+
}
83+
}
84+
85+
86+
companion object {
87+
const val Threshold = 200
88+
}
89+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.kronos.dokit.pthread
2+
3+
import android.content.Context
4+
import android.util.Log
5+
import com.tencent.matrix.fd.FDDumpBridge
6+
import com.tencent.matrix.hook.pthread.PthreadHook
7+
import java.io.File
8+
9+
/**
10+
*
11+
* @Author LiABao
12+
* @Since 2021/11/16
13+
*
14+
*/
15+
16+
// 当前没有删除这部分文件 后续需要考虑的
17+
fun Context.dump(invoke: MutableList<PThreadEntity>.() -> Unit = {}) {
18+
try {
19+
val parent = "$cacheDir/pthread"
20+
val output = "$parent/pthread_hook_${System.currentTimeMillis() / 1000L}.log"
21+
parent.createDirectory()
22+
PthreadHook.INSTANCE.dump(output)
23+
fdLimit()
24+
val pthreads = getJson(output).parserPThread()
25+
pthreads.forEach {
26+
Log.i(TAG, "pthread error :$it\n")
27+
}
28+
invoke.invoke(pthreads)
29+
} catch (e: Exception) {
30+
31+
}
32+
}
33+
34+
fun String.createDirectory() {
35+
val file = File(this)
36+
if (!file.exists()) {
37+
file.mkdir()
38+
}
39+
}
40+
41+
fun fdLimit() {
42+
Log.i(TAG, "FD limit = " + FDDumpBridge.getFDLimit())
43+
}
44+
45+
fun getJson(path: String): String {
46+
val stream = File(path).bufferedReader().readText()
47+
return stream.apply {
48+
Log.i(TAG, "pThread:$this")
49+
}
50+
}
51+
52+
const val TAG = "PThreadDumpHelper"
53+
54+
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.kronos.dokit.pthread
2+
3+
import org.json.JSONObject
4+
5+
/**
6+
*
7+
* @Author LiABao
8+
* @Since 2021/11/17
9+
*
10+
*/
11+
data class PThreadEntity(
12+
val hash: String, val native: String, val java: String,
13+
val count: Int, val threads: MutableList<ThreadNameEntity>
14+
) {
15+
16+
17+
}
18+
19+
data class ThreadNameEntity(val tid: String, val name: String)
20+
21+
22+
fun JSONObject.parserThreads(): MutableList<ThreadNameEntity> {
23+
val jsonArray = optJSONArray("threads")
24+
val index = jsonArray?.length() ?: 0
25+
val threads = mutableListOf<ThreadNameEntity>()
26+
for (index in 0 until index) {
27+
val thread = jsonArray.optJSONObject(index)
28+
threads.add(ThreadNameEntity(thread.optString("tid"), thread.optString("name")))
29+
}
30+
return threads
31+
}
32+
33+
fun String.parserPThread(): MutableList<PThreadEntity> {
34+
val jsonObject = JSONObject(this)
35+
val pThreadArray = jsonObject.optJSONArray("PthreadHook_not_exited")
36+
val pThreads = mutableListOf<PThreadEntity>()
37+
pThreadArray?.apply {
38+
for (index in 0 until length()) {
39+
val jsonObject = optJSONObject(index)
40+
pThreads.add(
41+
PThreadEntity(
42+
jsonObject.optString("hash"), jsonObject.optString("native"),
43+
jsonObject.optString("java"), jsonObject.optInt("count", 0),
44+
jsonObject.parserThreads()
45+
)
46+
)
47+
}
48+
}
49+
return pThreads
50+
}
51+

0 commit comments

Comments
 (0)