main

square/leakcanary

Last updated at: 29/12/2023 09:38

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)
          }
        }
      }
    }
  }
}