Remove JMX bean invocation for internal build FUS metrics
#KT-58768 In Progress
This commit is contained in:
committed by
Space Team
parent
9b5c331c8e
commit
cf3672314a
+53
-12
@@ -15,6 +15,10 @@ import kotlin.io.path.pathString
|
||||
class BuildFusStatisticsIT : KGPDaemonsBaseTest() {
|
||||
@DisplayName("works for project with buildSrc and kotlinDsl plugin")
|
||||
@GradleTest
|
||||
@GradleTestVersions(
|
||||
additionalVersions = [TestVersions.Gradle.G_7_6, TestVersions.Gradle.G_8_0, TestVersions.Gradle.G_8_2, TestVersions.Gradle.G_8_3],
|
||||
maxVersion = TestVersions.Gradle.G_8_5
|
||||
)
|
||||
fun testCompatibilityBuildSrcWithKotlinDsl(gradleVersion: GradleVersion) {
|
||||
project(
|
||||
"buildSrcUsingKotlinCompilationAndKotlinPlugin",
|
||||
@@ -23,22 +27,59 @@ class BuildFusStatisticsIT : KGPDaemonsBaseTest() {
|
||||
) {
|
||||
build("assemble") {
|
||||
//register build service for buildSrc.
|
||||
val legacyBuildServiceMessagesCount = if (gradleVersion < GradleVersion.version(TestVersions.Gradle.G_8_0)) {
|
||||
when {
|
||||
// until 8.0, Gradle was embedding the Kotlin version that used a slightly different approach to detect build finish,
|
||||
// so the service was unregistered after the finish of the buildSrc build
|
||||
// and then registered again in the root build
|
||||
2
|
||||
} else {
|
||||
1
|
||||
gradleVersion < GradleVersion.version(TestVersions.Gradle.G_8_0) -> {
|
||||
assertOutputContainsExactTimes(
|
||||
"Instantiated class org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService: new instance", // the service for buildSrc
|
||||
1
|
||||
)
|
||||
assertOutputContainsExactTimes(
|
||||
"Instantiated class org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsBeanService: new instance", // the legacy service for compatibility
|
||||
1
|
||||
)
|
||||
assertOutputContainsExactTimes(
|
||||
"Instantiated class org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsBeanService_v2: new instance", // the current default version of the service
|
||||
1
|
||||
)
|
||||
}
|
||||
gradleVersion < GradleVersion.version(TestVersions.Gradle.G_8_3) -> {
|
||||
assertOutputContainsExactTimes(
|
||||
"Instantiated class org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService: new instance", // the legacy service for compatibility
|
||||
1
|
||||
)
|
||||
assertOutputContainsExactTimes(
|
||||
"Instantiated class org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsBeanService_v2: new instance", // the current default version of the service
|
||||
1
|
||||
)
|
||||
}
|
||||
//for gradle 8.3 kotlin 1.9.0 is used, log message is changed
|
||||
gradleVersion < GradleVersion.version(TestVersions.Gradle.G_8_5) -> {
|
||||
assertOutputContainsExactTimes(
|
||||
"Register JMX service for backward compatibility", // the legacy service for compatibility
|
||||
1
|
||||
)
|
||||
assertOutputContainsExactTimes(
|
||||
"Instantiated class org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService_v2: new instance", // the current default version of the service
|
||||
1
|
||||
)
|
||||
}
|
||||
//for other versions KGP from buildSrc registered both services
|
||||
else -> {
|
||||
assertOutputContainsExactTimes(
|
||||
"Instantiated class org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService: new instance", // the legacy service for compatibility
|
||||
1
|
||||
)
|
||||
assertOutputContainsExactTimes(
|
||||
"Instantiated class org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService_v2: new instance", // the current default version of the service
|
||||
1
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
assertOutputContainsExactTimes(
|
||||
"Instantiated class org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService: new instance", // the legacy service for compatibility
|
||||
legacyBuildServiceMessagesCount
|
||||
)
|
||||
assertOutputContainsExactTimes(
|
||||
"Instantiated class org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService_v2: new instance", // the current default version of the service
|
||||
1
|
||||
)
|
||||
|
||||
assertOutputDoesNotContain("[org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatHandler] Could not execute")
|
||||
}
|
||||
}
|
||||
|
||||
+20
-31
@@ -12,7 +12,9 @@ import org.jetbrains.kotlin.gradle.util.replaceText
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.deleteIfExists
|
||||
import kotlin.streams.toList
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@DisplayName("FUS statistic")
|
||||
//Tests for FUS statistics have to create new instance of KotlinBuildStatsService
|
||||
@@ -95,9 +97,10 @@ class FusStatisticsIT : KGPDaemonsBaseTest() {
|
||||
@DisplayName("for project with buildSrc")
|
||||
@GradleTest
|
||||
@GradleTestVersions(
|
||||
maxVersion = TestVersions.Gradle.G_7_6
|
||||
additionalVersions = [TestVersions.Gradle.G_7_6, TestVersions.Gradle.G_8_0]
|
||||
)
|
||||
fun testProjectWithBuildSrcForGradleVersion7(gradleVersion: GradleVersion) {
|
||||
//KT-64022 there are a different build instances in buildSrc and rest project:
|
||||
project(
|
||||
"instantExecutionWithBuildSrc",
|
||||
gradleVersion,
|
||||
@@ -112,26 +115,6 @@ class FusStatisticsIT : KGPDaemonsBaseTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("for project with buildSrc")
|
||||
@GradleTest
|
||||
@GradleTestVersions(
|
||||
minVersion = TestVersions.Gradle.G_8_0
|
||||
)
|
||||
fun testProjectWithBuildSrc(gradleVersion: GradleVersion) {
|
||||
project(
|
||||
"instantExecutionWithBuildSrc",
|
||||
gradleVersion,
|
||||
) {
|
||||
build("compileKotlin", "-Pkotlin.session.logger.root.path=$projectPath") {
|
||||
assertFileContains(
|
||||
fusStatisticsPath,
|
||||
*expectedMetrics,
|
||||
"BUILD_SRC_EXISTS=true"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("for project with included build")
|
||||
@GradleTest
|
||||
@GradleTestVersions(
|
||||
@@ -139,24 +122,27 @@ class FusStatisticsIT : KGPDaemonsBaseTest() {
|
||||
maxVersion = TestVersions.Gradle.G_8_0
|
||||
)
|
||||
fun testProjectWithIncludedBuild(gradleVersion: GradleVersion) {
|
||||
//KT-64022
|
||||
//there are a different build instances in buildSrc and rest project
|
||||
|
||||
project(
|
||||
"instantExecutionWithIncludedBuildPlugin",
|
||||
gradleVersion,
|
||||
buildOptions = defaultBuildOptions.copy(configurationCache = true)
|
||||
) {
|
||||
build("compileKotlin", "-Pkotlin.session.logger.root.path=$projectPath") {
|
||||
val fusStatisticsPath = fusStatisticsPath
|
||||
assertFileContains(
|
||||
fusStatisticsPath,
|
||||
*expectedMetrics,
|
||||
)
|
||||
Files.list(projectPath.resolve("kotlin-profile")).forEach {
|
||||
assertFileContains(it, *expectedMetrics)
|
||||
}
|
||||
}
|
||||
Files.list(projectPath.resolve("kotlin-profile")).forEach {
|
||||
assertTrue(it.deleteIfExists())
|
||||
}
|
||||
|
||||
build("compileKotlin", "-Pkotlin.session.logger.root.path=$projectPath") {
|
||||
val fusStatisticsPath = fusStatisticsPath
|
||||
assertFileContains(
|
||||
fusStatisticsPath,
|
||||
*expectedMetrics,
|
||||
)
|
||||
Files.list(projectPath.resolve("kotlin-profile")).forEach {
|
||||
assertFileContains(it, *expectedMetrics)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -256,6 +242,9 @@ class FusStatisticsIT : KGPDaemonsBaseTest() {
|
||||
)
|
||||
}
|
||||
|
||||
assertTrue(fusStatisticsPath.deleteIfExists())
|
||||
build("clean", buildOptions = buildOptions)
|
||||
|
||||
build(
|
||||
"compileKotlin",
|
||||
"-Pkotlin.session.logger.root.path=$projectPath",
|
||||
|
||||
+1
@@ -28,6 +28,7 @@ interface TestVersions {
|
||||
const val G_8_2 = "8.2.1"
|
||||
const val G_8_3 = "8.3"
|
||||
const val G_8_4 = "8.4"
|
||||
const val G_8_5 = "8.5"
|
||||
|
||||
const val MIN_SUPPORTED = minSupportedGradleVersion
|
||||
const val MIN_SUPPORTED_KPM = G_7_0
|
||||
|
||||
+1
-6
@@ -60,17 +60,12 @@ internal class BuildFinishFlowAction : FlowAction<BuildFinishFlowAction.Paramete
|
||||
@get:ServiceReference
|
||||
val buildFusServiceProperty: Property<BuildFusService>
|
||||
|
||||
@get:Input
|
||||
val action: Property<String?>
|
||||
|
||||
@get:Input
|
||||
val buildFailed: Property<Boolean>
|
||||
|
||||
}
|
||||
|
||||
override fun execute(parameters: Parameters) {
|
||||
parameters.buildFusServiceProperty.orNull?.recordBuildFinished(
|
||||
parameters.action.orNull, parameters.buildFailed.get()
|
||||
)
|
||||
parameters.buildFusServiceProperty.orNull?.recordBuildFinished(parameters.buildFailed.get())
|
||||
}
|
||||
}
|
||||
|
||||
+39
-25
@@ -49,14 +49,17 @@ internal interface UsesBuildFusService : Task {
|
||||
abstract class BuildFusService : BuildService<BuildFusService.Parameters>, AutoCloseable, OperationCompletionListener {
|
||||
private var buildFailed: Boolean = false
|
||||
private val log = Logging.getLogger(this.javaClass)
|
||||
private val buildId = randomUUID().toString()
|
||||
|
||||
init {
|
||||
log.kotlinDebug("Initialize ${this.javaClass.simpleName}")
|
||||
KotlinBuildStatsBeanService.recordBuildStart(buildId)
|
||||
}
|
||||
|
||||
interface Parameters : BuildServiceParameters {
|
||||
val configurationMetrics: ListProperty<MetricContainer>
|
||||
val useBuildFinishFlowAction: Property<Boolean>
|
||||
val buildStatisticsConfiguration: Property<KotlinBuildStatsConfiguration>
|
||||
}
|
||||
|
||||
private val fusMetricsConsumer = NonSynchronizedMetricsContainer()
|
||||
@@ -67,10 +70,11 @@ abstract class BuildFusService : BuildService<BuildFusService.Parameters>, AutoC
|
||||
reportAction(fusMetricsConsumer)
|
||||
}
|
||||
|
||||
private val buildId = randomUUID().toString()
|
||||
private val projectEvaluatedTime: Long = System.currentTimeMillis()
|
||||
|
||||
companion object {
|
||||
internal val serviceName = "${BuildFusService::class.simpleName}_${BuildFusService::class.java.classLoader.hashCode()}"
|
||||
private var buildStartTime: Long = System.currentTimeMillis()
|
||||
|
||||
fun registerIfAbsent(project: Project, pluginVersion: String) =
|
||||
registerIfAbsentImpl(project, pluginVersion).also { serviceProvider ->
|
||||
@@ -90,20 +94,18 @@ abstract class BuildFusService : BuildService<BuildFusService.Parameters>, AutoC
|
||||
val isProjectIsolationEnabled = project.isProjectIsolationEnabled
|
||||
|
||||
project.gradle.sharedServices.registrations.findByName(serviceName)?.let {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return (it.service as Provider<BuildFusService>).also { buildServiceProvider ->
|
||||
project.afterEvaluate {
|
||||
buildServiceProvider.get().parameters.configurationMetrics.add(
|
||||
project.provider { KotlinBuildStatHandler.collectProjectConfigurationTimeMetrics(project) }
|
||||
)
|
||||
(it.parameters as Parameters).configurationMetrics.add(
|
||||
project.provider {
|
||||
collectProjectConfigurationTimeMetrics(project)
|
||||
}
|
||||
}
|
||||
|
||||
)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return (it.service as Provider<BuildFusService>)
|
||||
}
|
||||
|
||||
//init buildStatsService
|
||||
KotlinBuildStatsService.getOrCreateInstance(project)?.also {
|
||||
it.recordProjectsEvaluated(project)
|
||||
}
|
||||
KotlinBuildStatsBeanService.initStatsService(project)
|
||||
|
||||
val buildReportOutputs = reportingSettings(project).buildReportOutputs
|
||||
val useClasspathSnapshot = PropertiesProvider(project).useClasspathSnapshot
|
||||
@@ -113,9 +115,8 @@ abstract class BuildFusService : BuildService<BuildFusService.Parameters>, AutoC
|
||||
// when this OperationCompletionListener is called services can be already closed for Gradle 8,
|
||||
// so there is a change that no VariantImplementationFactory will be found
|
||||
return gradle.sharedServices.registerIfAbsent(serviceName, BuildFusService::class.java) { spec ->
|
||||
|
||||
spec.parameters.configurationMetrics.add(project.provider {
|
||||
KotlinBuildStatHandler.collectGeneralConfigurationTimeMetrics(
|
||||
collectGeneralConfigurationTimeMetrics(
|
||||
gradle,
|
||||
buildReportOutputs,
|
||||
useClasspathSnapshot,
|
||||
@@ -123,8 +124,18 @@ abstract class BuildFusService : BuildService<BuildFusService.Parameters>, AutoC
|
||||
isProjectIsolationEnabled
|
||||
)
|
||||
})
|
||||
spec.parameters.configurationMetrics.add(
|
||||
project.provider {
|
||||
collectProjectConfigurationTimeMetrics(project)
|
||||
}
|
||||
)
|
||||
spec.parameters.useBuildFinishFlowAction.set(GradleVersion.current().baseVersion >= GradleVersion.version("8.1"))
|
||||
spec.parameters.buildStatisticsConfiguration.set(KotlinBuildStatsConfiguration(project))
|
||||
}.also { buildService ->
|
||||
//DO NOT call buildService.get() before all parameters.configurationMetrics are set.
|
||||
// buildService.get() call will cause parameters calculation and configuration cache storage.
|
||||
|
||||
//Gradle throws an exception when Gradle version less than 7.4 with configuration cache enabled and buildSrc,
|
||||
@Suppress("DEPRECATION")
|
||||
if (GradleVersion.current().baseVersion >= GradleVersion.version("7.4")
|
||||
|| !project.isConfigurationCacheRequested
|
||||
@@ -132,19 +143,15 @@ abstract class BuildFusService : BuildService<BuildFusService.Parameters>, AutoC
|
||||
) {
|
||||
BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry.onTaskCompletion(buildService)
|
||||
}
|
||||
|
||||
if (GradleVersion.current().baseVersion >= GradleVersion.version("8.1")) {
|
||||
StatisticsBuildFlowManager.getInstance(project).subscribeForBuildResult()
|
||||
}
|
||||
project.afterEvaluate {
|
||||
buildService.get().parameters.configurationMetrics.add(project.provider {
|
||||
KotlinBuildStatHandler.collectProjectConfigurationTimeMetrics(project)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized //access fusMetricsConsumer requires synchronisation as long as tasks are executed in parallel
|
||||
override fun onFinish(event: FinishEvent?) {
|
||||
if (event is TaskFinishEvent) {
|
||||
if (event.result is TaskFailureResult) {
|
||||
@@ -154,23 +161,30 @@ abstract class BuildFusService : BuildService<BuildFusService.Parameters>, AutoC
|
||||
val taskExecutionResult = TaskExecutionResults[event.descriptor.taskPath]
|
||||
taskExecutionResult?.also { reportTaskMetrics(it, event) }
|
||||
}
|
||||
event?.descriptor?.name?.also {
|
||||
getMetricToReport(it)?.also { fusMetricsConsumer.report(it, true) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
if (!parameters.useBuildFinishFlowAction.get()) {
|
||||
recordBuildFinished(null, buildFailed)
|
||||
recordBuildFinished(buildFailed)
|
||||
}
|
||||
log.kotlinDebug("Close ${this.javaClass.simpleName}")
|
||||
}
|
||||
|
||||
internal fun recordBuildFinished(action: String?, buildFailed: Boolean) {
|
||||
KotlinBuildStatHandler.reportGlobalMetrics(fusMetricsConsumer)
|
||||
internal fun recordBuildFinished(buildFailed: Boolean) {
|
||||
reportGlobalMetrics(log, fusMetricsConsumer)
|
||||
parameters.configurationMetrics.orElse(emptyList()).get().forEach { it.addToConsumer(fusMetricsConsumer) }
|
||||
KotlinBuildStatsService.applyIfInitialised {
|
||||
it.recordBuildFinish(action, buildFailed, fusMetricsConsumer, buildId)
|
||||
reportBuildFinished(log, buildFailed, buildStartTime, projectEvaluatedTime, fusMetricsConsumer)
|
||||
parameters.buildStatisticsConfiguration.orNull?.also {
|
||||
val loggerService = KotlinBuildStatsLoggerService(it)
|
||||
loggerService.initSessionLogger(buildId)
|
||||
loggerService.reportBuildFinished(fusMetricsConsumer)
|
||||
}
|
||||
KotlinBuildStatsService.closeServices()
|
||||
KotlinBuildStatsBeanService.closeServices()
|
||||
}
|
||||
|
||||
private fun reportTaskMetrics(taskExecutionResult: TaskExecutionResult, event: TaskFinishEvent) {
|
||||
val totalTimeMs = event.result.endTime - event.result.startTime
|
||||
val buildMetrics = taskExecutionResult.buildMetrics
|
||||
|
||||
-316
@@ -1,316 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.gradle.plugin.statistics
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.DependencySet
|
||||
import org.gradle.api.artifacts.ProjectDependency
|
||||
import org.gradle.api.invocation.Gradle
|
||||
import org.gradle.api.logging.Logging
|
||||
import org.jetbrains.kotlin.gradle.plugin.statistics.plugins.ObservablePlugins
|
||||
import org.jetbrains.kotlin.gradle.report.BuildReportType
|
||||
import org.jetbrains.kotlin.gradle.utils.*
|
||||
import org.jetbrains.kotlin.statistics.BuildSessionLogger
|
||||
import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics
|
||||
import org.jetbrains.kotlin.statistics.metrics.NumericalMetrics
|
||||
import org.jetbrains.kotlin.statistics.metrics.StringMetrics
|
||||
import org.jetbrains.kotlin.statistics.MetricValueValidationFailed
|
||||
import org.jetbrains.kotlin.statistics.metrics.StatisticsValuesConsumer
|
||||
import java.lang.management.ManagementFactory
|
||||
import javax.management.MBeanServer
|
||||
import javax.management.ObjectName
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
class KotlinBuildStatHandler {
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
internal fun getLogger() = Logging.getLogger(KotlinBuildStatHandler::class.java)
|
||||
|
||||
internal fun <T> runSafe(methodName: String, action: () -> T?): T? {
|
||||
return try {
|
||||
getLogger().debug("Executing [$methodName]")
|
||||
action.invoke()
|
||||
} catch (e: MetricValueValidationFailed) {
|
||||
throw e
|
||||
} catch (e: Throwable) {
|
||||
logException("Could not execute [$methodName]", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
internal fun logException(description: String, e: Throwable) {
|
||||
getLogger().info(description)
|
||||
getLogger().debug(e.message, e)
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect general configuration metrics
|
||||
*/
|
||||
internal fun collectGeneralConfigurationTimeMetrics(
|
||||
gradle: Gradle,
|
||||
buildReportOutputs: List<BuildReportType>,
|
||||
useClasspathSnapshot: Boolean,
|
||||
pluginVersion: String,
|
||||
isProjectIsolationEnabled: Boolean,
|
||||
): MetricContainer {
|
||||
val configurationTimeMetrics = MetricContainer()
|
||||
|
||||
val statisticOverhead = measureTimeMillis {
|
||||
configurationTimeMetrics.put(StringMetrics.KOTLIN_COMPILER_VERSION, pluginVersion)
|
||||
configurationTimeMetrics.put(StringMetrics.USE_CLASSPATH_SNAPSHOT, useClasspathSnapshot.toString())
|
||||
buildReportOutputs.forEach {
|
||||
when (it) {
|
||||
BuildReportType.BUILD_SCAN -> configurationTimeMetrics.put(BooleanMetrics.BUILD_SCAN_BUILD_REPORT, true)
|
||||
BuildReportType.FILE -> configurationTimeMetrics.put(BooleanMetrics.FILE_BUILD_REPORT, true)
|
||||
BuildReportType.HTTP -> configurationTimeMetrics.put(BooleanMetrics.HTTP_BUILD_REPORT, true)
|
||||
BuildReportType.SINGLE_FILE -> configurationTimeMetrics.put(BooleanMetrics.SINGLE_FILE_BUILD_REPORT, true)
|
||||
BuildReportType.TRY_NEXT_CONSOLE -> {}//ignore
|
||||
}
|
||||
}
|
||||
configurationTimeMetrics.put(StringMetrics.PROJECT_PATH, gradle.rootProject.projectDir.absolutePath)
|
||||
configurationTimeMetrics.put(StringMetrics.GRADLE_VERSION, gradle.gradleVersion)
|
||||
|
||||
//will be updated with KT-58266
|
||||
if (!isProjectIsolationEnabled) {
|
||||
gradle.taskGraph.whenReady { taskExecutionGraph ->
|
||||
val executedTaskNames = taskExecutionGraph.allTasks.map { it.name }.distinct()
|
||||
configurationTimeMetrics.put(BooleanMetrics.MAVEN_PUBLISH_EXECUTED, executedTaskNames.contains("install"))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
configurationTimeMetrics.put(NumericalMetrics.STATISTICS_VISIT_ALL_PROJECTS_OVERHEAD, statisticOverhead)
|
||||
|
||||
return configurationTimeMetrics
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect project's configuration metrics including applied plugins. It should be called inside afterEvaluate block.
|
||||
*/
|
||||
internal fun collectProjectConfigurationTimeMetrics(
|
||||
project: Project,
|
||||
): MetricContainer {
|
||||
val configurationTimeMetrics = MetricContainer()
|
||||
|
||||
val statisticOverhead = measureTimeMillis {
|
||||
collectAppliedPluginsStatistics(project, configurationTimeMetrics)
|
||||
|
||||
val configurations = project.configurations.asMap.values
|
||||
for (configuration in configurations) {
|
||||
try {
|
||||
val configurationName = configuration.name
|
||||
val dependencies = configuration.dependencies
|
||||
|
||||
when (configurationName) {
|
||||
"KoverEngineConfig" -> {
|
||||
configurationTimeMetrics.put(BooleanMetrics.ENABLED_KOVER, true)
|
||||
}
|
||||
"kapt" -> {
|
||||
configurationTimeMetrics.put(BooleanMetrics.ENABLED_KAPT, true)
|
||||
for (dependency in dependencies) {
|
||||
when (dependency.group) {
|
||||
"com.google.dagger" -> configurationTimeMetrics.put(BooleanMetrics.ENABLED_DAGGER, true)
|
||||
"com.android.databinding" -> configurationTimeMetrics.put(BooleanMetrics.ENABLED_DATABINDING, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
API -> {
|
||||
configurationTimeMetrics.put(NumericalMetrics.CONFIGURATION_API_COUNT, 1)
|
||||
reportLibrariesVersions(configurationTimeMetrics, dependencies)
|
||||
}
|
||||
IMPLEMENTATION -> {
|
||||
configurationTimeMetrics.put(NumericalMetrics.CONFIGURATION_IMPLEMENTATION_COUNT, 1)
|
||||
reportLibrariesVersions(configurationTimeMetrics, dependencies)
|
||||
}
|
||||
COMPILE -> {
|
||||
configurationTimeMetrics.put(NumericalMetrics.CONFIGURATION_COMPILE_COUNT, 1)
|
||||
reportLibrariesVersions(configurationTimeMetrics, dependencies)
|
||||
}
|
||||
COMPILE_ONLY -> {
|
||||
configurationTimeMetrics.put(NumericalMetrics.CONFIGURATION_COMPILE_ONLY_COUNT, 1)
|
||||
reportLibrariesVersions(configurationTimeMetrics, dependencies)
|
||||
}
|
||||
RUNTIME -> {
|
||||
configurationTimeMetrics.put(NumericalMetrics.CONFIGURATION_RUNTIME_COUNT, 1)
|
||||
reportLibrariesVersions(configurationTimeMetrics, dependencies)
|
||||
}
|
||||
RUNTIME_ONLY -> {
|
||||
configurationTimeMetrics.put(NumericalMetrics.CONFIGURATION_RUNTIME_ONLY_COUNT, 1)
|
||||
reportLibrariesVersions(configurationTimeMetrics, dependencies)
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
// log?
|
||||
}
|
||||
}
|
||||
|
||||
configurationTimeMetrics.put(NumericalMetrics.NUMBER_OF_SUBPROJECTS, 1)
|
||||
|
||||
|
||||
configurationTimeMetrics.put(
|
||||
BooleanMetrics.KOTLIN_KTS_USED,
|
||||
project.buildscript.sourceFile?.name?.endsWith(".kts") ?: false
|
||||
)
|
||||
|
||||
addTaskMetrics(project, configurationTimeMetrics)
|
||||
|
||||
if (project.name == "buildSrc") {
|
||||
configurationTimeMetrics.put(NumericalMetrics.BUILD_SRC_COUNT, 1)
|
||||
configurationTimeMetrics.put(BooleanMetrics.BUILD_SRC_EXISTS, true)
|
||||
}
|
||||
}
|
||||
configurationTimeMetrics.put(NumericalMetrics.STATISTICS_VISIT_ALL_PROJECTS_OVERHEAD, statisticOverhead)
|
||||
|
||||
return configurationTimeMetrics
|
||||
}
|
||||
|
||||
private fun addTaskMetrics(
|
||||
project: Project,
|
||||
configurationTimeMetrics: MetricContainer,
|
||||
) {
|
||||
try {
|
||||
val taskNames = project.tasks.names.toList()
|
||||
configurationTimeMetrics.put(NumericalMetrics.GRADLE_NUMBER_OF_TASKS, taskNames.size.toLong())
|
||||
configurationTimeMetrics.put(
|
||||
NumericalMetrics.GRADLE_NUMBER_OF_UNCONFIGURED_TASKS,
|
||||
taskNames.count { name ->
|
||||
try {
|
||||
project.tasks.named(name).javaClass.name.contains("TaskCreatingProvider")
|
||||
} catch (_: Exception) {
|
||||
true
|
||||
}
|
||||
}.toLong()
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
//ignore exceptions for KT-62131.
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectAppliedPluginsStatistics(
|
||||
project: Project,
|
||||
configurationTimeMetrics: MetricContainer,
|
||||
) {
|
||||
for (plugin in ObservablePlugins.values()) {
|
||||
project.plugins.withId(plugin.title) {
|
||||
configurationTimeMetrics.put(plugin.metric, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun reportLibrariesVersions(
|
||||
configurationTimeMetrics: MetricContainer,
|
||||
dependencies: DependencySet?,
|
||||
) {
|
||||
dependencies?.filter { it !is ProjectDependency }?.forEach { dependency ->
|
||||
when {
|
||||
dependency.group?.startsWith("org.springframework") ?: false -> configurationTimeMetrics.put(
|
||||
StringMetrics.LIBRARY_SPRING_VERSION,
|
||||
dependency.version ?: "0.0.0"
|
||||
)
|
||||
dependency.group?.startsWith("com.vaadin") ?: false -> configurationTimeMetrics.put(
|
||||
StringMetrics.LIBRARY_VAADIN_VERSION,
|
||||
dependency.version ?: "0.0.0"
|
||||
)
|
||||
dependency.group?.startsWith("com.google.gwt") ?: false -> configurationTimeMetrics.put(
|
||||
StringMetrics.LIBRARY_GWT_VERSION,
|
||||
dependency.version ?: "0.0.0"
|
||||
)
|
||||
dependency.group?.startsWith("org.hibernate") ?: false -> configurationTimeMetrics.put(
|
||||
StringMetrics.LIBRARY_HIBERNATE_VERSION,
|
||||
dependency.version ?: "0.0.0"
|
||||
)
|
||||
dependency.group == "org.jetbrains.kotlin" && dependency.name.startsWith("kotlin-stdlib") -> configurationTimeMetrics.put(
|
||||
StringMetrics.KOTLIN_STDLIB_VERSION,
|
||||
dependency.version ?: "0.0.0"
|
||||
)
|
||||
dependency.group == "org.jetbrains.kotlinx" && dependency.name == "kotlinx-coroutines" -> configurationTimeMetrics.put(
|
||||
StringMetrics.KOTLIN_COROUTINES_VERSION,
|
||||
dependency.version ?: "0.0.0"
|
||||
)
|
||||
dependency.group == "org.jetbrains.kotlin" && dependency.name == "kotlin-reflect" -> configurationTimeMetrics.put(
|
||||
StringMetrics.KOTLIN_REFLECT_VERSION,
|
||||
dependency.version ?: "0.0.0"
|
||||
)
|
||||
dependency.group == "org.jetbrains.kotlinx" && dependency.name
|
||||
.startsWith("kotlinx-serialization-runtime") -> configurationTimeMetrics.put(
|
||||
StringMetrics.KOTLIN_SERIALIZATION_VERSION,
|
||||
dependency.version ?: "0.0.0"
|
||||
)
|
||||
dependency.group == "com.android.tools.build" && dependency.name.startsWith("gradle") -> configurationTimeMetrics.put(
|
||||
StringMetrics.ANDROID_GRADLE_PLUGIN_VERSION,
|
||||
dependency.version ?: "0.0.0"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun reportGlobalMetrics(metricConsumer: StatisticsValuesConsumer) {
|
||||
runSafe("${KotlinBuildStatHandler::class.java}.reportGlobalMetrics") {
|
||||
System.getProperty("os.name")?.also { metricConsumer.report(StringMetrics.OS_TYPE, System.getProperty("os.name")) }
|
||||
metricConsumer.report(NumericalMetrics.CPU_NUMBER_OF_CORES, Runtime.getRuntime().availableProcessors().toLong())
|
||||
metricConsumer.report(BooleanMetrics.EXECUTED_FROM_IDEA, System.getProperty("idea.active") != null)
|
||||
metricConsumer.report(NumericalMetrics.GRADLE_DAEMON_HEAP_SIZE, Runtime.getRuntime().maxMemory())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun buildFinished(
|
||||
beanName: ObjectName,
|
||||
) {
|
||||
runSafe("${KotlinBuildStatHandler::class.java}.buildFinished") {
|
||||
val mbs: MBeanServer = ManagementFactory.getPlatformMBeanServer()
|
||||
if (mbs.isRegistered(beanName)) {
|
||||
mbs.unregisterMBean(beanName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun reportBuildFinished(
|
||||
sessionLogger: BuildSessionLogger,
|
||||
action: String?,
|
||||
buildFailed: Boolean,
|
||||
buildId: String,
|
||||
metricsContainer: NonSynchronizedMetricsContainer,
|
||||
) {
|
||||
runSafe("${KotlinBuildStatHandler::class.java}.reportBuildFinish") {
|
||||
metricsContainer.sendToConsumer(sessionLogger)
|
||||
sessionLogger.finishBuildSession(action, buildFailed, buildId)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun report(
|
||||
sessionLogger: BuildSessionLogger,
|
||||
metric: BooleanMetrics,
|
||||
value: Boolean,
|
||||
subprojectName: String?,
|
||||
weight: Long? = null,
|
||||
) = runSafe("report metric ${metric.name}") {
|
||||
sessionLogger.report(metric, value, subprojectName, weight)
|
||||
} ?: false
|
||||
|
||||
internal fun report(
|
||||
sessionLogger: BuildSessionLogger,
|
||||
metric: NumericalMetrics,
|
||||
value: Long,
|
||||
subprojectName: String?,
|
||||
weight: Long? = null,
|
||||
) = runSafe("report metric ${metric.name}") {
|
||||
sessionLogger.report(metric, value, subprojectName, weight)
|
||||
} ?: false
|
||||
|
||||
internal fun report(
|
||||
sessionLogger: BuildSessionLogger,
|
||||
metric: StringMetrics,
|
||||
value: String,
|
||||
subprojectName: String?,
|
||||
weight: Long? = null,
|
||||
) = runSafe("report metric ${metric.name}") {
|
||||
sessionLogger.report(metric, value, subprojectName, weight)
|
||||
} ?: false
|
||||
}
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.gradle.plugin.statistics
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.invocation.Gradle
|
||||
import org.gradle.api.logging.Logging
|
||||
import org.gradle.api.provider.ProviderFactory
|
||||
import org.jetbrains.kotlin.gradle.plugin.internal.ConfigurationTimePropertiesAccessor
|
||||
import org.jetbrains.kotlin.gradle.plugin.internal.configurationTimePropertiesAccessor
|
||||
import org.jetbrains.kotlin.gradle.plugin.internal.usedAtConfigurationTime
|
||||
import org.jetbrains.kotlin.gradle.utils.runMetricMethodSafely
|
||||
import org.jetbrains.kotlin.statistics.BuildSessionLogger
|
||||
import org.jetbrains.kotlin.statistics.BuildSessionLogger.Companion.STATISTICS_FOLDER_NAME
|
||||
import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics
|
||||
import org.jetbrains.kotlin.statistics.metrics.StatisticsValuesConsumer
|
||||
import org.jetbrains.kotlin.statistics.metrics.NumericalMetrics
|
||||
import org.jetbrains.kotlin.statistics.metrics.StringMetrics
|
||||
import java.io.Closeable
|
||||
import java.io.File
|
||||
import java.lang.management.ManagementFactory
|
||||
import javax.management.MBeanServer
|
||||
import javax.management.ObjectName
|
||||
|
||||
/**
|
||||
* Interface for populating statistics collection method via JXM interface
|
||||
* JMX could be used for reporting both from other JVMs, other versions
|
||||
* of Kotlin Plugin and other classloaders
|
||||
*/
|
||||
|
||||
interface KotlinBuildStatsMXBean {
|
||||
fun reportBoolean(name: String, value: Boolean, subprojectName: String?, weight: Long?): Boolean
|
||||
|
||||
fun reportNumber(name: String, value: Long, subprojectName: String?, weight: Long?): Boolean
|
||||
|
||||
fun reportString(name: String, value: String, subprojectName: String?, weight: Long?): Boolean
|
||||
}
|
||||
|
||||
internal abstract class KotlinBuildStatsBeanService internal constructor(
|
||||
project: Project,
|
||||
private val beanName: ObjectName,
|
||||
) : StatisticsValuesConsumer, Closeable {
|
||||
|
||||
companion object {
|
||||
// Property name for disabling saving statistical information
|
||||
private const val ENABLE_STATISTICS_PROPERTY_NAME = "enable_kotlin_performance_profile"
|
||||
|
||||
// default state
|
||||
private const val DEFAULT_STATISTICS_STATE = true
|
||||
|
||||
// "emergency file" collecting statistics is disabled it the file exists
|
||||
private const val DISABLE_STATISTICS_FILE_NAME = "${STATISTICS_FOLDER_NAME}/.disable"
|
||||
|
||||
@JvmStatic
|
||||
internal val logger = Logging.getLogger(KotlinBuildStatsBeanService::class.java)
|
||||
|
||||
/**
|
||||
* Method for creating new instance of [StatisticsValuesConsumer]
|
||||
* It could be invoked only when applying Kotlin gradle plugin.
|
||||
* When executed, this method checks, whether it is already executed in the current build (whether it was already executed
|
||||
* in the same classpath (i.e., with the same version of Kotlin plugin)).
|
||||
* If it was not executed, the new instance of StatisticsValuesConsumer is created
|
||||
*
|
||||
* [closeServices] must be called at the end of the build in order to release resources.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Synchronized
|
||||
internal fun initStatsService(project: Project) {
|
||||
runMetricMethodSafely(logger, "${KotlinBuildStatsBeanService::class.java}.initStatsService") {
|
||||
val gradle = project.gradle
|
||||
val configurationTimePropertiesAccessor = project.configurationTimePropertiesAccessor
|
||||
val statisticsIsEnabled = checkStatisticsEnabled(gradle, project.providers, configurationTimePropertiesAccessor)
|
||||
if (!statisticsIsEnabled) {
|
||||
null
|
||||
} else {
|
||||
//register JMX service to support old kotlin version compatibility
|
||||
|
||||
val registry = kotlinBuildStatsServicesRegistry ?: KotlinBuildStatsServicesRegistry().also {
|
||||
kotlinBuildStatsServicesRegistry = it
|
||||
}
|
||||
|
||||
registry.registerServices(project)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun recordBuildStart(buildId: String) {
|
||||
kotlinBuildStatsServicesRegistry?.recordBuildStart(buildId)
|
||||
}
|
||||
|
||||
fun closeServices() {
|
||||
kotlinBuildStatsServicesRegistry?.close()
|
||||
kotlinBuildStatsServicesRegistry = null
|
||||
}
|
||||
|
||||
private var kotlinBuildStatsServicesRegistry: KotlinBuildStatsServicesRegistry? = null
|
||||
|
||||
private fun checkStatisticsEnabled(
|
||||
gradle: Gradle,
|
||||
providerFactory: ProviderFactory,
|
||||
configurationTimePropertiesAccessor: ConfigurationTimePropertiesAccessor,
|
||||
): Boolean {
|
||||
return if (File(gradle.gradleUserHomeDir, DISABLE_STATISTICS_FILE_NAME).exists()) {
|
||||
false
|
||||
} else {
|
||||
providerFactory.gradleProperty(ENABLE_STATISTICS_PROPERTY_NAME)
|
||||
.usedAtConfigurationTime(configurationTimePropertiesAccessor)
|
||||
.orNull?.toBoolean() ?: DEFAULT_STATISTICS_STATE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected val kotlinBuildLogger = KotlinBuildStatsLoggerService(KotlinBuildStatsConfiguration(project))
|
||||
|
||||
@Synchronized
|
||||
override fun close() {
|
||||
runMetricMethodSafely(logger, "${KotlinBuildStatsBeanService::class.java}.buildFinished") {
|
||||
val mbs: MBeanServer = ManagementFactory.getPlatformMBeanServer()
|
||||
if (mbs.isRegistered(beanName)) {
|
||||
mbs.unregisterMBean(beanName)
|
||||
logger.debug("Unregistered $this")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun recordBuildStart(buildId: String) {
|
||||
kotlinBuildLogger.initSessionLogger(buildId)
|
||||
}
|
||||
|
||||
internal fun report(
|
||||
sessionLogger: BuildSessionLogger,
|
||||
metric: BooleanMetrics,
|
||||
value: Boolean,
|
||||
subprojectName: String?,
|
||||
weight: Long? = null,
|
||||
) = runMetricMethodSafely(logger, "report metric ${metric.name}") {
|
||||
sessionLogger.report(metric, value, subprojectName, weight)
|
||||
} ?: false
|
||||
|
||||
internal fun report(
|
||||
sessionLogger: BuildSessionLogger,
|
||||
metric: NumericalMetrics,
|
||||
value: Long,
|
||||
subprojectName: String?,
|
||||
weight: Long? = null,
|
||||
) = runMetricMethodSafely(logger, "report metric ${metric.name}") {
|
||||
sessionLogger.report(metric, value, subprojectName, weight)
|
||||
} ?: false
|
||||
|
||||
internal fun report(
|
||||
sessionLogger: BuildSessionLogger,
|
||||
metric: StringMetrics,
|
||||
value: String,
|
||||
subprojectName: String?,
|
||||
weight: Long? = null,
|
||||
) = runMetricMethodSafely(logger, "report metric ${metric.name}") {
|
||||
sessionLogger.report(metric, value, subprojectName, weight)
|
||||
} ?: false
|
||||
}
|
||||
|
||||
internal class DefaultKotlinBuildStatsBeanService internal constructor(
|
||||
project: Project,
|
||||
beanName: ObjectName,
|
||||
) : KotlinBuildStatsBeanService(project, beanName),
|
||||
KotlinBuildStatsMXBean {
|
||||
|
||||
override fun report(metric: BooleanMetrics, value: Boolean, subprojectName: String?, weight: Long?): Boolean =
|
||||
report(kotlinBuildLogger.sessionLogger, metric, value, subprojectName, weight)
|
||||
|
||||
override fun report(metric: NumericalMetrics, value: Long, subprojectName: String?, weight: Long?): Boolean =
|
||||
report(kotlinBuildLogger.sessionLogger, metric, value, subprojectName, weight)
|
||||
|
||||
override fun report(metric: StringMetrics, value: String, subprojectName: String?, weight: Long?): Boolean =
|
||||
report(kotlinBuildLogger.sessionLogger, metric, value, subprojectName, weight)
|
||||
|
||||
override fun reportBoolean(name: String, value: Boolean, subprojectName: String?, weight: Long?): Boolean =
|
||||
report(BooleanMetrics.valueOf(name), value, subprojectName, weight)
|
||||
|
||||
override fun reportNumber(name: String, value: Long, subprojectName: String?, weight: Long?): Boolean =
|
||||
report(NumericalMetrics.valueOf(name), value, subprojectName, weight)
|
||||
|
||||
override fun reportString(name: String, value: String, subprojectName: String?, weight: Long?): Boolean =
|
||||
report(StringMetrics.valueOf(name), value, subprojectName, weight)
|
||||
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2010-2024 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.gradle.plugin.statistics
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.logging.Logging
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesBuildService
|
||||
import org.jetbrains.kotlin.statistics.BuildSessionLogger
|
||||
import org.jetbrains.kotlin.gradle.utils.runMetricMethodSafely
|
||||
import java.io.File
|
||||
import java.io.Serializable
|
||||
|
||||
class KotlinBuildStatsConfiguration(
|
||||
internal val forcePropertiesValidation: Boolean,
|
||||
internal val sessionLoggerRootPath: File,
|
||||
) : Serializable {
|
||||
companion object {
|
||||
// Property used for tests.
|
||||
private const val CUSTOM_LOGGER_ROOT_PATH = "kotlin.session.logger.root.path"
|
||||
|
||||
// Property used for tests. Build will fail fast if collected value doesn't fit regexp
|
||||
private const val FORCE_VALUES_VALIDATION = "kotlin_performance_profile_force_validation"
|
||||
private val logger = Logging.getLogger(KotlinBuildStatsConfiguration::class.java)
|
||||
}
|
||||
|
||||
constructor(project: Project) : this(
|
||||
PropertiesBuildService.registerIfAbsent(project).get().get(FORCE_VALUES_VALIDATION, project)?.toBoolean() ?: false,
|
||||
PropertiesBuildService.registerIfAbsent(project).get().get(CUSTOM_LOGGER_ROOT_PATH, project)
|
||||
?.also {
|
||||
logger.warn("$CUSTOM_LOGGER_ROOT_PATH property for test purpose only")
|
||||
}?.let { File(it) } ?: project.gradle.gradleUserHomeDir
|
||||
)
|
||||
}
|
||||
|
||||
internal open class KotlinBuildStatsLoggerService(
|
||||
configuration: KotlinBuildStatsConfiguration,
|
||||
) {
|
||||
companion object {
|
||||
private val logger = Logging.getLogger(KotlinBuildStatsLoggerService::class.java)
|
||||
}
|
||||
|
||||
internal val sessionLogger = BuildSessionLogger(
|
||||
configuration.sessionLoggerRootPath,
|
||||
forceValuesValidation = configuration.forcePropertiesValidation,
|
||||
)
|
||||
|
||||
fun initSessionLogger(buildId: String) {
|
||||
runMetricMethodSafely(logger, "${KotlinBuildStatsLoggerService::class.java}.initSessionLogger") {
|
||||
if (!sessionLogger.isBuildSessionStarted()) {
|
||||
sessionLogger.startBuildSession(buildId)
|
||||
} else {
|
||||
logger.debug("Unable to initiate build logger for \"$buildId\" build. It is already initialized for \"${sessionLogger.getActiveBuildId()}\" build")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun reportBuildFinished(metricsContainer: NonSynchronizedMetricsContainer? = null) {
|
||||
runMetricMethodSafely(logger, "${KotlinBuildStatsLoggerService::class.java}.reportBuildFinish") {
|
||||
metricsContainer?.sendToConsumer(sessionLogger)
|
||||
sessionLogger.finishBuildSession()
|
||||
}
|
||||
}
|
||||
}
|
||||
-308
@@ -1,308 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.gradle.plugin.statistics
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.invocation.Gradle
|
||||
import org.gradle.api.logging.Logging
|
||||
import org.gradle.api.provider.ProviderFactory
|
||||
import org.gradle.initialization.BuildRequestMetaData
|
||||
import org.gradle.invocation.DefaultGradle
|
||||
import org.gradle.tooling.events.OperationCompletionListener
|
||||
import org.gradle.tooling.events.task.TaskFinishEvent
|
||||
import org.jetbrains.kotlin.gradle.plugin.BuildEventsListenerRegistryHolder
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesBuildService
|
||||
import org.jetbrains.kotlin.gradle.plugin.internal.ConfigurationTimePropertiesAccessor
|
||||
import org.jetbrains.kotlin.gradle.plugin.internal.configurationTimePropertiesAccessor
|
||||
import org.jetbrains.kotlin.gradle.plugin.internal.usedAtConfigurationTime
|
||||
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatHandler.Companion.runSafe
|
||||
import org.jetbrains.kotlin.statistics.BuildSessionLogger
|
||||
import org.jetbrains.kotlin.statistics.BuildSessionLogger.Companion.STATISTICS_FOLDER_NAME
|
||||
import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics
|
||||
import org.jetbrains.kotlin.statistics.metrics.StatisticsValuesConsumer
|
||||
import org.jetbrains.kotlin.statistics.metrics.NumericalMetrics
|
||||
import org.jetbrains.kotlin.statistics.metrics.StringMetrics
|
||||
import java.io.Closeable
|
||||
import java.io.File
|
||||
import javax.management.MBeanServer
|
||||
import javax.management.ObjectName
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
/**
|
||||
* Interface for populating statistics collection method via JXM interface
|
||||
* JMX could be used for reporting both from other JVMs, other versions
|
||||
* of Kotlin Plugin and other classloaders
|
||||
*/
|
||||
|
||||
interface KotlinBuildStatsMXBean {
|
||||
fun reportBoolean(name: String, value: Boolean, subprojectName: String?, weight: Long?): Boolean
|
||||
|
||||
fun reportNumber(name: String, value: Long, subprojectName: String?, weight: Long?): Boolean
|
||||
|
||||
fun reportString(name: String, value: String, subprojectName: String?, weight: Long?): Boolean
|
||||
}
|
||||
|
||||
internal abstract class KotlinBuildStatsService internal constructor() : StatisticsValuesConsumer, Closeable {
|
||||
companion object {
|
||||
// Property name for disabling saving statistical information
|
||||
private const val ENABLE_STATISTICS_PROPERTY_NAME = "enable_kotlin_performance_profile"
|
||||
|
||||
// Property used for tests. Build will fail fast if collected value doesn't fit regexp
|
||||
const val FORCE_VALUES_VALIDATION = "kotlin_performance_profile_force_validation"
|
||||
|
||||
// default state
|
||||
private const val DEFAULT_STATISTICS_STATE = true
|
||||
|
||||
// "emergency file" collecting statistics is disabled it the file exists
|
||||
private const val DISABLE_STATISTICS_FILE_NAME = "${STATISTICS_FOLDER_NAME}/.disable"
|
||||
|
||||
/**
|
||||
* Method for getting IStatisticsValuesConsumer for reporting some statistics
|
||||
* Could be invoked during any build phase after applying first Kotlin plugin and
|
||||
* until build is completed
|
||||
*/
|
||||
@JvmStatic
|
||||
@Synchronized
|
||||
private fun getInstance(): KotlinBuildStatsService? {
|
||||
if (statisticsIsEnabled != true) {
|
||||
return null
|
||||
}
|
||||
return kotlinBuildStatsServicesRegistry?.getDefaultService()
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for creating new instance of IStatisticsValuesConsumer
|
||||
* It could be invoked only when applying Kotlin gradle plugin.
|
||||
* When executed, this method checks, whether it is already executed in the current build.
|
||||
* If it was not executed, the new instance of IStatisticsValuesConsumer is created
|
||||
*
|
||||
* If it was already executed in the same classpath (i.e., with the same version of Kotlin plugin),
|
||||
* the previously returned instance is returned.
|
||||
*
|
||||
* If it was already executed in the other classpath, a JXM implementation is returned.
|
||||
*
|
||||
* [closeServices] must be called at the end of the build in order to release resources.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Synchronized
|
||||
internal fun getOrCreateInstance(project: Project): KotlinBuildStatsService? {
|
||||
|
||||
return runSafe("${KotlinBuildStatsService::class.java}.getOrCreateInstance") {
|
||||
val gradle = project.gradle
|
||||
val configurationTimePropertiesAccessor = project.configurationTimePropertiesAccessor
|
||||
statisticsIsEnabled = statisticsIsEnabled
|
||||
?: checkStatisticsEnabled(gradle, project.providers, configurationTimePropertiesAccessor)
|
||||
if (statisticsIsEnabled != true) {
|
||||
null
|
||||
} else {
|
||||
val registry = kotlinBuildStatsServicesRegistry ?: KotlinBuildStatsServicesRegistry().also {
|
||||
kotlinBuildStatsServicesRegistry = it
|
||||
}
|
||||
|
||||
val defaultServiceName =
|
||||
KotlinBuildStatsServicesRegistry.getBeanName(KotlinBuildStatsServicesRegistry.DEFAULT_SERVICE_QUALIFIER)
|
||||
val instance = kotlinBuildStatsServicesRegistry?.getDefaultService()
|
||||
if (instance != null) {
|
||||
registry.logger.debug("$defaultServiceName is already instantiated. Current instance is $instance")
|
||||
return@runSafe instance
|
||||
}
|
||||
|
||||
registry.registerServices(project)
|
||||
|
||||
BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry.onTaskCompletion(project.provider {
|
||||
OperationCompletionListener { event ->
|
||||
if (event is TaskFinishEvent) {
|
||||
reportTaskIfNeed(event.descriptor.name)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
registry.getDefaultService() ?: error("The default kotlin build stats $defaultServiceName service wasn't initialized")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun closeServices() {
|
||||
kotlinBuildStatsServicesRegistry?.close()
|
||||
kotlinBuildStatsServicesRegistry = null
|
||||
}
|
||||
|
||||
protected fun reportTaskIfNeed(task: String) {
|
||||
val metric = when (task.substringAfterLast(":")) {
|
||||
"dokkaHtml" -> BooleanMetrics.ENABLED_DOKKA_HTML
|
||||
"dokkaGfm" -> BooleanMetrics.ENABLED_DOKKA_GFM
|
||||
"dokkaJavadoc" -> BooleanMetrics.ENABLED_DOKKA_JAVADOC
|
||||
"dokkaJekyll" -> BooleanMetrics.ENABLED_DOKKA_JEKYLL
|
||||
"dokkaHtmlMultiModule" -> BooleanMetrics.ENABLED_DOKKA_HTML_MULTI_MODULE
|
||||
"dokkaGfmMultiModule" -> BooleanMetrics.ENABLED_DOKKA_GFM_MULTI_MODULE
|
||||
"dokkaJekyllMultiModule" -> BooleanMetrics.ENABLED_DOKKA_JEKYLL_MULTI_MODULE
|
||||
"dokkaHtmlCollector" -> BooleanMetrics.ENABLED_DOKKA_HTML_COLLECTOR
|
||||
"dokkaGfmCollector" -> BooleanMetrics.ENABLED_DOKKA_GFM_COLLECTOR
|
||||
"dokkaJavadocCollector" -> BooleanMetrics.ENABLED_DOKKA_JAVADOC_COLLECTOR
|
||||
"dokkaJekyllCollector" -> BooleanMetrics.ENABLED_DOKKA_JEKYLL_COLLECTOR
|
||||
else -> null
|
||||
}
|
||||
metric?.also { getInstance()?.report(it, true) }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invokes provided collector if the reporting service is initialised.
|
||||
* The duration of collector's wall time is reported into overall overhead metric.
|
||||
*/
|
||||
fun applyIfInitialised(collector: (KotlinBuildStatsService) -> Unit) {
|
||||
getInstance()?.apply {
|
||||
try {
|
||||
val duration = measureTimeMillis {
|
||||
collector.invoke(this)
|
||||
}
|
||||
this.report(NumericalMetrics.STATISTICS_COLLECT_METRICS_OVERHEAD, duration)
|
||||
} catch (e: Throwable) {
|
||||
KotlinBuildStatHandler.logException("Could collect statistics metrics", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var kotlinBuildStatsServicesRegistry: KotlinBuildStatsServicesRegistry? = null
|
||||
|
||||
private var statisticsIsEnabled: Boolean? = null
|
||||
|
||||
private fun checkStatisticsEnabled(
|
||||
gradle: Gradle,
|
||||
providerFactory: ProviderFactory,
|
||||
configurationTimePropertiesAccessor: ConfigurationTimePropertiesAccessor,
|
||||
): Boolean {
|
||||
return if (File(gradle.gradleUserHomeDir, DISABLE_STATISTICS_FILE_NAME).exists()) {
|
||||
false
|
||||
} else {
|
||||
providerFactory.gradleProperty(ENABLE_STATISTICS_PROPERTY_NAME)
|
||||
.usedAtConfigurationTime(configurationTimePropertiesAccessor)
|
||||
.orNull?.toBoolean() ?: DEFAULT_STATISTICS_STATE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
}
|
||||
/**
|
||||
* Collects metrics at the end of a build
|
||||
*/
|
||||
open fun recordBuildFinish(action: String?, buildFailed: Boolean, metricsContainer: NonSynchronizedMetricsContainer, buildId: String) {}
|
||||
|
||||
open fun recordProjectsEvaluated(project: Project) {}
|
||||
}
|
||||
|
||||
internal class JMXKotlinBuildStatsService(private val mbs: MBeanServer, private val beanName: ObjectName) :
|
||||
KotlinBuildStatsService() {
|
||||
|
||||
private fun callJmx(method: String, type: String, metricName: String, value: Any, subprojectName: String?, weight: Long?): Any? {
|
||||
return mbs.invoke(
|
||||
beanName,
|
||||
method,
|
||||
arrayOf(metricName, value, subprojectName, weight),
|
||||
arrayOf("java.lang.String", type, "java.lang.String", "java.lang.Long")
|
||||
)
|
||||
}
|
||||
|
||||
override fun report(metric: BooleanMetrics, value: Boolean, subprojectName: String?, weight: Long?) =
|
||||
runSafe("report metric ${metric.name}") {
|
||||
callJmx("reportBoolean", "boolean", metric.name, value, subprojectName, weight)
|
||||
} as? Boolean ?: false
|
||||
|
||||
override fun report(metric: NumericalMetrics, value: Long, subprojectName: String?, weight: Long?) =
|
||||
runSafe("report metric ${metric.name}") {
|
||||
callJmx("reportNumber", "long", metric.name, value, subprojectName, weight)
|
||||
} as? Boolean ?: false
|
||||
|
||||
override fun report(metric: StringMetrics, value: String, subprojectName: String?, weight: Long?) =
|
||||
runSafe("report metric ${metric.name}") {
|
||||
callJmx("reportString", "java.lang.String", metric.name, value, subprojectName, weight)
|
||||
} as? Boolean ?: false
|
||||
|
||||
}
|
||||
|
||||
internal abstract class AbstractKotlinBuildStatsService(
|
||||
project: Project,
|
||||
private val beanName: ObjectName,
|
||||
) : KotlinBuildStatsService() {
|
||||
companion object {
|
||||
//test only
|
||||
const val CUSTOM_LOGGER_ROOT_PATH = "kotlin.session.logger.root.path"
|
||||
|
||||
private val logger = Logging.getLogger(AbstractKotlinBuildStatsService::class.java)
|
||||
}
|
||||
|
||||
private val forcePropertiesValidation: Boolean
|
||||
|
||||
private val customSessionLoggerRootPath: String?
|
||||
|
||||
init {
|
||||
val propertiesBuildService = PropertiesBuildService.registerIfAbsent(project).get()
|
||||
forcePropertiesValidation = propertiesBuildService.get(FORCE_VALUES_VALIDATION, project)?.toBoolean() ?: false
|
||||
customSessionLoggerRootPath = propertiesBuildService.get(CUSTOM_LOGGER_ROOT_PATH, project)?.also {
|
||||
logger.warn("$CUSTOM_LOGGER_ROOT_PATH property for test purpose only")
|
||||
}
|
||||
}
|
||||
|
||||
private val sessionLoggerRootPath =
|
||||
customSessionLoggerRootPath?.let { File(it) } ?: project.gradle.gradleUserHomeDir
|
||||
|
||||
protected val sessionLogger = BuildSessionLogger(
|
||||
sessionLoggerRootPath,
|
||||
forceValuesValidation = forcePropertiesValidation,
|
||||
)
|
||||
|
||||
private fun gradleBuildStartTime(gradle: Gradle): Long? {
|
||||
return (gradle as? DefaultGradle)?.services?.get(BuildRequestMetaData::class.java)?.startTime
|
||||
}
|
||||
|
||||
override fun recordProjectsEvaluated(project: Project) {
|
||||
runSafe("${DefaultKotlinBuildStatsService::class.java}.projectEvaluated") {
|
||||
if (!sessionLogger.isBuildSessionStarted()) {
|
||||
sessionLogger.startBuildSession(
|
||||
DaemonReuseCounter.incrementAndGetOrdinal(),
|
||||
gradleBuildStartTime(project.gradle),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun close() {
|
||||
KotlinBuildStatHandler().buildFinished(beanName)
|
||||
super.close()
|
||||
}
|
||||
}
|
||||
|
||||
internal class DefaultKotlinBuildStatsService internal constructor(
|
||||
project: Project,
|
||||
beanName: ObjectName,
|
||||
) : AbstractKotlinBuildStatsService(project, beanName),
|
||||
KotlinBuildStatsMXBean {
|
||||
|
||||
override fun report(metric: BooleanMetrics, value: Boolean, subprojectName: String?, weight: Long?): Boolean =
|
||||
KotlinBuildStatHandler().report(sessionLogger, metric, value, subprojectName, weight)
|
||||
|
||||
override fun report(metric: NumericalMetrics, value: Long, subprojectName: String?, weight: Long?): Boolean =
|
||||
KotlinBuildStatHandler().report(sessionLogger, metric, value, subprojectName, weight)
|
||||
|
||||
override fun report(metric: StringMetrics, value: String, subprojectName: String?, weight: Long?): Boolean =
|
||||
KotlinBuildStatHandler().report(sessionLogger, metric, value, subprojectName, weight)
|
||||
|
||||
override fun reportBoolean(name: String, value: Boolean, subprojectName: String?, weight: Long?): Boolean =
|
||||
report(BooleanMetrics.valueOf(name), value, subprojectName, weight)
|
||||
|
||||
override fun reportNumber(name: String, value: Long, subprojectName: String?, weight: Long?): Boolean =
|
||||
report(NumericalMetrics.valueOf(name), value, subprojectName, weight)
|
||||
|
||||
override fun reportString(name: String, value: String, subprojectName: String?, weight: Long?): Boolean =
|
||||
report(StringMetrics.valueOf(name), value, subprojectName, weight)
|
||||
|
||||
//only one jmx bean service should report global metrics
|
||||
override fun recordBuildFinish(action: String?, buildFailed: Boolean, metricsContainer: NonSynchronizedMetricsContainer, buildId: String) {
|
||||
KotlinBuildStatHandler().reportBuildFinished(sessionLogger, action, buildFailed, buildId, metricsContainer)
|
||||
}
|
||||
}
|
||||
+25
-30
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.gradle.plugin.statistics
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.logging.Logging
|
||||
import org.jetbrains.kotlin.gradle.plugin.statistics.old.Pre232IdeaKotlinBuildStatsMXBean
|
||||
import org.jetbrains.kotlin.gradle.plugin.statistics.old.Pre232IdeaKotlinBuildStatsService
|
||||
import org.jetbrains.kotlin.gradle.plugin.statistics.old.Pre232IdeaKotlinBuildStatsBeanService
|
||||
import java.io.Closeable
|
||||
import java.lang.management.ManagementFactory
|
||||
import javax.management.MBeanServer
|
||||
@@ -25,58 +25,53 @@ import javax.management.StandardMBean
|
||||
*/
|
||||
internal class KotlinBuildStatsServicesRegistry : Closeable {
|
||||
internal val logger = Logging.getLogger(this::class.java)
|
||||
private val services = hashMapOf<String, KotlinBuildStatsService>()
|
||||
private val services = hashMapOf<String, KotlinBuildStatsBeanService>()
|
||||
|
||||
/**
|
||||
* Registers the Kotlin build stats services for the given project.
|
||||
*
|
||||
* After a call of that method, [getDefaultService] is expected to return a non-null value.
|
||||
*
|
||||
* The registry must be closed at the end of the usage.
|
||||
*/
|
||||
fun registerServices(project: Project) {
|
||||
val defaultBeanName = getBeanName(DEFAULT_SERVICE_QUALIFIER)
|
||||
val mbs: MBeanServer = ManagementFactory.getPlatformMBeanServer()
|
||||
if (mbs.isRegistered(defaultBeanName)) {
|
||||
// because we use only the default service, and it's already registered in the MBean server, then there's no need in caring of legacy services as they're already registered
|
||||
logger.debug("${KotlinBuildStatsService::class.simpleName} $defaultBeanName is already instantiated in another classpath. Creating JMX wrapper for the main service")
|
||||
services[DEFAULT_SERVICE_QUALIFIER] = JMXKotlinBuildStatsService(mbs, defaultBeanName)
|
||||
} else {
|
||||
registerStatsService(
|
||||
mbs,
|
||||
DefaultKotlinBuildStatsService(project, getBeanName(DEFAULT_SERVICE_QUALIFIER)),
|
||||
KotlinBuildStatsMXBean::class.java,
|
||||
DEFAULT_SERVICE_QUALIFIER
|
||||
)
|
||||
// to support backward compatibility with Idea before version 232
|
||||
registerStatsService(
|
||||
mbs,
|
||||
Pre232IdeaKotlinBuildStatsService(project, getBeanName(LEGACY_SERVICE_QUALIFIER)),
|
||||
Pre232IdeaKotlinBuildStatsMXBean::class.java,
|
||||
LEGACY_SERVICE_QUALIFIER
|
||||
)
|
||||
}
|
||||
registerStatsService(
|
||||
mbs,
|
||||
DefaultKotlinBuildStatsBeanService(project, getBeanName(DEFAULT_SERVICE_QUALIFIER)),
|
||||
KotlinBuildStatsMXBean::class.java,
|
||||
DEFAULT_SERVICE_QUALIFIER
|
||||
)
|
||||
// to support backward compatibility with Idea before version 232
|
||||
registerStatsService(
|
||||
mbs,
|
||||
Pre232IdeaKotlinBuildStatsBeanService(project, getBeanName(LEGACY_SERVICE_QUALIFIER)),
|
||||
Pre232IdeaKotlinBuildStatsMXBean::class.java,
|
||||
LEGACY_SERVICE_QUALIFIER
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
private fun <T : KotlinBuildStatsService> registerStatsService(
|
||||
fun recordBuildStart(buildId: String) {
|
||||
services.forEach { it.value.recordBuildStart(buildId) }
|
||||
}
|
||||
|
||||
|
||||
private fun <T : KotlinBuildStatsBeanService> registerStatsService(
|
||||
mbs: MBeanServer,
|
||||
service: T,
|
||||
beanInterfaceType: Class<in T>,
|
||||
qualifier: String,
|
||||
) {
|
||||
val beanName = getBeanName(qualifier)
|
||||
val loggedServiceName = "${KotlinBuildStatsBeanService::class.java}" + if (qualifier.isNotEmpty()) "_$qualifier" else ""
|
||||
if (!mbs.isRegistered(beanName)) {
|
||||
mbs.registerMBean(StandardMBean(service, beanInterfaceType), beanName)
|
||||
services[qualifier] = service
|
||||
val loggedServiceName = "${KotlinBuildStatsService::class.java}" + if (qualifier.isNotEmpty()) "_$qualifier" else ""
|
||||
logger.debug("Instantiated $loggedServiceName: new instance $service")
|
||||
} else {
|
||||
logger.debug("$loggedServiceName is already instantiated in another classpath.")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the default Kotlin build stats service. That's the main service we are working with.
|
||||
*/
|
||||
fun getDefaultService() = services[DEFAULT_SERVICE_QUALIFIER]
|
||||
|
||||
/**
|
||||
* Unregisters all the registered JMX services and may release other resources allocated by a service.
|
||||
|
||||
+266
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
* 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.gradle.plugin.statistics
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.DependencySet
|
||||
import org.gradle.api.artifacts.ProjectDependency
|
||||
import org.gradle.api.invocation.Gradle
|
||||
import org.gradle.api.logging.Logger
|
||||
import org.jetbrains.kotlin.gradle.plugin.statistics.plugins.ObservablePlugins
|
||||
import org.jetbrains.kotlin.gradle.report.BuildReportType
|
||||
import org.jetbrains.kotlin.gradle.utils.*
|
||||
import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics
|
||||
import org.jetbrains.kotlin.statistics.metrics.NumericalMetrics
|
||||
import org.jetbrains.kotlin.statistics.metrics.StatisticsValuesConsumer
|
||||
import org.jetbrains.kotlin.statistics.metrics.StringMetrics
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
/**
|
||||
* Collect general configuration metrics
|
||||
**/
|
||||
internal fun collectGeneralConfigurationTimeMetrics(
|
||||
gradle: Gradle,
|
||||
buildReportOutputs: List<BuildReportType>,
|
||||
useClasspathSnapshot: Boolean,
|
||||
pluginVersion: String,
|
||||
isProjectIsolationEnabled: Boolean,
|
||||
): MetricContainer {
|
||||
val configurationTimeMetrics = MetricContainer()
|
||||
|
||||
val statisticOverhead = measureTimeMillis {
|
||||
configurationTimeMetrics.put(StringMetrics.KOTLIN_COMPILER_VERSION, pluginVersion)
|
||||
configurationTimeMetrics.put(StringMetrics.USE_CLASSPATH_SNAPSHOT, useClasspathSnapshot.toString())
|
||||
buildReportOutputs.forEach {
|
||||
when (it) {
|
||||
BuildReportType.BUILD_SCAN -> configurationTimeMetrics.put(BooleanMetrics.BUILD_SCAN_BUILD_REPORT, true)
|
||||
BuildReportType.FILE -> configurationTimeMetrics.put(BooleanMetrics.FILE_BUILD_REPORT, true)
|
||||
BuildReportType.HTTP -> configurationTimeMetrics.put(BooleanMetrics.HTTP_BUILD_REPORT, true)
|
||||
BuildReportType.SINGLE_FILE -> configurationTimeMetrics.put(BooleanMetrics.SINGLE_FILE_BUILD_REPORT, true)
|
||||
BuildReportType.TRY_NEXT_CONSOLE -> {}//ignore
|
||||
}
|
||||
}
|
||||
configurationTimeMetrics.put(StringMetrics.PROJECT_PATH, gradle.rootProject.projectDir.absolutePath)
|
||||
configurationTimeMetrics.put(StringMetrics.GRADLE_VERSION, gradle.gradleVersion)
|
||||
|
||||
//will be updated with KT-58266
|
||||
if (!isProjectIsolationEnabled) {
|
||||
gradle.taskGraph.whenReady { taskExecutionGraph ->
|
||||
val executedTaskNames = taskExecutionGraph.allTasks.map { it.name }.distinct()
|
||||
configurationTimeMetrics.put(BooleanMetrics.MAVEN_PUBLISH_EXECUTED, executedTaskNames.contains("install"))
|
||||
}
|
||||
}
|
||||
}
|
||||
configurationTimeMetrics.put(NumericalMetrics.STATISTICS_VISIT_ALL_PROJECTS_OVERHEAD, statisticOverhead)
|
||||
|
||||
return configurationTimeMetrics
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect project's configuration metrics including applied plugins. It should be called inside afterEvaluate block.
|
||||
*/
|
||||
internal fun collectProjectConfigurationTimeMetrics(
|
||||
project: Project,
|
||||
): MetricContainer {
|
||||
val configurationTimeMetrics = MetricContainer()
|
||||
|
||||
val statisticOverhead = measureTimeMillis {
|
||||
collectAppliedPluginsStatistics(project, configurationTimeMetrics)
|
||||
|
||||
val configurations = project.configurations.asMap.values
|
||||
for (configuration in configurations) {
|
||||
try {
|
||||
val configurationName = configuration.name
|
||||
val dependencies = configuration.dependencies
|
||||
|
||||
when (configurationName) {
|
||||
"KoverEngineConfig" -> {
|
||||
configurationTimeMetrics.put(BooleanMetrics.ENABLED_KOVER, true)
|
||||
}
|
||||
"kapt" -> {
|
||||
configurationTimeMetrics.put(BooleanMetrics.ENABLED_KAPT, true)
|
||||
for (dependency in dependencies) {
|
||||
when (dependency.group) {
|
||||
"com.google.dagger" -> configurationTimeMetrics.put(BooleanMetrics.ENABLED_DAGGER, true)
|
||||
"com.android.databinding" -> configurationTimeMetrics.put(BooleanMetrics.ENABLED_DATABINDING, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
API -> {
|
||||
configurationTimeMetrics.put(NumericalMetrics.CONFIGURATION_API_COUNT, 1)
|
||||
reportLibrariesVersions(configurationTimeMetrics, dependencies)
|
||||
}
|
||||
IMPLEMENTATION -> {
|
||||
configurationTimeMetrics.put(NumericalMetrics.CONFIGURATION_IMPLEMENTATION_COUNT, 1)
|
||||
reportLibrariesVersions(configurationTimeMetrics, dependencies)
|
||||
}
|
||||
COMPILE -> {
|
||||
configurationTimeMetrics.put(NumericalMetrics.CONFIGURATION_COMPILE_COUNT, 1)
|
||||
reportLibrariesVersions(configurationTimeMetrics, dependencies)
|
||||
}
|
||||
COMPILE_ONLY -> {
|
||||
configurationTimeMetrics.put(NumericalMetrics.CONFIGURATION_COMPILE_ONLY_COUNT, 1)
|
||||
reportLibrariesVersions(configurationTimeMetrics, dependencies)
|
||||
}
|
||||
RUNTIME -> {
|
||||
configurationTimeMetrics.put(NumericalMetrics.CONFIGURATION_RUNTIME_COUNT, 1)
|
||||
reportLibrariesVersions(configurationTimeMetrics, dependencies)
|
||||
}
|
||||
RUNTIME_ONLY -> {
|
||||
configurationTimeMetrics.put(NumericalMetrics.CONFIGURATION_RUNTIME_ONLY_COUNT, 1)
|
||||
reportLibrariesVersions(configurationTimeMetrics, dependencies)
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
// Ignore exceptions for FUS metrics
|
||||
}
|
||||
}
|
||||
|
||||
configurationTimeMetrics.put(NumericalMetrics.NUMBER_OF_SUBPROJECTS, 1)
|
||||
|
||||
|
||||
configurationTimeMetrics.put(
|
||||
BooleanMetrics.KOTLIN_KTS_USED,
|
||||
project.buildscript.sourceFile?.name?.endsWith(".kts") ?: false
|
||||
)
|
||||
|
||||
addTaskMetrics(project, configurationTimeMetrics)
|
||||
|
||||
if (project.name == "buildSrc") {
|
||||
configurationTimeMetrics.put(NumericalMetrics.BUILD_SRC_COUNT, 1)
|
||||
configurationTimeMetrics.put(BooleanMetrics.BUILD_SRC_EXISTS, true)
|
||||
}
|
||||
}
|
||||
configurationTimeMetrics.put(NumericalMetrics.STATISTICS_VISIT_ALL_PROJECTS_OVERHEAD, statisticOverhead)
|
||||
|
||||
return configurationTimeMetrics
|
||||
}
|
||||
|
||||
private fun addTaskMetrics(
|
||||
project: Project,
|
||||
configurationTimeMetrics: MetricContainer,
|
||||
) {
|
||||
try {
|
||||
val taskNames = project.tasks.names.toList()
|
||||
configurationTimeMetrics.put(NumericalMetrics.GRADLE_NUMBER_OF_TASKS, taskNames.size.toLong())
|
||||
configurationTimeMetrics.put(
|
||||
NumericalMetrics.GRADLE_NUMBER_OF_UNCONFIGURED_TASKS,
|
||||
taskNames.count { name ->
|
||||
try {
|
||||
project.tasks.named(name).javaClass.name.contains("TaskCreatingProvider")
|
||||
} catch (_: Exception) {
|
||||
true
|
||||
}
|
||||
}.toLong()
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
//ignore exceptions for KT-62131.
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectAppliedPluginsStatistics(
|
||||
project: Project,
|
||||
configurationTimeMetrics: MetricContainer,
|
||||
) {
|
||||
for (plugin in ObservablePlugins.values()) {
|
||||
project.plugins.withId(plugin.title) {
|
||||
configurationTimeMetrics.put(plugin.metric, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun reportLibrariesVersions(
|
||||
configurationTimeMetrics: MetricContainer,
|
||||
dependencies: DependencySet?,
|
||||
) {
|
||||
dependencies?.filter { it !is ProjectDependency }?.forEach { dependency ->
|
||||
when {
|
||||
dependency.group?.startsWith("org.springframework") ?: false -> configurationTimeMetrics.put(
|
||||
StringMetrics.LIBRARY_SPRING_VERSION,
|
||||
dependency.version ?: "0.0.0"
|
||||
)
|
||||
dependency.group?.startsWith("com.vaadin") ?: false -> configurationTimeMetrics.put(
|
||||
StringMetrics.LIBRARY_VAADIN_VERSION,
|
||||
dependency.version ?: "0.0.0"
|
||||
)
|
||||
dependency.group?.startsWith("com.google.gwt") ?: false -> configurationTimeMetrics.put(
|
||||
StringMetrics.LIBRARY_GWT_VERSION,
|
||||
dependency.version ?: "0.0.0"
|
||||
)
|
||||
dependency.group?.startsWith("org.hibernate") ?: false -> configurationTimeMetrics.put(
|
||||
StringMetrics.LIBRARY_HIBERNATE_VERSION,
|
||||
dependency.version ?: "0.0.0"
|
||||
)
|
||||
dependency.group == "org.jetbrains.kotlin" && dependency.name.startsWith("kotlin-stdlib") -> configurationTimeMetrics.put(
|
||||
StringMetrics.KOTLIN_STDLIB_VERSION,
|
||||
dependency.version ?: "0.0.0"
|
||||
)
|
||||
dependency.group == "org.jetbrains.kotlinx" && dependency.name == "kotlinx-coroutines" -> configurationTimeMetrics.put(
|
||||
StringMetrics.KOTLIN_COROUTINES_VERSION,
|
||||
dependency.version ?: "0.0.0"
|
||||
)
|
||||
dependency.group == "org.jetbrains.kotlin" && dependency.name == "kotlin-reflect" -> configurationTimeMetrics.put(
|
||||
StringMetrics.KOTLIN_REFLECT_VERSION,
|
||||
dependency.version ?: "0.0.0"
|
||||
)
|
||||
dependency.group == "org.jetbrains.kotlinx" && dependency.name
|
||||
.startsWith("kotlinx-serialization-runtime") -> configurationTimeMetrics.put(
|
||||
StringMetrics.KOTLIN_SERIALIZATION_VERSION,
|
||||
dependency.version ?: "0.0.0"
|
||||
)
|
||||
dependency.group == "com.android.tools.build" && dependency.name.startsWith("gradle") -> configurationTimeMetrics.put(
|
||||
StringMetrics.ANDROID_GRADLE_PLUGIN_VERSION,
|
||||
dependency.version ?: "0.0.0"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun reportGlobalMetrics(logger: Logger, metricConsumer: StatisticsValuesConsumer) {
|
||||
runMetricMethodSafely(logger, "reportGlobalMetrics") {
|
||||
System.getProperty("os.name")?.also { metricConsumer.report(StringMetrics.OS_TYPE, System.getProperty("os.name")) }
|
||||
metricConsumer.report(NumericalMetrics.CPU_NUMBER_OF_CORES, Runtime.getRuntime().availableProcessors().toLong())
|
||||
metricConsumer.report(BooleanMetrics.EXECUTED_FROM_IDEA, System.getProperty("idea.active") != null)
|
||||
metricConsumer.report(NumericalMetrics.GRADLE_DAEMON_HEAP_SIZE, Runtime.getRuntime().maxMemory())
|
||||
metricConsumer.report(NumericalMetrics.GRADLE_BUILD_NUMBER_IN_CURRENT_DAEMON, DaemonReuseCounter.incrementAndGetOrdinal())
|
||||
}
|
||||
}
|
||||
|
||||
internal fun reportBuildFinished(
|
||||
logger: Logger,
|
||||
buildFailed: Boolean,
|
||||
buildStartedTime: Long?,
|
||||
projectEvaluatedTime: Long?,
|
||||
metricsContainer: NonSynchronizedMetricsContainer,
|
||||
) {
|
||||
runMetricMethodSafely(logger, "reportBuildFinish") {
|
||||
val finishTime = System.currentTimeMillis()
|
||||
if (buildStartedTime != null) {
|
||||
metricsContainer.report(NumericalMetrics.GRADLE_BUILD_DURATION, finishTime - buildStartedTime)
|
||||
}
|
||||
if (projectEvaluatedTime != null) {
|
||||
metricsContainer.report(NumericalMetrics.GRADLE_EXECUTION_DURATION, finishTime - projectEvaluatedTime)
|
||||
}
|
||||
metricsContainer.report(NumericalMetrics.BUILD_FINISH_TIME, finishTime)
|
||||
metricsContainer.report(BooleanMetrics.BUILD_FAILED, buildFailed)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun getMetricToReport(task: String) = when (task.substringAfterLast(":")) {
|
||||
"dokkaHtml" -> BooleanMetrics.ENABLED_DOKKA_HTML
|
||||
"dokkaGfm" -> BooleanMetrics.ENABLED_DOKKA_GFM
|
||||
"dokkaJavadoc" -> BooleanMetrics.ENABLED_DOKKA_JAVADOC
|
||||
"dokkaJekyll" -> BooleanMetrics.ENABLED_DOKKA_JEKYLL
|
||||
"dokkaHtmlMultiModule" -> BooleanMetrics.ENABLED_DOKKA_HTML_MULTI_MODULE
|
||||
"dokkaGfmMultiModule" -> BooleanMetrics.ENABLED_DOKKA_GFM_MULTI_MODULE
|
||||
"dokkaJekyllMultiModule" -> BooleanMetrics.ENABLED_DOKKA_JEKYLL_MULTI_MODULE
|
||||
"dokkaHtmlCollector" -> BooleanMetrics.ENABLED_DOKKA_HTML_COLLECTOR
|
||||
"dokkaGfmCollector" -> BooleanMetrics.ENABLED_DOKKA_GFM_COLLECTOR
|
||||
"dokkaJavadocCollector" -> BooleanMetrics.ENABLED_DOKKA_JAVADOC_COLLECTOR
|
||||
"dokkaJekyllCollector" -> BooleanMetrics.ENABLED_DOKKA_JEKYLL_COLLECTOR
|
||||
else -> null
|
||||
}
|
||||
+6
-7
@@ -6,27 +6,26 @@
|
||||
package org.jetbrains.kotlin.gradle.plugin.statistics.old
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.jetbrains.kotlin.gradle.plugin.statistics.AbstractKotlinBuildStatsService
|
||||
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatHandler
|
||||
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsBeanService
|
||||
import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics
|
||||
import org.jetbrains.kotlin.statistics.metrics.NumericalMetrics
|
||||
import org.jetbrains.kotlin.statistics.metrics.StringMetrics
|
||||
import javax.management.ObjectName
|
||||
|
||||
internal class Pre232IdeaKotlinBuildStatsService internal constructor(
|
||||
internal class Pre232IdeaKotlinBuildStatsBeanService internal constructor(
|
||||
project: Project,
|
||||
beanName: ObjectName
|
||||
) : AbstractKotlinBuildStatsService(project, beanName),
|
||||
) : KotlinBuildStatsBeanService(project, beanName),
|
||||
Pre232IdeaKotlinBuildStatsMXBean {
|
||||
|
||||
override fun report(metric: BooleanMetrics, value: Boolean, subprojectName: String?, weight: Long?): Boolean =
|
||||
KotlinBuildStatHandler().report(sessionLogger, metric, value, subprojectName, weight)
|
||||
report(kotlinBuildLogger.sessionLogger, metric, value, subprojectName, weight)
|
||||
|
||||
override fun report(metric: NumericalMetrics, value: Long, subprojectName: String?, weight: Long?): Boolean =
|
||||
KotlinBuildStatHandler().report(sessionLogger, metric, value, subprojectName, weight)
|
||||
report(kotlinBuildLogger.sessionLogger, metric, value, subprojectName, weight)
|
||||
|
||||
override fun report(metric: StringMetrics, value: String, subprojectName: String?, weight: Long?): Boolean =
|
||||
KotlinBuildStatHandler().report(sessionLogger, metric, value, subprojectName, weight)
|
||||
report(kotlinBuildLogger.sessionLogger, metric, value, subprojectName, weight)
|
||||
|
||||
override fun reportBoolean(name: String, value: Boolean, subprojectName: String?, weight: Long?): Boolean =
|
||||
report(BooleanMetrics.valueOf(name), value, subprojectName, weight)
|
||||
+21
-6
@@ -5,25 +5,40 @@
|
||||
package org.jetbrains.kotlin.gradle.utils
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.logging.Logger
|
||||
import org.jetbrains.kotlin.gradle.plugin.statistics.BuildFusService
|
||||
import org.jetbrains.kotlin.gradle.plugin.statistics.MetricContainer
|
||||
import org.jetbrains.kotlin.statistics.MetricValueValidationFailed
|
||||
|
||||
|
||||
/**
|
||||
* Invokes build FUS service if the reporting service is initialised and add configuration time metrics.
|
||||
*
|
||||
* BuildFusService.reportFusMetrics should be called for execution time metrics
|
||||
* New value will be present in configuration cache if BuildFusService.Parameters are not calculated yet
|
||||
* [BuildFusService.reportFusMetrics] should be called for execution time metrics
|
||||
*/
|
||||
internal fun Project.addConfigurationMetrics(reportAction: (MetricContainer) -> Unit) {
|
||||
project.gradle.sharedServices.registrations.findByName(BuildFusService.serviceName)?.also {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
(it.service as Provider<BuildFusService>).also {
|
||||
it.get().parameters.configurationMetrics.add(project.provider {
|
||||
with(it.parameters as BuildFusService.Parameters) {
|
||||
configurationMetrics.add(project.provider {
|
||||
val configurationTimeMetrics = MetricContainer()
|
||||
reportAction(configurationTimeMetrics)
|
||||
configurationTimeMetrics
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun <T> runMetricMethodSafely(logger: Logger, methodName: String, action: () -> T?): T? {
|
||||
return try {
|
||||
logger.debug("Executing [$methodName]")
|
||||
action.invoke()
|
||||
} catch (e: MetricValueValidationFailed) {
|
||||
throw e
|
||||
} catch (e: Throwable) {
|
||||
val description = "Could not execute [$methodName]"
|
||||
logger.info(description)
|
||||
logger.debug(e.message, e)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.gradle.plugin.statistics
|
||||
|
||||
import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics
|
||||
import org.jetbrains.kotlin.statistics.metrics.NumericalMetrics
|
||||
import org.jetbrains.kotlin.statistics.metrics.StringMetrics
|
||||
import org.junit.Test
|
||||
import java.lang.management.ManagementFactory
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import javax.management.MBeanServer
|
||||
import javax.management.StandardMBean
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class BuildStatServiceTest {
|
||||
|
||||
@Test
|
||||
fun testJmxDoesNotFail() {
|
||||
//Test that JMX service does not throw exception even if JMX beans are not configured
|
||||
val beanName = KotlinBuildStatsServicesRegistry.getBeanName(KotlinBuildStatsServicesRegistry.DEFAULT_SERVICE_QUALIFIER)
|
||||
val mbs: MBeanServer = ManagementFactory.getPlatformMBeanServer()
|
||||
|
||||
val jmxService = JMXKotlinBuildStatsService(mbs, beanName)
|
||||
jmxService.report(StringMetrics.KOTLIN_COMPILER_VERSION, "1.2.3")
|
||||
jmxService.report(NumericalMetrics.NUMBER_OF_SUBPROJECTS, 10)
|
||||
jmxService.report(BooleanMetrics.ENABLED_DATABINDING, true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testJmxServerWorks() {
|
||||
val callsCount = AtomicInteger(0)
|
||||
val instance = object : KotlinBuildStatsMXBean {
|
||||
override fun reportBoolean(name: String, value: Boolean, subprojectName: String?, weight: Long?): Boolean {
|
||||
callsCount.incrementAndGet()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun reportNumber(name: String, value: Long, subprojectName: String?, weight: Long?): Boolean {
|
||||
callsCount.incrementAndGet()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun reportString(name: String, value: String, subprojectName: String?, weight: Long?): Boolean {
|
||||
callsCount.incrementAndGet()
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val beanName = KotlinBuildStatsServicesRegistry.getBeanName(KotlinBuildStatsServicesRegistry.DEFAULT_SERVICE_QUALIFIER)
|
||||
val mbs: MBeanServer = ManagementFactory.getPlatformMBeanServer()
|
||||
mbs.registerMBean(StandardMBean(instance, KotlinBuildStatsMXBean::class.java), beanName)
|
||||
|
||||
try {
|
||||
val jmxService = JMXKotlinBuildStatsService(mbs, beanName)
|
||||
assertTrue(jmxService.report(StringMetrics.KOTLIN_COMPILER_VERSION, "1.2.3"))
|
||||
assertTrue(jmxService.report(NumericalMetrics.NUMBER_OF_SUBPROJECTS, 10))
|
||||
assertTrue(jmxService.report(BooleanMetrics.ENABLED_DATABINDING, true))
|
||||
assertEquals(3, callsCount.get())
|
||||
} finally {
|
||||
mbs.unregisterMBean(beanName)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+16
-20
@@ -43,18 +43,18 @@ class BuildSessionLoggerTest {
|
||||
|
||||
//check that starting session creates a new log file
|
||||
assertEquals(false, logger1.isBuildSessionStarted())
|
||||
logger1.startBuildSession(0, 0)
|
||||
logger1.startBuildSession("build_1")
|
||||
|
||||
// check that file locks work properly and do not use the same file
|
||||
logger2.startBuildSession(0, 0)
|
||||
logger2.startBuildSession("build_2")
|
||||
|
||||
assertEquals(true, logger1.isBuildSessionStarted())
|
||||
|
||||
logger1.finishBuildSession("", false, buildId = "1")
|
||||
logger1.finishBuildSession()
|
||||
// created single folder with kotlin statistics
|
||||
assertEquals(1, statFilesCount())
|
||||
|
||||
logger2.finishBuildSession("", false, buildId = "2")
|
||||
logger2.finishBuildSession()
|
||||
assertEquals(2, statFilesCount())
|
||||
|
||||
rootFolder.listFiles()?.first()?.listFiles()?.forEach { file ->
|
||||
@@ -69,8 +69,8 @@ class BuildSessionLoggerTest {
|
||||
fun testDeleteExtraFiles() {
|
||||
val maxFiles = 100
|
||||
val logger = BuildSessionLogger(rootFolder, maxFiles)
|
||||
logger.startBuildSession(0, null)
|
||||
logger.finishBuildSession("", false, buildId = "1")
|
||||
logger.startBuildSession("buildId_1")
|
||||
logger.finishBuildSession()
|
||||
assertEquals(1, statFilesCount())
|
||||
|
||||
val statsFolder = rootFolder.listFiles()?.single() ?: fail("${rootFolder.absolutePath} was not created")
|
||||
@@ -81,8 +81,8 @@ class BuildSessionLoggerTest {
|
||||
File(statsFolder, "${UUID.randomUUID()}.profile").createNewFile()
|
||||
}
|
||||
|
||||
logger.startBuildSession(0, 0)
|
||||
logger.finishBuildSession("", false, buildId = "1")
|
||||
logger.startBuildSession("buildId_1")
|
||||
logger.finishBuildSession()
|
||||
|
||||
assertTrue(
|
||||
statsFolder.listFiles()?.count { it.name == singleStatFile.name } == 1,
|
||||
@@ -109,22 +109,21 @@ class BuildSessionLoggerTest {
|
||||
val logger = BuildSessionLogger(rootFolder)
|
||||
logger.report(StringMetrics.KOTLIN_COMPILER_VERSION, "1.2.3.4-snapshot")
|
||||
|
||||
val startTime = System.currentTimeMillis() - 1001
|
||||
logger.startBuildSession(1, startTime)
|
||||
logger.startBuildSession(buildId)
|
||||
val reportFile = rootFolder.resolve(BuildSessionLogger.Companion.STATISTICS_FOLDER_NAME)
|
||||
.resolve(buildId + BuildSessionLogger.Companion.PROFILE_FILE_NAME_SUFFIX)
|
||||
|
||||
reportFile.createNewFile()
|
||||
reportFile.appendText("${StringMetrics.USE_FIR.name}=true\n")
|
||||
|
||||
logger.finishBuildSession("Build", false, buildId)
|
||||
logger.finishBuildSession()
|
||||
assertEquals(1, statFilesCount())
|
||||
|
||||
val statFile = rootFolder.listFiles()?.single()?.listFiles()?.single() ?: fail("Could not find stat file")
|
||||
statFile.appendBytes("break format of the file".toByteArray()) //this line should be filtered by MetricsContainer.readFromFile
|
||||
|
||||
logger.startBuildSession(1, startTime)
|
||||
logger.finishBuildSession("", false, buildId)
|
||||
logger.startBuildSession(buildId)
|
||||
logger.finishBuildSession()
|
||||
|
||||
val metrics = ArrayList<MetricsContainer>()
|
||||
MetricsContainer.readFromFile(statFile) {
|
||||
@@ -146,15 +145,12 @@ class BuildSessionLoggerTest {
|
||||
null,
|
||||
metrics[1].getMetric(StringMetrics.KOTLIN_COMPILER_VERSION)?.getValue()
|
||||
)
|
||||
|
||||
val buildDuration = metrics[0].getMetric(NumericalMetrics.GRADLE_BUILD_DURATION)?.getValue() ?: 0L
|
||||
assertTrue(buildDuration > 1000, "It was expected that build duration is > 1000, but got $buildDuration")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSaveAndReadAllMetrics() {
|
||||
val logger = BuildSessionLogger(rootFolder)
|
||||
logger.startBuildSession(1, 1)
|
||||
logger.startBuildSession("test")
|
||||
for (metric in StringMetrics.values()) {
|
||||
logger.report(metric, "value")
|
||||
logger.report(metric, metric.name)
|
||||
@@ -165,7 +161,7 @@ class BuildSessionLoggerTest {
|
||||
for (metric in NumericalMetrics.values()) {
|
||||
logger.report(metric, System.currentTimeMillis())
|
||||
}
|
||||
logger.finishBuildSession("Build", false, buildId = "test")
|
||||
logger.finishBuildSession()
|
||||
|
||||
MetricsContainer.readFromFile(rootFolder.listFiles()?.single()?.listFiles()?.single() ?: fail("Could not find stat file")) {
|
||||
for (metric in StringMetrics.values()) {
|
||||
@@ -183,12 +179,12 @@ class BuildSessionLoggerTest {
|
||||
@Test
|
||||
fun testWeight() {
|
||||
val logger = BuildSessionLogger(rootFolder)
|
||||
logger.startBuildSession(1, 1)
|
||||
logger.startBuildSession("build_1")
|
||||
|
||||
logger.report(NumericalMetrics.ANALYSIS_LINES_PER_SECOND, 10, null, 9)
|
||||
logger.report(NumericalMetrics.ANALYSIS_LINES_PER_SECOND, 100, null, 1)
|
||||
|
||||
logger.finishBuildSession("Build", false, buildId = "1")
|
||||
logger.finishBuildSession()
|
||||
MetricsContainer.readFromFile(rootFolder.listFiles()?.single()?.listFiles()?.single() ?: fail("Could not find stat file")) {
|
||||
assertEquals(19L, it.getMetric(NumericalMetrics.ANALYSIS_LINES_PER_SECOND)?.getValue())
|
||||
}
|
||||
|
||||
+1
-3
@@ -5,6 +5,4 @@
|
||||
|
||||
package org.jetbrains.kotlin.statistics
|
||||
|
||||
class BuildSession(val buildStartedTime: Long?) {
|
||||
val projectEvaluatedTime = System.currentTimeMillis()
|
||||
}
|
||||
class BuildSession(val buildUid: String)
|
||||
|
||||
+10
-25
@@ -46,15 +46,16 @@ class BuildSessionLogger(
|
||||
private val metricsContainer = MetricsContainer(forceValuesValidation)
|
||||
|
||||
@Synchronized
|
||||
fun startBuildSession(buildSinceDaemonStart: Long, buildStartedTime: Long?) {
|
||||
buildSession = BuildSession(buildStartedTime)
|
||||
report(NumericalMetrics.GRADLE_BUILD_NUMBER_IN_CURRENT_DAEMON, buildSinceDaemonStart)
|
||||
|
||||
fun startBuildSession(buildUid: String) {
|
||||
buildSession = BuildSession(buildUid)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun isBuildSessionStarted() = buildSession != null
|
||||
|
||||
@Synchronized
|
||||
fun getActiveBuildId() = buildSession?.buildUid
|
||||
|
||||
/**
|
||||
* Initializes a new build report file
|
||||
* The following contracts are implemented:
|
||||
@@ -94,28 +95,12 @@ class BuildSessionLogger(
|
||||
|
||||
|
||||
@Synchronized
|
||||
fun finishBuildSession(
|
||||
@Suppress("UNUSED_PARAMETER") action: String?,
|
||||
buildFailed: Boolean,
|
||||
buildId: String,
|
||||
) {
|
||||
try {
|
||||
// nanotime could not be used as build start time in nanotime is unknown. As result, the measured duration
|
||||
// could be affected by system clock correction
|
||||
val finishTime = System.currentTimeMillis()
|
||||
buildSession?.also {
|
||||
if (it.buildStartedTime != null) {
|
||||
report(NumericalMetrics.GRADLE_BUILD_DURATION, finishTime - it.buildStartedTime)
|
||||
}
|
||||
report(NumericalMetrics.GRADLE_EXECUTION_DURATION, finishTime - it.projectEvaluatedTime)
|
||||
report(NumericalMetrics.BUILD_FINISH_TIME, finishTime)
|
||||
report(BooleanMetrics.BUILD_FAILED, buildFailed)
|
||||
}
|
||||
} finally {
|
||||
buildSession = null
|
||||
storeMetricsIntoFile(buildId)
|
||||
clearOldFiles()
|
||||
fun finishBuildSession() {
|
||||
buildSession?.also {
|
||||
storeMetricsIntoFile(it.buildUid)
|
||||
}
|
||||
buildSession = null
|
||||
clearOldFiles()
|
||||
}
|
||||
|
||||
override fun report(metric: BooleanMetrics, value: Boolean, subprojectName: String?, weight: Long?) =
|
||||
|
||||
+1
@@ -150,6 +150,7 @@ class MetricsContainer(private val forceValuesValidation: Boolean = false) : Sta
|
||||
allMetrics.putAll(booleanMetrics)
|
||||
allMetrics.putAll(stringMetrics)
|
||||
}
|
||||
writer.appendLine()
|
||||
for (entry in allMetrics.entries) {
|
||||
val suffix = if (entry.key.projectHash == null) "" else ".${entry.key.projectHash}"
|
||||
writer.appendLine("${entry.key.name}$suffix=${entry.value.toStringRepresentation()}")
|
||||
|
||||
Reference in New Issue
Block a user