<lambda>null1 package leakcanary.internal
2 
3 import android.os.SystemClock
4 import java.io.File
5 import shark.ConstantMemoryMetricsDualSourceProvider
6 import shark.FileSourceProvider
7 import shark.HeapAnalysis
8 import shark.HeapAnalysisException
9 import shark.HeapAnalysisFailure
10 import shark.HeapAnalysisSuccess
11 import shark.HeapAnalyzer
12 import shark.HprofHeapGraph
13 import shark.HprofHeapGraph.Companion.openHeapGraph
14 import shark.LeakingObjectFinder
15 import shark.MetadataExtractor
16 import shark.ObjectInspector
17 import shark.OnAnalysisProgressListener
18 import shark.ProguardMapping
19 import shark.ReferenceMatcher
20 import shark.SharkLog
21 
22 /**
23  * Sets up [HeapAnalyzer] for instrumentation tests and delegates heap analysis.
24  */
25 internal class InstrumentationHeapAnalyzer(
26   val leakingObjectFinder: LeakingObjectFinder,
27   val referenceMatchers: List<ReferenceMatcher>,
28   val computeRetainedHeapSize: Boolean,
29   val metadataExtractor: MetadataExtractor,
30   val objectInspectors: List<ObjectInspector>,
31   val proguardMapping: ProguardMapping?
32 ) {
33 
34   fun analyze(heapDumpFile: File): HeapAnalysis {
35     var lastStepUptimeMs = -1L
36     val heapAnalyzer = HeapAnalyzer { newStep ->
37       val now = SystemClock.uptimeMillis()
38       val lastStepString = if (lastStepUptimeMs != -1L) {
39         val lastStepDurationMs = now - lastStepUptimeMs
40         val lastStep = OnAnalysisProgressListener.Step.values()[newStep.ordinal - 1]
41         "${lastStep.humanReadableName} took $lastStepDurationMs ms, now "
42       } else {
43         ""
44       }
45       SharkLog.d { "${lastStepString}working on ${newStep.humanReadableName}" }
46       lastStepUptimeMs = now
47     }
48 
49     val sourceProvider = ConstantMemoryMetricsDualSourceProvider(FileSourceProvider(heapDumpFile))
50 
51     val closeableGraph = try {
52       sourceProvider.openHeapGraph(proguardMapping)
53     } catch (throwable: Throwable) {
54       return HeapAnalysisFailure(
55         heapDumpFile = heapDumpFile,
56         createdAtTimeMillis = System.currentTimeMillis(),
57         analysisDurationMillis = 0,
58         exception = HeapAnalysisException(throwable)
59       )
60     }
61     return closeableGraph
62       .use { graph ->
63         val result = heapAnalyzer.analyze(
64           heapDumpFile = heapDumpFile,
65           graph = graph,
66           leakingObjectFinder = leakingObjectFinder,
67           referenceMatchers = referenceMatchers,
68           computeRetainedHeapSize = computeRetainedHeapSize,
69           objectInspectors = objectInspectors,
70           metadataExtractor = metadataExtractor
71         )
72         if (result is HeapAnalysisSuccess) {
73           val lruCacheStats = (graph as HprofHeapGraph).lruCacheStats()
74           val randomAccessStats =
75             "RandomAccess[" +
76               "bytes=${sourceProvider.randomAccessByteReads}," +
77               "reads=${sourceProvider.randomAccessReadCount}," +
78               "travel=${sourceProvider.randomAccessByteTravel}," +
79               "range=${sourceProvider.byteTravelRange}," +
80               "size=${heapDumpFile.length()}" +
81               "]"
82           val stats = "$lruCacheStats $randomAccessStats"
83           result.copy(metadata = result.metadata + ("Stats" to stats))
84         } else result
85       }
86   }
87 }
88