Add GC metric to build statistic

This commit is contained in:
nataliya.valtman
2023-02-27 18:59:00 +01:00
committed by Space Team
parent c16dabba1f
commit a34657195b
12 changed files with 122 additions and 4 deletions
@@ -10,12 +10,14 @@ import java.io.Serializable
data class BuildMetrics(
val buildTimes: BuildTimes = BuildTimes(),
val buildPerformanceMetrics: BuildPerformanceMetrics = BuildPerformanceMetrics(),
val buildAttributes: BuildAttributes = BuildAttributes()
val buildAttributes: BuildAttributes = BuildAttributes(),
val gcMetrics: GcMetrics = GcMetrics()
) : Serializable {
fun addAll(other: BuildMetrics) {
buildTimes.addAll(other.buildTimes)
buildPerformanceMetrics.addAll(other.buildPerformanceMetrics)
buildAttributes.addAll(other.buildAttributes)
gcMetrics.addAll(other.gcMetrics)
}
companion object {
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.build.report.metrics
import java.lang.management.ManagementFactory
interface BuildMetricsReporter {
fun startMeasure(time: BuildTime)
fun endMeasure(time: BuildTime)
@@ -14,6 +16,11 @@ interface BuildMetricsReporter {
fun addMetric(metric: BuildPerformanceMetric, value: Long)
fun addTimeMetric(metric: BuildPerformanceMetric)
//Change metric to enum if possible
fun addGcMetric(metric: String, value: GcMetric)
fun startGcMetric(name: String, value: GcMetric)
fun endGcMetric(name: String, value: GcMetric)
fun addAttribute(attribute: BuildAttribute)
fun getMetrics(): BuildMetrics
@@ -27,4 +34,17 @@ inline fun <T> BuildMetricsReporter.measure(time: BuildTime, fn: () -> T): T {
} finally {
endMeasure(time)
}
}
fun BuildMetricsReporter.startMeasureGc() {
ManagementFactory.getGarbageCollectorMXBeans().forEach {
startGcMetric(it.name, GcMetric(it.collectionTime, it.collectionCount))
}
}
fun BuildMetricsReporter.endMeasureGc() {
ManagementFactory.getGarbageCollectorMXBeans().forEach {
endGcMetric(it.name, GcMetric(it.collectionTime, it.collectionCount))
}
}
@@ -13,9 +13,11 @@ class BuildMetricsReporterImpl : BuildMetricsReporter, Serializable {
EnumMap(
BuildTime::class.java
)
private val myGcPerformance = HashMap<String, GcMetric>()
private val myBuildTimes = BuildTimes()
private val myBuildMetrics = BuildPerformanceMetrics()
private val myBuildAttributes = BuildAttributes()
private val myGcMetrics = GcMetrics()
override fun startMeasure(time: BuildTime) {
if (time in myBuildTimeStartNs) {
@@ -30,6 +32,19 @@ class BuildMetricsReporterImpl : BuildMetricsReporter, Serializable {
myBuildTimes.addTimeNs(time, durationNs)
}
override fun startGcMetric(name: String, value: GcMetric) {
if (name in myGcPerformance) {
error("$name was restarted before it finished")
}
myGcPerformance[name] = value
}
override fun endGcMetric(name: String, value: GcMetric) {
val startValue = myGcPerformance.remove(name) ?: error("$name finished before it started")
val diff = value - startValue
myGcMetrics.add(name, diff)
}
override fun addTimeMetricNs(time: BuildTime, durationNs: Long) {
myBuildTimes.addTimeNs(time, durationNs)
}
@@ -47,6 +62,10 @@ class BuildMetricsReporterImpl : BuildMetricsReporter, Serializable {
}
override fun addGcMetric(metric: String, value: GcMetric) {
myGcMetrics.add(metric, value)
}
override fun addAttribute(attribute: BuildAttribute) {
myBuildAttributes.add(attribute)
}
@@ -55,12 +74,14 @@ class BuildMetricsReporterImpl : BuildMetricsReporter, Serializable {
BuildMetrics(
buildTimes = myBuildTimes,
buildPerformanceMetrics = myBuildMetrics,
buildAttributes = myBuildAttributes
buildAttributes = myBuildAttributes,
gcMetrics = myGcMetrics
)
override fun addMetrics(metrics: BuildMetrics) {
myBuildAttributes.addAll(metrics.buildAttributes)
myBuildTimes.addAll(metrics.buildTimes)
myBuildMetrics.addAll(metrics.buildPerformanceMetrics)
myGcMetrics.addAll(metrics.gcMetrics)
}
}
@@ -18,6 +18,9 @@ enum class BuildPerformanceMetric(val parent: BuildPerformanceMetric? = null, va
DAEMON_INCREASED_MEMORY(readableString = "Increase memory usage", type = ValueType.BYTES),
DAEMON_MEMORY_USAGE(readableString = "Total memory usage at the end of build", type = ValueType.BYTES),
DAEMON_GC_TIME(readableString = "Time spent in GC", type = ValueType.NANOSECONDS),
DAEMON_GC_COUNT(readableString = "Count of GC", type = ValueType.NUMBER),
COMPILE_ITERATION(parent = null, "Total compiler iteration", type = ValueType.NUMBER),
ANALYZED_LINES_NUMBER(parent = COMPILE_ITERATION, "Number of lines analyzed", type = ValueType.NUMBER),
CODE_GENERATED_LINES_NUMBER(parent = COMPILE_ITERATION, "Number of lines for code generation", type = ValueType.NUMBER),
@@ -24,6 +24,15 @@ object DoNothingBuildMetricsReporter : BuildMetricsReporter {
override fun addAttribute(attribute: BuildAttribute) {
}
override fun addGcMetric(metric: String, value: GcMetric) {
}
override fun startGcMetric(name: String, value: GcMetric) {
}
override fun endGcMetric(name: String, value: GcMetric) {
}
override fun getMetrics(): BuildMetrics =
BuildMetrics(
BuildTimes(),
@@ -0,0 +1,33 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.build.report.metrics
import java.io.Serializable
import kotlin.collections.HashMap
class GcMetrics : Serializable {
private val myGcMetrics = HashMap<String, GcMetric>()
fun addAll(gcMetrics: GcMetrics) {
myGcMetrics.putAll(gcMetrics.myGcMetrics)
}
fun add(metric: String, value: GcMetric) {
myGcMetrics[metric] = value
}
fun asGcCountMap(): Map<String, Long> = myGcMetrics.mapValues { it.value.count }
fun asGcTimeMap(): Map<String, Long> = myGcMetrics.mapValues { it.value.time }
fun asMap(): Map<String, GcMetric> = myGcMetrics
}
data class GcMetric(
val time: Long,
val count: Long
): Serializable {
operator fun minus(increment: GcMetric): GcMetric {
return GcMetric(time - increment.time, count - increment.count)
}
}
@@ -23,6 +23,8 @@ import com.intellij.openapi.vfs.impl.jar.CoreJarFileSystem
import org.jetbrains.kotlin.build.DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
import org.jetbrains.kotlin.build.report.RemoteBuildReporter
import org.jetbrains.kotlin.build.report.info
import org.jetbrains.kotlin.build.report.metrics.endMeasureGc
import org.jetbrains.kotlin.build.report.metrics.startMeasureGc
import org.jetbrains.kotlin.cli.common.CLICompiler
import org.jetbrains.kotlin.cli.common.CompilerSystemProperties
import org.jetbrains.kotlin.cli.common.ExitCode
@@ -532,6 +534,7 @@ abstract class CompileServiceImplBase(
compilerMessageCollector: MessageCollector,
reporter: RemoteBuildReporter
): ExitCode {
reporter.startMeasureGc()
val allKotlinFiles = arrayListOf<File>()
val freeArgsWithoutKotlinFiles = arrayListOf<String>()
args.freeArgs.forEach {
@@ -565,6 +568,7 @@ abstract class CompileServiceImplBase(
return try {
compiler.compile(allKotlinFiles, args, compilerMessageCollector, changedFiles)
} finally {
reporter.endMeasureGc()
reporter.flush()
}
}
@@ -575,6 +579,7 @@ abstract class CompileServiceImplBase(
compilerMessageCollector: MessageCollector,
reporter: RemoteBuildReporter
): ExitCode {
reporter.startMeasureGc()
val allKotlinExtensions = (DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS +
(incrementalCompilationOptions.kotlinScriptExtensions ?: emptyArray())).distinct()
val dotExtensions = allKotlinExtensions.map { ".$it" }
@@ -630,6 +635,7 @@ abstract class CompileServiceImplBase(
return try {
compiler.compile(allKotlinFiles, k2jvmArgs, compilerMessageCollector, changedFiles, projectRoot)
} finally {
reporter.endMeasureGc()
reporter.flush()
}
}
@@ -73,6 +73,9 @@ class BuildReportsIT : KGPBaseTest() {
//for non-incremental builds
assertTrue { report.contains("Build attributes:") }
assertTrue { report.contains("REBUILD_REASON:") }
//gc metrics
assertTrue {report.contains("GC count:")}
assertTrue {report.contains("GC time:")}
}
}
@@ -32,6 +32,8 @@ data class CompileStatisticsData(
//TODO think about it,time in milliseconds
val buildTimesMetrics: Map<BuildTime, Long>,
val performanceMetrics: Map<BuildPerformanceMetric, Long>,
val gcTimeMetrics: Map<String, Long>?,
val gcCountMetrics: Map<String, Long>?,
val type: String = BuildDataType.TASK_DATA.name
)
@@ -171,7 +171,7 @@ internal class TaskRecord(
override val buildMetrics: BuildMetrics,
override val didWork: Boolean,
override val skipMessage: String?,
override val icLogLines: List<String>
override val icLogLines: List<String>,
) : BuildOperationRecord {
override val isFromKotlinPlugin: Boolean = classFqName.startsWith("org.jetbrains.kotlin")
}
@@ -85,6 +85,9 @@ internal class PlainTextBuildReportWriter(
if (newLineBetweenSections) p.println()
printBuildAttributes(buildMetrics.buildAttributes)
if (newLineBetweenSections) p.println()
printGcMetrics(buildMetrics.gcMetrics)
}
private fun printBuildTimes(buildTimes: BuildTimes) {
@@ -162,6 +165,20 @@ internal class PlainTextBuildReportWriter(
}
}
private fun printGcMetrics(gcMetrics: GcMetrics) {
val allGcMetrics = gcMetrics.asMap()
if (allGcMetrics.isEmpty()) return
p.withIndent("GC metrics:") {
for (gcMetric in allGcMetrics) {
p.println("${gcMetric.key}:")
p.withIndent {
p.println("GC count: ${gcMetric.value.count}")
p.println("GC time: ${gcMetric.value.time}")
}
}
}
}
private fun printTaskOverview(build: BuildExecutionData) {
var allTasksTimeMs = 0L
var kotlinTotalTimeMs = 0L
@@ -81,7 +81,9 @@ internal fun prepareData(
kotlinVersion = kotlinVersion,
buildUuid = uuid,
finishTime = System.currentTimeMillis(),
compilerArguments = taskExecutionResult?.taskInfo?.compilerArguments?.asList() ?: emptyList()
compilerArguments = taskExecutionResult?.taskInfo?.compilerArguments?.asList() ?: emptyList(),
gcCountMetrics = buildMetrics?.gcMetrics?.asGcCountMap(),
gcTimeMetrics = buildMetrics?.gcMetrics?.asGcTimeMap()
)
}