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( data class BuildMetrics(
val buildTimes: BuildTimes = BuildTimes(), val buildTimes: BuildTimes = BuildTimes(),
val buildPerformanceMetrics: BuildPerformanceMetrics = BuildPerformanceMetrics(), val buildPerformanceMetrics: BuildPerformanceMetrics = BuildPerformanceMetrics(),
val buildAttributes: BuildAttributes = BuildAttributes() val buildAttributes: BuildAttributes = BuildAttributes(),
val gcMetrics: GcMetrics = GcMetrics()
) : Serializable { ) : Serializable {
fun addAll(other: BuildMetrics) { fun addAll(other: BuildMetrics) {
buildTimes.addAll(other.buildTimes) buildTimes.addAll(other.buildTimes)
buildPerformanceMetrics.addAll(other.buildPerformanceMetrics) buildPerformanceMetrics.addAll(other.buildPerformanceMetrics)
buildAttributes.addAll(other.buildAttributes) buildAttributes.addAll(other.buildAttributes)
gcMetrics.addAll(other.gcMetrics)
} }
companion object { companion object {
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.build.report.metrics package org.jetbrains.kotlin.build.report.metrics
import java.lang.management.ManagementFactory
interface BuildMetricsReporter { interface BuildMetricsReporter {
fun startMeasure(time: BuildTime) fun startMeasure(time: BuildTime)
fun endMeasure(time: BuildTime) fun endMeasure(time: BuildTime)
@@ -14,6 +16,11 @@ interface BuildMetricsReporter {
fun addMetric(metric: BuildPerformanceMetric, value: Long) fun addMetric(metric: BuildPerformanceMetric, value: Long)
fun addTimeMetric(metric: BuildPerformanceMetric) 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 addAttribute(attribute: BuildAttribute)
fun getMetrics(): BuildMetrics fun getMetrics(): BuildMetrics
@@ -28,3 +35,16 @@ inline fun <T> BuildMetricsReporter.measure(time: BuildTime, fn: () -> T): T {
endMeasure(time) 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( EnumMap(
BuildTime::class.java BuildTime::class.java
) )
private val myGcPerformance = HashMap<String, GcMetric>()
private val myBuildTimes = BuildTimes() private val myBuildTimes = BuildTimes()
private val myBuildMetrics = BuildPerformanceMetrics() private val myBuildMetrics = BuildPerformanceMetrics()
private val myBuildAttributes = BuildAttributes() private val myBuildAttributes = BuildAttributes()
private val myGcMetrics = GcMetrics()
override fun startMeasure(time: BuildTime) { override fun startMeasure(time: BuildTime) {
if (time in myBuildTimeStartNs) { if (time in myBuildTimeStartNs) {
@@ -30,6 +32,19 @@ class BuildMetricsReporterImpl : BuildMetricsReporter, Serializable {
myBuildTimes.addTimeNs(time, durationNs) 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) { override fun addTimeMetricNs(time: BuildTime, durationNs: Long) {
myBuildTimes.addTimeNs(time, durationNs) 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) { override fun addAttribute(attribute: BuildAttribute) {
myBuildAttributes.add(attribute) myBuildAttributes.add(attribute)
} }
@@ -55,12 +74,14 @@ class BuildMetricsReporterImpl : BuildMetricsReporter, Serializable {
BuildMetrics( BuildMetrics(
buildTimes = myBuildTimes, buildTimes = myBuildTimes,
buildPerformanceMetrics = myBuildMetrics, buildPerformanceMetrics = myBuildMetrics,
buildAttributes = myBuildAttributes buildAttributes = myBuildAttributes,
gcMetrics = myGcMetrics
) )
override fun addMetrics(metrics: BuildMetrics) { override fun addMetrics(metrics: BuildMetrics) {
myBuildAttributes.addAll(metrics.buildAttributes) myBuildAttributes.addAll(metrics.buildAttributes)
myBuildTimes.addAll(metrics.buildTimes) myBuildTimes.addAll(metrics.buildTimes)
myBuildMetrics.addAll(metrics.buildPerformanceMetrics) 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_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_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), COMPILE_ITERATION(parent = null, "Total compiler iteration", type = ValueType.NUMBER),
ANALYZED_LINES_NUMBER(parent = COMPILE_ITERATION, "Number of lines analyzed", 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), 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 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 = override fun getMetrics(): BuildMetrics =
BuildMetrics( BuildMetrics(
BuildTimes(), 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.DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
import org.jetbrains.kotlin.build.report.RemoteBuildReporter import org.jetbrains.kotlin.build.report.RemoteBuildReporter
import org.jetbrains.kotlin.build.report.info 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.CLICompiler
import org.jetbrains.kotlin.cli.common.CompilerSystemProperties import org.jetbrains.kotlin.cli.common.CompilerSystemProperties
import org.jetbrains.kotlin.cli.common.ExitCode import org.jetbrains.kotlin.cli.common.ExitCode
@@ -532,6 +534,7 @@ abstract class CompileServiceImplBase(
compilerMessageCollector: MessageCollector, compilerMessageCollector: MessageCollector,
reporter: RemoteBuildReporter reporter: RemoteBuildReporter
): ExitCode { ): ExitCode {
reporter.startMeasureGc()
val allKotlinFiles = arrayListOf<File>() val allKotlinFiles = arrayListOf<File>()
val freeArgsWithoutKotlinFiles = arrayListOf<String>() val freeArgsWithoutKotlinFiles = arrayListOf<String>()
args.freeArgs.forEach { args.freeArgs.forEach {
@@ -565,6 +568,7 @@ abstract class CompileServiceImplBase(
return try { return try {
compiler.compile(allKotlinFiles, args, compilerMessageCollector, changedFiles) compiler.compile(allKotlinFiles, args, compilerMessageCollector, changedFiles)
} finally { } finally {
reporter.endMeasureGc()
reporter.flush() reporter.flush()
} }
} }
@@ -575,6 +579,7 @@ abstract class CompileServiceImplBase(
compilerMessageCollector: MessageCollector, compilerMessageCollector: MessageCollector,
reporter: RemoteBuildReporter reporter: RemoteBuildReporter
): ExitCode { ): ExitCode {
reporter.startMeasureGc()
val allKotlinExtensions = (DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS + val allKotlinExtensions = (DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS +
(incrementalCompilationOptions.kotlinScriptExtensions ?: emptyArray())).distinct() (incrementalCompilationOptions.kotlinScriptExtensions ?: emptyArray())).distinct()
val dotExtensions = allKotlinExtensions.map { ".$it" } val dotExtensions = allKotlinExtensions.map { ".$it" }
@@ -630,6 +635,7 @@ abstract class CompileServiceImplBase(
return try { return try {
compiler.compile(allKotlinFiles, k2jvmArgs, compilerMessageCollector, changedFiles, projectRoot) compiler.compile(allKotlinFiles, k2jvmArgs, compilerMessageCollector, changedFiles, projectRoot)
} finally { } finally {
reporter.endMeasureGc()
reporter.flush() reporter.flush()
} }
} }
@@ -73,6 +73,9 @@ class BuildReportsIT : KGPBaseTest() {
//for non-incremental builds //for non-incremental builds
assertTrue { report.contains("Build attributes:") } assertTrue { report.contains("Build attributes:") }
assertTrue { report.contains("REBUILD_REASON:") } 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 //TODO think about it,time in milliseconds
val buildTimesMetrics: Map<BuildTime, Long>, val buildTimesMetrics: Map<BuildTime, Long>,
val performanceMetrics: Map<BuildPerformanceMetric, Long>, val performanceMetrics: Map<BuildPerformanceMetric, Long>,
val gcTimeMetrics: Map<String, Long>?,
val gcCountMetrics: Map<String, Long>?,
val type: String = BuildDataType.TASK_DATA.name val type: String = BuildDataType.TASK_DATA.name
) )
@@ -171,7 +171,7 @@ internal class TaskRecord(
override val buildMetrics: BuildMetrics, override val buildMetrics: BuildMetrics,
override val didWork: Boolean, override val didWork: Boolean,
override val skipMessage: String?, override val skipMessage: String?,
override val icLogLines: List<String> override val icLogLines: List<String>,
) : BuildOperationRecord { ) : BuildOperationRecord {
override val isFromKotlinPlugin: Boolean = classFqName.startsWith("org.jetbrains.kotlin") override val isFromKotlinPlugin: Boolean = classFqName.startsWith("org.jetbrains.kotlin")
} }
@@ -85,6 +85,9 @@ internal class PlainTextBuildReportWriter(
if (newLineBetweenSections) p.println() if (newLineBetweenSections) p.println()
printBuildAttributes(buildMetrics.buildAttributes) printBuildAttributes(buildMetrics.buildAttributes)
if (newLineBetweenSections) p.println()
printGcMetrics(buildMetrics.gcMetrics)
} }
private fun printBuildTimes(buildTimes: BuildTimes) { 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) { private fun printTaskOverview(build: BuildExecutionData) {
var allTasksTimeMs = 0L var allTasksTimeMs = 0L
var kotlinTotalTimeMs = 0L var kotlinTotalTimeMs = 0L
@@ -81,7 +81,9 @@ internal fun prepareData(
kotlinVersion = kotlinVersion, kotlinVersion = kotlinVersion,
buildUuid = uuid, buildUuid = uuid,
finishTime = System.currentTimeMillis(), finishTime = System.currentTimeMillis(),
compilerArguments = taskExecutionResult?.taskInfo?.compilerArguments?.asList() ?: emptyList() compilerArguments = taskExecutionResult?.taskInfo?.compilerArguments?.asList() ?: emptyList(),
gcCountMetrics = buildMetrics?.gcMetrics?.asGcCountMap(),
gcTimeMetrics = buildMetrics?.gcMetrics?.asGcTimeMap()
) )
} }