From a353a67c197c5b204b770d2ea020887ddffc2318 Mon Sep 17 00:00:00 2001 From: Inaki Villar Date: Fri, 9 Jun 2023 16:50:11 -0700 Subject: [PATCH 1/2] rework jstat parse and including gc in the jinfo --- .../kotlinprocess/ConsolidateProcesses.kt | 2 +- .../cdsap/kotlinprocess/model/ProcessJInfo.kt | 5 +- .../cdsap/kotlinprocess/model/ProcessJstat.kt | 3 +- .../cdsap/kotlinprocess/parser/JInfoData.kt | 19 +- .../cdsap/kotlinprocess/parser/JStatData.kt | 204 +++++++----------- .../InfoKotlinProcessPluginTest.kt | 2 +- .../kotlinprocess/parser/JInfoDataTest.kt | 26 +++ .../kotlinprocess/parser/JStatDataTest.kt | 32 +++ 8 files changed, 162 insertions(+), 131 deletions(-) diff --git a/src/main/kotlin/io/github/cdsap/kotlinprocess/ConsolidateProcesses.kt b/src/main/kotlin/io/github/cdsap/kotlinprocess/ConsolidateProcesses.kt index deb6a7d..e60e6c3 100644 --- a/src/main/kotlin/io/github/cdsap/kotlinprocess/ConsolidateProcesses.kt +++ b/src/main/kotlin/io/github/cdsap/kotlinprocess/ConsolidateProcesses.kt @@ -26,7 +26,7 @@ class ConsolidateProcesses { capacity = jStatData[it.key]?.capacity?.toGigsFromKb()!!, gcTime = jStatData[it.key]?.gcTime?.toMinutes()!!, uptime = jStatData[it.key]?.uptime?.toMinutes()!!, - type = jStatData[it.key]?.typeGC!! + type = it.value.gcType ) ) } diff --git a/src/main/kotlin/io/github/cdsap/kotlinprocess/model/ProcessJInfo.kt b/src/main/kotlin/io/github/cdsap/kotlinprocess/model/ProcessJInfo.kt index 69f5301..977963d 100644 --- a/src/main/kotlin/io/github/cdsap/kotlinprocess/model/ProcessJInfo.kt +++ b/src/main/kotlin/io/github/cdsap/kotlinprocess/model/ProcessJInfo.kt @@ -1,3 +1,6 @@ package io.github.cdsap.kotlinprocess.model -data class ProcessJInfo(val max: Double) +data class ProcessJInfo( + val max: Double, + val gcType: String +) diff --git a/src/main/kotlin/io/github/cdsap/kotlinprocess/model/ProcessJstat.kt b/src/main/kotlin/io/github/cdsap/kotlinprocess/model/ProcessJstat.kt index f32b11d..d0c95eb 100644 --- a/src/main/kotlin/io/github/cdsap/kotlinprocess/model/ProcessJstat.kt +++ b/src/main/kotlin/io/github/cdsap/kotlinprocess/model/ProcessJstat.kt @@ -4,6 +4,5 @@ data class ProcessJstat( val usage: Double, val capacity: Double, val gcTime: Double, - val uptime: Double, - val typeGC: String + val uptime: Double ) diff --git a/src/main/kotlin/io/github/cdsap/kotlinprocess/parser/JInfoData.kt b/src/main/kotlin/io/github/cdsap/kotlinprocess/parser/JInfoData.kt index 9f717f8..4fed89b 100644 --- a/src/main/kotlin/io/github/cdsap/kotlinprocess/parser/JInfoData.kt +++ b/src/main/kotlin/io/github/cdsap/kotlinprocess/parser/JInfoData.kt @@ -15,17 +15,34 @@ class JInfoData { var auxIndex = 0 for (i in 0 until xNumberOfProcess) { val flags = lines[auxIndex].split("\\s+".toRegex()) + val gcType = getCollector(flags) if (!flags.last().contains("-XX:MaxHeapSize=")) { flags.dropLast(1) } + val heapSizeFlag = flags.firstOrNull { it.contains("-XX:MaxHeapSize=") }?.replace("-XX:MaxHeapSize=", "") val process = lines[++auxIndex].split("\\s+".toRegex()) if (heapSizeFlag != null) { auxIndex++ - processP[process.first()] = ProcessJInfo(heapSizeFlag.toDouble()) + processP[process.first()] = ProcessJInfo(heapSizeFlag.toDouble(), gcType) } } return processP } + private fun getCollector(flags: List): String { + if (flags.contains("-XX:+UseZGC")) { + return "-XX:+UseZGC" + } else if (flags.contains("-XX:+UseSerialGC")) { + return "-XX:+UseSerialGC" + } else if (flags.contains("-XX:+UseShenandoahGC")) { + return "-XX:+UseShenandoahGC" + } else if (flags.contains("-XX:+UseG1GC")) { + return "-XX:+UseG1GC" + } else if (flags.contains("-XX:+UseParallelGC")) { + return "-XX:+UseParallelGC" + } else { + return "" + } + } } diff --git a/src/main/kotlin/io/github/cdsap/kotlinprocess/parser/JStatData.kt b/src/main/kotlin/io/github/cdsap/kotlinprocess/parser/JStatData.kt index 2248eaa..b8b75f8 100644 --- a/src/main/kotlin/io/github/cdsap/kotlinprocess/parser/JStatData.kt +++ b/src/main/kotlin/io/github/cdsap/kotlinprocess/parser/JStatData.kt @@ -21,151 +21,105 @@ class JStatData { for (i in 0 until numberOfProcessesDetected) { val rawHeaders = lines[currentIndex].split("\\s+".toRegex()).filter { it != "" } val rawValues = lines[++currentIndex].split("\\s+".toRegex()).filter { it != "" } - - val typeOfCollector = getCollector(rawHeaders, rawValues) - - val (headers, value) = preparePairsByCollector(typeOfCollector, rawHeaders, rawValues) - - if (headers.size == value.size && checkValuesAraValid(value)) { - val process = lines[++currentIndex].split("\\s+".toRegex()) - val jspMapValues = mutableMapOf() - var aux = 0 - currentIndex++ - - headers.forEach { - jspMapValues[it] = value[aux].toDouble() - aux++ - } - processes[process.first()] = ProcessJstat( - capacity = totalCapacity(typeOfCollector, jspMapValues), - usage = usage(typeOfCollector, jspMapValues), - gcTime = gcTime(jspMapValues), - uptime = uptime(jspMapValues), - typeGC = typeOfCollector.name - ) - - } + val mapOfValues = getMapValues(rawHeaders, rawValues) + val process = lines[++currentIndex].split("\\s+".toRegex()) + currentIndex++ + processes[process.first()] = ProcessJstat( + capacity = getCapacity(mapOfValues), + usage = getUsage(mapOfValues), + gcTime = gcTime(mapOfValues), + uptime = uptime(mapOfValues) + ) } return processes } - private fun getCollector(rawHeaders: List, rawValues: List): TypeCollector { - val socHeaderPosition = rawHeaders.indexOf("S0C") - val soc = rawValues[socHeaderPosition] - if (soc == "-") { - return TypeCollector.Z - } else { - val socCGC = rawHeaders.indexOf("CGC") - val cgc = rawValues[socCGC] - if (cgc == "-") { - return TypeCollector.PARALLEL - } else { - return TypeCollector.G1 - } + private fun getMapValues(rawHeaders: List, rawValues: List): Map { + val parsedValues = mutableMapOf() + var i = 0 + rawHeaders.forEach { + parsedValues[it] = rawValues[i] + i++ } + return parsedValues } +} - private fun preparePairsByCollector( - typeOfCollector: TypeCollector, - rawHeaders: List, - rawValues: List - ): Pair, List> { - when (typeOfCollector) { - TypeCollector.G1 -> { - return Pair(rawHeaders, rawValues) - } - - TypeCollector.PARALLEL -> { - val concurrentGCTime = rawHeaders.indexOf("CGC") - val concurrentGCTimeTotal = rawHeaders.indexOf("CGCT") - - val headers = rawHeaders.toMutableList() - headers.removeAt(concurrentGCTime) - headers.removeAt(concurrentGCTimeTotal - 1) - val value = rawValues.toMutableList() - value.removeAt(concurrentGCTime) - value.removeAt(concurrentGCTimeTotal - 1) - return Pair(headers.toList(), value.toList()) - } - - TypeCollector.Z -> { - val soc = rawHeaders.indexOf("S0C") - val s1c = rawHeaders.indexOf("S1C") - val sou = rawHeaders.indexOf("S0U") - val s1u = rawHeaders.indexOf("S1U") - val ec = rawHeaders.indexOf("EC") - val eu = rawHeaders.indexOf("EU") - val ygc = rawHeaders.indexOf("YGC") - val ygct = rawHeaders.indexOf("YGCT") - val fgc = rawHeaders.indexOf("FGC") - val fgct = rawHeaders.indexOf("FGCT") - - val headers = rawHeaders.toMutableList() - headers.removeAt(soc) - headers.removeAt(s1c - 1) - headers.removeAt(sou - 2) - headers.removeAt(s1u - 3) - headers.removeAt(ec - 4) - headers.removeAt(eu - 5) - headers.removeAt(ygc - 6) - headers.removeAt(ygct - 7) - headers.removeAt(fgc - 8) - headers.removeAt(fgct - 9) - - val value = rawValues.toMutableList() - value.removeAt(soc) - value.removeAt(s1c - 1) - value.removeAt(sou - 2) - value.removeAt(s1u - 3) - value.removeAt(ec - 4) - value.removeAt(eu - 5) - value.removeAt(ygc - 6) - value.removeAt(ygct - 7) - value.removeAt(fgc - 8) - value.removeAt(fgct - 9) - return Pair(headers.toList(), value.toList()) - } +private fun getCapacity(values: Map): Double { + // ZGC is not Current survivor space + if (values["S0C"] == "-") { + val oc = values["OC"] + val mc = values["MC"] + if (oc != null && mc != null) { + val ocNumber = oc.jstatValueToDouble() + val mcNumber = mc.jstatValueToDouble() + return mcNumber + ocNumber + } else { + return 0.0 } - } + } else { + val ec = values["EC"] + val oc = values["OC"] + val soc = values["S0C"] + val s1c = values["S1C"] + if (ec != null && oc != null && soc != null && s1c != null) { + val ecNumber = ec.jstatValueToDouble() + val ocNumber = oc.jstatValueToDouble() + val socNumber = soc.jstatValueToDouble() + val s1cNumber = s1c.jstatValueToDouble() + return ecNumber + ocNumber + socNumber + s1cNumber - private fun checkValuesAraValid(jspMapValues: List): Boolean { - jspMapValues.forEach { - try { - it.toDouble() - } catch (e: java.lang.NumberFormatException) { - return false - } + } else { + return 0.0 } - return true } +} - private fun totalCapacity(typeOfCollector: TypeCollector, jspMapValues: Map): Double { - if(typeOfCollector == TypeCollector.Z) { - return jspMapValues["OC"]!! + jspMapValues["MC"]!! +private fun getUsage(values: Map): Double { + // ZGC is not using Eden region + if (values["S0C"] == "-") { + val ou = values["OU"] + val mu = values["MU"] + if (ou != null && mu != null) { + val ouNumber = ou.jstatValueToDouble() + val muNumber = mu.jstatValueToDouble() + return muNumber + ouNumber } else { - return jspMapValues["EC"]!! + jspMapValues["OC"]!! + jspMapValues["S0C"]!! + jspMapValues["S1C"]!! + return 0.0 } - } + } else { + val eu = values["EU"] + val ou = values["OU"] + val sou = values["S0U"] + val s1u = values["S1U"] + if (eu != null && ou != null && sou != null && s1u != null) { + val euNumber = eu.jstatValueToDouble() + val ouNumber = ou.jstatValueToDouble() + val souNumber = sou.jstatValueToDouble() + val s1uNumber = s1u.jstatValueToDouble() + return euNumber + ouNumber + souNumber + s1uNumber - private fun usage(typeOfCollector: TypeCollector, jspMapValues: Map): Double { - if(typeOfCollector == TypeCollector.Z) { - return jspMapValues["OU"]!! + jspMapValues["MU"]!! } else { - return jspMapValues["S0U"]!! + jspMapValues["S1U"]!! + jspMapValues["EU"]!! + jspMapValues["OU"]!! + return 0.0 } - } - private fun gcTime(jspMapValues: Map): Double { - return jspMapValues["GCT"]!! } +} - private fun uptime(jspMapValues: Map): Double { - return jspMapValues["Timestamp"]!! - } +private fun gcTime(values: Map): Double { + val gct = values["GCT"] + return gct?.jstatValueToDouble() ?: 0.0 } -enum class TypeCollector { - G1, - PARALLEL, - Z +private fun uptime(values: Map): Double { + val timeStamp = values["Timestamp"] + return timeStamp?.jstatValueToDouble() ?: 0.0 +} + +fun String.jstatValueToDouble(): Double { + return try { + this.toDouble() + } catch (e: java.lang.NumberFormatException) { + 0.0 + } } diff --git a/src/test/kotlin/io/github/cdsap/kotlinprocess/InfoKotlinProcessPluginTest.kt b/src/test/kotlin/io/github/cdsap/kotlinprocess/InfoKotlinProcessPluginTest.kt index 08ba4c0..3cb45d0 100644 --- a/src/test/kotlin/io/github/cdsap/kotlinprocess/InfoKotlinProcessPluginTest.kt +++ b/src/test/kotlin/io/github/cdsap/kotlinprocess/InfoKotlinProcessPluginTest.kt @@ -133,7 +133,7 @@ class InfoKotlinProcessPluginTest { gradleVersions.forEach { val build = simpleKotlinCompileBuild(it) assertTerminalOutput(build) - assertTrue(build.output.contains("PARALLEL")) + assertTrue(build.output.contains("UseParallelGC")) } } diff --git a/src/test/kotlin/io/github/cdsap/kotlinprocess/parser/JInfoDataTest.kt b/src/test/kotlin/io/github/cdsap/kotlinprocess/parser/JInfoDataTest.kt index 41718e0..55f1715 100644 --- a/src/test/kotlin/io/github/cdsap/kotlinprocess/parser/JInfoDataTest.kt +++ b/src/test/kotlin/io/github/cdsap/kotlinprocess/parser/JInfoDataTest.kt @@ -74,4 +74,30 @@ class JInfoDataTest { assertTrue(result.containsKey("12345")) assertTrue(result["12345"]?.max == 2.0) } + + @Test + fun testGcTypeIsRetrieved() { + val jInfoData = JInfoData() + val output = """ + -XX:CICompilerCount=4 -XX:InitialHeapSize=1073741824 -XX:MaxHeapSize=17179869184 -XX:MinHeapDeltaBytes=2097152 -XX:MinHeapSize=8388608 -XX:NonNMethodCodeHeapSize=5839564 -XX:NonProfiledCodeHeapSize=122909338 -XX:ProfiledCodeHeapSize=122909338 -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:SoftMaxHeapSize=17179869184 -XX:+UseCompressedClassPointers -XX:-UseCompressedOops -XX:-UseNUMA -XX:-UseNUMAInterleaving -XX:+UseZGC + 12345 + """.trimIndent() + val result = jInfoData.process(output) + assertTrue(result.size == 1) + assertTrue(result.containsKey("12345")) + assertTrue(result["12345"]?.gcType == "-XX:+UseZGC") + } + + @Test + fun testNotSupportedGcTypeIsEmpty() { + val jInfoData = JInfoData() + val output = """ + -XX:CICompilerCount=4 -XX:InitialHeapSize=1073741824 -XX:MaxHeapSize=17179869184 -XX:MinHeapDeltaBytes=2097152 -XX:MinHeapSize=8388608 -XX:NonNMethodCodeHeapSize=5839564 -XX:NonProfiledCodeHeapSize=122909338 -XX:ProfiledCodeHeapSize=122909338 -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:SoftMaxHeapSize=17179869184 -XX:+UseCompressedClassPointers -XX:-UseCompressedOops -XX:-UseNUMA -XX:-UseNUMAInterleaving -XX:+CCGC + 12345 + """.trimIndent() + val result = jInfoData.process(output) + assertTrue(result.size == 1) + assertTrue(result.containsKey("12345")) + assertTrue(result["12345"]?.gcType == "") + } } diff --git a/src/test/kotlin/io/github/cdsap/kotlinprocess/parser/JStatDataTest.kt b/src/test/kotlin/io/github/cdsap/kotlinprocess/parser/JStatDataTest.kt index 219be74..d10fb86 100644 --- a/src/test/kotlin/io/github/cdsap/kotlinprocess/parser/JStatDataTest.kt +++ b/src/test/kotlin/io/github/cdsap/kotlinprocess/parser/JStatDataTest.kt @@ -84,4 +84,36 @@ class JStatDataTest { assertTrue(result["12345"]?.usage == (30.0 + 40.0 + 60.0 + 80.3)) assertTrue(result["12345"]?.uptime == 1000.0) } + + @Test + fun testCGCIsNotPresent() { + val jStatData = JStatData() + val result = jStatData.process( + """ + Timestamp S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT + 15166.0 0.0 12288.0 0.0 12288.0 89088.0 65536.0 207872.0 87565.3 95824.0 84374.6 11264.0 8760.1 87 0.315 1 0.163 0.663 + 42050 + """.trimIndent() + ) + assertTrue(result.containsKey("42050")) + assertTrue(result["42050"]?.uptime == 15166.0) + assertTrue(result["42050"]?.gcTime == 0.663) + } + + @Test + fun testZGCIsParsedWithoutSurvivorSpace() { + val jStatData = JStatData() + val result = jStatData.process( + """ + Timestamp S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT CGC CGCT GCT + 1000.0 - - 30.0 40.0 50.0 60.0 70.0 2 3 1 2 3 4 5 150 160.163 170 1 190.663 + 42050 + """.trimIndent() + ) + assertTrue(result.containsKey("42050")) + assertTrue(result["42050"]?.uptime == 1000.0) + assertTrue(result["42050"]?.capacity == 73.0) + assertTrue(result["42050"]?.gcTime == 190.663) + assertTrue(result["42050"]?.usage == 3.0) + } } From ae39502903df221fd5353162e14e73d7d2f51cae Mon Sep 17 00:00:00 2001 From: Inaki Villar Date: Fri, 9 Jun 2023 16:59:45 -0700 Subject: [PATCH 2/2] fix incorrect number of fields --- .../cdsap/kotlinprocess/parser/JStatData.kt | 21 +++++++++++-------- .../kotlinprocess/parser/JStatDataTest.kt | 2 +- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/main/kotlin/io/github/cdsap/kotlinprocess/parser/JStatData.kt b/src/main/kotlin/io/github/cdsap/kotlinprocess/parser/JStatData.kt index b8b75f8..af6d93d 100644 --- a/src/main/kotlin/io/github/cdsap/kotlinprocess/parser/JStatData.kt +++ b/src/main/kotlin/io/github/cdsap/kotlinprocess/parser/JStatData.kt @@ -21,15 +21,18 @@ class JStatData { for (i in 0 until numberOfProcessesDetected) { val rawHeaders = lines[currentIndex].split("\\s+".toRegex()).filter { it != "" } val rawValues = lines[++currentIndex].split("\\s+".toRegex()).filter { it != "" } - val mapOfValues = getMapValues(rawHeaders, rawValues) - val process = lines[++currentIndex].split("\\s+".toRegex()) - currentIndex++ - processes[process.first()] = ProcessJstat( - capacity = getCapacity(mapOfValues), - usage = getUsage(mapOfValues), - gcTime = gcTime(mapOfValues), - uptime = uptime(mapOfValues) - ) + if (rawHeaders.size == rawValues.size) { + val mapOfValues = getMapValues(rawHeaders, rawValues) + val process = lines[++currentIndex].split("\\s+".toRegex()) + currentIndex++ + + processes[process.first()] = ProcessJstat( + capacity = getCapacity(mapOfValues), + usage = getUsage(mapOfValues), + gcTime = gcTime(mapOfValues), + uptime = uptime(mapOfValues) + ) + } } return processes } diff --git a/src/test/kotlin/io/github/cdsap/kotlinprocess/parser/JStatDataTest.kt b/src/test/kotlin/io/github/cdsap/kotlinprocess/parser/JStatDataTest.kt index d10fb86..604f09e 100644 --- a/src/test/kotlin/io/github/cdsap/kotlinprocess/parser/JStatDataTest.kt +++ b/src/test/kotlin/io/github/cdsap/kotlinprocess/parser/JStatDataTest.kt @@ -56,7 +56,7 @@ class JStatDataTest { 42050 """.trimIndent() ) - assertTrue(result.isEmpty()) + assertTrue(result["42050"]?.uptime == 0.0) } @Test