/
LegacyHprofTest.kt
176 lines (145 loc) · 6.46 KB
/
LegacyHprofTest.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package shark
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import shark.LegacyHprofTest.WRAPS_ACTIVITY.DESTROYED
import shark.LegacyHprofTest.WRAPS_ACTIVITY.NOT_ACTIVITY
import shark.LegacyHprofTest.WRAPS_ACTIVITY.NOT_DESTROYED
import java.io.File
class LegacyHprofTest {
@Test fun foo() {
val file = fileFromResources("gc_root_in_non_primary_heap.hprof")
// val file = fileFromResources("leak_asynctask_pre_m.hprof")
Hprof.open(file)
.use { hprof ->
val graph = HprofHeapGraph.indexHprof(hprof)
val activityThread = graph.findClassByName("android.app.ActivityThread")?.get("sCurrentActivityThread")?.valueAsInstance
val appBindData = activityThread?.get("android.app.ActivityThread", "mBoundApplication")?.valueAsInstance
val appInfo = appBindData?.get("android.app.ActivityThread\$AppBindData", "appInfo")?.valueAsInstance
val processName = appInfo?.get("android.content.pm.PackageItemInfo", "packageName")?.valueAsInstance?.readAsJavaString()
println("process name: [$processName] ")
// val loadedApk = graph.findClassByName("android.app.LoadedApk")
// ?.instances?.map { loadedApk ->
// loadedApk["android.app.LoadedApk", "mPackageName"]?.value?.readAsJavaString()
// ?: "Unknown"
// }!!.toList()
// .forEach { println("[$it]") }
// val packageCount = graph.findClassByName(
// "java.lang.String"
// )!!.instances.count { it.readAsJavaString() == "com.example.leakcanary" }
//
//
// println("packageCount=$packageCount")
}
// val heapAnalyzer = HeapAnalyzer(OnAnalysisProgressListener.NO_OP)
// val analysis = heapAnalyzer.analyze(
// file, AndroidReferenceMatchers.appDefaults, false, AndroidObjectInspectors.appDefaults,
// leakFinders = listOf(ObjectInspector {
// it.whenInstanceOf("java.lang.String") { heapInstance ->
// if (heapInstance.readAsJavaString() == "com.example.leakcanary") {
// leakingReasons += "foobar"
// }
// }
// })
// )
// println(analysis)
}
@Test fun preM() {
val analysis = analyzeHprof("leak_asynctask_pre_m.hprof")
assertThat(analysis.applicationLeaks).hasSize(2)
val leak1 = analysis.applicationLeaks[0]
val leak2 = analysis.applicationLeaks[1]
assertThat(leak1.className).isEqualTo("android.graphics.Bitmap")
assertThat(leak2.className).isEqualTo("com.example.leakcanary.MainActivity")
}
@Test fun androidM() {
val analysis = analyzeHprof("leak_asynctask_m.hprof")
assertThat(analysis.applicationLeaks).hasSize(1)
val leak = analysis.applicationLeaks[0]
assertThat(leak.className).isEqualTo("com.example.leakcanary.MainActivity")
assertThat(leak.leakTrace.elements[0].labels).contains("GC Root: System class")
}
@Test fun gcRootReferencesUnknownObject() {
val analysis = analyzeHprof("gcroot_unknown_object.hprof")
assertThat(analysis.applicationLeaks).hasSize(2)
}
@Test fun androidMStripped() {
val stripper = HprofPrimitiveArrayStripper()
val sourceHprof = fileFromResources("leak_asynctask_m.hprof")
val strippedHprof = stripper.stripPrimitiveArrays(sourceHprof)
assertThat(readThreadNames(sourceHprof)).contains("AsyncTask #1")
assertThat(readThreadNames(strippedHprof)).allMatch { threadName ->
threadName.all { character -> character == '?' }
}
}
private fun readThreadNames(hprofFile: File): List<String> {
val threadNames = Hprof.open(hprofFile)
.use { hprof ->
val graph = HprofHeapGraph.indexHprof(hprof)
graph.findClassByName("java.lang.Thread")!!.instances.map { instance ->
instance["java.lang.Thread", "name"]!!.value.readAsJavaString()!!
}
.toList()
}
return threadNames
}
@Test fun androidO() {
val analysis = analyzeHprof("leak_asynctask_o.hprof")
assertThat(analysis.applicationLeaks).hasSize(1)
val leak = analysis.applicationLeaks[0]
assertThat(leak.className).isEqualTo("com.example.leakcanary.MainActivity")
}
private enum class WRAPS_ACTIVITY {
DESTROYED,
NOT_DESTROYED,
NOT_ACTIVITY
}
@Test fun androidOCountActivityWrappingContexts() {
val contextWrapperStatuses = Hprof.open(fileFromResources("leak_asynctask_o.hprof"))
.use { hprof ->
val graph = HprofHeapGraph.indexHprof(hprof)
graph.instances.filter { it instanceOf "android.content.ContextWrapper" && !(it instanceOf "android.app.Activity") }
.map { instance ->
val reporter = ObjectReporter(instance)
AndroidObjectInspectors.CONTEXT_WRAPPER.inspect(reporter)
if (reporter.leakingReasons.size == 1) {
DESTROYED
} else if (reporter.labels.size == 1) {
if ("Activity.mDestroyed false" in reporter.labels.first()) {
NOT_DESTROYED
} else {
NOT_ACTIVITY
}
} else throw IllegalStateException(
"Unexpected, should have 1 leaking status ${reporter.leakingReasons} or one label ${reporter.labels}"
)
}
.toList()
}
assertThat(contextWrapperStatuses.filter { it == DESTROYED }).hasSize(12)
assertThat(contextWrapperStatuses.filter { it == NOT_DESTROYED }).hasSize(6)
assertThat(contextWrapperStatuses.filter { it == NOT_ACTIVITY }).hasSize(1)
}
@Test fun gcRootInNonPrimaryHeap() {
val analysis = analyzeHprof("gc_root_in_non_primary_heap.hprof")
assertThat(analysis.applicationLeaks).hasSize(1)
val leak = analysis.applicationLeaks[0]
assertThat(leak.className).isEqualTo("com.example.leakcanary.MainActivity")
}
private fun analyzeHprof(fileName: String): HeapAnalysisSuccess {
return analyzeHprof(fileFromResources(fileName))
}
private fun fileFromResources(fileName: String): File {
val classLoader = Thread.currentThread()
.contextClassLoader
val url = classLoader.getResource(fileName)
return File(url.path)
}
private fun analyzeHprof(hprofFile: File): HeapAnalysisSuccess {
val heapAnalyzer = HeapAnalyzer(OnAnalysisProgressListener.NO_OP)
val analysis = heapAnalyzer.analyze(
hprofFile, AndroidReferenceMatchers.appDefaults, false, AndroidObjectInspectors.appDefaults
)
println(analysis)
return analysis as HeapAnalysisSuccess
}
}