xref: /aosp_15_r20/external/leakcanary2/shark-hprof/src/main/java/shark/HprofHeader.kt (revision d9e8da70d8c9df9a41d7848ae506fb3115cae6e6)
1 package shark
2 
3 import okio.BufferedSource
4 import okio.Okio
5 import java.io.File
6 
7 /**
8  * Represents the header metadata of a Hprof file.
9  */
10 data class HprofHeader(
11   /** Unix timestamp at which the heap was dumped. */
12   val heapDumpTimestamp: Long = System.currentTimeMillis(),
13   /** Hprof version, which is tied to the runtime where the heap was dumped. */
14   val version: HprofVersion = HprofVersion.ANDROID,
15   /**
16    * Size of Hprof identifiers. Identifiers are used to represent UTF8 strings, objects,
17    * stack traces, etc. They can have the same size as host pointers or sizeof(void*), but are not
18    * required to be.
19    */
20   val identifierByteSize: Int = 4
21 ) {
22   /**
23    * How many bytes from the beginning of the file can we find the hprof records at.
24    * Version string, 0 delimiter (1 byte), identifier byte size int (4 bytes) ,timestamp long
25    * (8 bytes)
26    */
27   val recordsPosition: Int = version.versionString.toByteArray(Charsets.UTF_8).size + 1 + 4 + 8
28 
29   companion object {
<lambda>null30     private val supportedVersions = HprofVersion.values().associateBy { it.versionString }
31 
32     /**
33      * Reads the header of the provided [hprofFile] and returns it as a [HprofHeader]
34      */
parseHeaderOfnull35     fun parseHeaderOf(hprofFile: File): HprofHeader {
36       val fileLength = hprofFile.length()
37       if (fileLength == 0L) {
38         throw IllegalArgumentException("Hprof file is 0 byte length")
39       }
40       return Okio.buffer(Okio.source(hprofFile.inputStream())).use {
41         parseHeaderOf(it)
42       }
43     }
44 
45     /**
46      * Reads the header of the provided [source] and returns it as a [HprofHeader].
47      * This does not close the [source].
48      */
parseHeaderOfnull49     fun parseHeaderOf(source: BufferedSource): HprofHeader {
50       require(!source.exhausted()) {
51         throw IllegalArgumentException("Source has no available bytes")
52       }
53       val endOfVersionString = source.indexOf(0)
54       val versionName = source.readUtf8(endOfVersionString)
55 
56       val version = supportedVersions[versionName]
57       checkNotNull(version) {
58         "Unsupported Hprof version [$versionName] not in supported list ${supportedVersions.keys}"
59       }
60       // Skip the 0 at the end of the version string.
61       source.skip(1)
62       val identifierByteSize = source.readInt()
63       val heapDumpTimestamp = source.readLong()
64       return HprofHeader(heapDumpTimestamp, version, identifierByteSize)
65     }
66   }
67 }
68