Skip to content

Commit

Permalink
Merge pull request #21 from cdsap/cgc_issue
Browse files Browse the repository at this point in the history
Rework jstat parse and including gc in the jinfo
  • Loading branch information
cdsap committed Jun 10, 2023
2 parents 263e287 + ae39502 commit 13b721a
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 126 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
)
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Original file line number Diff line number Diff line change
Expand Up @@ -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>): 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 ""
}
}
}
195 changes: 76 additions & 119 deletions src/main/kotlin/io/github/cdsap/kotlinprocess/parser/JStatData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,151 +21,108 @@ 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)) {
if (rawHeaders.size == rawValues.size) {
val mapOfValues = getMapValues(rawHeaders, rawValues)
val process = lines[++currentIndex].split("\\s+".toRegex())
val jspMapValues = mutableMapOf<String, Double>()
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
capacity = getCapacity(mapOfValues),
usage = getUsage(mapOfValues),
gcTime = gcTime(mapOfValues),
uptime = uptime(mapOfValues)
)

}
}
return processes
}

private fun getCollector(rawHeaders: List<String>, rawValues: List<String>): 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<String>, rawValues: List<String>): Map<String, String> {
val parsedValues = mutableMapOf<String, String>()
var i = 0
rawHeaders.forEach {
parsedValues[it] = rawValues[i]
i++
}
return parsedValues
}
}

private fun preparePairsByCollector(
typeOfCollector: TypeCollector,
rawHeaders: List<String>,
rawValues: List<String>
): Pair<List<String>, List<String>> {
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<String, String>): 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<String>): 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<String, Double>): Double {
if(typeOfCollector == TypeCollector.Z) {
return jspMapValues["OC"]!! + jspMapValues["MC"]!!
private fun getUsage(values: Map<String, String>): 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<String, Double>): 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<String, Double>): Double {
return jspMapValues["GCT"]!!
}
}

private fun uptime(jspMapValues: Map<String, Double>): Double {
return jspMapValues["Timestamp"]!!
}
private fun gcTime(values: Map<String, String>): Double {
val gct = values["GCT"]
return gct?.jstatValueToDouble() ?: 0.0
}

enum class TypeCollector {
G1,
PARALLEL,
Z
private fun uptime(values: Map<String, String>): 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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ class InfoKotlinProcessPluginTest {
gradleVersions.forEach {
val build = simpleKotlinCompileBuild(it)
assertTerminalOutput(build)
assertTrue(build.output.contains("PARALLEL"))
assertTrue(build.output.contains("UseParallelGC"))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 == "")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class JStatDataTest {
42050
""".trimIndent()
)
assertTrue(result.isEmpty())
assertTrue(result["42050"]?.uptime == 0.0)
}

@Test
Expand Down Expand Up @@ -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)
}
}

0 comments on commit 13b721a

Please sign in to comment.