<lambda>null1 package shark
2
3 import okio.BufferedSink
4 import okio.Okio
5 import shark.HprofRecord.HeapDumpEndRecord
6 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.PrimitiveArrayDumpRecord.BooleanArrayDump
7 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.PrimitiveArrayDumpRecord.ByteArrayDump
8 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.PrimitiveArrayDumpRecord.CharArrayDump
9 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.PrimitiveArrayDumpRecord.DoubleArrayDump
10 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.PrimitiveArrayDumpRecord.FloatArrayDump
11 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.PrimitiveArrayDumpRecord.IntArrayDump
12 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.PrimitiveArrayDumpRecord.LongArrayDump
13 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.PrimitiveArrayDumpRecord.ShortArrayDump
14 import shark.StreamingRecordReaderAdapter.Companion.asStreamingRecordReader
15 import java.io.File
16
17 /**
18 * Converts a Hprof file to another file with all primitive arrays replaced with arrays of zeroes,
19 * which can be useful to remove PII. Char arrays are handled slightly differently because 0 would
20 * be the null character so instead these become arrays of '?'.
21 */
22 class HprofPrimitiveArrayStripper {
23
24 /**
25 * @see HprofPrimitiveArrayStripper
26 */
27 fun stripPrimitiveArrays(
28 inputHprofFile: File,
29 /**
30 * Optional output file. Defaults to a file in the same directory as [inputHprofFile], with
31 * the same name and "-stripped" prepended before the ".hprof" extension. If the file extension
32 * is not ".hprof", then "-stripped" is added at the end of the file.
33 */
34 outputHprofFile: File = File(
35 inputHprofFile.parent, inputHprofFile.name.replace(
36 ".hprof", "-stripped.hprof"
37 ).let { if (it != inputHprofFile.name) it else inputHprofFile.name + "-stripped" })
38 ): File {
39 stripPrimitiveArrays(
40 hprofSourceProvider = FileSourceProvider(inputHprofFile),
41 hprofSink = Okio.buffer(Okio.sink(outputHprofFile.outputStream()))
42 )
43 return outputHprofFile
44 }
45
46 /**
47 * @see HprofPrimitiveArrayStripper
48 */
49 fun stripPrimitiveArrays(
50 hprofSourceProvider: StreamingSourceProvider,
51 hprofSink: BufferedSink
52 ) {
53 val header = hprofSourceProvider.openStreamingSource().use { HprofHeader.parseHeaderOf(it) }
54 val reader =
55 StreamingHprofReader.readerFor(hprofSourceProvider, header).asStreamingRecordReader()
56 HprofWriter.openWriterFor(
57 hprofSink,
58 hprofHeader = header
59 )
60 .use { writer ->
61 reader.readRecords(setOf(HprofRecord::class),
62 OnHprofRecordListener { _,
63 record ->
64 // HprofWriter automatically emits HeapDumpEndRecord, because it flushes
65 // all continuous heap dump sub records as one heap dump record.
66 if (record is HeapDumpEndRecord) {
67 return@OnHprofRecordListener
68 }
69 writer.write(
70 when (record) {
71 is BooleanArrayDump -> BooleanArrayDump(
72 record.id, record.stackTraceSerialNumber,
73 BooleanArray(record.array.size)
74 )
75 is CharArrayDump -> CharArrayDump(
76 record.id, record.stackTraceSerialNumber,
77 CharArray(record.array.size) {
78 '?'
79 }
80 )
81 is FloatArrayDump -> FloatArrayDump(
82 record.id, record.stackTraceSerialNumber,
83 FloatArray(record.array.size)
84 )
85 is DoubleArrayDump -> DoubleArrayDump(
86 record.id, record.stackTraceSerialNumber,
87 DoubleArray(record.array.size)
88 )
89 is ByteArrayDump -> ByteArrayDump(
90 record.id, record.stackTraceSerialNumber,
91 ByteArray(record.array.size) {
92 // Converts to '?' in UTF-8 for byte backed strings
93 63
94 }
95 )
96 is ShortArrayDump -> ShortArrayDump(
97 record.id, record.stackTraceSerialNumber,
98 ShortArray(record.array.size)
99 )
100 is IntArrayDump -> IntArrayDump(
101 record.id, record.stackTraceSerialNumber,
102 IntArray(record.array.size)
103 )
104 is LongArrayDump -> LongArrayDump(
105 record.id, record.stackTraceSerialNumber,
106 LongArray(record.array.size)
107 )
108 else -> {
109 record
110 }
111 }
112 )
113 })
114 }
115 }
116 }
117