<lambda>null1 package shark
2
3 import okio.BufferedSource
4 import shark.GcRoot.Debugger
5 import shark.GcRoot.Finalizing
6 import shark.GcRoot.InternedString
7 import shark.GcRoot.JavaFrame
8 import shark.GcRoot.JniGlobal
9 import shark.GcRoot.JniLocal
10 import shark.GcRoot.JniMonitor
11 import shark.GcRoot.MonitorUsed
12 import shark.GcRoot.NativeStack
13 import shark.GcRoot.ReferenceCleanup
14 import shark.GcRoot.StickyClass
15 import shark.GcRoot.ThreadBlock
16 import shark.GcRoot.ThreadObject
17 import shark.GcRoot.Unknown
18 import shark.GcRoot.Unreachable
19 import shark.GcRoot.VmInternal
20 import shark.HprofRecord.HeapDumpRecord.HeapDumpInfoRecord
21 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.ClassDumpRecord
22 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.ClassDumpRecord.FieldRecord
23 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.ClassDumpRecord.StaticFieldRecord
24 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.InstanceDumpRecord
25 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.ObjectArrayDumpRecord
26 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.PrimitiveArrayDumpRecord
27 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.PrimitiveArrayDumpRecord.BooleanArrayDump
28 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.PrimitiveArrayDumpRecord.ByteArrayDump
29 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.PrimitiveArrayDumpRecord.CharArrayDump
30 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.PrimitiveArrayDumpRecord.DoubleArrayDump
31 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.PrimitiveArrayDumpRecord.FloatArrayDump
32 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.PrimitiveArrayDumpRecord.IntArrayDump
33 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.PrimitiveArrayDumpRecord.LongArrayDump
34 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.PrimitiveArrayDumpRecord.ShortArrayDump
35 import shark.HprofRecord.LoadClassRecord
36 import shark.HprofRecord.StackFrameRecord
37 import shark.HprofRecord.StackTraceRecord
38 import shark.HprofRecord.StringRecord
39 import shark.PrimitiveType.BOOLEAN
40 import shark.PrimitiveType.BYTE
41 import shark.PrimitiveType.CHAR
42 import shark.PrimitiveType.DOUBLE
43 import shark.PrimitiveType.FLOAT
44 import shark.PrimitiveType.INT
45 import shark.PrimitiveType.LONG
46 import shark.PrimitiveType.SHORT
47 import shark.ValueHolder.BooleanHolder
48 import shark.ValueHolder.ByteHolder
49 import shark.ValueHolder.CharHolder
50 import shark.ValueHolder.DoubleHolder
51 import shark.ValueHolder.FloatHolder
52 import shark.ValueHolder.IntHolder
53 import shark.ValueHolder.LongHolder
54 import shark.ValueHolder.ReferenceHolder
55 import shark.ValueHolder.ShortHolder
56 import java.nio.charset.Charset
57
58 /**
59 * Reads hprof content from an Okio [BufferedSource].
60 *
61 * Binary Dump Format reference: http://hg.openjdk.java.net/jdk6/jdk6/jdk/raw-file/tip/src/share
62 * /demo/jvmti/hprof/manual.html#mozTocId848088
63 *
64 * The Android Hprof format differs in some ways from that reference. This parser implementation
65 * is largely adapted from https://android.googlesource.com/platform/tools/base/+/studio-master-dev
66 * /perflib/src/main/java/com/android/tools/perflib
67 *
68 * Not thread safe, should be used from a single thread.
69 */
70 @Suppress("LargeClass", "TooManyFunctions")
71 class HprofRecordReader internal constructor(
72 header: HprofHeader,
73 private val source: BufferedSource
74 ) {
75
76 /**
77 * How many bytes this reader has read from [source]. Can only increase.
78 */
79 var bytesRead = 0L
80 private set
81
82 private val identifierByteSize = header.identifierByteSize
83
84 private val typeSizes: IntArray
85
86 init {
87 val typeSizesMap =
88 PrimitiveType.byteSizeByHprofType + (PrimitiveType.REFERENCE_HPROF_TYPE to identifierByteSize)
89
90 val maxKey = typeSizesMap.keys.max()!!
91
92 typeSizes = IntArray(maxKey + 1) { key ->
93 typeSizesMap[key] ?: 0
94 }
95 }
96
97 fun sizeOf(type: Int) = typeSizes[type]
98
99 fun readStringRecord(length: Long) = StringRecord(
100 id = readId(),
101 string = readUtf8(length - identifierByteSize)
102 )
103
104 fun readLoadClassRecord() = LoadClassRecord(
105 classSerialNumber = readInt(),
106 id = readId(),
107 stackTraceSerialNumber = readInt(),
108 classNameStringId = readId()
109 )
110
111 fun readStackFrameRecord() = StackFrameRecord(
112 id = readId(),
113 methodNameStringId = readId(),
114 methodSignatureStringId = readId(),
115 sourceFileNameStringId = readId(),
116 classSerialNumber = readInt(),
117 lineNumber = readInt()
118 )
119
120 fun readStackTraceRecord() = StackTraceRecord(
121 stackTraceSerialNumber = readInt(),
122 threadSerialNumber = readInt(),
123 stackFrameIds = readIdArray(readInt())
124 )
125
126 fun readUnknownGcRootRecord() = Unknown(id = readId())
127
128 fun readJniGlobalGcRootRecord() = JniGlobal(
129 id = readId(),
130 jniGlobalRefId = readId()
131 )
132
133 fun readJniLocalGcRootRecord() = JniLocal(
134 id = readId(),
135 threadSerialNumber = readInt(),
136 frameNumber = readInt()
137 )
138
139 fun readJavaFrameGcRootRecord() = JavaFrame(
140 id = readId(),
141 threadSerialNumber = readInt(),
142 frameNumber = readInt()
143 )
144
145 fun readNativeStackGcRootRecord() = NativeStack(
146 id = readId(),
147 threadSerialNumber = readInt()
148 )
149
150 fun readStickyClassGcRootRecord() = StickyClass(id = readId())
151
152 fun readThreadBlockGcRootRecord() = ThreadBlock(id = readId(), threadSerialNumber = readInt())
153
154 fun readMonitorUsedGcRootRecord() = MonitorUsed(id = readId())
155
156 fun readThreadObjectGcRootRecord() = ThreadObject(
157 id = readId(),
158 threadSerialNumber = readInt(),
159 stackTraceSerialNumber = readInt()
160 )
161
162 fun readInternedStringGcRootRecord() = InternedString(id = readId())
163
164 fun readFinalizingGcRootRecord() = Finalizing(id = readId())
165
166 fun readDebuggerGcRootRecord() = Debugger(id = readId())
167
168 fun readReferenceCleanupGcRootRecord() = ReferenceCleanup(id = readId())
169
170 fun readVmInternalGcRootRecord() = VmInternal(id = readId())
171
172 fun readJniMonitorGcRootRecord() = JniMonitor(
173 id = readId(),
174 stackTraceSerialNumber = readInt(),
175 stackDepth = readInt()
176 )
177
178 fun readUnreachableGcRootRecord() = Unreachable(id = readId())
179
180 /**
181 * Reads a full instance record after a instance dump tag.
182 */
183 fun readInstanceDumpRecord(): InstanceDumpRecord {
184 val id = readId()
185 val stackTraceSerialNumber = readInt()
186 val classId = readId()
187 val remainingBytesInInstance = readInt()
188 val fieldValues = readByteArray(remainingBytesInInstance)
189 return InstanceDumpRecord(
190 id = id,
191 stackTraceSerialNumber = stackTraceSerialNumber,
192 classId = classId,
193 fieldValues = fieldValues
194 )
195 }
196
197 fun readHeapDumpInfoRecord(): HeapDumpInfoRecord {
198 val heapId = readInt()
199 return HeapDumpInfoRecord(heapId = heapId, heapNameStringId = readId())
200 }
201
202 /**
203 * Reads a full class record after a class dump tag.
204 */
205 fun readClassDumpRecord(): ClassDumpRecord {
206 val id = readId()
207 // stack trace serial number
208 val stackTraceSerialNumber = readInt()
209 val superclassId = readId()
210 // class loader object ID
211 val classLoaderId = readId()
212 // signers object ID
213 val signersId = readId()
214 // protection domain object ID
215 val protectionDomainId = readId()
216 // reserved
217 readId()
218 // reserved
219 readId()
220
221 // instance size (in bytes)
222 // Useful to compute retained size
223 val instanceSize = readInt()
224
225 // Skip over the constant pool
226 val constantPoolCount = readUnsignedShort()
227 for (i in 0 until constantPoolCount) {
228 // constant pool index
229 skip(SHORT_SIZE)
230 skip(typeSizes[readUnsignedByte()])
231 }
232
233 val staticFieldCount = readUnsignedShort()
234 val staticFields = ArrayList<StaticFieldRecord>(staticFieldCount)
235 for (i in 0 until staticFieldCount) {
236
237 val nameStringId = readId()
238 val type = readUnsignedByte()
239 val value = readValue(type)
240
241 staticFields.add(
242 StaticFieldRecord(
243 nameStringId = nameStringId,
244 type = type,
245 value = value
246 )
247 )
248 }
249
250 val fieldCount = readUnsignedShort()
251 val fields = ArrayList<FieldRecord>(fieldCount)
252 for (i in 0 until fieldCount) {
253 fields.add(FieldRecord(nameStringId = readId(), type = readUnsignedByte()))
254 }
255
256 return ClassDumpRecord(
257 id = id,
258 stackTraceSerialNumber = stackTraceSerialNumber,
259 superclassId = superclassId,
260 classLoaderId = classLoaderId,
261 signersId = signersId,
262 protectionDomainId = protectionDomainId,
263 instanceSize = instanceSize,
264 staticFields = staticFields,
265 fields = fields
266 )
267 }
268
269 /**
270 * Reads a full primitive array record after a primitive array dump tag.
271 */
272 fun readPrimitiveArrayDumpRecord(): PrimitiveArrayDumpRecord {
273 val id = readId()
274 val stackTraceSerialNumber = readInt()
275 // length
276 val arrayLength = readInt()
277 return when (val type = readUnsignedByte()) {
278 BOOLEAN_TYPE -> BooleanArrayDump(
279 id, stackTraceSerialNumber, readBooleanArray(arrayLength)
280 )
281 CHAR_TYPE -> CharArrayDump(
282 id, stackTraceSerialNumber, readCharArray(arrayLength)
283 )
284 FLOAT_TYPE -> FloatArrayDump(
285 id, stackTraceSerialNumber, readFloatArray(arrayLength)
286 )
287 DOUBLE_TYPE -> DoubleArrayDump(
288 id, stackTraceSerialNumber, readDoubleArray(arrayLength)
289 )
290 BYTE_TYPE -> ByteArrayDump(
291 id, stackTraceSerialNumber, readByteArray(arrayLength)
292 )
293 SHORT_TYPE -> ShortArrayDump(
294 id, stackTraceSerialNumber, readShortArray(arrayLength)
295 )
296 INT_TYPE -> IntArrayDump(
297 id, stackTraceSerialNumber, readIntArray(arrayLength)
298 )
299 LONG_TYPE -> LongArrayDump(
300 id, stackTraceSerialNumber, readLongArray(arrayLength)
301 )
302 else -> throw IllegalStateException("Unexpected type $type")
303 }
304 }
305
306 /**
307 * Reads a full object array record after a object array dump tag.
308 */
309 fun readObjectArrayDumpRecord(): ObjectArrayDumpRecord {
310 val id = readId()
311 // stack trace serial number
312 val stackTraceSerialNumber = readInt()
313 val arrayLength = readInt()
314 val arrayClassId = readId()
315 val elementIds = readIdArray(arrayLength)
316 return ObjectArrayDumpRecord(
317 id = id,
318 stackTraceSerialNumber = stackTraceSerialNumber,
319 arrayClassId = arrayClassId,
320 elementIds = elementIds
321 )
322 }
323
324 fun skipClassDumpHeader() {
325 skip(INT_SIZE * 2 + identifierByteSize * 7)
326 skipClassDumpConstantPool()
327 }
328
329 fun skipClassDumpConstantPool() {
330 // Skip over the constant pool
331 val constantPoolCount = readUnsignedShort()
332 for (i in 0 until constantPoolCount) {
333 // constant pool index
334 skip(SHORT.byteSize)
335 skip(sizeOf(readUnsignedByte()))
336 }
337 }
338
339 fun skipClassDumpStaticFields() {
340 val staticFieldCount = readUnsignedShort()
341 for (i in 0 until staticFieldCount) {
342 skip(identifierByteSize)
343 val type = readUnsignedByte()
344 skip(
345 if (type == PrimitiveType.REFERENCE_HPROF_TYPE) {
346 identifierByteSize
347 } else {
348 PrimitiveType.byteSizeByHprofType.getValue(type)
349 }
350 )
351 }
352 }
353
354 fun skipClassDumpFields() {
355 val fieldCount = readUnsignedShort()
356 skip((identifierByteSize + 1) * fieldCount)
357 }
358
359 fun skipInstanceDumpRecord() {
360 skip(identifierByteSize + INT_SIZE + identifierByteSize)
361 val remainingBytesInInstance = readInt()
362 skip(remainingBytesInInstance)
363 }
364
365 fun skipClassDumpRecord() {
366 skip(
367 identifierByteSize + INT_SIZE + identifierByteSize + identifierByteSize + identifierByteSize +
368 identifierByteSize + identifierByteSize + identifierByteSize + INT_SIZE
369 )
370 // Skip over the constant pool
371 val constantPoolCount = readUnsignedShort()
372 for (i in 0 until constantPoolCount) {
373 // constant pool index
374 skip(SHORT_SIZE)
375 skip(typeSizes[readUnsignedByte()])
376 }
377
378 val staticFieldCount = readUnsignedShort()
379
380 for (i in 0 until staticFieldCount) {
381 skip(identifierByteSize)
382 val type = readUnsignedByte()
383 skip(typeSizes[type])
384 }
385
386 val fieldCount = readUnsignedShort()
387 skip(fieldCount * (identifierByteSize + BYTE_SIZE))
388 }
389
390 fun skipObjectArrayDumpRecord() {
391 skip(identifierByteSize + INT_SIZE)
392 val arrayLength = readInt()
393 skip(identifierByteSize + arrayLength * identifierByteSize)
394 }
395
396 fun skipPrimitiveArrayDumpRecord() {
397 skip(identifierByteSize + INT_SIZE)
398 val arrayLength = readInt()
399 val type = readUnsignedByte()
400 skip(arrayLength * typeSizes[type])
401 }
402
403 fun skipHeapDumpInfoRecord() {
404 skip(identifierByteSize + identifierByteSize)
405 }
406
407 fun skip(byteCount: Int) {
408 bytesRead += byteCount
409 return source.skip(byteCount.toLong())
410 }
411
412 fun skipId() {
413 skip(identifierByteSize)
414 }
415
416 fun skip(byteCount: Long) {
417 bytesRead += byteCount
418 return source.skip(byteCount)
419 }
420
421 fun readUnsignedInt(): Long {
422 return readInt().toLong() and INT_MASK
423 }
424
425 fun readUnsignedByte(): Int {
426 return readByte().toInt() and BYTE_MASK
427 }
428
429 /**
430 * Reads a value in the heap dump, which can be a reference or a primitive type.
431 */
432 fun readValue(type: Int): ValueHolder {
433 return when (type) {
434 PrimitiveType.REFERENCE_HPROF_TYPE -> ReferenceHolder(readId())
435 BOOLEAN_TYPE -> BooleanHolder(readBoolean())
436 CHAR_TYPE -> CharHolder(readChar())
437 FLOAT_TYPE -> FloatHolder(readFloat())
438 DOUBLE_TYPE -> DoubleHolder(readDouble())
439 BYTE_TYPE -> ByteHolder(readByte())
440 SHORT_TYPE -> ShortHolder(readShort())
441 INT_TYPE -> IntHolder(readInt())
442 LONG_TYPE -> LongHolder(readLong())
443 else -> throw IllegalStateException("Unknown type $type")
444 }
445 }
446
447 fun readShort(): Short {
448 bytesRead += SHORT_SIZE
449 return source.readShort()
450 }
451
452 fun readInt(): Int {
453 bytesRead += INT_SIZE
454 return source.readInt()
455 }
456
457 fun readIdArray(arrayLength: Int): LongArray {
458 return LongArray(arrayLength) { readId() }
459 }
460
461 fun readBooleanArray(arrayLength: Int): BooleanArray {
462 return BooleanArray(arrayLength) { readByte().toInt() != 0 }
463 }
464
465 fun readCharArray(arrayLength: Int): CharArray {
466 return CharArray(arrayLength) {
467 readChar()
468 }
469 }
470
471 fun readString(
472 byteCount: Int,
473 charset: Charset
474 ): String {
475 bytesRead += byteCount
476 return source.readString(byteCount.toLong(), charset)
477 }
478
479 fun readFloatArray(arrayLength: Int): FloatArray {
480 return FloatArray(arrayLength) { readFloat() }
481 }
482
483 fun readDoubleArray(arrayLength: Int): DoubleArray {
484 return DoubleArray(arrayLength) { readDouble() }
485 }
486
487 fun readShortArray(arrayLength: Int): ShortArray {
488 return ShortArray(arrayLength) { readShort() }
489 }
490
491 fun readIntArray(arrayLength: Int): IntArray {
492 return IntArray(arrayLength) { readInt() }
493 }
494
495 fun readLongArray(arrayLength: Int): LongArray {
496 return LongArray(arrayLength) { readLong() }
497 }
498
499 fun readLong(): Long {
500 bytesRead += LONG_SIZE
501 return source.readLong()
502 }
503
504 fun readByte(): Byte {
505 bytesRead += BYTE_SIZE
506 return source.readByte()
507 }
508
509 fun readBoolean(): Boolean {
510 bytesRead += BOOLEAN_SIZE
511 return source.readByte()
512 .toInt() != 0
513 }
514
515 fun readByteArray(byteCount: Int): ByteArray {
516 bytesRead += byteCount
517 return source.readByteArray(byteCount.toLong())
518 }
519
520 fun readChar(): Char {
521 return readString(CHAR_SIZE, Charsets.UTF_16BE)[0]
522 }
523
524 fun readFloat(): Float {
525 return Float.fromBits(readInt())
526 }
527
528 fun readDouble(): Double {
529 return Double.fromBits(readLong())
530 }
531
532 fun readId(): Long {
533 // As long as we don't interpret IDs, reading signed values here is fine.
534 return when (identifierByteSize) {
535 1 -> readByte().toLong()
536 2 -> readShort().toLong()
537 4 -> readInt().toLong()
538 8 -> readLong()
539 else -> throw IllegalArgumentException("ID Length must be 1, 2, 4, or 8")
540 }
541 }
542
543 fun readUtf8(byteCount: Long): String {
544 bytesRead += byteCount
545 return source.readUtf8(byteCount)
546 }
547
548 fun readUnsignedShort(): Int {
549 return readShort().toInt() and 0xFFFF
550 }
551
552 companion object {
553 private val BOOLEAN_SIZE = BOOLEAN.byteSize
554 private val CHAR_SIZE = CHAR.byteSize
555 private val BYTE_SIZE = BYTE.byteSize
556 private val SHORT_SIZE = SHORT.byteSize
557 private val INT_SIZE = INT.byteSize
558 private val LONG_SIZE = LONG.byteSize
559
560 private val BOOLEAN_TYPE = BOOLEAN.hprofType
561 private val CHAR_TYPE = CHAR.hprofType
562 private val FLOAT_TYPE = FLOAT.hprofType
563 private val DOUBLE_TYPE = DOUBLE.hprofType
564 private val BYTE_TYPE = BYTE.hprofType
565 private val SHORT_TYPE = SHORT.hprofType
566 private val INT_TYPE = INT.hprofType
567 private val LONG_TYPE = LONG.hprofType
568
569 private const val INT_MASK = 0xffffffffL
570 private const val BYTE_MASK = 0xff
571 }
572 }
573