HeapDumps.kt
TLDR
The provided file, HeapDumps.kt, contains multiple functions that write dump files for various scenarios. These scenarios include creating weak references, creating paths to instances, creating paths to strings, creating paths to char arrays, creating multiple paths to instances, creating multiple activity leaks, creating Java local leaks, and creating two-path Java local shorter leaks.
Methods
writeWeakReferenceCleared
This method writes a dump file containing a weak reference that has been cleared.
writeNoPathToInstance
This method writes a dump file containing a weak reference to an instance without any paths to it.
writeSinglePathToInstance
This method writes a dump file containing a weak reference to an instance with a single shortest path to it.
writeSinglePathToString
This method writes a dump file containing a weak reference to a string with a single shortest path to it.
writeSinglePathsToCharArrays
This method writes a dump file containing weak references to multiple char arrays, each with a single shortest path to it.
writeTwoPathsToInstance
This method writes a dump file containing a weak reference to an instance with two paths to it - a shortest path and a longest path.
writeMultipleActivityLeaks
This method writes a dump file containing multiple activity leaks in an ActivityHolder
class.
writeJavaLocalLeak
This method writes a dump file containing a Java local leak, which includes a thread object and a Java frame.
writeTwoPathJavaLocalShorterLeak
This method writes a dump file containing a two-path Java local shorter leak. It includes a thread object, a Java frame, and an instance with a longest path.
package shark
import shark.GcRoot.JavaFrame
import shark.GcRoot.ThreadObject
import shark.ValueHolder.BooleanHolder
import shark.ValueHolder.ReferenceHolder
import java.io.File
fun File.writeWeakReferenceCleared() {
dump {
keyedWeakReference(ReferenceHolder(0))
}
}
fun File.writeNoPathToInstance() {
dump {
keyedWeakReference(instance(clazz("Leaking")))
}
}
fun File.writeSinglePathToInstance() {
dump {
val leaking = instance(clazz("Leaking"))
keyedWeakReference(leaking)
clazz(
"GcRoot", staticFields = listOf(
"shortestPath" to leaking
)
)
}
}
fun File.writeSinglePathToString(value: String = "Hi") {
dump {
val leaking = string(value)
keyedWeakReference(leaking)
clazz(
"GcRoot", staticFields = listOf(
"shortestPath" to leaking
)
)
}
}
fun File.writeSinglePathsToCharArrays(values: List<String>) {
dump {
val arrays = mutableListOf<Long>()
values.forEach {
val leaking = it.charArrayDump
keyedWeakReference(leaking)
arrays.add(leaking.value)
}
clazz(
className = "GcRoot",
staticFields = listOf(
"arrays" to ReferenceHolder(
objectArray(clazz("char[][]"), arrays.toLongArray())
)
)
)
}
}
fun File.writeTwoPathsToInstance() {
dump {
val leaking = instance(clazz("Leaking"))
keyedWeakReference(leaking)
val hasLeaking = instance(
clazz("HasLeaking", fields = listOf("leaking" to ReferenceHolder::class)),
fields = listOf(leaking)
)
clazz(
"GcRoot", staticFields = listOf(
"shortestPath" to leaking,
"longestPath" to hasLeaking
)
)
}
}
fun File.writeMultipleActivityLeaks(leakCount: Int) {
dump {
val activityClassId = clazz(
className = "android.app.Activity",
fields = listOf("mDestroyed" to BooleanHolder::class)
)
val exampleActivityClassId = clazz(
superclassId = activityClassId,
className = "com.example.ExampleActivity"
)
val activityArrayClassId = arrayClass("com.example.ExampleActivity")
val destroyedActivities = mutableListOf<ReferenceHolder>()
for (i in 1..leakCount) {
destroyedActivities.add(instance(exampleActivityClassId, listOf(BooleanHolder(true))))
}
clazz(
className = "com.example.ActivityHolder",
staticFields = listOf(
"activities" to
objectArrayOf(
activityArrayClassId, *destroyedActivities.toTypedArray()
)
)
)
destroyedActivities.forEach { instanceId ->
keyedWeakReference(instanceId)
}
}
}
fun File.writeJavaLocalLeak(
threadClass: String? = null,
threadName: String? = null
) {
dump {
val threadClassId =
clazz(
className = Thread::class.java.name,
fields = if (threadName != null) listOf("name" to ReferenceHolder::class) else emptyList()
)
val myThreadClassId = if (threadClass == null) {
threadClassId
} else {
clazz(className = threadClass, superclassId = threadClassId)
}
val threadInstance = instance(
myThreadClassId, if (threadName != null) {
listOf(string(threadName))
} else {
emptyList()
}
)
gcRoot(
ThreadObject(
id = threadInstance.value, threadSerialNumber = 42, stackTraceSerialNumber = 0
)
)
val leaking = "Leaking" watchedInstance {}
gcRoot(JavaFrame(id = leaking.value, threadSerialNumber = 42, frameNumber = 0))
}
}
fun File.writeTwoPathJavaLocalShorterLeak(
threadClass: String,
threadName: String
) {
dump {
val threadClassId =
clazz(className = "java.lang.Thread", fields = listOf("name" to ReferenceHolder::class))
val myThreadClassId = clazz(className = threadClass, superclassId = threadClassId)
val threadInstance = instance(myThreadClassId, listOf(string(threadName)))
gcRoot(
ThreadObject(
id = threadInstance.value, threadSerialNumber = 42, stackTraceSerialNumber = 0
)
)
val leaking = "Leaking" watchedInstance {}
gcRoot(JavaFrame(id = leaking.value, threadSerialNumber = 42, frameNumber = 0))
val hasLeaking = instance(
clazz("HasLeaking", fields = listOf("leaking" to ReferenceHolder::class)),
fields = listOf(leaking)
)
clazz(
"GcRoot", staticFields = listOf(
"longestPath" to hasLeaking
)
)
}
}