All Downloads are FREE. Search and download functionalities are using the official Maven repository.

kshark.internal.HprofInMemoryIndex.kt Maven / Gradle / Ivy

package kshark.internal

import kshark.*
import kshark.HprofRecordTag.CLASS_DUMP
import kshark.HprofRecordTag.INSTANCE_DUMP
import kshark.HprofRecordTag.LOAD_CLASS
import kshark.HprofRecordTag.OBJECT_ARRAY_DUMP
import kshark.HprofRecordTag.PRIMITIVE_ARRAY_DUMP
import kshark.HprofRecordTag.ROOT_DEBUGGER
import kshark.HprofRecordTag.ROOT_FINALIZING
import kshark.HprofRecordTag.ROOT_INTERNED_STRING
import kshark.HprofRecordTag.ROOT_JAVA_FRAME
import kshark.HprofRecordTag.ROOT_JNI_GLOBAL
import kshark.HprofRecordTag.ROOT_JNI_LOCAL
import kshark.HprofRecordTag.ROOT_JNI_MONITOR
import kshark.HprofRecordTag.ROOT_MONITOR_USED
import kshark.HprofRecordTag.ROOT_NATIVE_STACK
import kshark.HprofRecordTag.ROOT_REFERENCE_CLEANUP
import kshark.HprofRecordTag.ROOT_STICKY_CLASS
import kshark.HprofRecordTag.ROOT_THREAD_BLOCK
import kshark.HprofRecordTag.ROOT_THREAD_OBJECT
import kshark.HprofRecordTag.ROOT_UNKNOWN
import kshark.HprofRecordTag.ROOT_UNREACHABLE
import kshark.HprofRecordTag.ROOT_VM_INTERNAL
import kshark.HprofRecordTag.STRING_IN_UTF8
import kshark.HprofVersion.ANDROID
import kshark.PrimitiveType.INT
import kshark.internal.IndexedObject.IndexedClass
import kshark.internal.IndexedObject.IndexedInstance
import kshark.internal.IndexedObject.IndexedObjectArray
import kshark.internal.IndexedObject.IndexedPrimitiveArray
import kshark.internal.hppc.IntObjectPair
import kshark.internal.hppc.LongLongScatterMap
import kshark.internal.hppc.LongObjectPair
import kshark.internal.hppc.LongObjectScatterMap
import kshark.internal.hppc.to
import java.util.EnumSet
import kotlin.math.max

/**
 * This class is not thread safe, should be used from a single thread.
 */
