[FIR/AA] WIP - Stats for speculative vs. precise symbol provider accesses
^KT-72663
diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirModuleWithDependenciesSymbolProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirModuleWithDependenciesSymbolProvider.kt
index 47d0010..6e38b6e 100644
--- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirModuleWithDependenciesSymbolProvider.kt
+++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirModuleWithDependenciesSymbolProvider.kt
@@ -5,7 +5,10 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.providers
+import org.jetbrains.kotlin.analysis.api.platform.KaCachedService
+import org.jetbrains.kotlin.analysis.low.level.api.fir.projectStructure.llFirModuleData
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSession
+import org.jetbrains.kotlin.analysis.low.level.api.fir.statistics.LLStatisticsService
import org.jetbrains.kotlin.analysis.low.level.api.fir.stubBased.deserialization.StubBasedFirDeserializedSymbolProvider
import org.jetbrains.kotlin.analysis.utils.collections.buildSmartList
import org.jetbrains.kotlin.fir.FirSession
@@ -34,6 +37,11 @@
val providers: List<FirSymbolProvider>,
val dependencyProvider: LLFirDependenciesSymbolProvider,
) : FirSymbolProvider(session) {
+ @KaCachedService
+ private val symbolProviderStatistics by lazy(LazyThreadSafetyMode.PUBLICATION) {
+ LLStatisticsService.getInstance(session.llFirModuleData.ktModule.project)?.symbolProviders
+ }
+
/**
* This symbol names provider is not used directly by [LLFirModuleWithDependenciesSymbolProvider], because in the IDE, Java symbol
* providers currently cannot provide name sets (see KTIJ-24642). So in most cases, name sets would be `null` anyway.
@@ -53,9 +61,24 @@
)
}
- override fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>? =
- getClassLikeSymbolByClassIdWithoutDependencies(classId)
- ?: dependencyProvider.getClassLikeSymbolByClassId(classId)
+ override fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>? {
+ val symbol =
+ getClassLikeSymbolByClassIdWithoutDependencies(classId)
+ ?: dependencyProvider.getClassLikeSymbolByClassId(classId)
+
+ symbolProviderStatistics?.let { statistics ->
+ if (symbol != null) {
+ statistics.moduleClassHits.add(1)
+ } else {
+ statistics.moduleClassMisses.add(1)
+ if (classId.isNestedClass) {
+ statistics.moduleNestedClassMisses.add(1)
+ }
+ }
+ }
+
+ return symbol
+ }
fun getClassLikeSymbolByClassIdWithoutDependencies(classId: ClassId): FirClassLikeSymbol<*>? =
providers.firstNotNullOfOrNull { it.getClassLikeSymbolByClassId(classId) }
diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/statistics/LLStatisticsScopes.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/statistics/LLStatisticsScopes.kt
index 2ad5b36..3d5b42b 100644
--- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/statistics/LLStatisticsScopes.kt
+++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/statistics/LLStatisticsScopes.kt
@@ -32,6 +32,19 @@
}
object SymbolProviders : LLStatisticsScope("$name.symbolProviders") {
+ // The number of uniquely computed "classifier names in package" sets per `KaModule`.
+ // How to calculate: Keep a global "`KaModule` -> package name" map and record a hit when a classifier name set is computed (in any
+ // symbol provider). Every new module/package name combination leads to one count increment.
+ object UniqueClassifierNameSets : LLStatisticsScope("$name.uniqueClassifierNameSets")
+
+ object Module : LLStatisticsScope("$name.module") {
+ // NOTE: These are symbol provider hits themselves, i.e. "does the symbol provider return non-null here?"
+ // Not to be confused with cache hits/misses, which need much clearer naming.
+ object ClassHits : LLStatisticsScope("$name.classHits")
+ object ClassMisses : LLStatisticsScope("$name.classMisses")
+ object NestedClassMisses : LLStatisticsScope("$name.nestedClassMisses")
+ }
+
object Combined : LLStatisticsScope("$name.combined"), LLCaffeineStatisticsScope {
object Hits : LLStatisticsScope("$name.hits")
object Misses : LLStatisticsScope("$name.misses")
diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/statistics/domains/LLSymbolProviderStatistics.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/statistics/domains/LLSymbolProviderStatistics.kt
index 12f7c08..80ec226 100644
--- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/statistics/domains/LLSymbolProviderStatistics.kt
+++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/statistics/domains/LLSymbolProviderStatistics.kt
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.statistics.domains
+import io.opentelemetry.api.metrics.LongCounter
import org.jetbrains.kotlin.analysis.low.level.api.fir.statistics.LLCaffeineStatsCounter
import org.jetbrains.kotlin.analysis.low.level.api.fir.statistics.LLStatisticsScopes
import org.jetbrains.kotlin.analysis.low.level.api.fir.statistics.LLStatisticsService
@@ -17,4 +18,8 @@
* A global [Caffeine stats counter][com.github.benmanes.caffeine.cache.stats.StatsCounter] for combined symbol provider caches.
*/
val combinedSymbolProviderCacheStatsCounter = LLCaffeineStatsCounter(meter, LLStatisticsScopes.SymbolProviders.Combined)
+
+ val moduleClassHits: LongCounter = meter.counterBuilder(LLStatisticsScopes.SymbolProviders.Module.ClassHits.name).build()
+ val moduleClassMisses: LongCounter = meter.counterBuilder(LLStatisticsScopes.SymbolProviders.Module.ClassMisses.name).build()
+ val moduleNestedClassMisses: LongCounter = meter.counterBuilder(LLStatisticsScopes.SymbolProviders.Module.NestedClassMisses.name).build()
}