From 1b89055e3724ae8483516ef78398c0719458ada9 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Ricau Date: Thu, 25 Mar 2021 15:30:12 -0700 Subject: [PATCH] Add object inspector for services Fixes #2083 --- shark-android/api/shark-android.api | 6 ++++ .../java/shark/AndroidObjectInspectors.kt | 21 ++++++++++++++ .../src/main/java/shark/AndroidServices.kt | 28 +++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 shark-android/src/main/java/shark/AndroidServices.kt diff --git a/shark-android/api/shark-android.api b/shark-android/api/shark-android.api index 74908fc7e5..52593388be 100644 --- a/shark-android/api/shark-android.api +++ b/shark-android/api/shark-android.api @@ -33,6 +33,7 @@ public abstract class shark/AndroidObjectInspectors : java/lang/Enum, shark/Obje public static final field MESSAGE_QUEUE Lshark/AndroidObjectInspectors; public static final field MORTAR_PRESENTER Lshark/AndroidObjectInspectors; public static final field MORTAR_SCOPE Lshark/AndroidObjectInspectors; + public static final field SERVICE Lshark/AndroidObjectInspectors; public static final field SUPPORT_FRAGMENT Lshark/AndroidObjectInspectors; public static final field TOAST Lshark/AndroidObjectInspectors; public static final field VIEW Lshark/AndroidObjectInspectors; @@ -166,3 +167,8 @@ public final class shark/AndroidResourceIdNames$Companion { public final fun saveToMemory (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V } +public final class shark/AndroidServices { + public static final field INSTANCE Lshark/AndroidServices; + public final fun getAliveAndroidServiceObjectIds (Lshark/HeapGraph;)Ljava/util/List; +} + diff --git a/shark-android/src/main/java/shark/AndroidObjectInspectors.kt b/shark-android/src/main/java/shark/AndroidObjectInspectors.kt index 5e2741bcab..a59c84959f 100644 --- a/shark-android/src/main/java/shark/AndroidObjectInspectors.kt +++ b/shark-android/src/main/java/shark/AndroidObjectInspectors.kt @@ -15,6 +15,7 @@ */ package shark +import shark.AndroidServices.aliveAndroidServiceObjectIds import shark.AndroidObjectInspectors.Companion.appDefaults import shark.FilteringLeakingObjectFinder.LeakingObjectFilter import shark.HeapObject.HeapInstance @@ -209,6 +210,26 @@ enum class AndroidObjectInspectors : ObjectInspector { } }, + SERVICE { + override val leakingObjectFilter = { heapObject: HeapObject -> + heapObject is HeapInstance && + heapObject instanceOf "android.app.Service" && + heapObject.objectId !in heapObject.graph.aliveAndroidServiceObjectIds + } + + override fun inspect( + reporter: ObjectReporter + ) { + reporter.whenInstanceOf("android.app.Service") { instance -> + if (instance.objectId in instance.graph.aliveAndroidServiceObjectIds) { + notLeakingReasons += "Service held by ActivityThread" + } else { + leakingReasons += "Service not held by ActivityThread" + } + } + } + }, + CONTEXT_FIELD { override fun inspect(reporter: ObjectReporter) { val instance = reporter.heapObject diff --git a/shark-android/src/main/java/shark/AndroidServices.kt b/shark-android/src/main/java/shark/AndroidServices.kt new file mode 100644 index 0000000000..eb9f667a61 --- /dev/null +++ b/shark-android/src/main/java/shark/AndroidServices.kt @@ -0,0 +1,28 @@ +package shark + +object AndroidServices { + val HeapGraph.aliveAndroidServiceObjectIds: List + get() { + return context.getOrPut(AndroidServices::class.java.name) { + val activityThreadClass = findClassByName("android.app.ActivityThread")!! + val currentActivityThread = activityThreadClass + .readStaticField("sCurrentActivityThread")!! + .valueAsInstance!! + + val mServices = currentActivityThread["android.app.ActivityThread", "mServices"]!! + .valueAsInstance!! + + val servicesArray = mServices["android.util.ArrayMap", "mArray"]!!.valueAsObjectArray!! + + servicesArray.readElements() + .filterIndexed { index, heapValue -> + // ArrayMap + // even: key, odd: value + index % 2 == 1 + && heapValue.isNonNullReference + } + .map { it.asNonNullObjectId!! } + .toList() + } + } +} \ No newline at end of file