StreamingRecordReaderAdapter.kt
TLDR
The StreamingRecordReaderAdapter.kt
file in the Demo Projects
project is a Kotlin file that provides a higher-level API for reading hprof records from a StreamingHprofReader
. It includes a StreamingRecordReaderAdapter
class that wraps a StreamingHprofReader
and provides a method for reading hprof records. The file also includes some utility methods and extensions for converting record types to hprof tags.
Methods
readRecords
This method reads hprof records from the streamingHprofReader
and calls the listener
back for each record that matches one of the provided recordTypes
. It returns the number of bytes read from the source.
- Parameters:
-
recordTypes
(Set<KClass<out HprofRecord>>): The set of record types to match against. -
listener
(OnHprofRecordListener): The listener callback to invoke for each matching record.
-
asStreamingRecordReader
This is a companion extension function for StreamingHprofReader
that converts it to a StreamingRecordReaderAdapter
.
asHprofTags
This is a companion utility function for Set<KClass<out HprofRecord>>
that converts it to an EnumSet<HprofRecordTag>
. It maps the provided record types to the corresponding hprof tags and returns an EnumSet
containing them.
END
package shark
import shark.HprofRecord.HeapDumpEndRecord
import shark.HprofRecord.HeapDumpRecord
import shark.HprofRecord.HeapDumpRecord.GcRootRecord
import shark.HprofRecord.HeapDumpRecord.HeapDumpInfoRecord
import shark.HprofRecord.HeapDumpRecord.ObjectRecord
import shark.HprofRecord.HeapDumpRecord.ObjectRecord.ClassDumpRecord
import shark.HprofRecord.HeapDumpRecord.ObjectRecord.InstanceDumpRecord
import shark.HprofRecord.HeapDumpRecord.ObjectRecord.ObjectArrayDumpRecord
import shark.HprofRecord.HeapDumpRecord.ObjectRecord.PrimitiveArrayDumpRecord
import shark.HprofRecord.LoadClassRecord
import shark.HprofRecord.StackFrameRecord
import shark.HprofRecord.StackTraceRecord
import shark.HprofRecord.StringRecord
import shark.HprofRecordTag.CLASS_DUMP
import shark.HprofRecordTag.HEAP_DUMP_END
import shark.HprofRecordTag.HEAP_DUMP_INFO
import shark.HprofRecordTag.INSTANCE_DUMP
import shark.HprofRecordTag.LOAD_CLASS
import shark.HprofRecordTag.OBJECT_ARRAY_DUMP
import shark.HprofRecordTag.PRIMITIVE_ARRAY_DUMP
import shark.HprofRecordTag.ROOT_DEBUGGER
import shark.HprofRecordTag.ROOT_FINALIZING
import shark.HprofRecordTag.ROOT_INTERNED_STRING
import shark.HprofRecordTag.ROOT_JAVA_FRAME
import shark.HprofRecordTag.ROOT_JNI_GLOBAL
import shark.HprofRecordTag.ROOT_JNI_LOCAL
import shark.HprofRecordTag.ROOT_JNI_MONITOR
import shark.HprofRecordTag.ROOT_MONITOR_USED
import shark.HprofRecordTag.ROOT_NATIVE_STACK
import shark.HprofRecordTag.ROOT_REFERENCE_CLEANUP
import shark.HprofRecordTag.ROOT_STICKY_CLASS
import shark.HprofRecordTag.ROOT_THREAD_BLOCK
import shark.HprofRecordTag.ROOT_THREAD_OBJECT
import shark.HprofRecordTag.ROOT_UNKNOWN
import shark.HprofRecordTag.ROOT_UNREACHABLE
import shark.HprofRecordTag.ROOT_VM_INTERNAL
import shark.HprofRecordTag.STACK_FRAME
import shark.HprofRecordTag.STACK_TRACE
import shark.HprofRecordTag.STRING_IN_UTF8
import java.util.EnumSet
import kotlin.reflect.KClass
/**
* Wraps a [StreamingHprofReader] to provide a higher level API that streams [HprofRecord]
* instances.
*/
class StreamingRecordReaderAdapter(private val streamingHprofReader: StreamingHprofReader) {
/**
* Obtains a new source to read all hprof records from and calls [listener] back for each record
* that matches one of the provided [recordTypes].
*
* @return the number of bytes read from the source
*/
@Suppress("ComplexMethod", "NestedBlockDepth")
fun readRecords(
recordTypes: Set<KClass<out HprofRecord>>,
listener: OnHprofRecordListener
): Long {
val recordTags = recordTypes.asHprofTags()
return streamingHprofReader.readRecords(
recordTags
) { tag, length, reader ->
when (tag) {
STRING_IN_UTF8 -> {
val recordPosition = reader.bytesRead
val record = reader.readStringRecord(length)
listener.onHprofRecord(recordPosition, record)
}
LOAD_CLASS -> {
val recordPosition = reader.bytesRead
val record = reader.readLoadClassRecord()
listener.onHprofRecord(recordPosition, record)
}
STACK_FRAME -> {
val recordPosition = reader.bytesRead
val record = reader.readStackFrameRecord()
listener.onHprofRecord(recordPosition, record)
}
STACK_TRACE -> {
val recordPosition = reader.bytesRead
val record = reader.readStackTraceRecord()
listener.onHprofRecord(recordPosition, record)
}
ROOT_UNKNOWN -> {
val recordPosition = reader.bytesRead
val record = reader.readUnknownGcRootRecord()
listener.onHprofRecord(recordPosition, GcRootRecord(record))
}
ROOT_JNI_GLOBAL -> {
val recordPosition = reader.bytesRead
val gcRootRecord = reader.readJniGlobalGcRootRecord()
listener.onHprofRecord(recordPosition, GcRootRecord(gcRootRecord))
}
ROOT_JNI_LOCAL -> {
val recordPosition = reader.bytesRead
val gcRootRecord = reader.readJniLocalGcRootRecord()
listener.onHprofRecord(recordPosition, GcRootRecord(gcRootRecord))
}
ROOT_JAVA_FRAME -> {
val recordPosition = reader.bytesRead
val gcRootRecord = reader.readJavaFrameGcRootRecord()
listener.onHprofRecord(recordPosition, GcRootRecord(gcRootRecord))
}
ROOT_NATIVE_STACK -> {
val recordPosition = reader.bytesRead
val gcRootRecord = reader.readNativeStackGcRootRecord()
listener.onHprofRecord(recordPosition, GcRootRecord(gcRootRecord))
}
ROOT_STICKY_CLASS -> {
val recordPosition = reader.bytesRead
val gcRootRecord = reader.readStickyClassGcRootRecord()
listener.onHprofRecord(recordPosition, GcRootRecord(gcRootRecord))
}
ROOT_THREAD_BLOCK -> {
val recordPosition = reader.bytesRead
val gcRootRecord = reader.readThreadBlockGcRootRecord()
listener.onHprofRecord(recordPosition, GcRootRecord(gcRootRecord))
}
ROOT_MONITOR_USED -> {
val recordPosition = reader.bytesRead
val gcRootRecord = reader.readMonitorUsedGcRootRecord()
listener.onHprofRecord(recordPosition, GcRootRecord(gcRootRecord))
}
ROOT_THREAD_OBJECT -> {
val recordPosition = reader.bytesRead
val gcRootRecord = reader.readThreadObjectGcRootRecord()
listener.onHprofRecord(recordPosition, GcRootRecord(gcRootRecord))
}
ROOT_INTERNED_STRING -> {
val recordPosition = reader.bytesRead
val gcRootRecord = reader.readInternedStringGcRootRecord()
listener.onHprofRecord(recordPosition, GcRootRecord(gcRootRecord))
}
ROOT_FINALIZING -> {
val recordPosition = reader.bytesRead
val gcRootRecord = reader.readFinalizingGcRootRecord()
listener.onHprofRecord(recordPosition, GcRootRecord(gcRootRecord))
}
ROOT_DEBUGGER -> {
val recordPosition = reader.bytesRead
val gcRootRecord = reader.readDebuggerGcRootRecord()
listener.onHprofRecord(recordPosition, GcRootRecord(gcRootRecord))
}
ROOT_REFERENCE_CLEANUP -> {
val recordPosition = reader.bytesRead
val gcRootRecord = reader.readReferenceCleanupGcRootRecord()
listener.onHprofRecord(recordPosition, GcRootRecord(gcRootRecord))
}
ROOT_VM_INTERNAL -> {
val recordPosition = reader.bytesRead
val gcRootRecord = reader.readVmInternalGcRootRecord()
listener.onHprofRecord(recordPosition, GcRootRecord(gcRootRecord))
}
ROOT_JNI_MONITOR -> {
val recordPosition = reader.bytesRead
val gcRootRecord = reader.readJniMonitorGcRootRecord()
listener.onHprofRecord(recordPosition, GcRootRecord(gcRootRecord))
}
ROOT_UNREACHABLE -> {
val recordPosition = reader.bytesRead
val gcRootRecord = reader.readUnreachableGcRootRecord()
listener.onHprofRecord(recordPosition, GcRootRecord(gcRootRecord))
}
CLASS_DUMP -> {
val recordPosition = reader.bytesRead
val record = reader.readClassDumpRecord()
listener.onHprofRecord(recordPosition, record)
}
INSTANCE_DUMP -> {
val recordPosition = reader.bytesRead
val record = reader.readInstanceDumpRecord()
listener.onHprofRecord(recordPosition, record)
}
OBJECT_ARRAY_DUMP -> {
val recordPosition = reader.bytesRead
val arrayRecord = reader.readObjectArrayDumpRecord()
listener.onHprofRecord(recordPosition, arrayRecord)
}
PRIMITIVE_ARRAY_DUMP -> {
val recordPosition = reader.bytesRead
val record = reader.readPrimitiveArrayDumpRecord()
listener.onHprofRecord(recordPosition, record)
}
HEAP_DUMP_INFO -> {
val recordPosition = reader.bytesRead
val record = reader.readHeapDumpInfoRecord()
listener.onHprofRecord(recordPosition, record)
}
HEAP_DUMP_END -> {
val recordPosition = reader.bytesRead
val record = HeapDumpEndRecord
listener.onHprofRecord(recordPosition, record)
}
else -> error("Unexpected heap dump tag $tag at position ${reader.bytesRead}")
}
}
}
companion object {
fun StreamingHprofReader.asStreamingRecordReader() = StreamingRecordReaderAdapter(this)
fun Set<KClass<out HprofRecord>>.asHprofTags(): EnumSet<HprofRecordTag> {
val recordTypes = this
return if (HprofRecord::class in recordTypes) {
EnumSet.allOf(HprofRecordTag::class.java)
} else {
EnumSet.noneOf(HprofRecordTag::class.java).apply {
if (StringRecord::class in recordTypes) {
add(STRING_IN_UTF8)
}
if (LoadClassRecord::class in recordTypes) {
add(LOAD_CLASS)
}
if (HeapDumpEndRecord::class in recordTypes) {
add(HEAP_DUMP_END)
}
if (StackFrameRecord::class in recordTypes) {
add(STACK_FRAME)
}
if (StackTraceRecord::class in recordTypes) {
add(STACK_TRACE)
}
if (HeapDumpInfoRecord::class in recordTypes) {
add(HEAP_DUMP_INFO)
}
val readAllHeapDumpRecords = HeapDumpRecord::class in recordTypes
if (readAllHeapDumpRecords || GcRootRecord::class in recordTypes) {
addAll(HprofRecordTag.rootTags)
}
val readAllObjectRecords = readAllHeapDumpRecords || ObjectRecord::class in recordTypes
if (readAllObjectRecords || ClassDumpRecord::class in recordTypes) {
add(CLASS_DUMP)
}
if (readAllObjectRecords || InstanceDumpRecord::class in recordTypes) {
add(INSTANCE_DUMP)
}
if (readAllObjectRecords || ObjectArrayDumpRecord::class in recordTypes) {
add(OBJECT_ARRAY_DUMP)
}
if (readAllObjectRecords || PrimitiveArrayDumpRecord::class in recordTypes) {
add(PRIMITIVE_ARRAY_DUMP)
}
}
}
}
}
}