xref: /aosp_15_r20/external/leakcanary2/shark/src/main/java/shark/internal/JavaLocalReferenceReader.kt (revision d9e8da70d8c9df9a41d7848ae506fb3115cae6e6)

<lambda>null1 package shark.internal
2 
3 import shark.HeapGraph
4 import shark.internal.ChainingInstanceReferenceReader.VirtualInstanceReferenceReader
5 import shark.HeapObject.HeapInstance
6 import shark.IgnoredReferenceMatcher
7 import shark.LibraryLeakReferenceMatcher
8 import shark.internal.Reference.LazyDetails
9 import shark.internal.ReferenceLocationType.LOCAL
10 import shark.ReferenceMatcher
11 import shark.ReferencePattern.JavaLocalPattern
12 import shark.filterFor
13 
14 internal class JavaLocalReferenceReader(
15   val graph: HeapGraph,
16   referenceMatchers: List<ReferenceMatcher>
17 ) : VirtualInstanceReferenceReader {
18 
19   private val threadClassObjectIds: Set<Long> =
20     graph.findClassByName(Thread::class.java.name)?.let { threadClass ->
21       setOf(threadClass.objectId) + (threadClass.subclasses
22         .map { it.objectId }
23         .toSet())
24     }?: emptySet()
25 
26   private val threadNameReferenceMatchers: Map<String, ReferenceMatcher>
27 
28   init {
29     val threadNames = mutableMapOf<String, ReferenceMatcher>()
30     referenceMatchers.filterFor(graph).forEach { referenceMatcher ->
31       when (val pattern = referenceMatcher.pattern) {
32         is JavaLocalPattern -> {
33           threadNames[pattern.threadName] = referenceMatcher
34         }
35       }
36     }
37     this.threadNameReferenceMatchers = threadNames
38   }
39 
40   override fun matches(instance: HeapInstance): Boolean {
41     return instance.instanceClassId in threadClassObjectIds &&
42       ThreadObjects.getByThreadObjectId(graph, instance.objectId) != null
43   }
44 
45   override fun read(source: HeapInstance): Sequence<Reference> {
46     val referenceMatcher =  source[Thread::class, "name"]?.value?.readAsJavaString()?.let { threadName ->
47       threadNameReferenceMatchers[threadName]
48     }
49 
50     if (referenceMatcher is IgnoredReferenceMatcher) {
51       return emptySequence()
52     }
53     val threadClassId = source.instanceClassId
54     return JavaFrames.getByThreadObjectId(graph, source.objectId)?.let { frames ->
55       frames.asSequence().map { frame ->
56         Reference(
57           valueObjectId = frame.id,
58           // Java Frames always have low priority because their path is harder to understand
59           // for developers
60           isLowPriority = true,
61           lazyDetailsResolver = {
62             LazyDetails(
63               // Unfortunately Android heap dumps do not include stack trace data, so
64               // JavaFrame.frameNumber is always -1 and we cannot know which method is causing the
65               // reference to be held.
66               name = "",
67               locationClassObjectId = threadClassId,
68               locationType = LOCAL,
69               matchedLibraryLeak = referenceMatcher as LibraryLeakReferenceMatcher?,
70               isVirtual = true
71             )
72           }
73         )
74       }
75     } ?: emptySequence()
76   }
77 }
78