[Commonizer] Stats collector: support aggregated stats
This commit is contained in:
@@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.descriptors.commonizer
|
package org.jetbrains.kotlin.descriptors.commonizer
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.commonizer.stats.StatsCollector
|
||||||
|
|
||||||
class Parameters(
|
class Parameters(
|
||||||
val statsCollector: StatsCollector? = null
|
val statsCollector: StatsCollector? = null
|
||||||
) {
|
) {
|
||||||
|
|||||||
+1
-1
@@ -7,11 +7,11 @@ package org.jetbrains.kotlin.descriptors.commonizer.builder
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.commonizer.StatsCollector
|
|
||||||
import org.jetbrains.kotlin.descriptors.commonizer.Target
|
import org.jetbrains.kotlin.descriptors.commonizer.Target
|
||||||
import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirRootNode
|
import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirRootNode
|
||||||
import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.dimension
|
import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.dimension
|
||||||
import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.indexOfCommon
|
import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.indexOfCommon
|
||||||
|
import org.jetbrains.kotlin.descriptors.commonizer.stats.StatsCollector
|
||||||
import org.jetbrains.kotlin.descriptors.commonizer.utils.CommonizedGroup
|
import org.jetbrains.kotlin.descriptors.commonizer.utils.CommonizedGroup
|
||||||
import org.jetbrains.kotlin.descriptors.commonizer.utils.CommonizedGroupMap
|
import org.jetbrains.kotlin.descriptors.commonizer.utils.CommonizedGroupMap
|
||||||
import org.jetbrains.kotlin.descriptors.commonizer.utils.createKotlinNativeForwardDeclarationsModule
|
import org.jetbrains.kotlin.descriptors.commonizer.utils.createKotlinNativeForwardDeclarationsModule
|
||||||
|
|||||||
+7
-5
@@ -10,18 +10,20 @@ internal class BooleanOptionType(
|
|||||||
description: String,
|
description: String,
|
||||||
mandatory: Boolean
|
mandatory: Boolean
|
||||||
) : OptionType<Boolean>(alias, description, mandatory) {
|
) : OptionType<Boolean>(alias, description, mandatory) {
|
||||||
private val trueTokens = setOf("1", "on", "yes", "true")
|
|
||||||
private val falseTokens = setOf("0", "off", "no", "false")
|
|
||||||
|
|
||||||
override fun parse(rawValue: String, onError: (reason: String) -> Nothing): Option<Boolean> {
|
override fun parse(rawValue: String, onError: (reason: String) -> Nothing): Option<Boolean> {
|
||||||
val value = rawValue.toLowerCase().let {
|
val value = rawValue.toLowerCase().let {
|
||||||
when (it) {
|
when (it) {
|
||||||
in trueTokens -> true
|
in TRUE_TOKENS -> true
|
||||||
in falseTokens -> false
|
in FALSE_TOKENS -> false
|
||||||
else -> onError("Invalid boolean value: $it")
|
else -> onError("Invalid boolean value: $it")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Option(this, value)
|
return Option(this, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val TRUE_TOKENS = setOf("1", "on", "yes", "true")
|
||||||
|
private val FALSE_TOKENS = setOf("0", "off", "no", "false")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* 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.descriptors.commonizer.cli
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.commonizer.konan.NativeDistributionCommonizer.StatsType
|
||||||
|
|
||||||
|
internal object StatsTypeOptionType : OptionType<StatsType>("log-stats", DESCRIPTION, mandatory = false) {
|
||||||
|
override fun parse(rawValue: String, onError: (reason: String) -> Nothing): Option<StatsType> {
|
||||||
|
val value = StatsType.values().firstOrNull { it.name.equals(rawValue, ignoreCase = true) }
|
||||||
|
?: onError("Invalid stats type: $rawValue")
|
||||||
|
return Option(this, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val DESCRIPTION = buildString {
|
||||||
|
StatsType.values().joinTo(this) {
|
||||||
|
val item = "\"${it.name.toLowerCase()}\""
|
||||||
|
if (it == StatsType.NONE) "$item (default)" else item
|
||||||
|
}
|
||||||
|
append(";\nlog commonization stats")
|
||||||
|
}
|
||||||
@@ -28,7 +28,7 @@ internal enum class TaskType(
|
|||||||
"Boolean (default false);\nwhether to copy Kotlin/Native endorsed libraries to the destination",
|
"Boolean (default false);\nwhether to copy Kotlin/Native endorsed libraries to the destination",
|
||||||
mandatory = false
|
mandatory = false
|
||||||
),
|
),
|
||||||
BooleanOptionType("log-stats", "Boolean (default false); Log commonization stats", mandatory = false)
|
StatsTypeOptionType
|
||||||
),
|
),
|
||||||
::NativeDistributionCommonize
|
::NativeDistributionCommonize
|
||||||
),
|
),
|
||||||
|
|||||||
+3
-2
@@ -6,6 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.descriptors.commonizer.cli
|
package org.jetbrains.kotlin.descriptors.commonizer.cli
|
||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.commonizer.konan.NativeDistributionCommonizer
|
import org.jetbrains.kotlin.descriptors.commonizer.konan.NativeDistributionCommonizer
|
||||||
|
import org.jetbrains.kotlin.descriptors.commonizer.konan.NativeDistributionCommonizer.StatsType
|
||||||
import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_KLIB_DIR
|
import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_KLIB_DIR
|
||||||
import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR
|
import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR
|
||||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||||
@@ -44,7 +45,7 @@ internal class NativeDistributionCommonize(options: Collection<Option<*>>) : Tas
|
|||||||
|
|
||||||
val copyStdlib = getOptional<Boolean, BooleanOptionType> { it == "copy-stdlib" } ?: false
|
val copyStdlib = getOptional<Boolean, BooleanOptionType> { it == "copy-stdlib" } ?: false
|
||||||
val copyEndorsedLibs = getOptional<Boolean, BooleanOptionType> { it == "copy-endorsed-libs" } ?: false
|
val copyEndorsedLibs = getOptional<Boolean, BooleanOptionType> { it == "copy-endorsed-libs" } ?: false
|
||||||
val withStats = getOptional<Boolean, BooleanOptionType> { it == "log-stats" } ?: false
|
val statsType = getOptional<StatsType, StatsTypeOptionType> { it == "log-stats" } ?: StatsType.NONE
|
||||||
|
|
||||||
val descriptionSuffix = estimateLibrariesCount(distribution, targets)?.let { " ($it items)" } ?: ""
|
val descriptionSuffix = estimateLibrariesCount(distribution, targets)?.let { " ($it items)" } ?: ""
|
||||||
val description = "${logPrefix}Preparing commonized Kotlin/Native libraries for targets $targets$descriptionSuffix"
|
val description = "${logPrefix}Preparing commonized Kotlin/Native libraries for targets $targets$descriptionSuffix"
|
||||||
@@ -57,7 +58,7 @@ internal class NativeDistributionCommonize(options: Collection<Option<*>>) : Tas
|
|||||||
destination = destination,
|
destination = destination,
|
||||||
copyStdlib = copyStdlib,
|
copyStdlib = copyStdlib,
|
||||||
copyEndorsedLibs = copyEndorsedLibs,
|
copyEndorsedLibs = copyEndorsedLibs,
|
||||||
withStats = withStats,
|
statsType = statsType,
|
||||||
logger = CliLoggerAdapter(2)
|
logger = CliLoggerAdapter(2)
|
||||||
).run()
|
).run()
|
||||||
|
|
||||||
|
|||||||
+14
-2
@@ -13,6 +13,10 @@ import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
|
|||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.commonizer.*
|
import org.jetbrains.kotlin.descriptors.commonizer.*
|
||||||
import org.jetbrains.kotlin.descriptors.commonizer.Target
|
import org.jetbrains.kotlin.descriptors.commonizer.Target
|
||||||
|
import org.jetbrains.kotlin.descriptors.commonizer.konan.NativeDistributionCommonizer.StatsType.*
|
||||||
|
import org.jetbrains.kotlin.descriptors.commonizer.stats.AggregatedStatsCollector
|
||||||
|
import org.jetbrains.kotlin.descriptors.commonizer.stats.FileStatsOutput
|
||||||
|
import org.jetbrains.kotlin.descriptors.commonizer.stats.RawStatsCollector
|
||||||
import org.jetbrains.kotlin.descriptors.commonizer.utils.ResettableClockMark
|
import org.jetbrains.kotlin.descriptors.commonizer.utils.ResettableClockMark
|
||||||
import org.jetbrains.kotlin.descriptors.konan.NATIVE_STDLIB_MODULE_NAME
|
import org.jetbrains.kotlin.descriptors.konan.NATIVE_STDLIB_MODULE_NAME
|
||||||
import org.jetbrains.kotlin.konan.library.*
|
import org.jetbrains.kotlin.konan.library.*
|
||||||
@@ -37,9 +41,13 @@ class NativeDistributionCommonizer(
|
|||||||
private val destination: File,
|
private val destination: File,
|
||||||
private val copyStdlib: Boolean,
|
private val copyStdlib: Boolean,
|
||||||
private val copyEndorsedLibs: Boolean,
|
private val copyEndorsedLibs: Boolean,
|
||||||
private val withStats: Boolean,
|
private val statsType: StatsType,
|
||||||
private val logger: Logger
|
private val logger: Logger
|
||||||
) {
|
) {
|
||||||
|
enum class StatsType {
|
||||||
|
RAW, AGGREGATED, NONE
|
||||||
|
}
|
||||||
|
|
||||||
fun run() {
|
fun run() {
|
||||||
checkPreconditions()
|
checkPreconditions()
|
||||||
|
|
||||||
@@ -123,7 +131,11 @@ class NativeDistributionCommonizer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun commonize(librariesByTargets: Map<InputTarget, NativeDistributionLibraries>): Result {
|
private fun commonize(librariesByTargets: Map<InputTarget, NativeDistributionLibraries>): Result {
|
||||||
val statsCollector = if (withStats) NativeStatsCollector(targets, destination) else null
|
val statsCollector = when (statsType) {
|
||||||
|
RAW -> RawStatsCollector(targets, FileStatsOutput(destination, "raw"))
|
||||||
|
AGGREGATED -> AggregatedStatsCollector(targets, FileStatsOutput(destination, "aggregated"))
|
||||||
|
NONE -> null
|
||||||
|
}
|
||||||
statsCollector.use {
|
statsCollector.use {
|
||||||
val parameters = Parameters(statsCollector).apply {
|
val parameters = Parameters(statsCollector).apply {
|
||||||
librariesByTargets.forEach { (target, libraries) ->
|
librariesByTargets.forEach { (target, libraries) ->
|
||||||
|
|||||||
-188
@@ -1,188 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
|
||||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jetbrains.kotlin.descriptors.commonizer.konan
|
|
||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind.ENUM_CLASS
|
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind.ENUM_ENTRY
|
|
||||||
import org.jetbrains.kotlin.descriptors.commonizer.StatsCollector
|
|
||||||
import org.jetbrains.kotlin.descriptors.commonizer.utils.firstNonNull
|
|
||||||
import org.jetbrains.kotlin.descriptors.commonizer.utils.fqNameWithTypeParameters
|
|
||||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows printing commonization statistics to the file system.
|
|
||||||
*
|
|
||||||
* File format: text, "|"-separated columns.
|
|
||||||
*
|
|
||||||
* Header row: "FQ Name|Extension Receiver|Parameter Names|Parameter Types|Declaration Type|common|<platform1>|<platform2>[|<platformN>...]"
|
|
||||||
*
|
|
||||||
* Possible values for "Declaration Type":
|
|
||||||
* - MODULE
|
|
||||||
* - CLASS
|
|
||||||
* - INTERFACE
|
|
||||||
* - OBJECT
|
|
||||||
* - COMPANION_OBJECT
|
|
||||||
* - ENUM_CLASS
|
|
||||||
* - ENUM_ENTRY
|
|
||||||
* - TYPE_ALIAS
|
|
||||||
* - CLASS_CONSTRUCTOR
|
|
||||||
* - FUN
|
|
||||||
* - VAL
|
|
||||||
*
|
|
||||||
* Possible values for "common" column:
|
|
||||||
* - L = declaration lifted up to common fragment
|
|
||||||
* - E = successfully commonized, expect declaration generated
|
|
||||||
* - "-" = no common declaration
|
|
||||||
*
|
|
||||||
* Possible values for each target platform column:
|
|
||||||
* - A = successfully commonized, actual declaration generated
|
|
||||||
* - O = not commonized, the declaration is as in the original library
|
|
||||||
* - "-" = no such declaration in the original library (or declaration has been lifted up)
|
|
||||||
*
|
|
||||||
* Example of output:
|
|
||||||
|
|
||||||
FQ Name|Extension Receiver|Parameter Names|Parameter Types|Declaration Type|common|macos_x64|ios_x64
|
|
||||||
<SystemConfiguration>||||MODULE|E|A|A
|
|
||||||
platform.SystemConfiguration.SCPreferencesContext||||CLASS|E|A|A
|
|
||||||
platform.SystemConfiguration.SCPreferencesContext.Companion||||COMPANION_OBJECT|E|A|A
|
|
||||||
platform.SystemConfiguration.SCNetworkConnectionContext||||CLASS|E|A|A
|
|
||||||
platform.SystemConfiguration.SCNetworkConnectionContext.Companion||||COMPANION_OBJECT|E|A|A
|
|
||||||
platform.SystemConfiguration.SCDynamicStoreRefVar||||TYPE_ALIAS|-|O|O
|
|
||||||
platform.SystemConfiguration.SCVLANInterfaceRef||||TYPE_ALIAS|-|O|O
|
|
||||||
|
|
||||||
*/
|
|
||||||
class NativeStatsCollector(
|
|
||||||
private val targets: List<KonanTarget>,
|
|
||||||
destination: File
|
|
||||||
) : StatsCollector {
|
|
||||||
|
|
||||||
init {
|
|
||||||
destination.mkdirs()
|
|
||||||
}
|
|
||||||
|
|
||||||
private val writer = destination.resolve("plain_stats.csv").printWriter()
|
|
||||||
private var headerWritten = false
|
|
||||||
|
|
||||||
override fun logStats(output: List<DeclarationDescriptor?>) {
|
|
||||||
if (!headerWritten) {
|
|
||||||
headerWritten = true
|
|
||||||
writeHeader()
|
|
||||||
}
|
|
||||||
|
|
||||||
val firstNotNull = output.firstNonNull()
|
|
||||||
val lastIsNull = output.last() == null
|
|
||||||
|
|
||||||
val row = buildString {
|
|
||||||
appendName(firstNotNull)
|
|
||||||
append(SEPARATOR)
|
|
||||||
append(firstNotNull.declarationType) // readable declaration type
|
|
||||||
append(SEPARATOR)
|
|
||||||
|
|
||||||
var isLiftedUp = !lastIsNull
|
|
||||||
val platformItems = StringBuilder().apply {
|
|
||||||
for (index in 0 until output.size - 1) {
|
|
||||||
append(SEPARATOR)
|
|
||||||
append(
|
|
||||||
when {
|
|
||||||
output[index] == null -> '-' // absent
|
|
||||||
lastIsNull -> 'O' // original (not commonized)
|
|
||||||
else -> {
|
|
||||||
isLiftedUp = false
|
|
||||||
'A' // actual (commonized)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
append(
|
|
||||||
when {
|
|
||||||
isLiftedUp -> 'L'
|
|
||||||
lastIsNull -> '-'
|
|
||||||
else -> 'E'
|
|
||||||
}
|
|
||||||
) // common
|
|
||||||
|
|
||||||
append(platformItems)
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.println(row)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() = writer.close()
|
|
||||||
|
|
||||||
private fun writeHeader() {
|
|
||||||
val row = buildString {
|
|
||||||
append("FQ Name")
|
|
||||||
append(SEPARATOR)
|
|
||||||
append("Extension Receiver")
|
|
||||||
append(SEPARATOR)
|
|
||||||
append("Parameter Names")
|
|
||||||
append(SEPARATOR)
|
|
||||||
append("Parameter Types")
|
|
||||||
append(SEPARATOR)
|
|
||||||
append("Declaration Type")
|
|
||||||
append(SEPARATOR)
|
|
||||||
append("common")
|
|
||||||
|
|
||||||
targets.forEach { target ->
|
|
||||||
append(SEPARATOR)
|
|
||||||
append(target.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.println(row)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val SEPARATOR = '|'
|
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
private inline fun StringBuilder.appendName(descriptor: DeclarationDescriptor) {
|
|
||||||
// name
|
|
||||||
append(if (descriptor is ModuleDescriptor) descriptor.name.asString() else descriptor.fqNameSafe.asString())
|
|
||||||
append(SEPARATOR)
|
|
||||||
|
|
||||||
// extension receiver
|
|
||||||
if (descriptor is PropertyDescriptor || descriptor is SimpleFunctionDescriptor) {
|
|
||||||
append((descriptor as CallableDescriptor).extensionReceiverParameter?.type?.fqNameWithTypeParameters.orEmpty())
|
|
||||||
}
|
|
||||||
append(SEPARATOR)
|
|
||||||
|
|
||||||
if (descriptor is ConstructorDescriptor || descriptor is SimpleFunctionDescriptor) {
|
|
||||||
// parameter names
|
|
||||||
(descriptor as FunctionDescriptor).valueParameters.joinTo(this) { it.name.asString() }
|
|
||||||
append(SEPARATOR)
|
|
||||||
// parameter types
|
|
||||||
descriptor.valueParameters.joinTo(this) { it.type.fqNameWithTypeParameters }
|
|
||||||
} else {
|
|
||||||
append(SEPARATOR)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline val DeclarationDescriptor.topLevelnessPrefix: String
|
|
||||||
get() = if (DescriptorUtils.isTopLevelDeclaration(this)) "TOP-LEVEL " else "NESTED "
|
|
||||||
|
|
||||||
private inline val DeclarationDescriptor.declarationType: String
|
|
||||||
get() = when (this) {
|
|
||||||
is ClassDescriptor -> when {
|
|
||||||
isCompanionObject -> "COMPANION_OBJECT"
|
|
||||||
kind == ENUM_CLASS || kind == ENUM_ENTRY -> kind.toString()
|
|
||||||
else -> topLevelnessPrefix + kind.toString()
|
|
||||||
}
|
|
||||||
is TypeAliasDescriptor -> "TYPE_ALIAS"
|
|
||||||
is ClassConstructorDescriptor -> "CLASS_CONSTRUCTOR"
|
|
||||||
is FunctionDescriptor -> topLevelnessPrefix + "FUN"
|
|
||||||
is PropertyDescriptor -> topLevelnessPrefix + if (isConst) "CONST-VAL" else "VAL"
|
|
||||||
is ModuleDescriptor -> "MODULE"
|
|
||||||
else -> "UNKNOWN: ${this::class.java}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+110
@@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* 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.descriptors.commonizer.stats
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.commonizer.stats.AggregatedStatsCollector.AggregatedStatsRow
|
||||||
|
import org.jetbrains.kotlin.descriptors.commonizer.stats.RawStatsCollector.CommonDeclarationStatus.*
|
||||||
|
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||||
|
|
||||||
|
class AggregatedStatsCollector(
|
||||||
|
targets: List<KonanTarget>,
|
||||||
|
private val output: StatsOutput
|
||||||
|
) : StatsCollector {
|
||||||
|
private val aggregatingOutput = AggregatingOutput()
|
||||||
|
private val wrappedCollector = RawStatsCollector(targets, aggregatingOutput)
|
||||||
|
|
||||||
|
override fun logStats(result: List<DeclarationDescriptor?>) {
|
||||||
|
wrappedCollector.logStats(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
output.writeHeader(AggregatedStatsHeader)
|
||||||
|
|
||||||
|
aggregatingOutput.aggregatedStats.keys.sortedBy { it }.forEach { key ->
|
||||||
|
val row = aggregatingOutput.aggregatedStats.getValue(key)
|
||||||
|
output.writeRow(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
output.close()
|
||||||
|
wrappedCollector.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
object AggregatedStatsHeader : StatsOutput.StatsHeader {
|
||||||
|
private val headerItems = listOf(
|
||||||
|
"Declaration Type",
|
||||||
|
"Lifted Up",
|
||||||
|
"Lifted Up, %%",
|
||||||
|
"Commonized",
|
||||||
|
"Commonized, %%",
|
||||||
|
"Missed in s. targets",
|
||||||
|
"Missed in s. targets, %%",
|
||||||
|
"Failed: Other",
|
||||||
|
"Failed: Other, %%",
|
||||||
|
"Total"
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun toList(): List<String> = headerItems
|
||||||
|
}
|
||||||
|
|
||||||
|
class AggregatedStatsRow(
|
||||||
|
private val declarationType: DeclarationType
|
||||||
|
) : StatsOutput.StatsRow {
|
||||||
|
var liftedUp: Int = 0
|
||||||
|
var successfullyCommonized: Int = 0
|
||||||
|
var failedBecauseAbsent: Int = 0
|
||||||
|
var failedOther: Int = 0
|
||||||
|
|
||||||
|
override fun toList(): List<String> {
|
||||||
|
val total = liftedUp + successfullyCommonized + failedBecauseAbsent + failedOther
|
||||||
|
|
||||||
|
fun fraction(amount: Int): Double = if (total > 0) amount.toDouble() / total else 0.0
|
||||||
|
|
||||||
|
return listOf(
|
||||||
|
declarationType.alias,
|
||||||
|
liftedUp.toString(),
|
||||||
|
fraction(liftedUp).toString(),
|
||||||
|
successfullyCommonized.toString(),
|
||||||
|
fraction(successfullyCommonized).toString(),
|
||||||
|
failedBecauseAbsent.toString(),
|
||||||
|
fraction(failedBecauseAbsent).toString(),
|
||||||
|
failedOther.toString(),
|
||||||
|
fraction(failedOther).toString(),
|
||||||
|
total.toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("MoveVariableDeclarationIntoWhen")
|
||||||
|
private class AggregatingOutput : StatsOutput {
|
||||||
|
val aggregatedStats = HashMap<DeclarationType, AggregatedStatsRow>()
|
||||||
|
|
||||||
|
override fun writeHeader(header: StatsOutput.StatsHeader) {
|
||||||
|
check(header is RawStatsCollector.RawStatsHeader)
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeRow(row: StatsOutput.StatsRow) {
|
||||||
|
check(row is RawStatsCollector.RawStatsRow)
|
||||||
|
|
||||||
|
val aggregatedStatsRow = aggregatedStats.computeIfAbsent(row.declarationType, ::AggregatedStatsRow)
|
||||||
|
when (row.common) {
|
||||||
|
LIFTED_UP -> aggregatedStatsRow.liftedUp++
|
||||||
|
EXPECT -> aggregatedStatsRow.successfullyCommonized++
|
||||||
|
ABSENT -> {
|
||||||
|
if (row.platform.any { it == RawStatsCollector.PlatformDeclarationStatus.ABSENT }) {
|
||||||
|
aggregatedStatsRow.failedBecauseAbsent++
|
||||||
|
} else {
|
||||||
|
aggregatedStatsRow.failedOther++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() = Unit
|
||||||
|
}
|
||||||
+52
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* 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.descriptors.commonizer.stats
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
|
|
||||||
|
enum class DeclarationType(val alias: String) {
|
||||||
|
TOP_LEVEL_CONST_VAL("TOP-LEVEL CONST-VAL"),
|
||||||
|
TOP_LEVEL_VAL("TOP-LEVEL VAL"),
|
||||||
|
TOP_LEVEL_FUN("TOP-LEVEL FUN"),
|
||||||
|
TOP_LEVEL_CLASS("TOP-LEVEL CLASS"),
|
||||||
|
CLASS_CONSTRUCTOR("CLASS_CONSTRUCTOR"),
|
||||||
|
NESTED_VAL("NESTED VAL"),
|
||||||
|
NESTED_FUN("NESTED FUN"),
|
||||||
|
NESTED_CLASS("NESTED CLASS"),
|
||||||
|
NESTED_INTERFACE("NESTED INTERFACE"),
|
||||||
|
COMPANION_OBJECT("COMPANION_OBJECT"),
|
||||||
|
TOP_LEVEL_INTERFACE("TOP-LEVEL INTERFACE"),
|
||||||
|
TYPE_ALIAS("TYPE_ALIAS"),
|
||||||
|
ENUM_ENTRY("ENUM_ENTRY"),
|
||||||
|
ENUM_CLASS("ENUM_CLASS"),
|
||||||
|
MODULE("MODULE"),
|
||||||
|
UNKNOWN("UNKNOWN");
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val DeclarationDescriptor.declarationType: DeclarationType
|
||||||
|
get() = when (this) {
|
||||||
|
is ClassDescriptor -> {
|
||||||
|
if (isCompanionObject) {
|
||||||
|
COMPANION_OBJECT
|
||||||
|
} else when (kind) {
|
||||||
|
ClassKind.ENUM_CLASS -> ENUM_CLASS
|
||||||
|
ClassKind.ENUM_ENTRY -> ENUM_ENTRY
|
||||||
|
ClassKind.INTERFACE -> if (DescriptorUtils.isTopLevelDeclaration(this)) TOP_LEVEL_INTERFACE else NESTED_INTERFACE
|
||||||
|
else -> if (DescriptorUtils.isTopLevelDeclaration(this)) TOP_LEVEL_CLASS else NESTED_CLASS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is TypeAliasDescriptor -> TYPE_ALIAS
|
||||||
|
is ClassConstructorDescriptor -> CLASS_CONSTRUCTOR
|
||||||
|
is FunctionDescriptor -> if (DescriptorUtils.isTopLevelDeclaration(this)) TOP_LEVEL_FUN else NESTED_FUN
|
||||||
|
is PropertyDescriptor -> if (DescriptorUtils.isTopLevelDeclaration(this)) {
|
||||||
|
if (isConst) TOP_LEVEL_CONST_VAL else TOP_LEVEL_VAL
|
||||||
|
} else NESTED_VAL
|
||||||
|
is ModuleDescriptor -> MODULE
|
||||||
|
else -> UNKNOWN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+168
@@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
* 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.descriptors.commonizer.stats
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.descriptors.commonizer.stats.DeclarationType.Companion.declarationType
|
||||||
|
import org.jetbrains.kotlin.descriptors.commonizer.utils.firstNonNull
|
||||||
|
import org.jetbrains.kotlin.descriptors.commonizer.utils.fqNameWithTypeParameters
|
||||||
|
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows printing commonization statistics to the file system.
|
||||||
|
*
|
||||||
|
* Output format is defined in [RawStatsCollector.output].
|
||||||
|
*
|
||||||
|
* Header row: "FQ Name, Extension Receiver, Parameter Names, Parameter Types, Declaration Type, common, <platform1>, <platform2> [<platformN>...]"
|
||||||
|
*
|
||||||
|
* Possible values for "Declaration Type":
|
||||||
|
* - MODULE
|
||||||
|
* - CLASS
|
||||||
|
* - INTERFACE
|
||||||
|
* - OBJECT
|
||||||
|
* - COMPANION_OBJECT
|
||||||
|
* - ENUM_CLASS
|
||||||
|
* - ENUM_ENTRY
|
||||||
|
* - TYPE_ALIAS
|
||||||
|
* - CLASS_CONSTRUCTOR
|
||||||
|
* - FUN
|
||||||
|
* - VAL
|
||||||
|
*
|
||||||
|
* Possible values for "common" column:
|
||||||
|
* - L = declaration lifted up to common fragment
|
||||||
|
* - E = successfully commonized, expect declaration generated
|
||||||
|
* - "-" = no common declaration
|
||||||
|
*
|
||||||
|
* Possible values for each target platform column:
|
||||||
|
* - A = successfully commonized, actual declaration generated
|
||||||
|
* - O = not commonized, the declaration is as in the original library
|
||||||
|
* - "-" = no such declaration in the original library (or declaration has been lifted up)
|
||||||
|
*
|
||||||
|
* Example of output:
|
||||||
|
|
||||||
|
FQ Name|Extension Receiver|Parameter Names|Parameter Types|Declaration Type|common|macos_x64|ios_x64
|
||||||
|
<SystemConfiguration>||||MODULE|E|A|A
|
||||||
|
platform.SystemConfiguration.SCPreferencesContext||||CLASS|E|A|A
|
||||||
|
platform.SystemConfiguration.SCPreferencesContext.Companion||||COMPANION_OBJECT|E|A|A
|
||||||
|
platform.SystemConfiguration.SCNetworkConnectionContext||||CLASS|E|A|A
|
||||||
|
platform.SystemConfiguration.SCNetworkConnectionContext.Companion||||COMPANION_OBJECT|E|A|A
|
||||||
|
platform.SystemConfiguration.SCDynamicStoreRefVar||||TYPE_ALIAS|-|O|O
|
||||||
|
platform.SystemConfiguration.SCVLANInterfaceRef||||TYPE_ALIAS|-|O|O
|
||||||
|
|
||||||
|
*/
|
||||||
|
class RawStatsCollector(
|
||||||
|
private val targets: List<KonanTarget>,
|
||||||
|
private val output: StatsOutput
|
||||||
|
) : StatsCollector {
|
||||||
|
private var headerWritten = false
|
||||||
|
|
||||||
|
override fun logStats(result: List<DeclarationDescriptor?>) {
|
||||||
|
if (!headerWritten) {
|
||||||
|
writeHeader()
|
||||||
|
headerWritten = true
|
||||||
|
}
|
||||||
|
|
||||||
|
val firstNotNull = result.firstNonNull()
|
||||||
|
val lastIsNull = result.last() == null
|
||||||
|
|
||||||
|
val statsRow = RawStatsRow(
|
||||||
|
dimension = targets.size,
|
||||||
|
fqName = if (firstNotNull is ModuleDescriptor) firstNotNull.name.asString() else firstNotNull.fqNameSafe.asString(),
|
||||||
|
declarationType = firstNotNull.declarationType
|
||||||
|
)
|
||||||
|
|
||||||
|
// extension receiver
|
||||||
|
if (firstNotNull is PropertyDescriptor || firstNotNull is SimpleFunctionDescriptor) {
|
||||||
|
statsRow.extensionReceiver =
|
||||||
|
(firstNotNull as CallableDescriptor).extensionReceiverParameter?.type?.fqNameWithTypeParameters.orEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstNotNull is ConstructorDescriptor || firstNotNull is SimpleFunctionDescriptor) {
|
||||||
|
val functionDescriptor = (firstNotNull as FunctionDescriptor)
|
||||||
|
// parameter names
|
||||||
|
statsRow.parameterNames = functionDescriptor.valueParameters.joinToString { it.name.asString() }
|
||||||
|
// parameter types
|
||||||
|
statsRow.parameterTypes = functionDescriptor.valueParameters.joinToString { it.type.fqNameWithTypeParameters }
|
||||||
|
}
|
||||||
|
|
||||||
|
var isLiftedUp = !lastIsNull
|
||||||
|
for (index in 0 until result.size - 1) {
|
||||||
|
statsRow.platform += when {
|
||||||
|
result[index] == null -> PlatformDeclarationStatus.ABSENT
|
||||||
|
lastIsNull -> PlatformDeclarationStatus.ORIGINAL
|
||||||
|
else -> {
|
||||||
|
isLiftedUp = false
|
||||||
|
PlatformDeclarationStatus.ACTUAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
statsRow.common = when {
|
||||||
|
isLiftedUp -> CommonDeclarationStatus.LIFTED_UP
|
||||||
|
lastIsNull -> CommonDeclarationStatus.ABSENT
|
||||||
|
else -> CommonDeclarationStatus.EXPECT
|
||||||
|
}
|
||||||
|
|
||||||
|
output.writeRow(statsRow)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
output.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun writeHeader() {
|
||||||
|
output.writeHeader(RawStatsHeader(targets.map { it.name }))
|
||||||
|
}
|
||||||
|
|
||||||
|
class RawStatsHeader(
|
||||||
|
private val targetNames: List<String>
|
||||||
|
) : StatsOutput.StatsHeader {
|
||||||
|
override fun toList(): List<String> = mutableListOf<String>().apply {
|
||||||
|
this += "FQ Name"
|
||||||
|
this += "Extension Receiver"
|
||||||
|
this += "Parameter Names"
|
||||||
|
this += "Parameter Types"
|
||||||
|
this += "Declaration Type"
|
||||||
|
this += "common"
|
||||||
|
this += targetNames
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RawStatsRow(
|
||||||
|
dimension: Int,
|
||||||
|
val fqName: String,
|
||||||
|
val declarationType: DeclarationType
|
||||||
|
) : StatsOutput.StatsRow {
|
||||||
|
var extensionReceiver: String = ""
|
||||||
|
var parameterNames: String = ""
|
||||||
|
var parameterTypes: String = ""
|
||||||
|
lateinit var common: CommonDeclarationStatus
|
||||||
|
val platform: MutableList<PlatformDeclarationStatus> = ArrayList(dimension)
|
||||||
|
|
||||||
|
override fun toList(): List<String> = mutableListOf<String>().apply {
|
||||||
|
this += fqName
|
||||||
|
this += extensionReceiver
|
||||||
|
this += parameterNames
|
||||||
|
this += parameterTypes
|
||||||
|
this += declarationType.alias
|
||||||
|
this += common.alias.toString()
|
||||||
|
platform.forEach { this += it.alias.toString() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class CommonDeclarationStatus(val alias: Char) {
|
||||||
|
LIFTED_UP('L'),
|
||||||
|
EXPECT('E'),
|
||||||
|
ABSENT('-')
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class PlatformDeclarationStatus(val alias: Char) {
|
||||||
|
ACTUAL('A'),
|
||||||
|
ORIGINAL('O'),
|
||||||
|
ABSENT('-')
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-3
@@ -1,13 +1,13 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* 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.
|
* 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.descriptors.commonizer
|
package org.jetbrains.kotlin.descriptors.commonizer.stats
|
||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
|
|
||||||
interface StatsCollector : Closeable {
|
interface StatsCollector : Closeable {
|
||||||
fun logStats(output: List<DeclarationDescriptor?>)
|
fun logStats(result: List<DeclarationDescriptor?>)
|
||||||
}
|
}
|
||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* 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.descriptors.commonizer.stats
|
||||||
|
|
||||||
|
import java.io.Closeable
|
||||||
|
import java.io.File
|
||||||
|
import java.io.PrintWriter
|
||||||
|
|
||||||
|
interface StatsOutput : Closeable {
|
||||||
|
interface StatsHeader {
|
||||||
|
fun toList(): List<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StatsRow {
|
||||||
|
fun toList(): List<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeHeader(header: StatsHeader)
|
||||||
|
fun writeRow(row: StatsRow)
|
||||||
|
}
|
||||||
|
|
||||||
|
class FileStatsOutput(directory: File, baseName: String) : StatsOutput {
|
||||||
|
init {
|
||||||
|
directory.mkdirs()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val writer: PrintWriter = directory.resolve("${baseName}_stats.csv").printWriter()
|
||||||
|
private var width: Int = 0
|
||||||
|
|
||||||
|
override fun writeHeader(header: StatsOutput.StatsHeader) {
|
||||||
|
check(width == 0)
|
||||||
|
val headerItems = header.toList()
|
||||||
|
require(headerItems.isNotEmpty())
|
||||||
|
|
||||||
|
width = headerItems.size
|
||||||
|
headerItems.joinTo(writer, separator = "|", postfix = "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeRow(row: StatsOutput.StatsRow) {
|
||||||
|
check(width > 0)
|
||||||
|
val rowItems = row.toList()
|
||||||
|
require(rowItems.size == width)
|
||||||
|
|
||||||
|
rowItems.joinTo(writer, separator = "|", postfix = "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
writer.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user