StringPathFinderOptimTest.kt
TLDR
This file contains a test class, StringPathFinderOptimTest
, which tests the path finding algorithm for strings. The algorithm avoids unnecessary reads by skipping the content of strings. The file includes three test methods that test different scenarios for the reachability of String#value
on different Android versions.
Methods
findStringContent(hprofFile: File): HeapAnalysisSuccess
This private method takes a File
parameter representing the HPROF file to be analyzed. It uses a HeapAnalyzer
to perform the analysis on the given heap dump file. The method finds all instances of the java.lang.String
class and retrieves the value
field of each instance. It then returns the result of the analysis as a HeapAnalysisSuccess
object.
Classes
StringPathFinderOptimTest
This test class contains three test methods that test the findStringContent
method in different scenarios. The tests check the reachability of String#value
on different Android versions. The class imports org.assertj.core.api.Assertions.assertThat
and java.io.File
.
package shark
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import java.io.File
/**
* Our path finding algorithm skips going through the content of strings to avoid unnecessary reads.
* We add back the corresponding size when computing the shallow size of retained strings,
* however that only works if those byte arrays aren't reachable through other references.
* If they were, this could either inflate the retained size number (byte array should not be
* considered retained) or deflate it (byte array counted twice when reached from two retained
* instances).
*/
class StringPathFinderOptimTest {
@Test fun `String#value not reachable on Android O`() {
val hprofFile = "leak_asynctask_o.hprof".classpathFile()
val analysis = findStringContent(hprofFile)
assertThat(analysis.allLeaks.count()).isEqualTo(0)
}
@Test fun `String#value not reachable on Android M`() {
val hprofFile = "leak_asynctask_m.hprof".classpathFile()
val analysis = findStringContent(hprofFile)
assertThat(analysis.allLeaks.count()).isEqualTo(0)
}
@Test fun `String#value only reachable for String#ASCII pre Android M`() {
val hprofFile = "leak_asynctask_pre_m.hprof".classpathFile()
val analysis = findStringContent(hprofFile)
assertThat(analysis.allLeaks.count()).isEqualTo(1)
val path = analysis.applicationLeaks.first().leakTraces.first()
assertThat(path.referencePath.first().referenceName).isEqualTo("ASCII")
}
private fun findStringContent(hprofFile: File): HeapAnalysisSuccess {
val heapAnalyzer = HeapAnalyzer(OnAnalysisProgressListener.NO_OP)
val analysis = heapAnalyzer.analyze(
heapDumpFile = hprofFile,
leakingObjectFinder = { graph ->
graph.findClassByName("java.lang.String")!!.instances.map { instance ->
instance["java.lang.String", "value"]?.value?.asNonNullObjectId!!
}.toSet()
},
referenceMatchers = AndroidReferenceMatchers.appDefaults,
computeRetainedHeapSize = true,
objectInspectors = AndroidObjectInspectors.appDefaults,
metadataExtractor = AndroidMetadataExtractor
)
println(analysis)
return analysis as HeapAnalysisSuccess
}
}