Move kotlin-build-statistic project to :compiler

This commit is contained in:
Nataliya.Valtman
2023-04-18 03:49:29 +02:00
committed by Space Team
parent e34dd043da
commit 2a391f7330
53 changed files with 166 additions and 249 deletions
@@ -0,0 +1,28 @@
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion as GradleKotlinVersion
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
description = "Kotlin Build Report Common"
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
compileOnly(project(":core:util.runtime"))
compileOnly(project(":compiler:util"))
compileOnly(project(":kotlin-util-io"))
compileOnly(commonDependency("org.jetbrains.kotlin:kotlin-reflect")) { isTransitive = false }
compileOnly(kotlinStdlib())
compileOnly(intellijCore())
implementation(commonDependency("com.google.code.gson:gson"))
testApi(kotlinStdlib())
}
sourceSets {
"main" { projectDefault() }
"test" { projectDefault() }
}
projectTest(jUnitMode = JUnitMode.JUnit5, parallel = true)
@@ -0,0 +1,44 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.build.report.metrics
import java.io.Serializable
enum class BuildAttributeKind : Serializable {
REBUILD_REASON;
companion object {
const val serialVersionUID = 0L
}
}
enum class BuildAttribute(val kind: BuildAttributeKind, val readableString: String) : Serializable {
NO_BUILD_HISTORY(BuildAttributeKind.REBUILD_REASON, "Build history file not found"),
NO_ABI_SNAPSHOT(BuildAttributeKind.REBUILD_REASON, "ABI snapshot not found"),
NO_LAST_BUILD_INFO(BuildAttributeKind.REBUILD_REASON, "Last build info not found"),
INVALID_LAST_BUILD_INFO(BuildAttributeKind.REBUILD_REASON, "Last build info corrupted"),
CLASSPATH_SNAPSHOT_NOT_FOUND(BuildAttributeKind.REBUILD_REASON, "Classpath snapshot not found"),
IC_FAILED_TO_GET_CHANGED_FILES(BuildAttributeKind.REBUILD_REASON, "Failed to get changed files"),
IC_FAILED_TO_COMPUTE_FILES_TO_RECOMPILE(BuildAttributeKind.REBUILD_REASON, "Failed to compute files to recompile"),
IC_FAILED_TO_COMPILE_INCREMENTALLY(BuildAttributeKind.REBUILD_REASON, "Failed to compile incrementally"),
IC_FAILED_TO_CLOSE_CACHES(BuildAttributeKind.REBUILD_REASON, "Failed to close caches"),
UNKNOWN_CHANGES_IN_GRADLE_INPUTS(BuildAttributeKind.REBUILD_REASON, "Unknown Gradle changes"),
JAVA_CHANGE_UNTRACKED_FILE_IS_REMOVED(BuildAttributeKind.REBUILD_REASON, "Untracked Java file is removed"),
JAVA_CHANGE_UNEXPECTED_PSI(BuildAttributeKind.REBUILD_REASON, "Java PSI file is expected"),
JAVA_CHANGE_UNKNOWN_QUALIFIER(BuildAttributeKind.REBUILD_REASON, "Unknown Java qualifier name"),
DEP_CHANGE_REMOVED_ENTRY(BuildAttributeKind.REBUILD_REASON, "Jar file is removed form dependency"),
DEP_CHANGE_HISTORY_IS_NOT_FOUND(BuildAttributeKind.REBUILD_REASON, "Dependency history not found"),
DEP_CHANGE_HISTORY_CANNOT_BE_READ(BuildAttributeKind.REBUILD_REASON, "Dependency history can not be read"),
DEP_CHANGE_HISTORY_NO_KNOWN_BUILDS(BuildAttributeKind.REBUILD_REASON, "Dependency history id not available"),
DEP_CHANGE_NON_INCREMENTAL_BUILD_IN_DEP(BuildAttributeKind.REBUILD_REASON, "Non incremental build in history"),
IN_PROCESS_EXECUTION(BuildAttributeKind.REBUILD_REASON, "In-process execution"),
OUT_OF_PROCESS_EXECUTION(BuildAttributeKind.REBUILD_REASON, "Out of process execution"),
IC_IS_NOT_ENABLED(BuildAttributeKind.REBUILD_REASON, "Incremental compilation is not enabled");
companion object {
const val serialVersionUID = 0L
}
}
@@ -0,0 +1,30 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.build.report.metrics
import java.io.Serializable
import java.util.*
class BuildAttributes : Serializable {
private val myAttributes =
EnumMap<BuildAttribute, Int>(
BuildAttribute::class.java
)
fun add(attr: BuildAttribute, count: Int = 1) {
myAttributes[attr] = myAttributes.getOrDefault(attr, 0) + count
}
fun addAll(other: BuildAttributes) {
other.myAttributes.forEach { (attr, n) -> add(attr, n) }
}
fun asMap(): Map<BuildAttribute, Int> = myAttributes
companion object {
const val serialVersionUID = 0L
}
}
@@ -0,0 +1,26 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.build.report.metrics
import java.io.Serializable
data class BuildMetrics(
val buildTimes: BuildTimes = BuildTimes(),
val buildPerformanceMetrics: BuildPerformanceMetrics = BuildPerformanceMetrics(),
val buildAttributes: BuildAttributes = BuildAttributes(),
val gcMetrics: GcMetrics = GcMetrics()
) : Serializable {
fun addAll(other: BuildMetrics) {
buildTimes.addAll(other.buildTimes)
buildPerformanceMetrics.addAll(other.buildPerformanceMetrics)
buildAttributes.addAll(other.buildAttributes)
gcMetrics.addAll(other.gcMetrics)
}
companion object {
const val serialVersionUID = 0L
}
}
@@ -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.build.report.metrics
import java.lang.management.ManagementFactory
interface BuildMetricsReporter {
fun startMeasure(time: BuildTime)
fun endMeasure(time: BuildTime)
fun addTimeMetricNs(time: BuildTime, durationNs: Long)
fun addTimeMetricMs(time: BuildTime, durationMs: Long) = addTimeMetricNs(time, durationMs * 1_000_000)
fun addMetric(metric: BuildPerformanceMetric, value: Long)
fun addTimeMetric(metric: BuildPerformanceMetric)
//Change metric to enum if possible
fun addGcMetric(metric: String, value: GcMetric)
fun startGcMetric(name: String, value: GcMetric)
fun endGcMetric(name: String, value: GcMetric)
fun addAttribute(attribute: BuildAttribute)
fun getMetrics(): BuildMetrics
fun addMetrics(metrics: BuildMetrics)
}
inline fun <T> BuildMetricsReporter.measure(time: BuildTime, fn: () -> T): T {
startMeasure(time)
try {
return fn()
} finally {
endMeasure(time)
}
}
fun BuildMetricsReporter.startMeasureGc() {
ManagementFactory.getGarbageCollectorMXBeans().forEach {
startGcMetric(it.name, GcMetric(it.collectionTime, it.collectionCount))
}
}
fun BuildMetricsReporter.endMeasureGc() {
ManagementFactory.getGarbageCollectorMXBeans().forEach {
endGcMetric(it.name, GcMetric(it.collectionTime, it.collectionCount))
}
}
@@ -0,0 +1,88 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.build.report.metrics
import java.io.Serializable
import java.util.*
class BuildMetricsReporterImpl : BuildMetricsReporter, Serializable {
private val myBuildTimeStartNs: EnumMap<BuildTime, Long> =
EnumMap(
BuildTime::class.java
)
private val myGcPerformance = HashMap<String, GcMetric>()
private val myBuildTimes = BuildTimes()
private val myBuildMetrics = BuildPerformanceMetrics()
private val myBuildAttributes = BuildAttributes()
private val myGcMetrics = GcMetrics()
override fun startMeasure(time: BuildTime) {
if (time in myBuildTimeStartNs) {
error("$time was restarted before it finished")
}
myBuildTimeStartNs[time] = System.nanoTime()
}
override fun endMeasure(time: BuildTime) {
val startNs = myBuildTimeStartNs.remove(time) ?: error("$time finished before it started")
val durationNs = System.nanoTime() - startNs
myBuildTimes.addTimeNs(time, durationNs)
}
override fun startGcMetric(name: String, value: GcMetric) {
if (name in myGcPerformance) {
error("$name was restarted before it finished")
}
myGcPerformance[name] = value
}
override fun endGcMetric(name: String, value: GcMetric) {
val startValue = myGcPerformance.remove(name) ?: error("$name finished before it started")
val diff = value - startValue
myGcMetrics.add(name, diff)
}
override fun addTimeMetricNs(time: BuildTime, durationNs: Long) {
myBuildTimes.addTimeNs(time, durationNs)
}
override fun addMetric(metric: BuildPerformanceMetric, value: Long) {
myBuildMetrics.add(metric, value)
}
override fun addTimeMetric(metric: BuildPerformanceMetric) {
when (metric.type) {
ValueType.NANOSECONDS -> myBuildMetrics.add(metric, System.nanoTime())
ValueType.MILLISECONDS -> myBuildMetrics.add(metric, System.currentTimeMillis())
ValueType.TIME -> myBuildMetrics.add(metric, System.currentTimeMillis())
else -> error("Unable to add time metric for '${metric.type}' type")
}
}
override fun addGcMetric(metric: String, value: GcMetric) {
myGcMetrics.add(metric, value)
}
override fun addAttribute(attribute: BuildAttribute) {
myBuildAttributes.add(attribute)
}
override fun getMetrics(): BuildMetrics =
BuildMetrics(
buildTimes = myBuildTimes,
buildPerformanceMetrics = myBuildMetrics,
buildAttributes = myBuildAttributes,
gcMetrics = myGcMetrics
)
override fun addMetrics(metrics: BuildMetrics) {
myBuildAttributes.addAll(metrics.buildAttributes)
myBuildTimes.addAll(metrics.buildTimes)
myBuildMetrics.addAll(metrics.buildPerformanceMetrics)
myGcMetrics.addAll(metrics.gcMetrics)
}
}
@@ -0,0 +1,68 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.build.report.metrics
import java.io.Serializable
@Suppress("Reformat")
enum class BuildPerformanceMetric(val parent: BuildPerformanceMetric? = null, val readableString: String, val type: ValueType) : Serializable {
CACHE_DIRECTORY_SIZE(readableString = "Total size of the cache directory", type = ValueType.BYTES),
LOOKUP_SIZE(CACHE_DIRECTORY_SIZE, "Lookups size", type = ValueType.BYTES),
SNAPSHOT_SIZE(CACHE_DIRECTORY_SIZE, "ABI snapshot size", type = ValueType.BYTES),
BUNDLE_SIZE(readableString = "Total size of the final bundle", type = ValueType.BYTES),
DAEMON_INCREASED_MEMORY(readableString = "Increase memory usage", type = ValueType.BYTES),
DAEMON_MEMORY_USAGE(readableString = "Total memory usage at the end of build", type = ValueType.BYTES),
DAEMON_GC_TIME(readableString = "Time spent in GC", type = ValueType.NANOSECONDS),
DAEMON_GC_COUNT(readableString = "Count of GC", type = ValueType.NUMBER),
COMPILE_ITERATION(parent = null, "Total compiler iteration", type = ValueType.NUMBER),
ANALYZED_LINES_NUMBER(parent = COMPILE_ITERATION, "Number of lines analyzed", type = ValueType.NUMBER),
CODE_GENERATED_LINES_NUMBER(parent = COMPILE_ITERATION, "Number of lines for code generation", type = ValueType.NUMBER),
ANALYSIS_LPS(parent = COMPILE_ITERATION, "Analysis lines per second", type = ValueType.NUMBER),
CODE_GENERATION_LPS(parent = COMPILE_ITERATION, "Code generation lines per second", type = ValueType.NUMBER),
// Metrics for the `kotlin.incremental.useClasspathSnapshot` feature
CLASSPATH_ENTRY_SNAPSHOT_TRANSFORM_EXECUTION_COUNT(parent = null, "Number of times 'ClasspathEntrySnapshotTransform' ran", type = ValueType.NUMBER),
JAR_CLASSPATH_ENTRY_SIZE(parent = CLASSPATH_ENTRY_SNAPSHOT_TRANSFORM_EXECUTION_COUNT, "Size of jar classpath entry", type = ValueType.BYTES),
JAR_CLASSPATH_ENTRY_SNAPSHOT_SIZE(parent = CLASSPATH_ENTRY_SNAPSHOT_TRANSFORM_EXECUTION_COUNT, "Size of jar classpath entry's snapshot", type = ValueType.BYTES),
DIRECTORY_CLASSPATH_ENTRY_SNAPSHOT_SIZE(parent = CLASSPATH_ENTRY_SNAPSHOT_TRANSFORM_EXECUTION_COUNT, "Size of directory classpath entry's snapshot", type = ValueType.BYTES),
COMPUTE_CLASSPATH_CHANGES_EXECUTION_COUNT(parent = null, "Number of times classpath changes are computed", type = ValueType.NUMBER),
SHRINK_AND_SAVE_CLASSPATH_SNAPSHOT_EXECUTION_COUNT(parent = null, "Number of times classpath snapshot is shrunk and saved after compilation", type = ValueType.NUMBER),
CLASSPATH_ENTRY_COUNT(parent = SHRINK_AND_SAVE_CLASSPATH_SNAPSHOT_EXECUTION_COUNT, "Number of classpath entries", type = ValueType.NUMBER),
CLASSPATH_SNAPSHOT_SIZE(parent = SHRINK_AND_SAVE_CLASSPATH_SNAPSHOT_EXECUTION_COUNT, "Size of classpath snapshot", type = ValueType.BYTES),
SHRUNK_CLASSPATH_SNAPSHOT_SIZE(parent = SHRINK_AND_SAVE_CLASSPATH_SNAPSHOT_EXECUTION_COUNT, "Size of shrunk classpath snapshot", type = ValueType.BYTES),
LOAD_CLASSPATH_SNAPSHOT_EXECUTION_COUNT(parent = null, "Number of times classpath snapshot is loaded", type = ValueType.NUMBER),
LOAD_CLASSPATH_ENTRY_SNAPSHOT_CACHE_HITS(parent = LOAD_CLASSPATH_SNAPSHOT_EXECUTION_COUNT, "Number of cache hits when loading classpath entry snapshots", type = ValueType.NUMBER),
LOAD_CLASSPATH_ENTRY_SNAPSHOT_CACHE_MISSES(parent = LOAD_CLASSPATH_SNAPSHOT_EXECUTION_COUNT, "Number of cache misses when loading classpath entry snapshots", type = ValueType.NUMBER),
//time metrics
START_TASK_ACTION_EXECUTION(readableString = "Start time of task action", type = ValueType.TIME),
FINISH_KOTLIN_DAEMON_EXECUTION(readableString = "Finish time of kotlin daemon execution", type = ValueType.TIME),
CALL_KOTLIN_DAEMON(readableString = "Finish gradle part of task execution", type = ValueType.NANOSECONDS),
CALL_WORKER(readableString = "Worker submit time", type = ValueType.NANOSECONDS),
START_WORKER_EXECUTION(readableString = "Start time of worker execution", type = ValueType.NANOSECONDS),
START_KOTLIN_DAEMON_EXECUTION(readableString = "Start time of kotlin daemon task execution", type = ValueType.NANOSECONDS),
;
companion object {
const val serialVersionUID = 0L
val children by lazy {
values().filter { it.parent != null }.groupBy { it.parent }
}
}
}
enum class ValueType {
BYTES,
NUMBER,
NANOSECONDS,
MILLISECONDS,
TIME
}
@@ -0,0 +1,30 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.build.report.metrics
import java.io.Serializable
import java.util.*
class BuildPerformanceMetrics : Serializable {
companion object {
const val serialVersionUID = 0L
}
private val myBuildMetrics = EnumMap<BuildPerformanceMetric, Long>(BuildPerformanceMetric::class.java)
fun addAll(other: BuildPerformanceMetrics) {
for ((bt, timeNs) in other.myBuildMetrics) {
add(bt, timeNs)
}
}
fun add(metric: BuildPerformanceMetric, value: Long = 1) {
myBuildMetrics[metric] = myBuildMetrics.getOrDefault(metric, 0) + value
}
fun asMap(): Map<BuildPerformanceMetric, Long> = myBuildMetrics
}
@@ -0,0 +1,89 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.build.report.metrics
import java.io.Serializable
@Suppress("Reformat")
enum class BuildTime(val parent: BuildTime? = null, val readableString: String) : Serializable {
GRADLE_TASK(readableString = "Total Gradle task time"),
GRADLE_TASK_PREPARATION(readableString = "Spent time before task action"),
GRADLE_TASK_ACTION(readableString = "Task action"),
OUT_OF_WORKER_TASK_ACTION(GRADLE_TASK_ACTION, "Task action before worker execution"),
BACKUP_OUTPUT(OUT_OF_WORKER_TASK_ACTION, "Backup output"),
RUN_WORKER_DELAY(readableString = "Start gradle worker"),
RUN_COMPILATION_IN_WORKER(GRADLE_TASK_ACTION, "Run compilation in Gradle worker"),
CLEAR_JAR_CACHE(RUN_COMPILATION_IN_WORKER, "Clear jar cache"),
CLEAR_OUTPUT(RUN_COMPILATION_IN_WORKER, "Clear output"),
PRECISE_BACKUP_OUTPUT(RUN_COMPILATION_IN_WORKER, "Precise backup output"),
RESTORE_OUTPUT_FROM_BACKUP(RUN_COMPILATION_IN_WORKER, "Restore output"),
CLEAN_BACKUP_STASH(RUN_COMPILATION_IN_WORKER, "Cleaning up the backup stash"),
CONNECT_TO_DAEMON(RUN_COMPILATION_IN_WORKER, "Connect to Kotlin daemon"),
CALCULATE_OUTPUT_SIZE(RUN_COMPILATION_IN_WORKER, "Calculate output size"),
RUN_COMPILATION(RUN_COMPILATION_IN_WORKER, "Run compilation"),
NON_INCREMENTAL_COMPILATION_IN_PROCESS(RUN_COMPILATION, "Non incremental inprocess compilation"),
NON_INCREMENTAL_COMPILATION_OUT_OF_PROCESS(RUN_COMPILATION, "Non incremental out of process compilation"),
NON_INCREMENTAL_COMPILATION_DAEMON(RUN_COMPILATION, "Non incremental compilation in daemon"),
INCREMENTAL_COMPILATION_DAEMON(RUN_COMPILATION, "Incremental compilation in daemon"),
STORE_BUILD_INFO(INCREMENTAL_COMPILATION_DAEMON, "Store build info"),
JAR_SNAPSHOT(INCREMENTAL_COMPILATION_DAEMON, "ABI JAR Snapshot support"),
SET_UP_ABI_SNAPSHOTS(JAR_SNAPSHOT, "Set up ABI snapshot"),
IC_ANALYZE_JAR_FILES(JAR_SNAPSHOT, "Analyze jar files"),
IC_CALCULATE_INITIAL_DIRTY_SET(INCREMENTAL_COMPILATION_DAEMON, "Calculate initial dirty sources set"), //TODO
COMPUTE_CLASSPATH_CHANGES(IC_CALCULATE_INITIAL_DIRTY_SET, "Compute classpath changes"),
LOAD_CURRENT_CLASSPATH_SNAPSHOT(COMPUTE_CLASSPATH_CHANGES, "Load current classpath snapshot"),
REMOVE_DUPLICATE_CLASSES(LOAD_CURRENT_CLASSPATH_SNAPSHOT, "Remove duplicate classes"),
SHRINK_CURRENT_CLASSPATH_SNAPSHOT(COMPUTE_CLASSPATH_CHANGES, "Shrink current classpath snapshot"),
GET_LOOKUP_SYMBOLS(SHRINK_CURRENT_CLASSPATH_SNAPSHOT, "Get lookup symbols"),
FIND_REFERENCED_CLASSES(SHRINK_CURRENT_CLASSPATH_SNAPSHOT, "Find referenced classes"),
FIND_TRANSITIVELY_REFERENCED_CLASSES(SHRINK_CURRENT_CLASSPATH_SNAPSHOT, "Find transitively referenced classes"),
LOAD_SHRUNK_PREVIOUS_CLASSPATH_SNAPSHOT(COMPUTE_CLASSPATH_CHANGES, "Load shrunk previous classpath snapshot"),
COMPUTE_CHANGED_AND_IMPACTED_SET(COMPUTE_CLASSPATH_CHANGES, "Compute changed and impacted set"),
COMPUTE_CLASS_CHANGES(COMPUTE_CHANGED_AND_IMPACTED_SET, "Compute class changes"),
COMPUTE_KOTLIN_CLASS_CHANGES(COMPUTE_CLASS_CHANGES, "Compute Kotlin class changes"),
COMPUTE_JAVA_CLASS_CHANGES(COMPUTE_CLASS_CHANGES, "Compute Java class changes"),
COMPUTE_IMPACTED_SET(COMPUTE_CHANGED_AND_IMPACTED_SET, "Compute impacted set"),
IC_ANALYZE_CHANGES_IN_DEPENDENCIES(IC_CALCULATE_INITIAL_DIRTY_SET, "Analyze dependency changes"),
IC_FIND_HISTORY_FILES(IC_ANALYZE_CHANGES_IN_DEPENDENCIES, "Find history files"),
IC_ANALYZE_HISTORY_FILES(IC_ANALYZE_CHANGES_IN_DEPENDENCIES, "Analyze history files"),
IC_ANALYZE_CHANGES_IN_JAVA_SOURCES(IC_CALCULATE_INITIAL_DIRTY_SET, "Analyze Java file changes"),
IC_ANALYZE_CHANGES_IN_ANDROID_LAYOUTS(IC_CALCULATE_INITIAL_DIRTY_SET, "Analyze Android layouts"),
IC_DETECT_REMOVED_CLASSES(IC_CALCULATE_INITIAL_DIRTY_SET, "Detect removed classes"),
CLEAR_OUTPUT_ON_REBUILD(INCREMENTAL_COMPILATION_DAEMON, "Clear outputs on rebuild"),
IC_UPDATE_CACHES(INCREMENTAL_COMPILATION_DAEMON, "Update caches"),
COMPILATION_ROUND(INCREMENTAL_COMPILATION_DAEMON, "Sources compilation round"),
COMPILER_PERFORMANCE(COMPILATION_ROUND, readableString = "Compiler time"),
COMPILER_INITIALIZATION(COMPILER_PERFORMANCE, "Compiler initialization time"),
CODE_ANALYSIS(COMPILER_PERFORMANCE, "Compiler code analysis"),
CODE_GENERATION(COMPILER_PERFORMANCE, "Compiler code generation"),
IC_WRITE_HISTORY_FILE(INCREMENTAL_COMPILATION_DAEMON, "Write history file"),
SHRINK_AND_SAVE_CURRENT_CLASSPATH_SNAPSHOT_AFTER_COMPILATION(INCREMENTAL_COMPILATION_DAEMON, "Shrink and save current classpath snapshot after compilation"),
INCREMENTAL_SHRINK_CURRENT_CLASSPATH_SNAPSHOT(SHRINK_AND_SAVE_CURRENT_CLASSPATH_SNAPSHOT_AFTER_COMPILATION, "Shrink current classpath snapshot incrementally"),
INCREMENTAL_LOAD_CURRENT_CLASSPATH_SNAPSHOT(INCREMENTAL_SHRINK_CURRENT_CLASSPATH_SNAPSHOT, "Load current classpath snapshot"),
INCREMENTAL_LOAD_SHRUNK_CURRENT_CLASSPATH_SNAPSHOT_AGAINST_PREVIOUS_LOOKUPS(INCREMENTAL_SHRINK_CURRENT_CLASSPATH_SNAPSHOT, "Load shrunk current classpath snapshot against previous lookups"),
NON_INCREMENTAL_SHRINK_CURRENT_CLASSPATH_SNAPSHOT(SHRINK_AND_SAVE_CURRENT_CLASSPATH_SNAPSHOT_AFTER_COMPILATION, "Shrink current classpath snapshot non-incrementally"),
NON_INCREMENTAL_LOAD_CURRENT_CLASSPATH_SNAPSHOT(NON_INCREMENTAL_SHRINK_CURRENT_CLASSPATH_SNAPSHOT, "Load current classpath snapshot"),
SAVE_SHRUNK_CURRENT_CLASSPATH_SNAPSHOT(SHRINK_AND_SAVE_CURRENT_CLASSPATH_SNAPSHOT_AFTER_COMPILATION, "Save shrunk current classpath snapshot"),
TASK_FINISH_LISTENER_NOTIFICATION(readableString = "Task finish event notification"),
CLASSPATH_ENTRY_SNAPSHOT_TRANSFORM(readableString = "Classpath entry snapshot transform"),
LOAD_CLASSES(parent = CLASSPATH_ENTRY_SNAPSHOT_TRANSFORM, "Load classes"),
SNAPSHOT_CLASSES(parent = CLASSPATH_ENTRY_SNAPSHOT_TRANSFORM, "Snapshot classes"),
READ_CLASSES_BASIC_INFO(parent = SNAPSHOT_CLASSES, "Read basic information about classes"),
FIND_INACCESSIBLE_CLASSES(parent = SNAPSHOT_CLASSES, "Find inaccessible classes"),
SNAPSHOT_KOTLIN_CLASSES(parent = SNAPSHOT_CLASSES, "Snapshot Kotlin classes"),
SNAPSHOT_JAVA_CLASSES(parent = SNAPSHOT_CLASSES, "Snapshot Java classes"),
SAVE_CLASSPATH_ENTRY_SNAPSHOT(parent = CLASSPATH_ENTRY_SNAPSHOT_TRANSFORM, "Save classpath entry snapshot"),
;
companion object {
const val serialVersionUID = 0L
val children by lazy {
values().filter { it.parent != null }.groupBy { it.parent }
}
}
}
@@ -0,0 +1,31 @@
/*
* 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.build.report.metrics
import java.io.Serializable
import java.util.*
class BuildTimes : Serializable {
private val buildTimesNs = EnumMap<BuildTime, Long>(BuildTime::class.java)
fun addAll(other: BuildTimes) {
for ((buildTime, timeNs) in other.buildTimesNs) {
addTimeNs(buildTime, timeNs)
}
}
fun addTimeNs(buildTime: BuildTime, timeNs: Long) {
buildTimesNs[buildTime] = buildTimesNs.getOrDefault(buildTime, 0) + timeNs
}
fun addTimeMs(buildTime: BuildTime, timeMs: Long) = addTimeNs(buildTime, timeMs * 1_000_000)
fun asMapMs(): Map<BuildTime, Long> = buildTimesNs.mapValues { it.value / 1_000_000 }
companion object {
const val serialVersionUID = 0L
}
}
@@ -0,0 +1,44 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.build.report.metrics
object DoNothingBuildMetricsReporter : BuildMetricsReporter {
override fun startMeasure(time: BuildTime) {
}
override fun endMeasure(time: BuildTime) {
}
override fun addTimeMetricNs(time: BuildTime, durationNs: Long) {
}
override fun addMetric(metric: BuildPerformanceMetric, value: Long) {
}
override fun addTimeMetric(metric: BuildPerformanceMetric) {
}
override fun addAttribute(attribute: BuildAttribute) {
}
override fun addGcMetric(metric: String, value: GcMetric) {
}
override fun startGcMetric(name: String, value: GcMetric) {
}
override fun endGcMetric(name: String, value: GcMetric) {
}
override fun getMetrics(): BuildMetrics =
BuildMetrics(
BuildTimes(),
BuildPerformanceMetrics(),
BuildAttributes()
)
override fun addMetrics(metrics: BuildMetrics) {}
}
@@ -0,0 +1,42 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.build.report.metrics
import java.io.Serializable
import kotlin.collections.HashMap
class GcMetrics : Serializable {
private val myGcMetrics = HashMap<String, GcMetric>()
fun addAll(gcMetrics: GcMetrics) {
gcMetrics.myGcMetrics.forEach { (key, value) ->
val gcMetric = myGcMetrics[key]
myGcMetrics[key] = gcMetric?.let { gcMetric + value } ?: value
}
}
fun add(metric: String, value: GcMetric) {
myGcMetrics[metric] = value
}
fun asGcCountMap(): Map<String, Long> = myGcMetrics.mapValues { it.value.count }
fun asGcTimeMap(): Map<String, Long> = myGcMetrics.mapValues { it.value.time }
fun asMap(): Map<String, GcMetric> = myGcMetrics
fun isEmpty() = myGcMetrics.isEmpty()
}
data class GcMetric(
val time: Long,
val count: Long
): Serializable {
operator fun minus(increment: GcMetric): GcMetric {
return GcMetric(time - increment.time, count - increment.count)
}
operator fun plus(increment: GcMetric?): GcMetric {
return GcMetric(time + (increment?.time ?: 0), count + (increment?.count ?: 0))
}
}
@@ -0,0 +1,30 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.build.report
import java.io.File
import java.io.Serializable
data class FileReportSettings(
val buildReportDir: File,
val includeMetricsInReport: Boolean = false,
) : Serializable {
companion object {
const val serialVersionUID: Long = 0
}
}
data class HttpReportSettings(
val url: String,
val password: String?,
val user: String?,
val verboseEnvironment: Boolean,
val includeGitBranchName: Boolean
) : Serializable {
companion object {
const val serialVersionUID: Long = 0
}
}
@@ -0,0 +1,93 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.build.report.statistics
import org.jetbrains.kotlin.build.report.metrics.BuildAttribute
import org.jetbrains.kotlin.build.report.metrics.BuildPerformanceMetric
import org.jetbrains.kotlin.build.report.metrics.BuildTime
import java.text.SimpleDateFormat
import java.util.*
//Sensitive data. This object is used directly for statistic via http
private val formatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").also { it.timeZone = TimeZone.getTimeZone("UTC")}
data class CompileStatisticsData(
val version: Int = 3,
val projectName: String?,
val label: String?,
val taskName: String,
val taskResult: String?,
val startTimeMs: Long,
val durationMs: Long,
val tags: Set<StatTag>,
val changes: List<String>,
val buildUuid: String = "Unset",
val kotlinVersion: String,
val kotlinLanguageVersion: String?,
val hostName: String? = "Unset",
val finishTime: Long,
val timestamp: String = formatter.format(finishTime),
val compilerArguments: List<String>,
val nonIncrementalAttributes: Set<BuildAttribute>,
//TODO think about it,time in milliseconds
val buildTimesMetrics: Map<BuildTime, Long>,
val performanceMetrics: Map<BuildPerformanceMetric, Long>,
val gcTimeMetrics: Map<String, Long>?,
val gcCountMetrics: Map<String, Long>?,
val type: String = BuildDataType.TASK_DATA.name,
val fromKotlinPlugin: Boolean?,
val compiledSources: List<String> = emptyList(),
val skipMessage: String?,
val icLogLines: List<String>,
)
enum class StatTag(val readableString: String) {
ABI_SNAPSHOT("ABI Snapshot"),
ARTIFACT_TRANSFORM("Classpath Snapshot"),
INCREMENTAL("Incremental compilation"),
NON_INCREMENTAL("Non incremental compilation"),
INCREMENTAL_AND_NON_INCREMENTAL("Incremental and Non incremental compilation"),
GRADLE_DEBUG("Gradle debug enabled"),
KOTLIN_DEBUG("Kotlin debug enabled"),
CONFIGURATION_CACHE("Configuration cache enabled"),
BUILD_CACHE("Build cache enabled"),
KOTLIN_1("Kotlin language version 1"),
KOTLIN_2("Kotlin language version 2"),
KOTLIN_1_AND_2("Kotlin language version 1 and 2"),
}
enum class BuildDataType {
TASK_DATA,
BUILD_DATA,
JPS_DATA
}
//Sensitive data. This object is used directly for statistic via http
data class BuildStartParameters(
val tasks: List<String>,
val excludedTasks: Set<String> = emptySet(),
val currentDir: String? = null,
val projectProperties: List<String> = emptyList(),
val systemProperties: List<String> = emptyList(),
) : java.io.Serializable
//Sensitive data. This object is used directly for statistic via http
data class BuildFinishStatisticsData(
val projectName: String,
val startParameters: BuildStartParameters,
val buildUuid: String = "Unset",
val label: String?,
val totalTime: Long,
val type: String = BuildDataType.BUILD_DATA.name,
val finishTime: Long,
val timestamp: String = formatter.format(finishTime),
val hostName: String? = "Unset",
val tags: Set<StatTag>,
val gitBranch: String = "Unset"
)
@@ -0,0 +1,76 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.build.report.statistics
import com.google.gson.Gson
import org.jetbrains.kotlin.compilerRunner.KotlinLogger
import java.io.IOException
import java.io.Serializable
import java.net.HttpURLConnection
import java.net.URL
import java.util.*
import kotlin.system.measureTimeMillis
class HttpReportService(
private val url: String,
private val password: String?,
private val user: String?,
) : Serializable {
private var invalidUrl = false
private var requestPreviousFailed = false
private fun checkResponseAndLog(connection: HttpURLConnection, log: KotlinLogger) {
val isResponseBad = connection.responseCode !in 200..299
if (isResponseBad) {
val message = "Failed to send statistic to ${connection.url} with ${connection.responseCode}: ${connection.responseMessage}"
if (!requestPreviousFailed) {
log.warn(message)
} else {
log.debug(message)
}
requestPreviousFailed = true
}
}
fun sendData(data: Any, log: KotlinLogger) {
val elapsedTime = measureTimeMillis {
if (invalidUrl) {
return
}
val connection = try {
URL(url).openConnection() as HttpURLConnection
} catch (e: IOException) {
log.warn("Unable to open connection to ${url}: ${e.message}")
invalidUrl = true
return
}
try {
if (user != null && password != null) {
val auth = Base64.getEncoder()
.encode("${user}:${password}".toByteArray())
.toString(Charsets.UTF_8)
connection.addRequestProperty("Authorization", "Basic $auth")
}
connection.addRequestProperty("Content-Type", "application/json")
connection.requestMethod = "POST"
connection.doOutput = true
connection.outputStream.use {
it.write(Gson().toJson(data).toByteArray())
}
connection.connect()
checkResponseAndLog(connection, log)
} catch (e: Exception) {
log.warn("Unexpected exception happened ${e.message}: ${e.stackTrace}")
checkResponseAndLog(connection, log)
} finally {
connection.disconnect()
}
}
log.debug("Report statistic by http takes $elapsedTime ms")
}
}
@@ -0,0 +1,292 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.build.report.statistics.file
import org.jetbrains.kotlin.build.report.metrics.*
import org.jetbrains.kotlin.build.report.statistics.*
import org.jetbrains.kotlin.build.report.statistics.asString
import org.jetbrains.kotlin.build.report.statistics.formatTime
import org.jetbrains.kotlin.compilerRunner.KotlinLogger
import java.io.File
import java.io.Serializable
import java.text.SimpleDateFormat
import java.util.*
class FileReportService(
private val outputFile: File,
private val printMetrics: Boolean,
private val logger: KotlinLogger
) : Serializable {
companion object {
private val formatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").also { it.timeZone = TimeZone.getTimeZone("UTC")}
fun reportBuildStatInFile(
buildReportDir: File,
projectName: String,
includeMetricsInReport: Boolean,
buildData: List<CompileStatisticsData>,
startParameters: BuildStartParameters,
failureMessages: List<String>,
logger: KotlinLogger
) {
val ts = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(Calendar.getInstance().time)
val reportFile = buildReportDir.resolve("$projectName-build-$ts.txt")
FileReportService(
outputFile = reportFile,
printMetrics = includeMetricsInReport,
logger = logger
).process(buildData, startParameters, failureMessages)
}
}
private lateinit var p: Printer
fun process(
statisticsData: List<CompileStatisticsData>,
startParameters: BuildStartParameters,
failureMessages: List<String> = emptyList()
) {
val buildReportPath = outputFile.toPath().toUri().toString()
try {
outputFile.parentFile.mkdirs()
if (!(outputFile.parentFile.exists() && outputFile.parentFile.isDirectory)) {
logger.error("Kotlin build report cannot be created: '$outputFile.parentFile' is a file or do not have permissions to create")
return
}
outputFile.bufferedWriter().use { writer ->
p = Printer(writer)
printBuildReport(statisticsData, startParameters, failureMessages)
}
logger.lifecycle("Kotlin build report is written to $buildReportPath")
} catch (e: Exception) {
logger.error("Could not write Kotlin build report to $buildReportPath", e)
}
}
private fun printBuildReport(
statisticsData: List<CompileStatisticsData>,
startParameters: BuildStartParameters,
failureMessages: List<String>
) {
// NOTE: BuildExecutionData / BuildOperationRecord contains data for both tasks and transforms.
// Where possible, we still use the term "tasks" because saying "tasks/transforms" is a bit verbose and "build operations" may sound
// a bit unfamiliar.
// TODO: If it is confusing, consider renaming "tasks" to "build operations" in this class.
printBuildInfo(startParameters, failureMessages)
if (printMetrics) {
printMetrics(
statisticsData.map { it.buildTimesMetrics }.reduce { agg, value ->
(agg.keys + value.keys).associateWith { (agg[it] ?: 0) + (value[it] ?: 0) }
},
statisticsData.map { it.performanceMetrics }.reduce { agg, value ->
(agg.keys + value.keys).associateWith { (agg[it] ?: 0) + (value[it] ?: 0) }
},
statisticsData.map { it.nonIncrementalAttributes.asSequence() }.reduce { agg, value -> agg + value }.toList(),
aggregatedMetric = true
)
p.println()
}
printTaskOverview(statisticsData)
printTasksLog(statisticsData)
}
private fun printBuildInfo(startParameters: BuildStartParameters, failureMessages: List<String>) {
p.withIndent("Gradle start parameters:") {
startParameters.let {
p.println("tasks = ${it.tasks}")
p.println("excluded tasks = ${it.excludedTasks}")
p.println("current dir = ${it.currentDir}")
p.println("project properties args = ${it.projectProperties}")
p.println("system properties args = ${it.systemProperties}")
}
}
p.println()
if (failureMessages.isNotEmpty()) {
p.println("Build failed: ${failureMessages}")
p.println()
}
}
private fun printMetrics(
buildTimesMetrics: Map<BuildTime, Long>,
performanceMetrics: Map<BuildPerformanceMetric, Long>,
nonIncrementalAttributes: Collection<BuildAttribute>,
gcTimeMetrics: Map<String, Long>? = emptyMap(),
gcCountMetrics: Map<String, Long>? = emptyMap(),
aggregatedMetric: Boolean = false
) {
printBuildTimes(buildTimesMetrics)
if (aggregatedMetric) p.println()
printBuildPerformanceMetrics(performanceMetrics)
if (aggregatedMetric) p.println()
printBuildAttributes(nonIncrementalAttributes)
//TODO: KT-57310 Implement build GC metric in
if (!aggregatedMetric) {
printGcMetrics(gcTimeMetrics, gcCountMetrics)
}
}
private fun printGcMetrics(
gcTimeMetrics: Map<String, Long>?,
gcCountMetrics: Map<String, Long>?
) {
val keys = HashSet<String>()
gcCountMetrics?.keys?.also { keys.addAll(it) }
gcTimeMetrics?.keys?.also { keys.addAll(it) }
if (keys.isEmpty()) return
p.withIndent("GC metrics:") {
for (key in keys) {
p.println("$key:")
p.withIndent {
gcCountMetrics?.get(key)?.also { p.println("GC count: ${it}") }
gcTimeMetrics?.get(key)?.also { p.println("GC time: ${formatTime(it)}") }
}
}
}
}
private fun printBuildTimes(buildTimes: Map<BuildTime, Long>) {
if (buildTimes.isEmpty()) return
p.println("Time metrics:")
p.withIndent {
val visitedBuildTimes = HashSet<BuildTime>()
fun printBuildTime(buildTime: BuildTime) {
if (!visitedBuildTimes.add(buildTime)) return
val timeMs = buildTimes[buildTime]
if (timeMs != null) {
p.println("${buildTime.readableString}: ${formatTime(timeMs)}")
p.withIndent {
BuildTime.children[buildTime]?.forEach { printBuildTime(it) }
}
} else {
//Skip formatting if parent metric does not set
BuildTime.children[buildTime]?.forEach { printBuildTime(it) }
}
}
for (buildTime in BuildTime.values()) {
if (buildTime.parent != null) continue
printBuildTime(buildTime)
}
}
}
private fun printBuildPerformanceMetrics(buildMetrics: Map<BuildPerformanceMetric, Long>) {
if (buildMetrics.isEmpty()) return
p.withIndent("Size metrics:") {
for (metric in BuildPerformanceMetric.values()) {
buildMetrics[metric]?.let { printSizeMetric(metric, it) }
}
}
}
private fun printSizeMetric(sizeMetric: BuildPerformanceMetric, value: Long) {
fun BuildPerformanceMetric.numberOfAncestors(): Int {
var count = 0
var parent: BuildPerformanceMetric? = parent
while (parent != null) {
count++
parent = parent.parent
}
return count
}
val indentLevel = sizeMetric.numberOfAncestors()
repeat(indentLevel) { p.pushIndent() }
when (sizeMetric.type) {
ValueType.BYTES -> p.println("${sizeMetric.readableString}: ${formatSize(value)}")
ValueType.NUMBER -> p.println("${sizeMetric.readableString}: $value")
ValueType.NANOSECONDS -> p.println("${sizeMetric.readableString}: $value")
ValueType.MILLISECONDS -> p.println("${sizeMetric.readableString}: ${formatTime(value)}")
ValueType.TIME -> p.println("${sizeMetric.readableString}: ${formatter.format(value)}")
}
repeat(indentLevel) { p.popIndent() }
}
private fun printBuildAttributes(buildAttributes: Collection<BuildAttribute>) {
if (buildAttributes.isEmpty()) return
val buildAttributesMap = buildAttributes.groupingBy { it }.eachCount()
p.withIndent("Build attributes:") {
val attributesByKind = buildAttributesMap.entries.groupBy { it.key.kind }.toSortedMap()
for ((kind, attributesCounts) in attributesByKind) {
printMap(p, kind.name, attributesCounts.associate { (k, v) -> k.readableString to v })
}
}
}
private fun printTaskOverview(statisticsData: Collection<CompileStatisticsData>) {
var allTasksTimeMs = 0L
var kotlinTotalTimeMs = 0L
val kotlinTasks = ArrayList<CompileStatisticsData>()
for (task in statisticsData) {
val taskTimeMs = task.durationMs
allTasksTimeMs += taskTimeMs
if (task.fromKotlinPlugin == true) {
kotlinTotalTimeMs += taskTimeMs
kotlinTasks.add(task)
}
}
if (kotlinTasks.isEmpty()) {
p.println("No Kotlin task was run")
return
}
val ktTaskPercent = (kotlinTotalTimeMs.toDouble() / allTasksTimeMs * 100).asString(1)
p.println("Total time for Kotlin tasks: ${formatTime(kotlinTotalTimeMs)} ($ktTaskPercent % of all tasks time)")
val table = TextTable("Time", "% of Kotlin time", "Task")
for (task in kotlinTasks.sortedWith(compareBy({ -it.durationMs }, { it.startTimeMs }))) {
val timeMs = task.durationMs
val percent = (timeMs.toDouble() / kotlinTotalTimeMs * 100).asString(1)
table.addRow(formatTime(timeMs), "$percent %", task.taskName)
}
table.printTo(p)
p.println()
}
private fun printTasksLog(statisticsData: List<CompileStatisticsData>) {
for (task in statisticsData.sortedWith(compareBy({ -it.durationMs }, { it.startTimeMs }))) {
printTaskLog(task)
p.println()
}
}
private fun printTaskLog(statisticsData: CompileStatisticsData) {
val skipMessage = statisticsData.skipMessage
if (skipMessage != null) {
p.println("Task '${statisticsData.taskName}' was skipped: $skipMessage")
} else {
p.println("Task '${statisticsData.taskName}' finished in ${formatTime(statisticsData.durationMs)}")
}
if (statisticsData.icLogLines.isNotEmpty()) {
p.withIndent("Compilation log for task '${statisticsData.taskName}':") {
statisticsData.icLogLines.forEach { p.println(it) }
}
}
if (printMetrics) {
printMetrics(statisticsData.buildTimesMetrics, statisticsData.performanceMetrics, statisticsData.nonIncrementalAttributes,
statisticsData.gcTimeMetrics, statisticsData.gcCountMetrics)
}
}
}
@@ -0,0 +1,51 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.build.report.statistics.file
import java.util.*
import kotlin.math.max
internal fun printMap(p: Printer, name: String, mapping: Map<String, Int>) {
if (mapping.isEmpty()) return
if (mapping.size == 1) {
p.println("$name: ${mapping.keys.single()}")
return
}
p.withIndent("$name:") {
val sortedEnumMap = mapping.toSortedMap()
for ((k, v) in sortedEnumMap) {
p.println("$k($v)")
}
}
}
internal class TextTable(vararg columnNames: String) {
private val rows = ArrayList<List<String>>()
private val columnsCount = columnNames.size
private val maxLengths = IntArray(columnsCount) { columnNames[it].length }
init {
rows.add(columnNames.toList())
}
fun addRow(vararg row: String) {
check(row.size == columnsCount) { "Row size ${row.size} differs from columns count $columnsCount" }
rows.add(row.toList())
for ((i, col) in row.withIndex()) {
maxLengths[i] = max(maxLengths[i], col.length)
}
}
fun printTo(p: Printer) {
for (row in rows) {
val rowStr = row.withIndex().joinToString("|") { (i, col) -> col.padEnd(maxLengths[i], ' ') }
p.println(rowStr)
}
}
}
@@ -0,0 +1,71 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.build.report.statistics.file
import java.io.IOException
private val LINE_SEPARATOR = System.getProperty("line.separator")
internal class Printer(
private val out: Appendable,
private val indentUnit: String = " ",
private var indent: String = ""
) {
private fun append(s: String) {
try {
out.append(s)
} catch (e: IOException) { // Do nothing
}
}
fun println(vararg strings: String) {
this.print(*strings)
printLineSeparator()
}
private fun printLineSeparator() {
append(LINE_SEPARATOR)
}
fun print(vararg strings: String) {
if (strings.isNotEmpty()) {
this.printIndent()
}
this.printWithNoIndent(*strings)
}
private fun printIndent() {
append(indent)
}
private fun printWithNoIndent(vararg strings: String) {
for (s in strings) {
append(s)
}
}
fun pushIndent() {
indent += indentUnit
}
fun popIndent() {
check(indent.length >= indentUnit.length) { "No indentation to pop" }
indent = indent.substring(indentUnit.length)
}
inline fun <T> withIndent(headLine: String? = null, fn: () -> T): T {
if (headLine != null) {
this.println(headLine)
}
pushIndent()
return try {
fn()
} finally {
popIndent()
}
}
}
@@ -0,0 +1,24 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.build.report.statistics
internal fun formatTime(ms: Long): String {
val seconds = ms.toDouble() / 1_000
return seconds.asString(2) + " s"
}
private const val kbSize = 1024
private const val mbSize = kbSize * 1024
private const val gbSize = mbSize * 1024
fun formatSize(sizeInBytes: Long): String = when {
sizeInBytes / gbSize >= 1 -> "${(sizeInBytes.toDouble() / gbSize).asString(1)} GB"
sizeInBytes / mbSize >= 1 -> "${(sizeInBytes.toDouble() / mbSize).asString(1)} MB"
sizeInBytes / kbSize >= 1 -> "${(sizeInBytes.toDouble() / kbSize).asString(1)} KB"
else -> "$sizeInBytes B"
}
internal fun Double.asString(decPoints: Int): String = "%,.${decPoints}f".format(this)
@@ -26,12 +26,7 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.isSubpackageOf
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.util.Logger
import org.jetbrains.kotlin.utils.KotlinPaths
import java.io.ByteArrayOutputStream
import java.io.File
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
import java.util.zip.ZipOutputStream
import kotlin.system.exitProcess
fun incrementalCompilationIsEnabled(arguments: CommonCompilerArguments): Boolean {
@@ -87,7 +82,7 @@ fun <PathProvider : Any> getLibraryFromHome(
fun MessageCollector.toLogger(): Logger =
object : Logger {
override fun error(message: String, throwable: Throwable?) {
override fun error(message: String) {
report(CompilerMessageSeverity.ERROR, message)
}
@@ -103,9 +98,5 @@ fun MessageCollector.toLogger(): Logger =
override fun log(message: String) {
report(CompilerMessageSeverity.LOGGING, message)
}
override fun lifecycle(message: String) {
report(CompilerMessageSeverity.INFO, message)
}
}
@@ -18,7 +18,7 @@ dependencies {
api(project(":compiler:backend.jvm.entrypoint"))
api(project(":kotlin-build-common"))
api(project(":daemon-common"))
api(project(":kotlin-build-statistic"))
api(project(":compiler:build-tools:kotlin-build-statistics"))
compileOnly(intellijCore())
testApi(commonDependency("junit:junit"))
@@ -29,7 +29,6 @@ dependencies {
testApi(intellijCore())
testApi(commonDependency("org.jetbrains.intellij.deps:log4j"))
testApi(commonDependency("org.jetbrains.intellij.deps:jdom"))
testApi(projectTests(":kotlin-build-statistic"))
testImplementation(commonDependency("com.google.code.gson:gson"))
testRuntimeOnly(commonDependency("org.jetbrains.kotlin:kotlin-reflect")) { isTransitive = false }
@@ -106,9 +106,8 @@ val CompilerConfiguration.resolverLogger: Logger
null -> DummyLogger
else -> object : Logger {
override fun log(message: String) = messageLogger.report(IrMessageLogger.Severity.INFO, message, null)
override fun error(message: String, throwable: Throwable?) = messageLogger.report(IrMessageLogger.Severity.ERROR, message, null)
override fun error(message: String) = messageLogger.report(IrMessageLogger.Severity.ERROR, message, null)
override fun warning(message: String) = messageLogger.report(IrMessageLogger.Severity.WARNING, message, null)
override fun lifecycle(message: String) = messageLogger.report(IrMessageLogger.Severity.INFO, message, null)
override fun fatal(message: String): Nothing {
messageLogger.report(IrMessageLogger.Severity.ERROR, message, null)
@@ -175,9 +175,8 @@ object GenerationUtils {
fun messageCollectorLogger(collector: MessageCollector) = object : Logger {
override fun warning(message: String) = collector.report(CompilerMessageSeverity.STRONG_WARNING, message)
override fun error(message: String, throwable: Throwable?) = collector.report(CompilerMessageSeverity.ERROR, message)
override fun error(message: String) = collector.report(CompilerMessageSeverity.ERROR, message)
override fun log(message: String) = collector.report(CompilerMessageSeverity.LOGGING, message)
override fun lifecycle(message: String) = collector.report(CompilerMessageSeverity.INFO, message)
override fun fatal(message: String): Nothing {
collector.report(CompilerMessageSeverity.ERROR, message)
(collector as? GroupingMessageCollector)?.flush()
@@ -4,10 +4,9 @@ import kotlin.system.exitProcess
interface Logger {
fun log(message: String)
fun error(message: String, throwable: Throwable? = null)
fun error(message: String)
fun warning(message: String)
fun fatal(message: String): Nothing
fun lifecycle(message: String)
}
interface WithLogger {
@@ -16,18 +15,10 @@ interface WithLogger {
object DummyLogger : Logger {
override fun log(message: String) = println(message)
override fun error(message: String, throwable: Throwable?) {
println("e: $message")
throwable?.also {
println("${it.message}:\n${it.stackTraceToString()} ")
}
}
override fun error(message: String) = println("e: $message")
override fun warning(message: String) = println("w: $message")
override fun fatal(message: String): Nothing {
println("e: $message")
exitProcess(1)
}
override fun lifecycle(message: String) = println("i: $message")
}
@@ -16,10 +16,9 @@ fun resolveSingleFileKlib(
libraryFile: File,
logger: Logger = object : Logger {
override fun log(message: String) {}
override fun error(message: String, throwable: Throwable?) = kotlin.error("e: $message")
override fun error(message: String) = kotlin.error("e: $message")
override fun warning(message: String) {}
override fun fatal(message: String) = kotlin.error("e: $message")
override fun lifecycle(message: String) {}
},
strategy: SingleFileKlibResolveStrategy = CompilerSingleFileKlibResolveStrategy
): KotlinLibrary = strategy.resolve(libraryFile, logger)
@@ -1,14 +1,15 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.compilerRunner
interface KotlinLogger {
fun error(msg: String)
val isDebugEnabled: Boolean
fun error(msg: String, throwable: Throwable? = null)
fun warn(msg: String)
fun info(msg: String)
fun debug(msg: String)
val isDebugEnabled: Boolean
fun lifecycle(msg: String)
}