xref: /aosp_15_r20/external/leakcanary2/shark-hprof/src/main/java/shark/StreamingHprofReader.kt (revision d9e8da70d8c9df9a41d7848ae506fb3115cae6e6)

<lambda>null1 package shark
2 
3 import java.io.File
4 import okio.Source
5 import shark.HprofRecordTag.CLASS_DUMP
6 import shark.HprofRecordTag.HEAP_DUMP
7 import shark.HprofRecordTag.HEAP_DUMP_END
8 import shark.HprofRecordTag.HEAP_DUMP_INFO
9 import shark.HprofRecordTag.HEAP_DUMP_SEGMENT
10 import shark.HprofRecordTag.INSTANCE_DUMP
11 import shark.HprofRecordTag.LOAD_CLASS
12 import shark.HprofRecordTag.OBJECT_ARRAY_DUMP
13 import shark.HprofRecordTag.PRIMITIVE_ARRAY_DUMP
14 import shark.HprofRecordTag.PRIMITIVE_ARRAY_NODATA
15 import shark.HprofRecordTag.ROOT_DEBUGGER
16 import shark.HprofRecordTag.ROOT_FINALIZING
17 import shark.HprofRecordTag.ROOT_INTERNED_STRING
18 import shark.HprofRecordTag.ROOT_JAVA_FRAME
19 import shark.HprofRecordTag.ROOT_JNI_GLOBAL
20 import shark.HprofRecordTag.ROOT_JNI_LOCAL
21 import shark.HprofRecordTag.ROOT_JNI_MONITOR
22 import shark.HprofRecordTag.ROOT_MONITOR_USED
23 import shark.HprofRecordTag.ROOT_NATIVE_STACK
24 import shark.HprofRecordTag.ROOT_REFERENCE_CLEANUP
25 import shark.HprofRecordTag.ROOT_STICKY_CLASS
26 import shark.HprofRecordTag.ROOT_THREAD_BLOCK
27 import shark.HprofRecordTag.ROOT_THREAD_OBJECT
28 import shark.HprofRecordTag.ROOT_UNKNOWN
29 import shark.HprofRecordTag.ROOT_UNREACHABLE
30 import shark.HprofRecordTag.ROOT_VM_INTERNAL
31 import shark.HprofRecordTag.STACK_FRAME
32 import shark.HprofRecordTag.STACK_TRACE
33 import shark.HprofRecordTag.STRING_IN_UTF8
34 import shark.PrimitiveType.Companion.REFERENCE_HPROF_TYPE
35 import shark.PrimitiveType.INT
36 import shark.StreamingHprofReader.Companion.readerFor
37 
38 /**
39  * Reads the entire content of a Hprof source in one fell swoop.
40  * Call [readerFor] to obtain a new instance.
41  */
42 class StreamingHprofReader private constructor(
43   private val sourceProvider: StreamingSourceProvider,
44   private val header: HprofHeader
45 ) {
46 
47   /**
48    * Obtains a new source to read all hprof records from and calls [listener] back for each record
49    * that matches one of the provided [recordTags].
50    *
51    * @return the number of bytes read from the source
52    */
53   @Suppress("ComplexMethod", "LongMethod", "NestedBlockDepth")
54   fun readRecords(
55     recordTags: Set<HprofRecordTag>,
56     listener: OnHprofRecordTagListener
57   ): Long {
58     return sourceProvider.openStreamingSource().use { source ->
59       val reader = HprofRecordReader(header, source)
60       reader.skip(header.recordsPosition)
61 
62       // Local ref optimizations
63       val intByteSize = INT.byteSize
64       val identifierByteSize = reader.sizeOf(REFERENCE_HPROF_TYPE)
65 
66       while (!source.exhausted()) {
67         // type of the record
68         val tag = reader.readUnsignedByte()
69 
70         // number of microseconds since the time stamp in the header
71         reader.skip(intByteSize)
72 
73         // number of bytes that follow and belong to this record
74         val length = reader.readUnsignedInt()
75 
76         when (tag) {
77           STRING_IN_UTF8.tag -> {
78             if (STRING_IN_UTF8 in recordTags) {
79               listener.onHprofRecord(STRING_IN_UTF8, length, reader)
80             } else {
81               reader.skip(length)
82             }
83           }
84           LOAD_CLASS.tag -> {
85             if (LOAD_CLASS in recordTags) {
86               listener.onHprofRecord(LOAD_CLASS, length, reader)
87             } else {
88               reader.skip(length)
89             }
90           }
91           STACK_FRAME.tag -> {
92             if (STACK_FRAME in recordTags) {
93               listener.onHprofRecord(STACK_FRAME, length, reader)
94             } else {
95               reader.skip(length)
96             }
97           }
98           STACK_TRACE.tag -> {
99             if (STACK_TRACE in recordTags) {
100               listener.onHprofRecord(STACK_TRACE, length, reader)
101             } else {
102               reader.skip(length)
103             }
104           }
105           HEAP_DUMP.tag, HEAP_DUMP_SEGMENT.tag -> {
106             val heapDumpStart = reader.bytesRead
107             var previousTag = 0
108             var previousTagPosition = 0L
109             while (reader.bytesRead - heapDumpStart < length) {
110               val heapDumpTagPosition = reader.bytesRead
111               val heapDumpTag = reader.readUnsignedByte()
112               when (heapDumpTag) {
113                 ROOT_UNKNOWN.tag -> {
114                   if (ROOT_UNKNOWN in recordTags) {
115                     listener.onHprofRecord(ROOT_UNKNOWN, -1, reader)
116                   } else {
117                     reader.skip(identifierByteSize)
118                   }
119                 }
120                 ROOT_JNI_GLOBAL.tag -> {
121                   if (ROOT_JNI_GLOBAL in recordTags) {
122                     listener.onHprofRecord(ROOT_JNI_GLOBAL, -1, reader)
123                   } else {
124                     reader.skip(identifierByteSize + identifierByteSize)
125                   }
126                 }
127                 ROOT_JNI_LOCAL.tag -> {
128                   if (ROOT_JNI_LOCAL in recordTags) {
129                     listener.onHprofRecord(ROOT_JNI_LOCAL, -1, reader)
130                   } else {
131                     reader.skip(identifierByteSize + intByteSize + intByteSize)
132                   }
133                 }
134 
135                 ROOT_JAVA_FRAME.tag -> {
136                   if (ROOT_JAVA_FRAME in recordTags) {
137                     listener.onHprofRecord(ROOT_JAVA_FRAME, -1, reader)
138                   } else {
139                     reader.skip(identifierByteSize + intByteSize + intByteSize)
140                   }
141                 }
142 
143                 ROOT_NATIVE_STACK.tag -> {
144                   if (ROOT_NATIVE_STACK in recordTags) {
145                     listener.onHprofRecord(ROOT_NATIVE_STACK, -1, reader)
146                   } else {
147                     reader.skip(identifierByteSize + intByteSize)
148                   }
149                 }
150 
151                 ROOT_STICKY_CLASS.tag -> {
152                   if (ROOT_STICKY_CLASS in recordTags) {
153                     listener.onHprofRecord(ROOT_STICKY_CLASS, -1, reader)
154                   } else {
155                     reader.skip(identifierByteSize)
156                   }
157                 }
158                 ROOT_THREAD_BLOCK.tag -> {
159                   if (ROOT_THREAD_BLOCK in recordTags) {
160                     listener.onHprofRecord(ROOT_THREAD_BLOCK, -1, reader)
161                   } else {
162                     reader.skip(identifierByteSize + intByteSize)
163                   }
164                 }
165 
166                 ROOT_MONITOR_USED.tag -> {
167                   if (ROOT_MONITOR_USED in recordTags) {
168                     listener.onHprofRecord(ROOT_MONITOR_USED, -1, reader)
169                   } else {
170                     reader.skip(identifierByteSize)
171                   }
172                 }
173 
174                 ROOT_THREAD_OBJECT.tag -> {
175                   if (ROOT_THREAD_OBJECT in recordTags) {
176                     listener.onHprofRecord(ROOT_THREAD_OBJECT, -1, reader)
177                   } else {
178                     reader.skip(identifierByteSize + intByteSize + intByteSize)
179                   }
180                 }
181 
182                 ROOT_INTERNED_STRING.tag -> {
183                   if (ROOT_INTERNED_STRING in recordTags) {
184                     listener.onHprofRecord(ROOT_INTERNED_STRING, -1, reader)
185                   } else {
186                     reader.skip(identifierByteSize)
187                   }
188                 }
189 
190                 ROOT_FINALIZING.tag -> {
191                   if (ROOT_FINALIZING in recordTags) {
192                     listener.onHprofRecord(ROOT_FINALIZING, -1, reader)
193                   } else {
194                     reader.skip(identifierByteSize)
195                   }
196                 }
197 
198                 ROOT_DEBUGGER.tag -> {
199                   if (ROOT_DEBUGGER in recordTags) {
200                     listener.onHprofRecord(ROOT_DEBUGGER, -1, reader)
201                   } else {
202                     reader.skip(identifierByteSize)
203                   }
204                 }
205 
206                 ROOT_REFERENCE_CLEANUP.tag -> {
207                   if (ROOT_REFERENCE_CLEANUP in recordTags) {
208                     listener.onHprofRecord(ROOT_REFERENCE_CLEANUP, -1, reader)
209                   } else {
210                     reader.skip(identifierByteSize)
211                   }
212                 }
213 
214                 ROOT_VM_INTERNAL.tag -> {
215                   if (ROOT_VM_INTERNAL in recordTags) {
216                     listener.onHprofRecord(ROOT_VM_INTERNAL, -1, reader)
217                   } else {
218                     reader.skip(identifierByteSize)
219                   }
220                 }
221 
222                 ROOT_JNI_MONITOR.tag -> {
223                   if (ROOT_JNI_MONITOR in recordTags) {
224                     listener.onHprofRecord(ROOT_JNI_MONITOR, -1, reader)
225                   } else {
226                     reader.skip(identifierByteSize + intByteSize + intByteSize)
227                   }
228                 }
229 
230                 ROOT_UNREACHABLE.tag -> {
231                   if (ROOT_UNREACHABLE in recordTags) {
232                     listener.onHprofRecord(ROOT_UNREACHABLE, -1, reader)
233                   } else {
234                     reader.skip(identifierByteSize)
235                   }
236                 }
237                 CLASS_DUMP.tag -> {
238                   if (CLASS_DUMP in recordTags) {
239                     listener.onHprofRecord(CLASS_DUMP, -1, reader)
240                   } else {
241                     reader.skipClassDumpRecord()
242                   }
243                 }
244                 INSTANCE_DUMP.tag -> {
245                   if (INSTANCE_DUMP in recordTags) {
246                     listener.onHprofRecord(INSTANCE_DUMP, -1, reader)
247                   } else {
248                     reader.skipInstanceDumpRecord()
249                   }
250                 }
251 
252                 OBJECT_ARRAY_DUMP.tag -> {
253                   if (OBJECT_ARRAY_DUMP in recordTags) {
254                     listener.onHprofRecord(OBJECT_ARRAY_DUMP, -1, reader)
255                   } else {
256                     reader.skipObjectArrayDumpRecord()
257                   }
258                 }
259 
260                 PRIMITIVE_ARRAY_DUMP.tag -> {
261                   if (PRIMITIVE_ARRAY_DUMP in recordTags) {
262                     listener.onHprofRecord(PRIMITIVE_ARRAY_DUMP, -1, reader)
263                   } else {
264                     reader.skipPrimitiveArrayDumpRecord()
265                   }
266                 }
267 
268                 PRIMITIVE_ARRAY_NODATA.tag -> {
269                   throw UnsupportedOperationException("$PRIMITIVE_ARRAY_NODATA cannot be parsed")
270                 }
271 
272                 HEAP_DUMP_INFO.tag -> {
273                   if (HEAP_DUMP_INFO in recordTags) {
274                     listener.onHprofRecord(HEAP_DUMP_INFO, -1, reader)
275                   } else {
276                     reader.skipHeapDumpInfoRecord()
277                   }
278                 }
279                 else -> throw IllegalStateException(
280                   "Unknown tag ${
281                     "0x%02x".format(
282                       heapDumpTag
283                     )
284                   } at $heapDumpTagPosition after ${
285                     "0x%02x".format(
286                       previousTag
287                     )
288                   } at $previousTagPosition"
289                 )
290               }
291               previousTag = heapDumpTag
292               previousTagPosition = heapDumpTagPosition
293             }
294           }
295           HEAP_DUMP_END.tag -> {
296             if (HEAP_DUMP_END in recordTags) {
297               listener.onHprofRecord(HEAP_DUMP_END, length, reader)
298             }
299           }
300           else -> {
301             reader.skip(length)
302           }
303         }
304       }
305       reader.bytesRead
306     }
307   }
308 
309   companion object {
310 
311     /**
312      * Creates a [StreamingHprofReader] for the provided [hprofFile]. [hprofHeader] will be read from
313      * [hprofFile] unless you provide it.
314      */
315     fun readerFor(
316       hprofFile: File,
317       hprofHeader: HprofHeader = HprofHeader.parseHeaderOf(hprofFile)
318     ): StreamingHprofReader {
319       val sourceProvider = FileSourceProvider(hprofFile)
320       return readerFor(sourceProvider, hprofHeader)
321     }
322 
323     /**
324      * Creates a [StreamingHprofReader] that will call [StreamingSourceProvider.openStreamingSource]
325      * on every [readRecords] to obtain a [Source] to read the hprof data from. Before reading the
326      * hprof records, [StreamingHprofReader] will skip [HprofHeader.recordsPosition] bytes.
327      */
328     fun readerFor(
329       hprofSourceProvider: StreamingSourceProvider,
330       hprofHeader: HprofHeader = hprofSourceProvider.openStreamingSource()
331         .use { HprofHeader.parseHeaderOf(it) }
332     ): StreamingHprofReader {
333       return StreamingHprofReader(hprofSourceProvider, hprofHeader)
334     }
335   }
336 }
337