<lambda>null1package shark 2 3 import java.io.File 4 import kotlin.reflect.KClass 5 import org.assertj.core.api.Assertions.assertThat 6 import org.junit.Rule 7 import org.junit.Test 8 import org.junit.rules.TemporaryFolder 9 import shark.HprofHeapGraph.Companion.openHeapGraph 10 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.PrimitiveArrayDumpRecord.IntArrayDump 11 import shark.PrimitiveType.INT 12 13 class JvmHprofParsingTest { 14 15 @get:Rule 16 var testFolder = TemporaryFolder() 17 18 @Test fun dumpHeapAndReadString() { 19 val hprofFolder = testFolder.newFolder() 20 val hprofFile = File(hprofFolder, "jvm_heap.hprof") 21 22 JvmTestHeapDumper.dumpHeap(hprofFile.absolutePath) 23 24 hprofFile.openHeapGraph().use { graph -> 25 val testInstances = graph.instances 26 .filter { it.instanceClassName == JvmHprofParsingTest::class.name } 27 .toList() 28 29 assertThat(testInstances).hasSize(1) 30 val test = testInstances[0] 31 val folderPath = test[JvmHprofParsingTest::class.name, "testFolder"]!! 32 .valueAsInstance!![TemporaryFolder::class.name, "folder"]!! 33 .valueAsInstance!![File::class.name, "path"]!! 34 .value.readAsJavaString()!! 35 36 assertThat(folderPath).isEqualTo(testFolder.root.path) 37 } 38 } 39 40 @Test fun `JVM object array class name is translated to brackets`() { 41 val objectArray = arrayOf<JvmHprofParsingTest>() 42 val hprofFile = dumpHeapRetaining(objectArray) 43 44 val expectedArrayClassName = "${JvmHprofParsingTest::class.java.name}[]" 45 46 hprofFile.openHeapGraph().use { graph -> 47 val arrayClass = graph.findClassByName(expectedArrayClassName) 48 assertThat(arrayClass).isNotNull 49 assertThat(arrayClass!!.isObjectArrayClass).isTrue() 50 assertThat(arrayClass.name).isEqualTo(expectedArrayClassName) 51 52 val array = arrayClass.objectArrayInstances.single() 53 assertThat(array.arrayClassName).isEqualTo(expectedArrayClassName) 54 } 55 } 56 57 @Test fun `JVM multi dimension object array class name is translated to brackets`() { 58 val objectArray = arrayOf(arrayOf(), arrayOf<JvmHprofParsingTest>()) 59 val hprofFile = dumpHeapRetaining(objectArray) 60 61 val expectedArrayClassName = "${JvmHprofParsingTest::class.java.name}[][]" 62 63 hprofFile.openHeapGraph().use { graph -> 64 val arrayClass = graph.findClassByName(expectedArrayClassName) 65 assertThat(arrayClass).isNotNull 66 assertThat(arrayClass!!.isObjectArrayClass).isTrue() 67 assertThat(arrayClass.name).isEqualTo(expectedArrayClassName) 68 69 val array = arrayClass.objectArrayInstances.single() 70 assertThat(array.arrayClassName).isEqualTo(expectedArrayClassName) 71 } 72 } 73 74 @Test fun `JVM primitive wrapper array class name is translated to brackets`() { 75 val hprofFile = dumpHeapRetaining(arrayOfNulls<Int?>(42)) 76 77 val expectedArrayClassName = "java.lang.Integer[]" 78 79 hprofFile.openHeapGraph().use { graph -> 80 val arrayClass = graph.findClassByName(expectedArrayClassName) 81 assertThat(arrayClass).isNotNull 82 assertThat(arrayClass!!.isObjectArrayClass).isTrue() 83 assertThat(arrayClass.name).isEqualTo(expectedArrayClassName) 84 85 val array = arrayClass.objectArrayInstances.single { it.readElements().count() == 42 } 86 assertThat(array.arrayClassName).isEqualTo(expectedArrayClassName) 87 } 88 } 89 90 @Test fun `JVM multi dimension wrapper primitive array class name is translated to brackets`() { 91 val hprofFile = dumpHeapRetaining(arrayOf(arrayOf(), arrayOf<Int?>())) 92 93 val expectedArrayClassName = "java.lang.Integer[][]" 94 95 hprofFile.openHeapGraph().use { graph -> 96 val arrayClass = graph.findClassByName(expectedArrayClassName) 97 assertThat(arrayClass).isNotNull 98 assertThat(arrayClass!!.isObjectArrayClass).isTrue() 99 assertThat(arrayClass.name).isEqualTo(expectedArrayClassName) 100 101 val array = arrayClass.objectArrayInstances.single { it.readElements().count() == 2 } 102 assertThat(array.arrayClassName).isEqualTo(expectedArrayClassName) 103 } 104 } 105 106 @Test fun `JVM primitive array class name is translated to brackets`() { 107 val hprofFile = dumpHeapRetaining(IntArray(42).apply { this[0] = 0xDad }) 108 109 val expectedArrayClassName = "int[]" 110 111 hprofFile.openHeapGraph().use { graph -> 112 val arrayClass = graph.findClassByName(expectedArrayClassName) 113 assertThat(arrayClass).isNotNull 114 assertThat(arrayClass!!.isPrimitiveArrayClass).isTrue() 115 assertThat(arrayClass.name).isEqualTo(expectedArrayClassName) 116 117 val array = arrayClass.primitiveArrayInstances.single { 118 it.primitiveType == INT && it.readRecord() 119 .run { size == 42 && (this as IntArrayDump).array[0] == 0xDad } 120 } 121 assertThat(array.arrayClassName).isEqualTo(expectedArrayClassName) 122 } 123 } 124 125 @Test fun `JVM multi dimension primitive array class name is translated to brackets`() { 126 val hprofFile = dumpHeapRetaining(arrayOf(IntArray(42), IntArray(42))) 127 128 val expectedArrayClassName = "int[][]" 129 130 hprofFile.openHeapGraph().use { graph -> 131 val arrayClass = graph.findClassByName(expectedArrayClassName) 132 assertThat(arrayClass).isNotNull 133 assertThat(arrayClass!!.isPrimitiveArrayClass).isFalse() 134 assertThat(arrayClass.name).isEqualTo(expectedArrayClassName) 135 136 val array = arrayClass.objectArrayInstances.single { 137 it.readRecord().elementIds.size == 2 138 } 139 assertThat(array.arrayClassName).isEqualTo(expectedArrayClassName) 140 } 141 } 142 143 private fun dumpHeapRetaining(retained: Any): File { 144 val hprofFolder = testFolder.newFolder() 145 val hprofFile = File(hprofFolder, "jvm_heap.hprof") 146 JvmTestHeapDumper.dumpHeap(hprofFile.absolutePath) 147 // Dumb check to prevent instance from being garbage collected. 148 check(retained::class::class.isInstance(KClass::class)) 149 return hprofFile 150 } 151 } 152 153 private val KClass<out Any>.name: String 154 get() = this.java.name 155