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

<lambda>null1 package shark.internal
2 
3 import shark.HeapGraph
4 import shark.HeapObject.HeapInstance
5 
6 internal class AndroidNativeSizeMapper(private val graph: HeapGraph) {
7 
8   /**
9    * Returns a map of Object id to native size as tracked by NativeAllocationRegistry$CleanerThunk
10    */
11   fun mapNativeSizes(): Map<Long, Int> {
12     return graph.context.getOrPut("AndroidNativeSizeMapper") {
13       buildNativeSizeMap()
14     }
15   }
16 
17   private fun buildNativeSizeMap(): Map<Long, Int> {
18     val nativeSizes = mutableMapOf<Long, Int>()
19     // Doc from perflib:
20     // Native allocations can be identified by looking at instances of
21     // libcore.util.NativeAllocationRegistry$CleanerThunk. The "owning" Java object is the
22     // "referent" field of the "sun.misc.Cleaner" instance with a hard reference to the
23     // CleanerThunk.
24     //
25     // The size is in the 'size' field of the libcore.util.NativeAllocationRegistry instance
26     // that the CleanerThunk has a pointer to. The native pointer is in the 'nativePtr' field of
27     // the CleanerThunk. The hprof does not include the native bytes pointed to.
28     graph.findClassByName("sun.misc.Cleaner")?.let { cleanerClass ->
29       cleanerClass.directInstances.forEach { cleaner ->
30         val thunkField = cleaner["sun.misc.Cleaner", "thunk"]
31         val thunkId = thunkField?.value?.asNonNullObjectId
32         val referentId =
33           cleaner["java.lang.ref.Reference", "referent"]?.value?.asNonNullObjectId
34         if (thunkId != null && referentId != null) {
35           val thunkRecord = thunkField.value.asObject
36           if (thunkRecord is HeapInstance && thunkRecord instanceOf "libcore.util.NativeAllocationRegistry\$CleanerThunk") {
37             val allocationRegistryIdField =
38               thunkRecord["libcore.util.NativeAllocationRegistry\$CleanerThunk", "this\$0"]
39             if (allocationRegistryIdField != null && allocationRegistryIdField.value.isNonNullReference) {
40               val allocationRegistryRecord = allocationRegistryIdField.value.asObject
41               if (allocationRegistryRecord is HeapInstance && allocationRegistryRecord instanceOf "libcore.util.NativeAllocationRegistry") {
42                 var nativeSize = nativeSizes[referentId] ?: 0
43                 nativeSize += allocationRegistryRecord["libcore.util.NativeAllocationRegistry", "size"]?.value?.asLong?.toInt()
44                   ?: 0
45                 nativeSizes[referentId] = nativeSize
46               }
47             }
48           }
49         }
50       }
51     }
52     return nativeSizes
53   }
54 
55   companion object {
56     fun mapNativeSizes(heapGraph: HeapGraph): Map<Long, Int> {
57       return AndroidNativeSizeMapper(heapGraph).mapNativeSizes()
58     }
59   }
60 }
61