Added FUS extensions and new metrics for reporting statistics from gradle

This commit is contained in:
Andrey Uskov
2020-01-17 09:14:59 +03:00
parent bac0309a9b
commit beb3165839
28 changed files with 440 additions and 119 deletions
@@ -21,11 +21,26 @@ enum class FUSEventGroups(groupIdSuffix: String, val events: Set<String> = setOf
Debug("ide.debugger"), Debug("ide.debugger"),
J2K("ide.j2k"), J2K("ide.j2k"),
Editor("ide.editor"), Editor("ide.editor"),
Settings("ide.settings"); Settings("ide.settings"),
GradlePerformance("gradle.performance");
val GROUP_ID: String = "kotlin.$groupIdSuffix" val GROUP_ID: String = "kotlin.$groupIdSuffix"
} }
@Suppress("EnumEntryName")
enum class GradleStatisticsEvents {
Environment,
Kapt,
CompilerPlugins,
MPP,
Libraries,
GradleConfiguration,
ComponentVersions,
KotlinFeatures,
GradlePerformance,
UseScenarios
}
val gradleTargetEvents = setOf( val gradleTargetEvents = setOf(
"kotlin-android", "kotlin-android",
"kotlin-platform-common", "kotlin-platform-common",
+2
View File
@@ -26,6 +26,8 @@ dependencies {
compileOnly(intellijPluginDep("junit")) compileOnly(intellijPluginDep("junit"))
compileOnly(intellijPluginDep("testng")) compileOnly(intellijPluginDep("testng"))
compileOnly(project(":kotlin-gradle-statistics"))
Platform[192].orHigher { Platform[192].orHigher {
compileOnly(intellijPluginDep("java")) compileOnly(intellijPluginDep("java"))
testCompileOnly(intellijPluginDep("java")) testCompileOnly(intellijPluginDep("java"))
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.idea.util.NotNullableCopyableDataNodeUserDataPropert
import org.jetbrains.kotlin.idea.util.PsiPrecedences import org.jetbrains.kotlin.idea.util.PsiPrecedences
import org.jetbrains.kotlin.idea.statistics.FUSEventGroups import org.jetbrains.kotlin.idea.statistics.FUSEventGroups
import org.jetbrains.kotlin.idea.statistics.KotlinFUSLogger import org.jetbrains.kotlin.idea.statistics.KotlinFUSLogger
import org.jetbrains.kotlin.idea.statistics.KotlinGradleFUSLogger
import org.jetbrains.plugins.gradle.model.ExternalProjectDependency import org.jetbrains.plugins.gradle.model.ExternalProjectDependency
import org.jetbrains.plugins.gradle.model.ExternalSourceSet import org.jetbrains.plugins.gradle.model.ExternalSourceSet
import org.jetbrains.plugins.gradle.model.FileCollectionDependency import org.jetbrains.plugins.gradle.model.FileCollectionDependency
@@ -294,6 +295,7 @@ class KotlinGradleProjectResolverExtension : AbstractProjectResolverExtension()
nextResolver.populateModuleContentRoots(gradleModule, ideModule) nextResolver.populateModuleContentRoots(gradleModule, ideModule)
val moduleNamePrefix = GradleProjectResolverUtil.getModuleId(resolverCtx, gradleModule) val moduleNamePrefix = GradleProjectResolverUtil.getModuleId(resolverCtx, gradleModule)
resolverCtx.getExtraProject(gradleModule, KotlinGradleModel::class.java)?.let { gradleModel -> resolverCtx.getExtraProject(gradleModule, KotlinGradleModel::class.java)?.let { gradleModel ->
KotlinGradleFUSLogger.populateGradleUserDir(gradleModel.gradleUserHome)
ideModule.pureKotlinSourceFolders = ideModule.pureKotlinSourceFolders =
gradleModel.kotlinTaskProperties.flatMap { it.value.pureKotlinSourceFolders ?: emptyList() }.map { it.absolutePath } gradleModel.kotlinTaskProperties.flatMap { it.value.pureKotlinSourceFolders ?: emptyList() }.map { it.absolutePath }
val gradleSourceSets = ideModule.children.filter { it.data is GradleSourceSetData } as Collection<DataNode<GradleSourceSetData>> val gradleSourceSets = ideModule.children.filter { it.data is GradleSourceSetData } as Collection<DataNode<GradleSourceSetData>>
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.idea.configuration package org.jetbrains.kotlin.idea.configuration
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.externalSystem.model.DataNode import com.intellij.openapi.externalSystem.model.DataNode
import com.intellij.openapi.externalSystem.model.ProjectKeys import com.intellij.openapi.externalSystem.model.ProjectKeys
@@ -54,6 +55,7 @@ import org.jetbrains.kotlin.idea.inspections.gradle.findKotlinPluginVersion
import org.jetbrains.kotlin.idea.inspections.gradle.getResolvedVersionByModuleData import org.jetbrains.kotlin.idea.inspections.gradle.getResolvedVersionByModuleData
import org.jetbrains.kotlin.idea.platform.tooling import org.jetbrains.kotlin.idea.platform.tooling
import org.jetbrains.kotlin.idea.roots.migrateNonJvmSourceFolders import org.jetbrains.kotlin.idea.roots.migrateNonJvmSourceFolders
import org.jetbrains.kotlin.idea.statistics.KotlinGradleFUSLogger
import org.jetbrains.kotlin.library.KLIB_FILE_EXTENSION import org.jetbrains.kotlin.library.KLIB_FILE_EXTENSION
import org.jetbrains.kotlin.platform.IdePlatformKind import org.jetbrains.kotlin.platform.IdePlatformKind
import org.jetbrains.kotlin.platform.impl.isCommon import org.jetbrains.kotlin.platform.impl.isCommon
@@ -153,6 +155,10 @@ class KotlinGradleProjectDataService : AbstractProjectDataService<ModuleData, Vo
val codeStyleStr = GradlePropertiesFileFacade.forProject(project).readProperty(KOTLIN_CODE_STYLE_GRADLE_SETTING) val codeStyleStr = GradlePropertiesFileFacade.forProject(project).readProperty(KOTLIN_CODE_STYLE_GRADLE_SETTING)
ProjectCodeStyleImporter.apply(project, codeStyleStr) ProjectCodeStyleImporter.apply(project, codeStyleStr)
ApplicationManager.getApplication().executeOnPooledThread {
KotlinGradleFUSLogger.reportStatistics()
}
} }
} }
@@ -0,0 +1,219 @@
/*
* 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.idea.statistics
import com.intellij.ide.util.PropertiesComponent
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.DumbAware
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.StartupActivity
import com.intellij.util.concurrency.AppExecutorUtil
import org.jetbrains.kotlin.statistics.BuildSessionLogger
import java.io.File
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicBoolean
import org.jetbrains.kotlin.statistics.BuildSessionLogger.Companion.STATISTICS_FOLDER_NAME
import org.jetbrains.kotlin.statistics.fileloggers.MetricsContainer
import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics
import org.jetbrains.kotlin.statistics.metrics.NumericalMetrics
import org.jetbrains.kotlin.statistics.metrics.StringMetrics
class KotlinGradleFUSLogger : StartupActivity, DumbAware, Runnable {
override fun runActivity(project: Project) {
AppExecutorUtil.getAppScheduledExecutorService()
.scheduleWithFixedDelay(this, EXECUTION_DELAY_MIN, EXECUTION_DELAY_MIN, TimeUnit.MINUTES)
}
override fun run() {
reportStatistics()
}
companion object {
/**
* Maximum amount of directories which were reported as gradle user dirs
* These directories should be monitored for reported gradle statistics.
*/
const val MAXIMUM_USER_DIRS = 10
/**
* Delay between sequential checks of gradle statistics
*/
const val EXECUTION_DELAY_MIN = 60L
/**
* Property name used for persisting gradle user dirs
*/
private const val GRADLE_USER_DIRS_PROPERTY_NAME = "kotlin-gradle-user-dirs"
private val isRunning = AtomicBoolean(false)
private fun MetricsContainer.log(event: GradleStatisticsEvents, vararg metrics: Any) {
val data = HashMap<String, String>()
fun putIfNotNull(key: String, value: String?) {
if (value != null) {
data[key.toLowerCase()] = value
}
}
for (metric in metrics) {
when (metric) {
is BooleanMetrics -> putIfNotNull(metric.name, this.getMetric(metric)?.toStringRepresentation())
is StringMetrics -> putIfNotNull(metric.name, this.getMetric(metric)?.toStringRepresentation())
is NumericalMetrics -> putIfNotNull(metric.name, this.getMetric(metric)?.toStringRepresentation())
is Pair<*, *> -> putIfNotNull(metric.first.toString(), metric.second.toString())
}
}
if (data.size > 0) {
KotlinFUSLogger.log(FUSEventGroups.GradlePerformance, event.name, data)
}
}
private fun processMetricsContainer(container: MetricsContainer, previous: MetricsContainer?) {
container.log(
GradleStatisticsEvents.Environment,
NumericalMetrics.CPU_NUMBER_OF_CORES,
StringMetrics.GRADLE_VERSION,
NumericalMetrics.ARTIFACTS_DOWNLOAD_SPEED,
StringMetrics.IDES_INSTALLED,
BooleanMetrics.EXECUTED_FROM_IDEA
)
container.log(
GradleStatisticsEvents.Kapt,
BooleanMetrics.ENABLED_KAPT,
BooleanMetrics.ENABLED_DAGGER,
BooleanMetrics.ENABLED_DATABINDING
)
container.log(
GradleStatisticsEvents.CompilerPlugins,
BooleanMetrics.ENABLED_COMPILER_PLUGIN_ALL_OPEN,
BooleanMetrics.ENABLED_COMPILER_PLUGIN_NO_ARG,
BooleanMetrics.ENABLED_COMPILER_PLUGIN_JPA_SUPPORT,
BooleanMetrics.ENABLED_COMPILER_PLUGIN_SAM_WITH_RECEIVER
)
container.log(
GradleStatisticsEvents.MPP,
StringMetrics.MPP_PLATFORMS,
BooleanMetrics.ENABLED_HMPP
)
container.log(
GradleStatisticsEvents.Libraries,
StringMetrics.LIBRARY_SPRING_VERSION,
StringMetrics.LIBRARY_VAADIN_VERSION,
StringMetrics.LIBRARY_GWT_VERSION,
StringMetrics.LIBRARY_HIBERNATE_VERSION
)
container.log(
GradleStatisticsEvents.GradleConfiguration,
NumericalMetrics.GRADLE_DAEMON_HEAP_SIZE,
NumericalMetrics.GRADLE_BUILD_NUMBER_IN_CURRENT_DAEMON,
NumericalMetrics.CONFIGURATION_API_COUNT,
NumericalMetrics.CONFIGURATION_IMPLEMENTATION_COUNT,
NumericalMetrics.CONFIGURATION_COMPILE_COUNT,
NumericalMetrics.CONFIGURATION_RUNTIME_COUNT,
NumericalMetrics.GRADLE_NUMBER_OF_TASKS,
NumericalMetrics.GRADLE_NUMBER_OF_UNCONFIGURED_TASKS,
NumericalMetrics.GRADLE_NUMBER_OF_INCREMENTAL_TASKS
)
container.log(
GradleStatisticsEvents.ComponentVersions,
StringMetrics.KOTLIN_COMPILER_VERSION,
StringMetrics.KOTLIN_STDLIB_VERSION,
StringMetrics.KOTLIN_REFLECT_VERSION,
StringMetrics.KOTLIN_COROUTINES_VERSION,
StringMetrics.KOTLIN_SERIALIZATION_VERSION,
StringMetrics.ANDROID_GRADLE_PLUGIN_VERSION
)
container.log(
GradleStatisticsEvents.KotlinFeatures,
StringMetrics.KOTLIN_LANGUAGE_VERSION,
StringMetrics.KOTLIN_API_VERSION,
BooleanMetrics.BUILD_SRC_EXISTS,
NumericalMetrics.BUILD_SRC_COUNT,
BooleanMetrics.GRADLE_BUILD_CACHE_USED,
BooleanMetrics.GRADLE_WORKER_API_USED,
BooleanMetrics.KOTLIN_OFFICIAL_CODESTYLE,
BooleanMetrics.KOTLIN_PROGRESSIVE_MODE,
BooleanMetrics.KOTLIN_KTS_USED
)
container.log(
GradleStatisticsEvents.GradlePerformance,
NumericalMetrics.GRADLE_BUILD_DURATION,
NumericalMetrics.GRADLE_EXECUTION_DURATION,
NumericalMetrics.NUMBER_OF_SUBPROJECTS,
NumericalMetrics.STATISTICS_VISIT_ALL_PROJECTS_OVERHEAD
)
val finishTime = container.getMetric(NumericalMetrics.BUILD_FINISH_TIME)?.getValue()
val prevFinishTime = previous?.getMetric(NumericalMetrics.BUILD_FINISH_TIME)?.getValue()
val betweenBuilds = if (finishTime != null && prevFinishTime != null) finishTime - prevFinishTime else null
container.log(
GradleStatisticsEvents.UseScenarios,
Pair("time_between_builds", betweenBuilds),
BooleanMetrics.DEBUGGER_ENABLED,
BooleanMetrics.COMPILATION_STARTED,
BooleanMetrics.TESTS_EXECUTED,
BooleanMetrics.MAVEN_PUBLISH_EXECUTED,
BooleanMetrics.BUILD_FAILED
)
}
fun reportStatistics() {
if (isRunning.compareAndSet(false, true)) {
try {
for (gradleUserHome in gradleUserDirs) {
BuildSessionLogger.listProfileFiles(File(gradleUserHome, STATISTICS_FOLDER_NAME))?.forEach { statisticFile ->
try {
var previousEvent: MetricsContainer? = null
MetricsContainer.readFromFile(statisticFile) { metricContainer ->
processMetricsContainer(metricContainer, previousEvent)
previousEvent = metricContainer
}
} catch (e: Exception) {
Logger.getInstance(KotlinFUSLogger::class.java)
.info("Failed to process file ${statisticFile.absolutePath}: ${e.message}", e)
} finally {
if (!statisticFile.delete()) {
Logger.getInstance(KotlinFUSLogger::class.java)
.warn("[FUS] Failed to delete file ${statisticFile.absolutePath}")
}
}
}
}
} finally {
isRunning.set(false)
}
}
}
private var gradleUserDirs: Array<String>
set(value) = PropertiesComponent.getInstance().setValues(
GRADLE_USER_DIRS_PROPERTY_NAME, value
)
get() = PropertiesComponent.getInstance().getValues(GRADLE_USER_DIRS_PROPERTY_NAME) ?: emptyArray()
fun populateGradleUserDir(path: String) {
val currentState = gradleUserDirs
if (path in currentState) return
val result = ArrayList<String>()
result.add(path)
result.addAll(currentState)
gradleUserDirs =
result.filter { filePath -> File(filePath).exists() }.take(MAXIMUM_USER_DIRS).toTypedArray()
}
}
}
@@ -54,6 +54,7 @@ interface KotlinGradleModel : Serializable {
val implements: List<String> val implements: List<String>
val kotlinTarget: String? val kotlinTarget: String?
val kotlinTaskProperties: KotlinTaskPropertiesBySourceSet val kotlinTaskProperties: KotlinTaskPropertiesBySourceSet
val gradleUserHome: String
} }
data class KotlinGradleModelImpl( data class KotlinGradleModelImpl(
@@ -63,7 +64,8 @@ data class KotlinGradleModelImpl(
override val platformPluginId: String?, override val platformPluginId: String?,
override val implements: List<String>, override val implements: List<String>,
override val kotlinTarget: String? = null, override val kotlinTarget: String? = null,
override val kotlinTaskProperties: KotlinTaskPropertiesBySourceSet override val kotlinTaskProperties: KotlinTaskPropertiesBySourceSet,
override val gradleUserHome: String
) : KotlinGradleModel ) : KotlinGradleModel
abstract class AbstractKotlinGradleModelBuilder : ModelBuilderService { abstract class AbstractKotlinGradleModelBuilder : ModelBuilderService {
@@ -189,7 +191,8 @@ class KotlinGradleModelBuilder : AbstractKotlinGradleModelBuilder() {
platform, platform,
implementedProjects.map { it.pathOrName() }, implementedProjects.map { it.pathOrName() },
platform ?: kotlinPluginId, platform ?: kotlinPluginId,
extraProperties extraProperties,
project.gradle.gradleUserHomeDir.absolutePath
) )
} }
} }
@@ -880,6 +880,8 @@
<postStartupActivity implementation="org.jetbrains.kotlin.idea.framework.CreateKotlinSdkActivity"/> <postStartupActivity implementation="org.jetbrains.kotlin.idea.framework.CreateKotlinSdkActivity"/>
<postStartupActivity implementation="org.jetbrains.kotlin.idea.statistics.KotlinGradleFUSLogger"/>
<moduleConfigurationEditorProvider implementation="org.jetbrains.kotlin.idea.roots.ui.NonJvmKotlinModuleEditorsProvider"/> <moduleConfigurationEditorProvider implementation="org.jetbrains.kotlin.idea.roots.ui.NonJvmKotlinModuleEditorsProvider"/>
<projectStructure.sourceRootEditHandler implementation="org.jetbrains.kotlin.idea.roots.ui.KotlinModuleSourceRootEditHandler$Source"/> <projectStructure.sourceRootEditHandler implementation="org.jetbrains.kotlin.idea.roots.ui.KotlinModuleSourceRootEditHandler$Source"/>
+1
View File
@@ -78,6 +78,7 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
<statistics.counterUsagesCollector groupId="kotlin.ide.debugger" version="2"/> <statistics.counterUsagesCollector groupId="kotlin.ide.debugger" version="2"/>
<statistics.counterUsagesCollector groupId="kotlin.ide.j2k" version="1"/> <statistics.counterUsagesCollector groupId="kotlin.ide.j2k" version="1"/>
<statistics.counterUsagesCollector groupId="kotlin.ide.editor" version="1"/> <statistics.counterUsagesCollector groupId="kotlin.ide.editor" version="1"/>
<statistics.counterUsagesCollector groupId="kotlin.gradle.performance" version="1"/>
<statistics.projectUsagesCollector implementation="org.jetbrains.kotlin.idea.statistics.IDESettingsFUSCollector"/> <statistics.projectUsagesCollector implementation="org.jetbrains.kotlin.idea.statistics.IDESettingsFUSCollector"/>
<statistics.projectUsagesCollector implementation="org.jetbrains.kotlin.idea.formatter.KotlinFormatterUsageCollector"/> <statistics.projectUsagesCollector implementation="org.jetbrains.kotlin.idea.formatter.KotlinFormatterUsageCollector"/>
<statistics.projectUsagesCollector implementation="org.jetbrains.kotlin.idea.statistics.ProjectConfigurationCollector"/> <statistics.projectUsagesCollector implementation="org.jetbrains.kotlin.idea.statistics.ProjectConfigurationCollector"/>
+1
View File
@@ -78,6 +78,7 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
<statistics.counterUsagesCollector groupId="kotlin.ide.debugger" version="2"/> <statistics.counterUsagesCollector groupId="kotlin.ide.debugger" version="2"/>
<statistics.counterUsagesCollector groupId="kotlin.ide.j2k" version="1"/> <statistics.counterUsagesCollector groupId="kotlin.ide.j2k" version="1"/>
<statistics.counterUsagesCollector groupId="kotlin.ide.editor" version="1"/> <statistics.counterUsagesCollector groupId="kotlin.ide.editor" version="1"/>
<statistics.counterUsagesCollector groupId="kotlin.gradle.performance" version="1"/>
<statistics.projectUsagesCollector implementation="org.jetbrains.kotlin.idea.statistics.IDESettingsFUSCollector"/> <statistics.projectUsagesCollector implementation="org.jetbrains.kotlin.idea.statistics.IDESettingsFUSCollector"/>
<statistics.projectUsagesCollector implementation="org.jetbrains.kotlin.idea.formatter.KotlinFormatterUsageCollector"/> <statistics.projectUsagesCollector implementation="org.jetbrains.kotlin.idea.formatter.KotlinFormatterUsageCollector"/>
<statistics.projectUsagesCollector implementation="org.jetbrains.kotlin.idea.statistics.ProjectConfigurationCollector"/> <statistics.projectUsagesCollector implementation="org.jetbrains.kotlin.idea.statistics.ProjectConfigurationCollector"/>
+1
View File
@@ -78,6 +78,7 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
<statistics.counterUsagesCollector groupId="kotlin.ide.debugger" version="2"/> <statistics.counterUsagesCollector groupId="kotlin.ide.debugger" version="2"/>
<statistics.counterUsagesCollector groupId="kotlin.ide.j2k" version="1"/> <statistics.counterUsagesCollector groupId="kotlin.ide.j2k" version="1"/>
<statistics.counterUsagesCollector groupId="kotlin.ide.editor" version="1"/> <statistics.counterUsagesCollector groupId="kotlin.ide.editor" version="1"/>
<statistics.counterUsagesCollector groupId="kotlin.gradle.performance" version="1"/>
<statistics.projectUsagesCollector implementation="org.jetbrains.kotlin.idea.statistics.IDESettingsFUSCollector"/> <statistics.projectUsagesCollector implementation="org.jetbrains.kotlin.idea.statistics.IDESettingsFUSCollector"/>
<statistics.projectUsagesCollector implementation="org.jetbrains.kotlin.idea.formatter.KotlinFormatterUsageCollector"/> <statistics.projectUsagesCollector implementation="org.jetbrains.kotlin.idea.formatter.KotlinFormatterUsageCollector"/>
<statistics.projectUsagesCollector implementation="org.jetbrains.kotlin.idea.statistics.ProjectConfigurationCollector"/> <statistics.projectUsagesCollector implementation="org.jetbrains.kotlin.idea.statistics.ProjectConfigurationCollector"/>
@@ -28,12 +28,14 @@ import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder
import org.jetbrains.kotlin.gradle.plugin.sources.checkSourceSetVisibilityRequirements import org.jetbrains.kotlin.gradle.plugin.sources.checkSourceSetVisibilityRequirements
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
import org.jetbrains.kotlin.gradle.scripting.internal.ScriptingGradleSubplugin import org.jetbrains.kotlin.gradle.scripting.internal.ScriptingGradleSubplugin
import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled
import org.jetbrains.kotlin.gradle.utils.* import org.jetbrains.kotlin.gradle.utils.*
import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget.* import org.jetbrains.kotlin.konan.target.KonanTarget.*
import org.jetbrains.kotlin.konan.target.presetName import org.jetbrains.kotlin.konan.target.presetName
import org.jetbrains.kotlin.statistics.metrics.StringMetrics
class KotlinMultiplatformPlugin( class KotlinMultiplatformPlugin(
private val kotlinPluginVersion: String, private val kotlinPluginVersion: String,
@@ -261,6 +263,8 @@ class KotlinMultiplatformPlugin(
target.compilations.findByName(KotlinCompilation.TEST_COMPILATION_NAME)?.let { testCompilation -> target.compilations.findByName(KotlinCompilation.TEST_COMPILATION_NAME)?.let { testCompilation ->
sourceSets.findByName(testCompilation.defaultSourceSetName)?.dependsOn(test) sourceSets.findByName(testCompilation.defaultSourceSetName)?.dependsOn(test)
} }
KotlinBuildStatsService.getInstance()?.report(StringMetrics.MPP_PLATFORMS, target.targetName)
} }
UnusedSourceSetsChecker.checkSourceSets(project) UnusedSourceSetsChecker.checkSourceSets(project)
@@ -12,11 +12,9 @@ import javax.management.ObjectName
import javax.management.StandardMBean import javax.management.StandardMBean
interface IDaemonReuseCounterMXBean { interface IDaemonReuseCounterMXBean {
fun getOrdinal(): Long fun getOrdinal(): Long
fun incrementAndGetOrdinal(): Long fun incrementAndGetOrdinal(): Long
} }
/** /**
@@ -7,9 +7,9 @@ package org.jetbrains.kotlin.gradle.plugin.statistics
import org.gradle.BuildAdapter import org.gradle.BuildAdapter
import org.gradle.BuildResult import org.gradle.BuildResult
import org.gradle.api.artifacts.DependencySet
import org.gradle.api.invocation.Gradle import org.gradle.api.invocation.Gradle
import org.gradle.api.logging.Logging import org.gradle.api.logging.Logging
import org.gradle.initialization.BuildCompletionListener
import org.gradle.initialization.BuildRequestMetaData import org.gradle.initialization.BuildRequestMetaData
import org.gradle.invocation.DefaultGradle import org.gradle.invocation.DefaultGradle
import org.jetbrains.kotlin.statistics.BuildSessionLogger import org.jetbrains.kotlin.statistics.BuildSessionLogger
@@ -20,6 +20,7 @@ import java.lang.management.ManagementFactory
import javax.management.MBeanServer import javax.management.MBeanServer
import javax.management.ObjectName import javax.management.ObjectName
import javax.management.StandardMBean import javax.management.StandardMBean
import kotlin.system.measureTimeMillis
/** /**
* Interface for populating statistics collection method via JXM interface * Interface for populating statistics collection method via JXM interface
@@ -27,21 +28,16 @@ import javax.management.StandardMBean
* of Kotlin Plugin and other classloaders * of Kotlin Plugin and other classloaders
*/ */
interface KotlinBuildStatsMXBean { interface KotlinBuildStatsMXBean {
fun reportBoolean(name: String, value: Boolean, subprojectName: String?) fun reportBoolean(name: String, value: Boolean, subprojectName: String?)
fun reportNumber(name: String, value: Long, subprojectName: String?) fun reportNumber(name: String, value: Long, subprojectName: String?)
fun reportString(name: String, value: String, subprojectName: String?) fun reportString(name: String, value: String, subprojectName: String?)
} }
internal abstract class KotlinBuildStatsService internal constructor() : BuildAdapter(), IStatisticsValuesConsumer, internal abstract class KotlinBuildStatsService internal constructor() : BuildAdapter(), IStatisticsValuesConsumer {
BuildCompletionListener {
companion object { companion object {
// Do not rename this bean otherwise compatibility with the older Kotlin Gradle Plugins would be lost // 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" const val JMX_BEAN_NAME = "org.jetbrains.kotlin.gradle.plugin.statistics:type=StatsService"
@@ -54,7 +50,6 @@ internal abstract class KotlinBuildStatsService internal constructor() : BuildAd
// "emergency file" collecting statistics is disabled it the file exists // "emergency file" collecting statistics is disabled it the file exists
const val DISABLE_STATISTICS_FILE_NAME = "${STATISTICS_FOLDER_NAME}/.disable" const val DISABLE_STATISTICS_FILE_NAME = "${STATISTICS_FOLDER_NAME}/.disable"
/** /**
* Method for getting IStatisticsValuesConsumer for reporting some statistics * Method for getting IStatisticsValuesConsumer for reporting some statistics
* Could be invoked during any build phase after applying first Kotlin plugin and * Could be invoked during any build phase after applying first Kotlin plugin and
@@ -145,16 +140,11 @@ internal abstract class KotlinBuildStatsService internal constructor() : BuildAd
} }
} }
} }
} }
internal class JMXKotlinBuildStatsService(private val mbs: MBeanServer, private val beanName: ObjectName) : internal class JMXKotlinBuildStatsService(private val mbs: MBeanServer, private val beanName: ObjectName) :
KotlinBuildStatsService() { KotlinBuildStatsService() {
override fun buildFinished(result: BuildResult) {
}
private fun callJmx(method: String, type: String, metricName: String, value: Any, subprojectName: String?) { private fun callJmx(method: String, type: String, metricName: String, value: Any, subprojectName: String?) {
mbs.invoke( mbs.invoke(
beanName, beanName,
@@ -182,50 +172,89 @@ internal class JMXKotlinBuildStatsService(private val mbs: MBeanServer, private
} }
} }
override fun completed() { override fun buildFinished(result: BuildResult) {
instance = null instance = null
} }
} }
internal class DefaultKotlinBuildStatsService internal constructor( internal class DefaultKotlinBuildStatsService internal constructor(
gradle: Gradle, gradle: Gradle,
val beanName: ObjectName val beanName: ObjectName
) : KotlinBuildStatsService(), KotlinBuildStatsMXBean { ) : KotlinBuildStatsService(), KotlinBuildStatsMXBean {
private val sessionLogger = BuildSessionLogger(gradle.gradleUserHomeDir) private val sessionLogger = BuildSessionLogger(gradle.gradleUserHomeDir)
private fun gradleBuildStartTime(gradle: Gradle): Long? { private fun gradleBuildStartTime(gradle: Gradle): Long? {
return (gradle as? DefaultGradle)?.services?.get(BuildRequestMetaData::class.java)?.startTime return (gradle as? DefaultGradle)?.services?.get(BuildRequestMetaData::class.java)?.startTime
} }
private fun reportLibrariesVersions(dependencies: DependencySet?) {
dependencies?.forEach { dependency ->
when {
dependency.group?.startsWith("org.springframework") ?: false -> sessionLogger.report(
StringMetrics.LIBRARY_SPRING_VERSION,
dependency.version ?: "0.0.0"
)
dependency.group?.startsWith("com.vaadin") ?: false -> sessionLogger.report(
StringMetrics.LIBRARY_VAADIN_VERSION,
dependency.version ?: "0.0.0"
)
dependency.group?.startsWith("com.google.gwt") ?: false -> sessionLogger.report(
StringMetrics.LIBRARY_GWT_VERSION,
dependency.version ?: "0.0.0"
)
dependency.group?.startsWith("org.hibernate") ?: false -> sessionLogger.report(
StringMetrics.LIBRARY_HIBERNATE_VERSION,
dependency.version ?: "0.0.0"
)
}
}
}
private fun reportGlobalMetrics(gradle: Gradle) { private fun reportGlobalMetrics(gradle: Gradle) {
System.getProperty("os.name")?.also { System.getProperty("os.name")?.also {
sessionLogger.report(StringMetrics.OS_TYPE, gradle.gradleVersion) sessionLogger.report(StringMetrics.OS_TYPE, System.getProperty("os.name"))
} }
sessionLogger.report(NumericalMetrics.CPU_NUMBER_OF_CORES, Runtime.getRuntime().availableProcessors().toLong()) sessionLogger.report(NumericalMetrics.CPU_NUMBER_OF_CORES, Runtime.getRuntime().availableProcessors().toLong())
sessionLogger.report(StringMetrics.GRADLE_VERSION, gradle.gradleVersion) sessionLogger.report(StringMetrics.GRADLE_VERSION, gradle.gradleVersion)
sessionLogger.report(BooleanMetrics.EXECUTED_FROM_IDEA, System.getProperty("idea.active") != null) sessionLogger.report(BooleanMetrics.EXECUTED_FROM_IDEA, System.getProperty("idea.active") != null)
val statisticOverhead = measureTimeMillis {
gradle.allprojects { project ->
for (configuration in project.configurations) {
val configurationName = configuration.name
val dependencies = configuration.dependencies
gradle.allprojects { project -> when (configurationName) {
for (configuration in project.configurations) { "kapt" -> {
val configurationName = configuration.name sessionLogger.report(BooleanMetrics.ENABLED_KAPT, true)
val dependencies = configuration.dependencies dependencies?.forEach { dependency ->
when (dependency.group) {
when (configurationName) { "com.google.dagger" -> sessionLogger.report(BooleanMetrics.ENABLED_DAGGER, true)
"kapt" -> { "com.android.databinding" -> sessionLogger.report(BooleanMetrics.ENABLED_DATABINDING, true)
sessionLogger.report(BooleanMetrics.ENABLED_KAPT, true) }
dependencies.forEach { dependency ->
when (dependency.group) {
"com.google.dagger" -> sessionLogger.report(BooleanMetrics.ENABLED_DAGGER, true)
"com.android.databinding" -> sessionLogger.report(BooleanMetrics.ENABLED_DATABINDING, true)
} }
} }
"api" -> {
sessionLogger.report(NumericalMetrics.CONFIGURATION_API_COUNT, 1)
reportLibrariesVersions(dependencies)
}
"implementation" -> {
sessionLogger.report(NumericalMetrics.CONFIGURATION_IMPLEMENTATION_COUNT, 1)
reportLibrariesVersions(dependencies)
}
"compile" -> {
sessionLogger.report(NumericalMetrics.CONFIGURATION_COMPILE_COUNT, 1)
reportLibrariesVersions(dependencies)
}
"runtime" -> {
sessionLogger.report(NumericalMetrics.CONFIGURATION_RUNTIME_COUNT, 1)
reportLibrariesVersions(dependencies)
}
} }
} }
} }
} }
sessionLogger.report(NumericalMetrics.STATISTICS_VISIT_ALL_PROJECTS_OVERHEAD, statisticOverhead)
} }
override fun projectsEvaluated(gradle: Gradle) { override fun projectsEvaluated(gradle: Gradle) {
@@ -243,16 +272,8 @@ internal class DefaultKotlinBuildStatsService internal constructor(
@Synchronized @Synchronized
override fun buildFinished(result: BuildResult) { override fun buildFinished(result: BuildResult) {
runSafe("${DefaultKotlinBuildStatsService::class.java}.buildFinished") { runSafe("${DefaultKotlinBuildStatsService::class.java}.buildFinished") {
sessionLogger.finishBuildSession(result.action, result.failure)
}
}
@Synchronized
override fun completed() {
runSafe("${DefaultKotlinBuildStatsService::class.java}.completed") {
try { try {
sessionLogger.unlockJournalFile() sessionLogger.finishBuildSession(result.action, result.failure)
} finally { } finally {
val mbs: MBeanServer = ManagementFactory.getPlatformMBeanServer() val mbs: MBeanServer = ManagementFactory.getPlatformMBeanServer()
if (mbs.isRegistered(beanName)) { if (mbs.isRegistered(beanName)) {
@@ -25,7 +25,5 @@ class BuildStatServiceTest {
jmxService.report(StringMetrics.KOTLIN_COMPILER_VERSION, "1.2.3") jmxService.report(StringMetrics.KOTLIN_COMPILER_VERSION, "1.2.3")
jmxService.report(NumericalMetrics.NUMBER_OF_SUBPROJECTS, 10) jmxService.report(NumericalMetrics.NUMBER_OF_SUBPROJECTS, 10)
jmxService.report(BooleanMetrics.ENABLED_DATABINDING, true) jmxService.report(BooleanMetrics.ENABLED_DATABINDING, true)
jmxService.completed()
} }
} }
@@ -7,7 +7,7 @@ plugins {
dependencies { dependencies {
compileOnly(kotlinStdlib()) compileOnly(kotlinStdlib())
testCompile(project(":kotlin-test::kotlin-test-junit")) testCompile(project(":kotlin-test:kotlin-test-junit"))
testCompile("junit:junit:4.12") testCompile("junit:junit:4.12")
} }
@@ -19,5 +19,3 @@ sourceSets {
projectTest { projectTest {
workingDir = rootDir workingDir = rootDir
} }
runtimeJar(rewriteDefaultJarDepsToShadedCompiler())
@@ -21,8 +21,9 @@ internal val salt: String by lazy {
fun anonymizeComponentVersion(version: String): String { fun anonymizeComponentVersion(version: String): String {
return version.replace('-', '.') return version.replace('-', '.')
.split(".") .split(".")
.filterIndexed { i, _ -> i < 3 } .plus(listOf("0", "0")) // pad with zeros
.map { s -> s.toIntOrNull() ?: "?" } .take(3)
.map { s -> s.toIntOrNull() ?: "0" }
.joinToString(".") .joinToString(".")
} }
@@ -6,7 +6,5 @@
package org.jetbrains.kotlin.statistics package org.jetbrains.kotlin.statistics
class BuildSession(val buildStartedTime: Long?) { class BuildSession(val buildStartedTime: Long?) {
val projectEvaluatedTime = System.currentTimeMillis() val projectEvaluatedTime = System.currentTimeMillis()
}
}
@@ -16,17 +16,23 @@ import java.time.LocalDateTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
class BuildSessionLogger( class BuildSessionLogger(
private val rootPath: File, rootPath: File,
private val maxProfileFiles: Int = DEFAULT_MAX_PROFILE_FILES, private val maxProfileFiles: Int = DEFAULT_MAX_PROFILE_FILES,
private val maxFileSize: Long = DEFAULT_MAX_PROFILE_FILE_SIZE private val maxFileSize: Long = DEFAULT_MAX_PROFILE_FILE_SIZE,
private val maxFileAge: Long = DEFAULT_MAX_FILE_AGE
) : IStatisticsValuesConsumer { ) : IStatisticsValuesConsumer {
companion object { companion object {
const val STATISTICS_FOLDER_NAME = "kotlin-profile" const val STATISTICS_FOLDER_NAME = "kotlin-profile"
const val STATISTICS_FILE_NAME_PATTERN = "\\d{4}-\\d{2}-\\d{2}-\\d{2}-\\d{2}-\\d{2}-\\d{3}.profile" internal const val STATISTICS_FILE_NAME_PATTERN = "\\d{4}-\\d{2}-\\d{2}-\\d{2}-\\d{2}-\\d{2}-\\d{3}.profile"
private const val DEFAULT_MAX_PROFILE_FILES = 1_000 private const val DEFAULT_MAX_PROFILE_FILES = 1_000
private const val DEFAULT_MAX_PROFILE_FILE_SIZE = 100_000L private const val DEFAULT_MAX_PROFILE_FILE_SIZE = 100_000L
private const val DEFAULT_MAX_FILE_AGE = 30 * 24 * 3600 * 1000L //30 days
fun listProfileFiles(statisticsFolder: File): List<File>? {
return statisticsFolder.listFiles()?.filterTo(ArrayList()) { it.name.matches(STATISTICS_FILE_NAME_PATTERN.toRegex()) }?.sorted()
}
} }
private val profileFileNameFormatter = DateTimeFormatter.ofPattern("YYYY-MM-dd-HH-mm-ss-SSS'.profile'") private val profileFileNameFormatter = DateTimeFormatter.ofPattern("YYYY-MM-dd-HH-mm-ss-SSS'.profile'")
@@ -53,29 +59,44 @@ class BuildSessionLogger(
@Synchronized @Synchronized
private fun closeTrackingFile() { private fun closeTrackingFile() {
metricsContainer.flush(trackingFile) trackingFile?.let {
trackingFile?.close() metricsContainer.flush(it)
trackingFile = null it.close()
trackingFile = null
}
} }
/**
* Initializes a new tracking file
* The following contracts are implemented:
* - number of tracking files should not be more than maxProfileFiles (the earlier file created the earlier deleted)
* - files with age (current time - last modified) more than maxFileAge should be deleted (if we trust lastModified returned by FS)
* - files are ordered on the basis of name (creation timestamp)
* - if the last file has size less then maxFileSize, the next record will be append to it (new file created otherwise)
* -
*/
@Synchronized @Synchronized
private fun initTrackingFile() { private fun initTrackingFile() {
closeTrackingFile() closeTrackingFile()
// Get list of existing files. Try to create folder if possible, return from function if failed to create folder // Get list of existing files. Try to create folder if possible, return from function if failed to create folder
val fileCandidates = val fileCandidates = listProfileFiles(statisticsFolder) ?: if (statisticsFolder.mkdirs()) emptyList<File>() else return
statisticsFolder.listFiles()?.filter { it.name.matches(STATISTICS_FILE_NAME_PATTERN.toRegex()) }?.toMutableList()
?: if (statisticsFolder.mkdirs()) emptyList<File>() else return
for (i in 0 until fileCandidates.size - maxProfileFiles) { for ((index, file) in fileCandidates.withIndex()) {
val file2delete = fileCandidates[i] val toDelete = if (index < fileCandidates.size - maxProfileFiles)
if (file2delete.isFile) { true
file2delete.delete() else {
val lastModified = file.lastModified()
(lastModified > 0) && (System.currentTimeMillis() - maxFileAge > lastModified)
}
if (toDelete) {
file.delete()
} }
} }
// emergency check. What if a lot of files are locked due to some reason // emergency check. What if a lot of files are locked due to some reason
if (statisticsFolder.listFiles()?.size ?: 0 > maxProfileFiles * 2) { if (listProfileFiles(statisticsFolder)?.size ?: 0 > maxProfileFiles * 2) {
trackingFile = NullRecordLogger()
return return
} }
@@ -99,20 +120,24 @@ class BuildSessionLogger(
@Synchronized @Synchronized
fun finishBuildSession(action: String?, failure: Throwable?) { fun finishBuildSession(action: String?, failure: Throwable?) {
// nanotime could not be used as build start time in nanotime is unknown. As result, the measured duration try {
// could be affected by system clock correction // nanotime could not be used as build start time in nanotime is unknown. As result, the measured duration
val finishTime = System.currentTimeMillis() // could be affected by system clock correction
buildSession?.also { val finishTime = System.currentTimeMillis()
if (it.buildStartedTime != null) { buildSession?.also {
report(NumericalMetrics.GRADLE_BUILD_DURATION, finishTime - it.buildStartedTime) if (it.buildStartedTime != null) {
report(NumericalMetrics.GRADLE_BUILD_DURATION, finishTime - it.buildStartedTime)
}
report(NumericalMetrics.GRADLE_EXECUTION_DURATION, finishTime - it.projectEvaluatedTime)
} }
report(NumericalMetrics.GRADLE_EXECUTION_DURATION, finishTime - it.projectEvaluatedTime) buildSession = null
} finally {
unlockJournalFile()
} }
buildSession = null
} }
@Synchronized @Synchronized
fun unlockJournalFile() { private fun unlockJournalFile() {
closeTrackingFile() closeTrackingFile()
} }
@@ -15,7 +15,6 @@ import java.nio.file.Paths
import java.nio.file.StandardOpenOption import java.nio.file.StandardOpenOption
class FileRecordLogger(file: File) : IRecordLogger { class FileRecordLogger(file: File) : IRecordLogger {
private val channel: FileChannel = private val channel: FileChannel =
FileChannel.open(Paths.get(file.toURI()), StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.APPEND) FileChannel.open(Paths.get(file.toURI()), StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.APPEND)
?: throw IOException("Could not open file $file") ?: throw IOException("Could not open file $file")
@@ -8,7 +8,5 @@ package org.jetbrains.kotlin.statistics.fileloggers
import java.io.Closeable import java.io.Closeable
interface IRecordLogger : Closeable { interface IRecordLogger : Closeable {
fun append(s: String) fun append(s: String)
}
}
@@ -15,11 +15,11 @@ import java.nio.file.StandardOpenOption
import java.util.* import java.util.*
class MetricsContainer : IStatisticsValuesConsumer { class MetricsContainer : IStatisticsValuesConsumer {
data class MetricDescriptor(val name: String, val projectHash: String?) : Comparable<MetricDescriptor> { data class MetricDescriptor(val name: String, val projectHash: String?) : Comparable<MetricDescriptor> {
override fun compareTo(other: MetricDescriptor): Int { override fun compareTo(other: MetricDescriptor): Int {
val compareNames = name.compareTo(other.name)
return when { return when {
name.compareTo(other.name) != 0 -> name.compareTo(other.name) compareNames != 0 -> compareNames
projectHash == other.projectHash -> 0 projectHash == other.projectHash -> 0
else -> (projectHash ?: "").compareTo(other.projectHash ?: "") else -> (projectHash ?: "").compareTo(other.projectHash ?: "")
} }
@@ -33,16 +33,15 @@ class MetricsContainer : IStatisticsValuesConsumer {
private val stringMetrics = TreeMap<MetricDescriptor, IMetricContainer<String>>() private val stringMetrics = TreeMap<MetricDescriptor, IMetricContainer<String>>()
companion object { companion object {
private const val BUILD_SESSION_SEPARATOR = "BUILD FINISHED" private const val BUILD_SESSION_SEPARATOR = "BUILD FINISHED"
val ENCODING = Charsets.UTF_8 val ENCODING = Charsets.UTF_8
private val stringMetricsMap = StringMetrics.values().map { m -> m.name to m }.toMap() private val stringMetricsMap = StringMetrics.values().associateBy(StringMetrics::name)
private val booleanMetricsMap = BooleanMetrics.values().map { m -> m.name to m }.toMap() private val booleanMetricsMap = BooleanMetrics.values().associateBy(BooleanMetrics::name)
private val numericalMetricsMap = NumericalMetrics.values().map { m -> m.name to m }.toMap() private val numericalMetricsMap = NumericalMetrics.values().associateBy(NumericalMetrics::name)
fun readFromFile(file: File, consumer: (MetricsContainer) -> Unit) { fun readFromFile(file: File, consumer: (MetricsContainer) -> Unit) {
val channel = FileChannel.open(Paths.get(file.toURI()), StandardOpenOption.WRITE, StandardOpenOption.READ) val channel = FileChannel.open(Paths.get(file.toURI()), StandardOpenOption.WRITE, StandardOpenOption.READ)
@@ -13,7 +13,6 @@ interface IMetricContainer<T> {
fun toStringRepresentation(): String fun toStringRepresentation(): String
fun getValue(): T? fun getValue(): T?
} }
interface IMetricContainerFactory<T> { interface IMetricContainerFactory<T> {
@@ -23,7 +22,6 @@ interface IMetricContainerFactory<T> {
} }
open class OverrideMetricContainer<T>() : IMetricContainer<T> { open class OverrideMetricContainer<T>() : IMetricContainer<T> {
internal var myValue: T? = null internal var myValue: T? = null
override fun addValue(t: T) { override fun addValue(t: T) {
@@ -41,8 +39,19 @@ open class OverrideMetricContainer<T>() : IMetricContainer<T> {
override fun getValue() = myValue override fun getValue() = myValue
} }
class SumMetricContainer() : OverrideMetricContainer<Long>() { class OverrideVersionMetricContainer() : OverrideMetricContainer<String>() {
constructor(v: String) : this() {
myValue = v
}
override fun addValue(t: String) {
if (myValue == null || myValue == "0.0.0") {
myValue = t
}
}
}
class SumMetricContainer() : OverrideMetricContainer<Long>() {
constructor(v: Long) : this() { constructor(v: Long) : this() {
myValue = v myValue = v
} }
@@ -85,7 +94,6 @@ class OrMetricContainer() : OverrideMetricContainer<Boolean>() {
} }
class ConcatMetricContainer() : IMetricContainer<String> { class ConcatMetricContainer() : IMetricContainer<String> {
private val myValues = TreeSet<String>() private val myValues = TreeSet<String>()
companion object { companion object {
@@ -101,9 +109,8 @@ class ConcatMetricContainer() : IMetricContainer<String> {
} }
override fun toStringRepresentation(): String { override fun toStringRepresentation(): String {
return myValues.joinToString(SEPARATOR) return myValues.sorted().joinToString(SEPARATOR)
} }
override fun getValue() = toStringRepresentation() override fun getValue() = toStringRepresentation()
} }
@@ -16,6 +16,11 @@ enum class StringOverridePolicy: IMetricContainerFactory<String> {
override fun fromStringRepresentation(state: String): IMetricContainer<String>? = OverrideMetricContainer(state) override fun fromStringRepresentation(state: String): IMetricContainer<String>? = OverrideMetricContainer(state)
}, },
OVERRIDE_VERSION_IF_NOT_SET {
override fun newMetricContainer(): IMetricContainer<String> = OverrideVersionMetricContainer()
override fun fromStringRepresentation(state: String): IMetricContainer<String>? = OverrideVersionMetricContainer(state)
},
CONCAT { CONCAT {
override fun newMetricContainer(): IMetricContainer<String> = ConcatMetricContainer() override fun newMetricContainer(): IMetricContainer<String> = ConcatMetricContainer()
@@ -47,6 +47,8 @@ enum class NumericalMetrics(val type: NumberOverridePolicy, val anonymization: N
NUMBER_OF_SUBPROJECTS(OVERRIDE, RANDOM_10_PERCENT), NUMBER_OF_SUBPROJECTS(OVERRIDE, RANDOM_10_PERCENT),
STATISTICS_VISIT_ALL_PROJECTS_OVERHEAD(SUM, RANDOM_10_PERCENT),
// User scenarios // User scenarios
// this value is not reported, only time intervals from the previous build are used // this value is not reported, only time intervals from the previous build are used
@@ -18,11 +18,9 @@ interface AdditiveStatisticsValue<T> : ReportStatisticsValue<T> {
} }
interface IStatisticsValuesConsumer { interface IStatisticsValuesConsumer {
fun report(metric: BooleanMetrics, value: Boolean, subprojectName: String? = null) fun report(metric: BooleanMetrics, value: Boolean, subprojectName: String? = null)
fun report(metric: NumericalMetrics, value: Long, subprojectName: String? = null) fun report(metric: NumericalMetrics, value: Long, subprojectName: String? = null)
fun report(metric: StringMetrics, value: String, subprojectName: String? = null) fun report(metric: StringMetrics, value: String, subprojectName: String? = null)
}
}
@@ -23,10 +23,10 @@ enum class StringMetrics(val type: StringOverridePolicy, val anonymization: Stri
MPP_PLATFORMS(CONCAT, SAFE), MPP_PLATFORMS(CONCAT, SAFE),
// Component versions // Component versions
LIBRARY_SPRING_VERSION(OVERRIDE, COMPONENT_VERSION), LIBRARY_SPRING_VERSION(OVERRIDE_VERSION_IF_NOT_SET, COMPONENT_VERSION),
LIBRARY_VAADIN_VERSION(OVERRIDE, COMPONENT_VERSION), LIBRARY_VAADIN_VERSION(OVERRIDE_VERSION_IF_NOT_SET, COMPONENT_VERSION),
LIBRARY_GWT_VERSION(OVERRIDE, COMPONENT_VERSION), LIBRARY_GWT_VERSION(OVERRIDE_VERSION_IF_NOT_SET, COMPONENT_VERSION),
LIBRARY_HYBERNATE_VERSION(OVERRIDE, COMPONENT_VERSION), LIBRARY_HIBERNATE_VERSION(OVERRIDE_VERSION_IF_NOT_SET, COMPONENT_VERSION),
KOTLIN_COMPILER_VERSION(OVERRIDE, COMPONENT_VERSION), KOTLIN_COMPILER_VERSION(OVERRIDE, COMPONENT_VERSION),
KOTLIN_STDLIB_VERSION(OVERRIDE, COMPONENT_VERSION), KOTLIN_STDLIB_VERSION(OVERRIDE, COMPONENT_VERSION),
@@ -39,9 +39,4 @@ enum class StringMetrics(val type: StringOverridePolicy, val anonymization: Stri
// Features // Features
KOTLIN_LANGUAGE_VERSION(OVERRIDE, COMPONENT_VERSION), KOTLIN_LANGUAGE_VERSION(OVERRIDE, COMPONENT_VERSION),
KOTLIN_API_VERSION(OVERRIDE, COMPONENT_VERSION), KOTLIN_API_VERSION(OVERRIDE, COMPONENT_VERSION),
} }
@@ -5,25 +5,21 @@
package org.jetbrains.kotlin.statistics package org.jetbrains.kotlin.statistics
import com.sun.javafx.font.Metrics
import org.jetbrains.kotlin.statistics.BuildSessionLogger.Companion.STATISTICS_FILE_NAME_PATTERN import org.jetbrains.kotlin.statistics.BuildSessionLogger.Companion.STATISTICS_FILE_NAME_PATTERN
import org.jetbrains.kotlin.statistics.BuildSessionLogger.Companion.listProfileFiles
import org.jetbrains.kotlin.statistics.fileloggers.MetricsContainer import org.jetbrains.kotlin.statistics.fileloggers.MetricsContainer
import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics
import org.jetbrains.kotlin.statistics.metrics.NumericalMetrics import org.jetbrains.kotlin.statistics.metrics.NumericalMetrics
import org.jetbrains.kotlin.statistics.metrics.OverrideMetricContainer
import org.jetbrains.kotlin.statistics.metrics.StringMetrics import org.jetbrains.kotlin.statistics.metrics.StringMetrics
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import java.io.File import java.io.File
import java.lang.IllegalStateException import java.lang.IllegalStateException
import kotlin.test.Test import kotlin.test.*
import kotlin.test.assertEquals
import kotlin.test.assertTrue
import kotlin.test.fail
class BuildSessionLoggerTest { class BuildSessionLoggerTest {
lateinit var rootFolder: File private lateinit var rootFolder: File
private fun statFilesCount() = rootFolder.listFiles().single().listFiles().size private fun statFilesCount() = rootFolder.listFiles().single().listFiles().size
@@ -74,7 +70,6 @@ class BuildSessionLoggerTest {
val logger = BuildSessionLogger(rootFolder, 100, maxFileSize.toLong()) val logger = BuildSessionLogger(rootFolder, 100, maxFileSize.toLong())
logger.startBuildSession(0, null) logger.startBuildSession(0, null)
logger.finishBuildSession("", null) logger.finishBuildSession("", null)
logger.unlockJournalFile()
assertEquals(1, statFilesCount()) assertEquals(1, statFilesCount())
@@ -82,7 +77,6 @@ class BuildSessionLoggerTest {
logger.startBuildSession(0, 0) logger.startBuildSession(0, 0)
logger.finishBuildSession("", null) logger.finishBuildSession("", null)
logger.unlockJournalFile()
//new file should not be created //new file should not be created
assertEquals(1, statFilesCount()) assertEquals(1, statFilesCount())
@@ -90,7 +84,6 @@ class BuildSessionLoggerTest {
logger.startBuildSession(0, 0) logger.startBuildSession(0, 0)
logger.finishBuildSession("", null) logger.finishBuildSession("", null)
logger.unlockJournalFile()
// a new file should be created as maximal file size // a new file should be created as maximal file size
assertEquals(2, statFilesCount()) assertEquals(2, statFilesCount())
@@ -102,7 +95,6 @@ class BuildSessionLoggerTest {
val logger = BuildSessionLogger(rootFolder, maxFiles) val logger = BuildSessionLogger(rootFolder, maxFiles)
logger.startBuildSession(0, null) logger.startBuildSession(0, null)
logger.finishBuildSession("", null) logger.finishBuildSession("", null)
logger.unlockJournalFile()
assertEquals(1, statFilesCount()) assertEquals(1, statFilesCount())
val statsFolder = rootFolder.listFiles().single() val statsFolder = rootFolder.listFiles().single()
@@ -116,7 +108,6 @@ class BuildSessionLoggerTest {
logger.startBuildSession(0, 0) logger.startBuildSession(0, 0)
logger.finishBuildSession("", null) logger.finishBuildSession("", null)
logger.unlockJournalFile()
assertTrue( assertTrue(
statsFolder.listFiles().filter { it.name == singleStatFile.name }.count() == 1, statsFolder.listFiles().filter { it.name == singleStatFile.name }.count() == 1,
@@ -135,6 +126,10 @@ class BuildSessionLoggerTest {
statsFolder.listFiles().filter { it.name.matches(STATISTICS_FILE_NAME_PATTERN.toRegex()) }.count(), statsFolder.listFiles().filter { it.name.matches(STATISTICS_FILE_NAME_PATTERN.toRegex()) }.count(),
"Some files which should not be affected, were removed" "Some files which should not be affected, were removed"
) )
assertEquals(
statsFolder.listFiles().filter { it.name.matches(STATISTICS_FILE_NAME_PATTERN.toRegex()) }.sorted(),
listProfileFiles(statsFolder)
)
} }
@Test @Test
@@ -142,10 +137,9 @@ class BuildSessionLoggerTest {
val logger = BuildSessionLogger(rootFolder) val logger = BuildSessionLogger(rootFolder)
logger.report(StringMetrics.KOTLIN_COMPILER_VERSION, "1.2.3.4-snapshot") logger.report(StringMetrics.KOTLIN_COMPILER_VERSION, "1.2.3.4-snapshot")
val startTime = System.currentTimeMillis() - 1000 val startTime = System.currentTimeMillis() - 1001
logger.startBuildSession(1, startTime) logger.startBuildSession(1, startTime)
logger.finishBuildSession("Build", null) logger.finishBuildSession("Build", null)
logger.unlockJournalFile()
assertEquals(1, statFilesCount()) assertEquals(1, statFilesCount())
val statFile = rootFolder.listFiles().single().listFiles().single() val statFile = rootFolder.listFiles().single().listFiles().single()
@@ -165,7 +159,6 @@ class BuildSessionLoggerTest {
logger.finishBuildSession("", null) logger.finishBuildSession("", null)
logger.unlockJournalFile()
val metrics = ArrayList<MetricsContainer>() val metrics = ArrayList<MetricsContainer>()
MetricsContainer.readFromFile(statFile) { MetricsContainer.readFromFile(statFile) {
@@ -186,4 +179,33 @@ class BuildSessionLoggerTest {
val buildDuration = metrics[0].getMetric(NumericalMetrics.GRADLE_BUILD_DURATION)?.getValue() ?: 0L 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") 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)
for (metric in StringMetrics.values()) {
logger.report(metric, "value")
logger.report(metric, metric.name)
}
for (metric in BooleanMetrics.values()) {
logger.report(metric, true)
}
for (metric in NumericalMetrics.values()) {
logger.report(metric, System.currentTimeMillis())
}
logger.finishBuildSession("Build", null)
MetricsContainer.readFromFile(rootFolder.listFiles().single().listFiles().single()) {
for (metric in StringMetrics.values()) {
assertNotNull(it.getMetric(metric), "Could not find metric ${metric.name}")
}
for (metric in BooleanMetrics.values()) {
assertTrue(it.getMetric(metric)?.getValue() ?: false, "Could not find metric ${metric.name}")
}
for (metric in NumericalMetrics.values()) {
assertNotNull(it.getMetric(metric), "Could not find metric ${metric.name}")
}
}
}
} }
+1
View File
@@ -67,6 +67,7 @@ val projectsToShadow by extra(listOf(
":js:js.translator", ":js:js.translator",
":kotlin-native:kotlin-native-utils", ":kotlin-native:kotlin-native-utils",
":kotlin-native:kotlin-native-library-reader", ":kotlin-native:kotlin-native-library-reader",
":kotlin-gradle-statistics",
":compiler:light-classes", ":compiler:light-classes",
":compiler:plugin-api", ":compiler:plugin-api",
":kotlin-preloader", ":kotlin-preloader",