internal class HprofInMemoryIndex private constructor(
  private val positionSize: Int,
  private val hprofStringCache: LongObjectScatterMap,
  private val classNames: LongLongScatterMap,
  private val classIndex: SortedBytesMap,
  private val instanceIndex: SortedBytesMap,
  private val objectArrayIndex: SortedBytesMap,
  private val primitiveArrayIndex: SortedBytesMap,
  private val gcRoots: List,
  private val proguardMapping: ProguardMapping?,
  private val bytesForClassSize: Int,
  private val bytesForInstanceSize: Int,
  private val bytesForObjectArraySize: Int,
  private val bytesForPrimitiveArraySize: Int,
  private val useForwardSlashClassPackageSeparator: Boolean,
  val classFieldsReader: ClassFieldsReader,
  private val classFieldsIndexSize: Int
) {

  val classCount: Int
    get() = classIndex.size

  val instanceCount: Int
    get() = instanceIndex.size

  val objectArrayCount: Int
    get() = objectArrayIndex.size

  val primitiveArrayCount: Int
    get() = primitiveArrayIndex.size

  fun fieldName(
    classId: Long,
    id: Long
  ): String {
    val fieldNameString = hprofStringById(id)
    return proguardMapping?.let {
      val classNameStringId = classNames[classId]
      val classNameString = hprofStringById(classNameStringId)
      proguardMapping.deobfuscateFieldName(classNameString, fieldNameString)
    } ?: fieldNameString
  }

  fun className(classId: Long): String {
    // String, primitive types
    val classNameStringId = classNames[classId]
    val classNameString = hprofStringById(classNameStringId)
    return (proguardMapping?.deobfuscateClassName(classNameString) ?: classNameString).run {
      if (useForwardSlashClassPackageSeparator) {
        // JVM heap dumps use "/" for package separators (vs "." for Android heap dumps)
        replace('/', '.')
      } else this
    }
  }

  fun classId(className: String): Long? {
    val internalClassName = if (useForwardSlashClassPackageSeparator) {
      // JVM heap dumps use "/" for package separators (vs "." for Android heap dumps)
      className.replace('.', '/')
    } else className

    // Note: this performs two linear scans over arrays
    val hprofStringId = hprofStringCache.entrySequence()
      .firstOrNull { it.second == internalClassName }
      ?.first
    return hprofStringId?.let { stringId ->
      classNames.entrySequence()
        .firstOrNull { it.second == stringId }
        ?.first
    }
  }

  fun indexedClassSequence(): Sequence> {
    return classIndex.entrySequence()
      .map {
        val id = it.first
        val array = it.second
        id to array.readClass()
      }
  }

  fun indexedInstanceSequence(): Sequence> {
    return instanceIndex.entrySequence()
      .map {
        val id = it.first
        val array = it.second
        val instance = IndexedInstance(
          position = array.readTruncatedLong(positionSize),
          classId = array.readId(),
          recordSize = array.readTruncatedLong(bytesForInstanceSize)
        )
        id to instance
      }
  }

  fun indexedObjectArraySequence(): Sequence> {
    return objectArrayIndex.entrySequence()
      .map {
        val id = it.first
        val array = it.second
        val objectArray = IndexedObjectArray(
          position = array.readTruncatedLong(positionSize),
          arrayClassId = array.readId(),
          recordSize = array.readTruncatedLong(bytesForObjectArraySize)
        )
        id to objectArray
      }
  }

  fun indexedPrimitiveArraySequence(): Sequence> {
    return primitiveArrayIndex.entrySequence()
      .map {
        val id = it.first
        val array = it.second

        val primitiveArray = IndexedPrimitiveArray(
          position = array.readTruncatedLong(positionSize),
          primitiveType = PrimitiveType.values()[array.readByte()
            .toInt()],
          recordSize = array.readTruncatedLong(bytesForPrimitiveArraySize)
        )
        id to primitiveArray
      }
  }

  fun indexedObjectSequence(): Sequence> {
    return indexedClassSequence() +
      indexedInstanceSequence() +
      indexedObjectArraySequence() +
      indexedPrimitiveArraySequence()
  }

  fun gcRoots(): List {
    return gcRoots
  }

  fun objectAtIndex(index: Int): LongObjectPair {
    require(index > 0)
    if (index < classIndex.size) {
      val objectId = classIndex.keyAt(index)
      val array = classIndex.getAtIndex(index)
      return objectId to array.readClass()
    }
    var shiftedIndex = index - classIndex.size
    if (shiftedIndex < instanceIndex.size) {
      val objectId = instanceIndex.keyAt(shiftedIndex)
      val array = instanceIndex.getAtIndex(shiftedIndex)
      return objectId to IndexedInstance(
        position = array.readTruncatedLong(positionSize),
        classId = array.readId(),
        recordSize = array.readTruncatedLong(bytesForInstanceSize)
      )
    }
    shiftedIndex -= instanceIndex.size
    if (shiftedIndex < objectArrayIndex.size) {
      val objectId = objectArrayIndex.keyAt(shiftedIndex)
      val array = objectArrayIndex.getAtIndex(shiftedIndex)
      return objectId to IndexedObjectArray(
        position = array.readTruncatedLong(positionSize),
        arrayClassId = array.readId(),
        recordSize = array.readTruncatedLong(bytesForObjectArraySize)
      )
    }
    shiftedIndex -= objectArrayIndex.size
    require(index < primitiveArrayIndex.size)
    val objectId = primitiveArrayIndex.keyAt(shiftedIndex)
    val array = primitiveArrayIndex.getAtIndex(shiftedIndex)
    return objectId to IndexedPrimitiveArray(
      position = array.readTruncatedLong(positionSize),
      primitiveType = PrimitiveType.values()[array.readByte()
        .toInt()],
      recordSize = array.readTruncatedLong(bytesForPrimitiveArraySize)
    )
  }

  @Suppress("ReturnCount")
  fun indexedObjectOrNull(objectId: Long): IntObjectPair? {
    var index = classIndex.indexOf(objectId)
    if (index >= 0) {
      val array = classIndex.getAtIndex(index)
      return index to array.readClass()
    }
    index = instanceIndex.indexOf(objectId)
    if (index >= 0) {
      val array = instanceIndex.getAtIndex(index)
      return classIndex.size + index to IndexedInstance(
        position = array.readTruncatedLong(positionSize),
        classId = array.readId(),
        recordSize = array.readTruncatedLong(bytesForInstanceSize)
      )
    }
    index = objectArrayIndex.indexOf(objectId)
    if (index >= 0) {
      val array = objectArrayIndex.getAtIndex(index)
      return classIndex.size + instanceIndex.size + index to IndexedObjectArray(
        position = array.readTruncatedLong(positionSize),
        arrayClassId = array.readId(),
        recordSize = array.readTruncatedLong(bytesForObjectArraySize)
      )
    }
    index = primitiveArrayIndex.indexOf(objectId)
    if (index >= 0) {
      val array = primitiveArrayIndex.getAtIndex(index)
      return classIndex.size + instanceIndex.size + index + primitiveArrayIndex.size to IndexedPrimitiveArray(
        position = array.readTruncatedLong(positionSize),
        primitiveType = PrimitiveType.values()[array.readByte()
          .toInt()],
        recordSize = array.readTruncatedLong(bytesForPrimitiveArraySize)
      )
    }
    return null
  }

  private fun ByteSubArray.readClass(): IndexedClass {
    val position = readTruncatedLong(positionSize)
    val superclassId = readId()
    val instanceSize = readInt()

    val recordSize = readTruncatedLong(bytesForClassSize)
    val fieldsIndex = readTruncatedLong(classFieldsIndexSize).toInt()

    return IndexedClass(
      position = position,
      superclassId = superclassId,
      instanceSize = instanceSize,
      recordSize = recordSize,
      fieldsIndex = fieldsIndex
    )
  }

  @Suppress("ReturnCount")
  fun objectIdIsIndexed(objectId: Long): Boolean {
    if (classIndex[objectId] != null) {
      return true
    }
    if (instanceIndex[objectId] != null) {
      return true
    }
    if (objectArrayIndex[objectId] != null) {
      return true
    }
    if (primitiveArrayIndex[objectId] != null) {
      return true
    }
    return false
  }

  private fun hprofStringById(id: Long): String {
    return hprofStringCache[id] ?: throw IllegalArgumentException("Hprof string $id not in cache")
  }

  private class Builder(
    longIdentifiers: Boolean,
    maxPosition: Long,
    classCount: Int,
    instanceCount: Int,
    objectArrayCount: Int,
    primitiveArrayCount: Int,
    val bytesForClassSize: Int,
    val bytesForInstanceSize: Int,
    val bytesForObjectArraySize: Int,
    val bytesForPrimitiveArraySize: Int,
    val classFieldsTotalBytes: Int
  ) : OnHprofRecordTagListener {

    private val identifierSize = if (longIdentifiers) 8 else 4
    private val positionSize = byteSizeForUnsigned(maxPosition)
    private val classFieldsIndexSize = byteSizeForUnsigned(classFieldsTotalBytes.toLong())

    /**
     * Map of string id to string
     * This currently keeps all the hprof strings that we could care about: class names,
     * static field names and instance fields names
     */
    // TODO Replacing with a radix trie reversed into a sparse array of long to trie leaf could save
    // memory. Can be stored as 3 arrays: array of keys, array of values which are indexes into
    // a large array of string bytes. Each "entry" consists of a size, the index of the previous
    // segment and then the segment content.

    private val hprofStringCache = LongObjectScatterMap()

    /**
     * class id to string id
     */
    private val classNames = LongLongScatterMap(expectedElements = classCount)

    private val classFieldBytes = ByteArray(classFieldsTotalBytes)

    private var classFieldsIndex = 0

    private val classIndex = UnsortedByteEntries(
      bytesPerValue = positionSize + identifierSize + 4 + bytesForClassSize + classFieldsIndexSize,
      longIdentifiers = longIdentifiers,
      initialCapacity = classCount
    )
    private val instanceIndex = UnsortedByteEntries(
      bytesPerValue = positionSize + identifierSize + bytesForInstanceSize,
      longIdentifiers = longIdentifiers,
      initialCapacity = instanceCount
    )
    private val objectArrayIndex = UnsortedByteEntries(
      bytesPerValue = positionSize + identifierSize + bytesForObjectArraySize,
      longIdentifiers = longIdentifiers,
      initialCapacity = objectArrayCount
    )
    private val primitiveArrayIndex = UnsortedByteEntries(
      bytesPerValue = positionSize + 1 + bytesForPrimitiveArraySize,
      longIdentifiers = longIdentifiers,
      initialCapacity = primitiveArrayCount
    )

    private val gcRoots = mutableListOf()

    private fun HprofRecordReader.copyToClassFields(byteCount: Int) {
      for (i in 1..byteCount) {
        classFieldBytes[classFieldsIndex++] = readByte()
      }
    }

    private fun lastClassFieldsShort() =
      ((classFieldBytes[classFieldsIndex - 2].toInt() and 0xff shl 8) or
        (classFieldBytes[classFieldsIndex - 1].toInt() and 0xff)).toShort()

    @Suppress("LongMethod")
    override fun onHprofRecord(
      tag: HprofRecordTag,
      length: Long,
      reader: HprofRecordReader
    ) {
      when (tag) {
        STRING_IN_UTF8 -> {
          hprofStringCache[reader.readId()] = reader.readUtf8(length - identifierSize)
        }
        LOAD_CLASS -> {
          // classSerialNumber
          reader.skip(INT.byteSize)
          val id = reader.readId()
          // stackTraceSerialNumber
          reader.skip(INT.byteSize)
          val classNameStringId = reader.readId()
          classNames[id] = classNameStringId
        }
        ROOT_UNKNOWN -> {
          reader.readUnknownGcRootRecord().apply {
            if (id != ValueHolder.NULL_REFERENCE) {
              gcRoots += this
            }
          }
        }
        ROOT_JNI_GLOBAL -> {
          reader.readJniGlobalGcRootRecord().apply {
            if (id != ValueHolder.NULL_REFERENCE) {
              gcRoots += this
            }
          }
        }
        ROOT_JNI_LOCAL -> {
          reader.readJniLocalGcRootRecord().apply {
            if (id != ValueHolder.NULL_REFERENCE) {
              gcRoots += this
            }
          }
        }
        ROOT_JAVA_FRAME -> {
          reader.readJavaFrameGcRootRecord().apply {
            if (id != ValueHolder.NULL_REFERENCE) {
              gcRoots += this
            }
          }
        }
        ROOT_NATIVE_STACK -> {
          reader.readNativeStackGcRootRecord().apply {
            if (id != ValueHolder.NULL_REFERENCE) {
              gcRoots += this
            }
          }
        }
        ROOT_STICKY_CLASS -> {
          reader.readStickyClassGcRootRecord().apply {
            if (id != ValueHolder.NULL_REFERENCE) {
              gcRoots += this
            }
          }
        }
        ROOT_THREAD_BLOCK -> {
          reader.readThreadBlockGcRootRecord().apply {
            if (id != ValueHolder.NULL_REFERENCE) {
              gcRoots += this
            }
          }
        }
        ROOT_MONITOR_USED -> {
          reader.readMonitorUsedGcRootRecord().apply {
            if (id != ValueHolder.NULL_REFERENCE) {
              gcRoots += this
            }
          }
        }
        ROOT_THREAD_OBJECT -> {
          reader.readThreadObjectGcRootRecord().apply {
            if (id != ValueHolder.NULL_REFERENCE) {
              gcRoots += this
            }
          }
        }
        ROOT_INTERNED_STRING -> {
          reader.readInternedStringGcRootRecord().apply {
            if (id != ValueHolder.NULL_REFERENCE) {
              gcRoots += this
            }
          }
        }
        ROOT_FINALIZING -> {
          reader.readFinalizingGcRootRecord().apply {
            if (id != ValueHolder.NULL_REFERENCE) {
              gcRoots += this
            }
          }
        }
        ROOT_DEBUGGER -> {
          reader.readDebuggerGcRootRecord().apply {
            if (id != ValueHolder.NULL_REFERENCE) {
              gcRoots += this
            }
          }
        }
        ROOT_REFERENCE_CLEANUP -> {
          reader.readReferenceCleanupGcRootRecord().apply {
            if (id != ValueHolder.NULL_REFERENCE) {
              gcRoots += this
            }
          }
        }
        ROOT_VM_INTERNAL -> {
          reader.readVmInternalGcRootRecord().apply {
            if (id != ValueHolder.NULL_REFERENCE) {
              gcRoots += this
            }
          }
        }
        ROOT_JNI_MONITOR -> {
          reader.readJniMonitorGcRootRecord().apply {
            if (id != ValueHolder.NULL_REFERENCE) {
              gcRoots += this
            }
          }
        }
        ROOT_UNREACHABLE -> {
          reader.readUnreachableGcRootRecord().apply {
            if (id != ValueHolder.NULL_REFERENCE) {
              gcRoots += this
            }
          }
        }
        CLASS_DUMP -> {
          val bytesReadStart = reader.bytesRead
          val id = reader.readId()
          // stack trace serial number
          reader.skip(INT.byteSize)
          val superclassId = reader.readId()
          reader.skip(5 * identifierSize)

          // instance size (in bytes)
          // Useful to compute retained size
          val instanceSize = reader.readInt()

          reader.skipClassDumpConstantPool()

          val startPosition = classFieldsIndex

          val bytesReadFieldStart = reader.bytesRead

          reader.copyToClassFields(2)
          val staticFieldCount = lastClassFieldsShort().toInt() and 0xFFFF
          for (i in 0 until staticFieldCount) {
            reader.copyToClassFields(identifierSize)
            reader.copyToClassFields(1)
            val type = classFieldBytes[classFieldsIndex - 1].toInt() and 0xff
            if (type == PrimitiveType.REFERENCE_HPROF_TYPE) {
              reader.copyToClassFields(identifierSize)
            } else {
              reader.copyToClassFields(PrimitiveType.byteSizeByHprofType.getValue(type))
            }
          }

          reader.copyToClassFields(2)
          val fieldCount = lastClassFieldsShort().toInt() and 0xFFFF
          for (i in 0 until fieldCount) {
            reader.copyToClassFields(identifierSize)
            reader.copyToClassFields(1)
          }

          val fieldsSize = (reader.bytesRead - bytesReadFieldStart).toInt()
          val recordSize = reader.bytesRead - bytesReadStart

          classIndex.append(id)
            .apply {
              writeTruncatedLong(bytesReadStart, positionSize)
              writeId(superclassId)
              writeInt(instanceSize)
              writeTruncatedLong(recordSize, bytesForClassSize)
              writeTruncatedLong(startPosition.toLong(), classFieldsIndexSize)
            }
          require(startPosition + fieldsSize == classFieldsIndex) {
            "Expected $classFieldsIndex to have moved by $fieldsSize and be equal to ${startPosition + fieldsSize}"
          }
        }
        INSTANCE_DUMP -> {
          val bytesReadStart = reader.bytesRead
          val id = reader.readId()
          reader.skip(INT.byteSize)
          val classId = reader.readId()
          val remainingBytesInInstance = reader.readInt()
          reader.skip(remainingBytesInInstance)
          val recordSize = reader.bytesRead - bytesReadStart
          instanceIndex.append(id)
            .apply {
              writeTruncatedLong(bytesReadStart, positionSize)
              writeId(classId)
              writeTruncatedLong(recordSize, bytesForInstanceSize)
            }
        }
        OBJECT_ARRAY_DUMP -> {
          val bytesReadStart = reader.bytesRead
          val id = reader.readId()
          reader.skip(INT.byteSize)
          val size = reader.readInt()
          val arrayClassId = reader.readId()
          reader.skip(identifierSize * size)
          val recordSize = reader.bytesRead - bytesReadStart
          objectArrayIndex.append(id)
            .apply {
              writeTruncatedLong(bytesReadStart, positionSize)
              writeId(arrayClassId)
              writeTruncatedLong(recordSize, bytesForObjectArraySize)
            }
        }
        PRIMITIVE_ARRAY_DUMP -> {
          val bytesReadStart = reader.bytesRead
          val id = reader.readId()
          reader.skip(INT.byteSize)
          val size = reader.readInt()
          val type = PrimitiveType.primitiveTypeByHprofType.getValue(reader.readUnsignedByte())
          reader.skip(size * type.byteSize)
          val recordSize = reader.bytesRead - bytesReadStart
          primitiveArrayIndex.append(id)
            .apply {
              writeTruncatedLong(bytesReadStart, positionSize)
              writeByte(type.ordinal.toByte())
              writeTruncatedLong(recordSize, bytesForPrimitiveArraySize)
            }
        }
      }
    }

    fun buildIndex(
      proguardMapping: ProguardMapping?,
      hprofHeader: HprofHeader
    ): HprofInMemoryIndex {
      require(classFieldsIndex == classFieldBytes.size) {
        "Read $classFieldsIndex into fields bytes instead of expected ${classFieldBytes.size}"
      }

      val sortedInstanceIndex = instanceIndex.moveToSortedMap()
      val sortedObjectArrayIndex = objectArrayIndex.moveToSortedMap()
      val sortedPrimitiveArrayIndex = primitiveArrayIndex.moveToSortedMap()
      val sortedClassIndex = classIndex.moveToSortedMap()
      // Passing references to avoid copying the underlying data structures.
      return HprofInMemoryIndex(
        positionSize = positionSize,
        hprofStringCache = hprofStringCache,
        classNames = classNames,
        classIndex = sortedClassIndex,
        instanceIndex = sortedInstanceIndex,
        objectArrayIndex = sortedObjectArrayIndex,
        primitiveArrayIndex = sortedPrimitiveArrayIndex,
        gcRoots = gcRoots,
        proguardMapping = proguardMapping,
        bytesForClassSize = bytesForClassSize,
        bytesForInstanceSize = bytesForInstanceSize,
        bytesForObjectArraySize = bytesForObjectArraySize,
        bytesForPrimitiveArraySize = bytesForPrimitiveArraySize,
        useForwardSlashClassPackageSeparator = hprofHeader.version != ANDROID,
        classFieldsReader = ClassFieldsReader(identifierSize, classFieldBytes),
        classFieldsIndexSize = classFieldsIndexSize
      )
    }
  }

  companion object {

    private fun byteSizeForUnsigned(maxValue: Long): Int {
      var value = maxValue
      var byteCount = 0
      while (value != 0L) {
        value = value shr 8
        byteCount++
      }
      return byteCount
    }

    fun indexHprof(
      reader: StreamingHprofReader,
      hprofHeader: HprofHeader,
      proguardMapping: ProguardMapping?,
      indexedGcRootTags: Set
    ): HprofInMemoryIndex {

      // First pass to count and correctly size arrays once and for all.
      var maxClassSize = 0L
      var maxInstanceSize = 0L
      var maxObjectArraySize = 0L
      var maxPrimitiveArraySize = 0L
      var classCount = 0
      var instanceCount = 0
      var objectArrayCount = 0
      var primitiveArrayCount = 0
      var classFieldsTotalBytes = 0

      val bytesRead = reader.readRecords(
        EnumSet.of(CLASS_DUMP, INSTANCE_DUMP, OBJECT_ARRAY_DUMP, PRIMITIVE_ARRAY_DUMP),
        OnHprofRecordTagListener { tag, _, reader ->
          val bytesReadStart = reader.bytesRead
          when (tag) {
            CLASS_DUMP -> {
              classCount++
              reader.skipClassDumpHeader()
              val bytesReadStaticFieldStart = reader.bytesRead
              reader.skipClassDumpStaticFields()
              reader.skipClassDumpFields()
              maxClassSize = max(maxClassSize, reader.bytesRead - bytesReadStart)
              classFieldsTotalBytes += (reader.bytesRead - bytesReadStaticFieldStart).toInt()
            }
            INSTANCE_DUMP -> {
              instanceCount++
              reader.skipInstanceDumpRecord()
              maxInstanceSize = max(maxInstanceSize, reader.bytesRead - bytesReadStart)
            }
            OBJECT_ARRAY_DUMP -> {
              objectArrayCount++
              reader.skipObjectArrayDumpRecord()
              maxObjectArraySize = max(maxObjectArraySize, reader.bytesRead - bytesReadStart)
            }
            PRIMITIVE_ARRAY_DUMP -> {
              primitiveArrayCount++
              reader.skipPrimitiveArrayDumpRecord()
              maxPrimitiveArraySize = max(maxPrimitiveArraySize, reader.bytesRead - bytesReadStart)
            }
          }
        })

      val bytesForClassSize = byteSizeForUnsigned(maxClassSize)
      val bytesForInstanceSize = byteSizeForUnsigned(maxInstanceSize)
      val bytesForObjectArraySize = byteSizeForUnsigned(maxObjectArraySize)
      val bytesForPrimitiveArraySize = byteSizeForUnsigned(maxPrimitiveArraySize)

      val indexBuilderListener = Builder(
        longIdentifiers = hprofHeader.identifierByteSize == 8,
        maxPosition = bytesRead,
        classCount = classCount,
        instanceCount = instanceCount,
        objectArrayCount = objectArrayCount,
        primitiveArrayCount = primitiveArrayCount,
        bytesForClassSize = bytesForClassSize,
        bytesForInstanceSize = bytesForInstanceSize,
        bytesForObjectArraySize = bytesForObjectArraySize,
        bytesForPrimitiveArraySize = bytesForPrimitiveArraySize,
        classFieldsTotalBytes = classFieldsTotalBytes
      )

      val recordTypes = EnumSet.of(
        STRING_IN_UTF8,
        LOAD_CLASS,
        CLASS_DUMP,
        INSTANCE_DUMP,
        OBJECT_ARRAY_DUMP,
        PRIMITIVE_ARRAY_DUMP
      ) + HprofRecordTag.rootTags.intersect(indexedGcRootTags)

      reader.readRecords(recordTypes, indexBuilderListener)

      SharkLog.d { "classCount:${classCount} instanceCount:${instanceCount} " +
              "objectArrayCount:${objectArrayCount} primitiveArrayCount:${primitiveArrayCount}"
      }

      return indexBuilderListener.buildIndex(proguardMapping, hprofHeader)
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy