<lambda>null1 package shark
2
3 import org.assertj.core.api.Assertions.assertThat
4 import org.junit.Test
5 import shark.HprofHeapGraph.Companion.openHeapGraph
6 import shark.HprofRecord.HeapDumpEndRecord
7 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.ClassDumpRecord
8 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.ClassDumpRecord.FieldRecord
9 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.ClassDumpRecord.StaticFieldRecord
10 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.InstanceDumpRecord
11 import shark.HprofRecord.LoadClassRecord
12 import shark.HprofRecord.StringRecord
13 import shark.StreamingRecordReaderAdapter.Companion.asStreamingRecordReader
14 import shark.ValueHolder.BooleanHolder
15 import shark.ValueHolder.IntHolder
16 import shark.ValueHolder.ReferenceHolder
17
18 class HprofWriterTest {
19
20 private var lastId = 0L
21 private val id: Long
22 get() = ++lastId
23
24 @Test
25 fun writeAndReadStringRecord() {
26 val record = StringRecord(id, MAGIC_WAND_CLASS_NAME)
27 val bytes = listOf(record).asHprofBytes()
28
29 val readRecords = bytes.readAllRecords()
30
31 assertThat(readRecords).hasSize(1)
32 assertThat(readRecords[0]).isInstanceOf(StringRecord::class.java)
33 assertThat((readRecords[0] as StringRecord).id).isEqualTo(record.id)
34 assertThat((readRecords[0] as StringRecord).string).isEqualTo(record.string)
35 }
36
37 @Test
38 fun writeAndReadClassRecord() {
39 val className = StringRecord(id, MAGIC_WAND_CLASS_NAME)
40 val loadClassRecord = LoadClassRecord(1, id, 1, className.id)
41 val classDump = ClassDumpRecord(
42 id = loadClassRecord.id,
43 stackTraceSerialNumber = 1,
44 superclassId = 0,
45 classLoaderId = 0,
46 signersId = 0,
47 protectionDomainId = 0,
48 instanceSize = 0,
49 staticFields = emptyList(),
50 fields = emptyList()
51 )
52
53 val bytes = listOf(className, loadClassRecord, classDump).asHprofBytes()
54 bytes.openHeapGraph().use { graph: HeapGraph ->
55 assertThat(graph.findClassByName(className.string)).isNotNull
56 }
57 }
58
59 @Test
60 fun writeAndReadStaticField() {
61 val className = StringRecord(id, MAGIC_WAND_CLASS_NAME)
62 val field1Name = StringRecord(id, "field1")
63 val field2Name = StringRecord(id, "field2")
64 val loadClassRecord = LoadClassRecord(1, id, 1, className.id)
65 val classDump = ClassDumpRecord(
66 id = loadClassRecord.id,
67 stackTraceSerialNumber = 1,
68 superclassId = 0,
69 classLoaderId = 0,
70 signersId = 0,
71 protectionDomainId = 0,
72 instanceSize = 0,
73 staticFields = listOf(
74 StaticFieldRecord(field1Name.id, PrimitiveType.BOOLEAN.hprofType, BooleanHolder(true)),
75 StaticFieldRecord(field2Name.id, PrimitiveType.INT.hprofType, IntHolder(42))
76 ),
77 fields = emptyList()
78 )
79 val bytes = listOf(className, field1Name, field2Name, loadClassRecord, classDump)
80 .asHprofBytes()
81 bytes.openHeapGraph().use { graph: HeapGraph ->
82 val heapClass = graph.findClassByName(className.string)!!
83 val staticFields = heapClass.readStaticFields().toList()
84 assertThat(staticFields).hasSize(2)
85 assertThat(staticFields[0].name).isEqualTo(field1Name.string)
86 assertThat(staticFields[0].value.asBoolean).isEqualTo(true)
87 assertThat(staticFields[1].name).isEqualTo(field2Name.string)
88 assertThat(staticFields[1].value.asInt).isEqualTo(42)
89 }
90 }
91
92 @Test
93 fun writeAndReadHprof() {
94 val records = createRecords()
95
96 val bytes = records.asHprofBytes()
97
98 val readRecords = bytes.readAllRecords()
99 assertThat(readRecords).hasSameSizeAs(records + HeapDumpEndRecord)
100
101 bytes.openHeapGraph().use { graph: HeapGraph ->
102 val treasureChestClass = graph.findClassByName(
103 TREASURE_CHEST_CLASS_NAME
104 )!!
105 val baguetteInstance =
106 treasureChestClass[CONTENT_FIELD_NAME]!!.value.asObject!!.asInstance!!
107
108 assertThat(
109 baguetteInstance[BAGUETTE_CLASS_NAME, ANSWER_FIELD_NAME]!!.value.asInt!!
110 ).isEqualTo(42)
111 }
112 }
113
114 private fun createRecords(): List<HprofRecord> {
115 val magicWandClassName = StringRecord(id, MAGIC_WAND_CLASS_NAME)
116 val baguetteClassName = StringRecord(id, BAGUETTE_CLASS_NAME)
117 val answerFieldName = StringRecord(id, ANSWER_FIELD_NAME)
118 val treasureChestClassName = StringRecord(
119 id,
120 TREASURE_CHEST_CLASS_NAME
121 )
122 val contentFieldName = StringRecord(id, CONTENT_FIELD_NAME)
123 val loadMagicWandClass = LoadClassRecord(1, id, 1, magicWandClassName.id)
124 val loadBaguetteClass = LoadClassRecord(1, id, 1, baguetteClassName.id)
125 val loadTreasureChestClass = LoadClassRecord(1, id, 1, treasureChestClassName.id)
126 val magicWandClassDump = ClassDumpRecord(
127 id = loadMagicWandClass.id,
128 stackTraceSerialNumber = 1,
129 superclassId = 0,
130 classLoaderId = 0,
131 signersId = 0,
132 protectionDomainId = 0,
133 instanceSize = 0,
134 staticFields = emptyList(),
135 fields = emptyList()
136 )
137 val baguetteClassDump = ClassDumpRecord(
138 id = loadBaguetteClass.id,
139 stackTraceSerialNumber = 1,
140 superclassId = loadMagicWandClass.id,
141 classLoaderId = 0,
142 signersId = 0,
143 protectionDomainId = 0,
144 instanceSize = 0,
145 staticFields = emptyList(),
146 fields = listOf(FieldRecord(answerFieldName.id, PrimitiveType.INT.hprofType))
147 )
148
149 val baguetteInstanceDump = InstanceDumpRecord(
150 id = id,
151 stackTraceSerialNumber = 1,
152 classId = loadBaguetteClass.id,
153 fieldValues = byteArrayOf(0x0, 0x0, 0x0, 0x2a)
154 )
155
156 val treasureChestClassDump = ClassDumpRecord(
157 id = loadTreasureChestClass.id,
158 stackTraceSerialNumber = 1,
159 superclassId = 0,
160 classLoaderId = 0,
161 signersId = 0,
162 protectionDomainId = 0,
163 instanceSize = 0,
164 staticFields = listOf(
165 StaticFieldRecord(
166 contentFieldName.id, PrimitiveType.REFERENCE_HPROF_TYPE,
167 ReferenceHolder(baguetteInstanceDump.id)
168 )
169 ),
170 fields = emptyList()
171 )
172
173 return listOf(
174 magicWandClassName, baguetteClassName, answerFieldName, treasureChestClassName,
175 contentFieldName, loadMagicWandClass,
176 loadBaguetteClass, loadTreasureChestClass,
177 magicWandClassDump, baguetteClassDump, baguetteInstanceDump, treasureChestClassDump
178 )
179 }
180
181 private fun DualSourceProvider.readAllRecords(): MutableList<HprofRecord> {
182 val readRecords = mutableListOf<HprofRecord>()
183 StreamingHprofReader.readerFor(this).asStreamingRecordReader()
184 .readRecords(setOf(HprofRecord::class)) { position, record ->
185 readRecords += record
186 }
187 return readRecords
188 }
189
190 companion object {
191 const val MAGIC_WAND_CLASS_NAME = "com.example.MagicWand"
192 const val BAGUETTE_CLASS_NAME = "com.example.Baguette"
193 const val ANSWER_FIELD_NAME = "answer"
194 const val TREASURE_CHEST_CLASS_NAME = "com.example.TreasureChest"
195 const val CONTENT_FIELD_NAME = "content"
196 }
197 }
198