<lambda>null1 package shark
2
3 import java.io.File
4 import okio.Source
5 import shark.HprofRecordTag.CLASS_DUMP
6 import shark.HprofRecordTag.HEAP_DUMP
7 import shark.HprofRecordTag.HEAP_DUMP_END
8 import shark.HprofRecordTag.HEAP_DUMP_INFO
9 import shark.HprofRecordTag.HEAP_DUMP_SEGMENT
10 import shark.HprofRecordTag.INSTANCE_DUMP
11 import shark.HprofRecordTag.LOAD_CLASS
12 import shark.HprofRecordTag.OBJECT_ARRAY_DUMP
13 import shark.HprofRecordTag.PRIMITIVE_ARRAY_DUMP
14 import shark.HprofRecordTag.PRIMITIVE_ARRAY_NODATA
15 import shark.HprofRecordTag.ROOT_DEBUGGER
16 import shark.HprofRecordTag.ROOT_FINALIZING
17 import shark.HprofRecordTag.ROOT_INTERNED_STRING
18 import shark.HprofRecordTag.ROOT_JAVA_FRAME
19 import shark.HprofRecordTag.ROOT_JNI_GLOBAL
20 import shark.HprofRecordTag.ROOT_JNI_LOCAL
21 import shark.HprofRecordTag.ROOT_JNI_MONITOR
22 import shark.HprofRecordTag.ROOT_MONITOR_USED
23 import shark.HprofRecordTag.ROOT_NATIVE_STACK
24 import shark.HprofRecordTag.ROOT_REFERENCE_CLEANUP
25 import shark.HprofRecordTag.ROOT_STICKY_CLASS
26 import shark.HprofRecordTag.ROOT_THREAD_BLOCK
27 import shark.HprofRecordTag.ROOT_THREAD_OBJECT
28 import shark.HprofRecordTag.ROOT_UNKNOWN
29 import shark.HprofRecordTag.ROOT_UNREACHABLE
30 import shark.HprofRecordTag.ROOT_VM_INTERNAL
31 import shark.HprofRecordTag.STACK_FRAME
32 import shark.HprofRecordTag.STACK_TRACE
33 import shark.HprofRecordTag.STRING_IN_UTF8
34 import shark.PrimitiveType.Companion.REFERENCE_HPROF_TYPE
35 import shark.PrimitiveType.INT
36 import shark.StreamingHprofReader.Companion.readerFor
37
38 /**
39 * Reads the entire content of a Hprof source in one fell swoop.
40 * Call [readerFor] to obtain a new instance.
41 */
42 class StreamingHprofReader private constructor(
43 private val sourceProvider: StreamingSourceProvider,
44 private val header: HprofHeader
45 ) {
46
47 /**
48 * Obtains a new source to read all hprof records from and calls [listener] back for each record
49 * that matches one of the provided [recordTags].
50 *
51 * @return the number of bytes read from the source
52 */
53 @Suppress("ComplexMethod", "LongMethod", "NestedBlockDepth")
54 fun readRecords(
55 recordTags: Set<HprofRecordTag>,
56 listener: OnHprofRecordTagListener
57 ): Long {
58 return sourceProvider.openStreamingSource().use { source ->
59 val reader = HprofRecordReader(header, source)
60 reader.skip(header.recordsPosition)
61
62 // Local ref optimizations
63 val intByteSize = INT.byteSize
64 val identifierByteSize = reader.sizeOf(REFERENCE_HPROF_TYPE)
65
66 while (!source.exhausted()) {
67 // type of the record
68 val tag = reader.readUnsignedByte()
69
70 // number of microseconds since the time stamp in the header
71 reader.skip(intByteSize)
72
73 // number of bytes that follow and belong to this record
74 val length = reader.readUnsignedInt()
75
76 when (tag) {
77 STRING_IN_UTF8.tag -> {
78 if (STRING_IN_UTF8 in recordTags) {
79 listener.onHprofRecord(STRING_IN_UTF8, length, reader)
80 } else {
81 reader.skip(length)
82 }
83 }
84 LOAD_CLASS.tag -> {
85 if (LOAD_CLASS in recordTags) {
86 listener.onHprofRecord(LOAD_CLASS, length, reader)
87 } else {
88 reader.skip(length)
89 }
90 }
91 STACK_FRAME.tag -> {
92 if (STACK_FRAME in recordTags) {
93 listener.onHprofRecord(STACK_FRAME, length, reader)
94 } else {
95 reader.skip(length)
96 }
97 }
98 STACK_TRACE.tag -> {
99 if (STACK_TRACE in recordTags) {
100 listener.onHprofRecord(STACK_TRACE, length, reader)
101 } else {
102 reader.skip(length)
103 }
104 }
105 HEAP_DUMP.tag, HEAP_DUMP_SEGMENT.tag -> {
106 val heapDumpStart = reader.bytesRead
107 var previousTag = 0
108 var previousTagPosition = 0L
109 while (reader.bytesRead - heapDumpStart < length) {
110 val heapDumpTagPosition = reader.bytesRead
111 val heapDumpTag = reader.readUnsignedByte()
112 when (heapDumpTag) {
113 ROOT_UNKNOWN.tag -> {
114 if (ROOT_UNKNOWN in recordTags) {
115 listener.onHprofRecord(ROOT_UNKNOWN, -1, reader)
116 } else {
117 reader.skip(identifierByteSize)
118 }
119 }
120 ROOT_JNI_GLOBAL.tag -> {
121 if (ROOT_JNI_GLOBAL in recordTags) {
122 listener.onHprofRecord(ROOT_JNI_GLOBAL, -1, reader)
123 } else {
124 reader.skip(identifierByteSize + identifierByteSize)
125 }
126 }
127 ROOT_JNI_LOCAL.tag -> {
128 if (ROOT_JNI_LOCAL in recordTags) {
129 listener.onHprofRecord(ROOT_JNI_LOCAL, -1, reader)
130 } else {
131 reader.skip(identifierByteSize + intByteSize + intByteSize)
132 }
133 }
134
135 ROOT_JAVA_FRAME.tag -> {
136 if (ROOT_JAVA_FRAME in recordTags) {
137 listener.onHprofRecord(ROOT_JAVA_FRAME, -1, reader)
138 } else {
139 reader.skip(identifierByteSize + intByteSize + intByteSize)
140 }
141 }
142
143 ROOT_NATIVE_STACK.tag -> {
144 if (ROOT_NATIVE_STACK in recordTags) {
145 listener.onHprofRecord(ROOT_NATIVE_STACK, -1, reader)
146 } else {
147 reader.skip(identifierByteSize + intByteSize)
148 }
149 }
150
151 ROOT_STICKY_CLASS.tag -> {
152 if (ROOT_STICKY_CLASS in recordTags) {
153 listener.onHprofRecord(ROOT_STICKY_CLASS, -1, reader)
154 } else {
155 reader.skip(identifierByteSize)
156 }
157 }
158 ROOT_THREAD_BLOCK.tag -> {
159 if (ROOT_THREAD_BLOCK in recordTags) {
160 listener.onHprofRecord(ROOT_THREAD_BLOCK, -1, reader)
161 } else {
162 reader.skip(identifierByteSize + intByteSize)
163 }
164 }
165
166 ROOT_MONITOR_USED.tag -> {
167 if (ROOT_MONITOR_USED in recordTags) {
168 listener.onHprofRecord(ROOT_MONITOR_USED, -1, reader)
169 } else {
170 reader.skip(identifierByteSize)
171 }
172 }
173
174 ROOT_THREAD_OBJECT.tag -> {
175 if (ROOT_THREAD_OBJECT in recordTags) {
176 listener.onHprofRecord(ROOT_THREAD_OBJECT, -1, reader)
177 } else {
178 reader.skip(identifierByteSize + intByteSize + intByteSize)
179 }
180 }
181
182 ROOT_INTERNED_STRING.tag -> {
183 if (ROOT_INTERNED_STRING in recordTags) {
184 listener.onHprofRecord(ROOT_INTERNED_STRING, -1, reader)
185 } else {
186 reader.skip(identifierByteSize)
187 }
188 }
189
190 ROOT_FINALIZING.tag -> {
191 if (ROOT_FINALIZING in recordTags) {
192 listener.onHprofRecord(ROOT_FINALIZING, -1, reader)
193 } else {
194 reader.skip(identifierByteSize)
195 }
196 }
197
198 ROOT_DEBUGGER.tag -> {
199 if (ROOT_DEBUGGER in recordTags) {
200 listener.onHprofRecord(ROOT_DEBUGGER, -1, reader)
201 } else {
202 reader.skip(identifierByteSize)
203 }
204 }
205
206 ROOT_REFERENCE_CLEANUP.tag -> {
207 if (ROOT_REFERENCE_CLEANUP in recordTags) {
208 listener.onHprofRecord(ROOT_REFERENCE_CLEANUP, -1, reader)
209 } else {
210 reader.skip(identifierByteSize)
211 }
212 }
213
214 ROOT_VM_INTERNAL.tag -> {
215 if (ROOT_VM_INTERNAL in recordTags) {
216 listener.onHprofRecord(ROOT_VM_INTERNAL, -1, reader)
217 } else {
218 reader.skip(identifierByteSize)
219 }
220 }
221
222 ROOT_JNI_MONITOR.tag -> {
223 if (ROOT_JNI_MONITOR in recordTags) {
224 listener.onHprofRecord(ROOT_JNI_MONITOR, -1, reader)
225 } else {
226 reader.skip(identifierByteSize + intByteSize + intByteSize)
227 }
228 }
229
230 ROOT_UNREACHABLE.tag -> {
231 if (ROOT_UNREACHABLE in recordTags) {
232 listener.onHprofRecord(ROOT_UNREACHABLE, -1, reader)
233 } else {
234 reader.skip(identifierByteSize)
235 }
236 }
237 CLASS_DUMP.tag -> {
238 if (CLASS_DUMP in recordTags) {
239 listener.onHprofRecord(CLASS_DUMP, -1, reader)
240 } else {
241 reader.skipClassDumpRecord()
242 }
243 }
244 INSTANCE_DUMP.tag -> {
245 if (INSTANCE_DUMP in recordTags) {
246 listener.onHprofRecord(INSTANCE_DUMP, -1, reader)
247 } else {
248 reader.skipInstanceDumpRecord()
249 }
250 }
251
252 OBJECT_ARRAY_DUMP.tag -> {
253 if (OBJECT_ARRAY_DUMP in recordTags) {
254 listener.onHprofRecord(OBJECT_ARRAY_DUMP, -1, reader)
255 } else {
256 reader.skipObjectArrayDumpRecord()
257 }
258 }
259
260 PRIMITIVE_ARRAY_DUMP.tag -> {
261 if (PRIMITIVE_ARRAY_DUMP in recordTags) {
262 listener.onHprofRecord(PRIMITIVE_ARRAY_DUMP, -1, reader)
263 } else {
264 reader.skipPrimitiveArrayDumpRecord()
265 }
266 }
267
268 PRIMITIVE_ARRAY_NODATA.tag -> {
269 throw UnsupportedOperationException("$PRIMITIVE_ARRAY_NODATA cannot be parsed")
270 }
271
272 HEAP_DUMP_INFO.tag -> {
273 if (HEAP_DUMP_INFO in recordTags) {
274 listener.onHprofRecord(HEAP_DUMP_INFO, -1, reader)
275 } else {
276 reader.skipHeapDumpInfoRecord()
277 }
278 }
279 else -> throw IllegalStateException(
280 "Unknown tag ${
281 "0x%02x".format(
282 heapDumpTag
283 )
284 } at $heapDumpTagPosition after ${
285 "0x%02x".format(
286 previousTag
287 )
288 } at $previousTagPosition"
289 )
290 }
291 previousTag = heapDumpTag
292 previousTagPosition = heapDumpTagPosition
293 }
294 }
295 HEAP_DUMP_END.tag -> {
296 if (HEAP_DUMP_END in recordTags) {
297 listener.onHprofRecord(HEAP_DUMP_END, length, reader)
298 }
299 }
300 else -> {
301 reader.skip(length)
302 }
303 }
304 }
305 reader.bytesRead
306 }
307 }
308
309 companion object {
310
311 /**
312 * Creates a [StreamingHprofReader] for the provided [hprofFile]. [hprofHeader] will be read from
313 * [hprofFile] unless you provide it.
314 */
315 fun readerFor(
316 hprofFile: File,
317 hprofHeader: HprofHeader = HprofHeader.parseHeaderOf(hprofFile)
318 ): StreamingHprofReader {
319 val sourceProvider = FileSourceProvider(hprofFile)
320 return readerFor(sourceProvider, hprofHeader)
321 }
322
323 /**
324 * Creates a [StreamingHprofReader] that will call [StreamingSourceProvider.openStreamingSource]
325 * on every [readRecords] to obtain a [Source] to read the hprof data from. Before reading the
326 * hprof records, [StreamingHprofReader] will skip [HprofHeader.recordsPosition] bytes.
327 */
328 fun readerFor(
329 hprofSourceProvider: StreamingSourceProvider,
330 hprofHeader: HprofHeader = hprofSourceProvider.openStreamingSource()
331 .use { HprofHeader.parseHeaderOf(it) }
332 ): StreamingHprofReader {
333 return StreamingHprofReader(hprofSourceProvider, hprofHeader)
334 }
335 }
336 }
337