diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildFusStatisticsIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildFusStatisticsIT.kt new file mode 100644 index 00000000000..265e03cb8fd --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildFusStatisticsIT.kt @@ -0,0 +1,35 @@ +/* + * 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 + +import org.gradle.api.logging.LogLevel +import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.gradle.testbase.* +import org.junit.jupiter.api.DisplayName + +@DisplayName("Build FUS statistics") +class BuildFusStatisticsIT : KGPBaseTest() { + @JvmGradlePluginTests + @DisplayName("works for project with buildSrc and kotlinDsl plugin") + @GradleTest + fun testCompatibilityBuildSrcWithKotlinDsl(gradleVersion: GradleVersion) { + project( + "buildSrcUsingKotlinCompilationAndKotlinPlugin", + gradleVersion, + buildOptions = defaultBuildOptions.copy(logLevel = LogLevel.DEBUG) + ) { + build("assemble") { + //register build service for buildSrc. + assertOutputContains("Instantiated class org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService: new instance") + assertOutputContains("Instantiated class org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService_v2: new instance") + //kotlin 1.4 in kotlinDsl does not create jmx service yet + assertOutputContains("Register JMX service for backward compatibility") + assertOutputDoesNotContain("[org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatHandler] Could not execute") + } + } + } + +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt index a8f76242dd8..96725b6965b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt @@ -358,7 +358,7 @@ fun Project.getKotlinPluginVersion() = getKotlinPluginVersion(project.logger) fun getKotlinPluginVersion(logger: Logger): String { if (!kotlinPluginVersionFromResources.isInitialized()) { logger.kotlinDebug("Loading version information") - logger.kotlinDebug("Found project version [${kotlinPluginVersionFromResources.value}") + logger.kotlinDebug("Found project version [${kotlinPluginVersionFromResources.value}]") } return kotlinPluginVersionFromResources.value } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatHandler.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatHandler.kt index cf805e845d0..b00468de383 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatHandler.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatHandler.kt @@ -49,13 +49,24 @@ class KotlinBuildStatHandler { } fun buildFinished( + beanName: ObjectName, + ) { + runSafe("${KotlinBuildStatHandler::class.java}.buildFinished") { + val mbs: MBeanServer = ManagementFactory.getPlatformMBeanServer() + if (mbs.isRegistered(beanName)) { + mbs.unregisterMBean(beanName) + } + } + } + + fun reportGlobalMetricsAndBuildFinished( gradle: Gradle?, beanName: ObjectName, sessionLogger: BuildSessionLogger, action: String?, failure: Throwable? ) { - runSafe("${KotlinBuildStatHandler::class.java}.buildFinished") { + runSafe("${KotlinBuildStatHandler::class.java}.reportGlobalMetrics") { try { try { if (gradle != null) reportGlobalMetrics(gradle, sessionLogger) @@ -63,10 +74,7 @@ class KotlinBuildStatHandler { sessionLogger.finishBuildSession(action, failure) } } finally { - val mbs: MBeanServer = ManagementFactory.getPlatformMBeanServer() - if (mbs.isRegistered(beanName)) { - mbs.unregisterMBean(beanName) - } + buildFinished(beanName) } } } @@ -215,7 +223,7 @@ class KotlinBuildStatHandler { weight: Long? = null ) = runSafe("report metric ${metric.name}") { sessionLogger.report(metric, value, subprojectName, weight) - } as? Boolean ?: false + } ?: false internal fun report( sessionLogger: BuildSessionLogger, @@ -224,6 +232,6 @@ class KotlinBuildStatHandler { subprojectName: String?, weight: Long? = null ) = runSafe("report metric ${metric.name}") { - sessionLogger.report(metric, value, subprojectName, weight) - } as? Boolean ?: false + sessionLogger.report(metric, value, subprojectName, weight) + } ?: false } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatsService.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatsService.kt index f0913706b35..3aa2f02e0a4 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatsService.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatsService.kt @@ -9,10 +9,13 @@ import org.gradle.BuildAdapter import org.gradle.BuildResult import org.gradle.api.Project import org.gradle.api.invocation.Gradle +import org.gradle.api.logging.Logger import org.gradle.api.logging.Logging import org.gradle.initialization.BuildRequestMetaData import org.gradle.invocation.DefaultGradle import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatHandler.Companion.runSafe +import org.jetbrains.kotlin.gradle.plugin.statistics.old.Pre232IdeaKotlinBuildStatsMXBean +import org.jetbrains.kotlin.gradle.plugin.statistics.old.Pre232IdeaKotlinBuildStatsService import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable import org.jetbrains.kotlin.statistics.BuildSessionLogger import org.jetbrains.kotlin.statistics.BuildSessionLogger.Companion.STATISTICS_FOLDER_NAME @@ -32,27 +35,25 @@ import kotlin.system.measureTimeMillis * 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 - - //support Idea up to 2023.1 version, before KT-57371 fix - - fun reportBoolean(name: String, value: Boolean, subprojectName: String?): Boolean - - fun reportNumber(name: String, value: Long, subprojectName: String?): Boolean - - fun reportString(name: String, value: String, subprojectName: String?): Boolean } internal abstract class KotlinBuildStatsService internal constructor() : BuildAdapter(), IStatisticsValuesConsumer { companion object { // Do not rename this bean otherwise compatibility with the older Kotlin Gradle Plugins would be lost - const val JMX_BEAN_NAME = "org.jetbrains.kotlin.gradle.plugin.statistics:type=StatsService" + private const val JMX_BEAN_NAME_BEFORE_232_IDEA = "org.jetbrains.kotlin.gradle.plugin.statistics:type=StatsService" + + //update name when API changed + private const val SERVICE_NAME = "v2" + const val JMX_BEAN_NAME = "org.jetbrains.kotlin.gradle.plugin.statistics:type=StatsService,name=$SERVICE_NAME" + // Property name for disabling saving statistical information const val ENABLE_STATISTICS_PROPERTY_NAME = "enable_kotlin_performance_profile" @@ -80,6 +81,8 @@ internal abstract class KotlinBuildStatsService internal constructor() : BuildAd return instance } + private fun getServiceName(): String = "${KotlinBuildStatsService::class.java}_$SERVICE_NAME" + /** * Method for creating new instance of IStatisticsValuesConsumer * It could be invoked only when applying Kotlin gradle plugin. @@ -106,21 +109,23 @@ internal abstract class KotlinBuildStatsService internal constructor() : BuildAd val log = getLogger() if (instance != null) { - log.debug("${KotlinBuildStatsService::class.java} is already instantiated. Current instance is $instance") + log.debug("${getServiceName()} is already instantiated. Current instance is $instance") } else { val beanName = ObjectName(JMX_BEAN_NAME) val mbs: MBeanServer = ManagementFactory.getPlatformMBeanServer() if (mbs.isRegistered(beanName)) { log.debug( - "${KotlinBuildStatsService::class.java} is already instantiated in another classpath. Creating JMX-wrapper" + "${getServiceName()} is already instantiated in another classpath. Creating JMX-wrapper" ) instance = JMXKotlinBuildStatsService(mbs, beanName) } else { val newInstance = DefaultKotlinBuildStatsService(gradle, beanName) instance = newInstance - log.debug("Instantiated ${KotlinBuildStatsService::class.java}: new instance $instance") + log.debug("Instantiated ${getServiceName()}: new instance $instance") mbs.registerMBean(StandardMBean(newInstance, KotlinBuildStatsMXBean::class.java), beanName) + + registerPre232IdeaStatsBean(mbs, gradle, log) } if (!isConfigurationCacheAvailable(gradle)) { @@ -132,6 +137,17 @@ internal abstract class KotlinBuildStatsService internal constructor() : BuildAd } } + //To support backward compatibility with Idea before 232 version + private fun registerPre232IdeaStatsBean(mbs: MBeanServer, gradle: Gradle, log: Logger) { + val beanName = ObjectName(JMX_BEAN_NAME_BEFORE_232_IDEA) + if (!mbs.isRegistered(beanName)) { + val newInstance = Pre232IdeaKotlinBuildStatsService(gradle, beanName) + mbs.registerMBean(StandardMBean(newInstance, Pre232IdeaKotlinBuildStatsMXBean::class.java), beanName) + log.debug("Register JMX service for backward compatibility") + } + } + + /** * Invokes provided collector if the reporting service is initialised. * The duration of collector's wall time is reported into overall overhead metric. @@ -173,7 +189,7 @@ internal abstract class KotlinBuildStatsService internal constructor() : BuildAd internal class JMXKotlinBuildStatsService(private val mbs: MBeanServer, private val beanName: ObjectName) : KotlinBuildStatsService() { - private fun callJmx_v2(method: String, type: String, metricName: String, value: Any, subprojectName: String?, weight: Long): Any? { + private fun callJmx(method: String, type: String, metricName: String, value: Any, subprojectName: String?, weight: Long?): Any? { return mbs.invoke( beanName, method, @@ -182,24 +198,6 @@ internal class JMXKotlinBuildStatsService(private val mbs: MBeanServer, private ) } - private fun callJmx_v1(method: String, type: String, metricName: String, value: Any, subprojectName: String?): Any? { - return mbs.invoke( - beanName, - method, - arrayOf(metricName, value, subprojectName), - arrayOf("java.lang.String", type, "java.lang.String") - ) - } - - //Temporary solution before KT-57371 - private fun callJmx(method: String, type: String, metricName: String, value: Any, subprojectName: String?, weight: Long?): Any? { - return if (weight == null) { - callJmx_v1(method, type, metricName, value, subprojectName) - } else { - callJmx_v2(method, type, metricName, value, subprojectName, weight) - } - } - override fun report(metric: BooleanMetrics, value: Boolean, subprojectName: String?, weight: Long?) = runSafe("report metric ${metric.name}") { callJmx("reportBoolean", "boolean", metric.name, value, subprojectName, weight) @@ -220,16 +218,16 @@ internal class JMXKotlinBuildStatsService(private val mbs: MBeanServer, private } } -internal class DefaultKotlinBuildStatsService internal constructor( +internal abstract class AbstractKotlinBuildStatsService( gradle: Gradle, - val beanName: ObjectName -) : KotlinBuildStatsService(), KotlinBuildStatsMXBean { + protected val beanName: ObjectName +) : KotlinBuildStatsService() { private val forcePropertiesValidation = if (gradle.rootProject.hasProperty(FORCE_VALUES_VALIDATION)) { gradle.rootProject.property(FORCE_VALUES_VALIDATION).toString().toBoolean() } else { false } - private val sessionLogger = BuildSessionLogger(gradle.gradleUserHomeDir, forceValuesValidation = forcePropertiesValidation) + protected val sessionLogger = BuildSessionLogger(gradle.gradleUserHomeDir, forceValuesValidation = forcePropertiesValidation) private fun gradleBuildStartTime(gradle: Gradle): Long? { return (gradle as? DefaultGradle)?.services?.get(BuildRequestMetaData::class.java)?.startTime @@ -248,9 +246,15 @@ internal class DefaultKotlinBuildStatsService internal constructor( @Synchronized override fun buildFinished(result: BuildResult) { - KotlinBuildStatHandler().buildFinished(result.gradle, beanName, sessionLogger, result.action, result.failure) + KotlinBuildStatHandler().buildFinished(beanName) instance = null } +} + +internal class DefaultKotlinBuildStatsService internal constructor( + gradle: Gradle, + beanName: ObjectName +) : AbstractKotlinBuildStatsService(gradle, beanName), KotlinBuildStatsMXBean { override fun report(metric: BooleanMetrics, value: Boolean, subprojectName: String?, weight: Long?): Boolean = KotlinBuildStatHandler().report(sessionLogger, metric, value, subprojectName, weight) @@ -270,15 +274,10 @@ internal class DefaultKotlinBuildStatsService internal constructor( override fun reportString(name: String, value: String, subprojectName: String?, weight: Long?): Boolean = report(StringMetrics.valueOf(name), value, subprojectName, weight) - override fun reportBoolean(name: String, value: Boolean, subprojectName: String?): Boolean = - report(BooleanMetrics.valueOf(name), value, subprojectName, null) - - - override fun reportNumber(name: String, value: Long, subprojectName: String?): Boolean = - report(NumericalMetrics.valueOf(name), value, subprojectName, null) - - - override fun reportString(name: String, value: String, subprojectName: String?): Boolean = - report(StringMetrics.valueOf(name), value, subprojectName, null) - + //only one jmx bean service should report global metrics + @Synchronized + override fun buildFinished(result: BuildResult) { + KotlinBuildStatHandler().reportGlobalMetricsAndBuildFinished(result.gradle, beanName, sessionLogger, result.action, result.failure) + instance = null + } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/old/Pre232IdeaKotlinBuildStatsMXBean.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/old/Pre232IdeaKotlinBuildStatsMXBean.kt new file mode 100644 index 00000000000..3495bbcdf4e --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/old/Pre232IdeaKotlinBuildStatsMXBean.kt @@ -0,0 +1,20 @@ +/* + * 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.old + +internal interface Pre232IdeaKotlinBuildStatsMXBean { + 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 + + fun reportBoolean(name: String, value: Boolean, subprojectName: String?): Boolean + + fun reportNumber(name: String, value: Long, subprojectName: String?): Boolean + + fun reportString(name: String, value: String, subprojectName: String?): Boolean +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/old/Pre232IdeaKotlinBuildStatsService.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/old/Pre232IdeaKotlinBuildStatsService.kt new file mode 100644 index 00000000000..67088439963 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/old/Pre232IdeaKotlinBuildStatsService.kt @@ -0,0 +1,50 @@ +/* + * 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.old + +import org.gradle.api.invocation.Gradle +import org.jetbrains.kotlin.gradle.plugin.statistics.AbstractKotlinBuildStatsService +import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatHandler +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( + gradle: Gradle, + beanName: ObjectName +) : AbstractKotlinBuildStatsService(gradle, beanName), Pre232IdeaKotlinBuildStatsMXBean { + + 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) + + override fun reportBoolean(name: String, value: Boolean, subprojectName: String?): Boolean = + report(BooleanMetrics.valueOf(name), value, subprojectName, null) + + + override fun reportNumber(name: String, value: Long, subprojectName: String?): Boolean = + report(NumericalMetrics.valueOf(name), value, subprojectName, null) + + + override fun reportString(name: String, value: String, subprojectName: String?): Boolean = + report(StringMetrics.valueOf(name), value, subprojectName, null) + +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/BuildStatServiceTest.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/BuildStatServiceTest.kt index d7f788731c1..c3bd784c967 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/BuildStatServiceTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/BuildStatServiceTest.kt @@ -50,21 +50,6 @@ class BuildStatServiceTest { return true } - override fun reportBoolean(name: String, value: Boolean, subprojectName: String?): Boolean { - callsCount.incrementAndGet() - return true - } - - override fun reportNumber(name: String, value: Long, subprojectName: String?): Boolean { - callsCount.incrementAndGet() - return true - } - - override fun reportString(name: String, value: String, subprojectName: String?): Boolean { - callsCount.incrementAndGet() - return true - } - } val beanName = ObjectName(KotlinBuildStatsService.JMX_BEAN_NAME) diff --git a/libraries/tools/kotlin-gradle-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/metrics/StatisticsValues.kt b/libraries/tools/kotlin-gradle-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/metrics/StatisticsValues.kt index f1d05b5cce9..43b142f89a1 100644 --- a/libraries/tools/kotlin-gradle-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/metrics/StatisticsValues.kt +++ b/libraries/tools/kotlin-gradle-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/metrics/StatisticsValues.kt @@ -5,18 +5,6 @@ package org.jetbrains.kotlin.statistics.metrics -interface ReportStatisticsValue { - val name: String - val value: T -} - -class ReportOnceStatisticsValue(override val name: String, override val value: T) : - ReportStatisticsValue - -interface AdditiveStatisticsValue : ReportStatisticsValue { - fun addValue(t: T) -} - interface IStatisticsValuesConsumer { fun report(metric: BooleanMetrics, value: Boolean, subprojectName: String? = null, weight: Long? = null): Boolean