[evaluation-plugin] Record FUS to file

Co-authored-by:
Gleb Marin <gleb.marin@jetbrains.com>
Igor Davidenko <Igor.Davidenko@jetbrains.com>

Merge-request: IJ-MR-139810
Merged-by: Igor Davidenko <Igor.Davidenko@jetbrains.com>

GitOrigin-RevId: e6918392f03aa7eaf6f0b0c9a6ec3758a58d33d5
This commit is contained in:
Gleb Marin
2024-07-24 18:34:10 +00:00
committed by intellij-monorepo-bot
parent 39883358b0
commit 06a027d4c2
20 changed files with 354 additions and 108 deletions

View File

@@ -17,5 +17,10 @@
<orderEntry type="module" module-name="intellij.platform.core" />
<orderEntry type="module" module-name="intellij.tools.ide.metrics.collector" />
<orderEntry type="library" name="jackson-databind" level="project" />
<orderEntry type="module" module-name="intellij.platform.statistics" />
<orderEntry type="module" module-name="intellij.platform.util" />
<orderEntry type="module" module-name="intellij.platform.extensions" />
<orderEntry type="module" module-name="intellij.platform.statistics.uploader" />
<orderEntry type="module" module-name="intellij.platform.util.ex" />
</component>
</module>

View File

@@ -0,0 +1,72 @@
package com.intellij.cce.fus
import com.intellij.cce.workspace.storages.LogsSaver
import com.intellij.internal.statistic.eventLog.LogEventSerializer
import com.intellij.openapi.application.PathManager
import com.intellij.openapi.util.io.toNioPathOrNull
import com.intellij.util.io.delete
import com.jetbrains.fus.reporting.model.lion3.LogEvent
import java.nio.file.Files
import java.nio.file.Path
import kotlin.io.path.*
/**
* Saves FUS logs of the action that will be passed in [invokeRememberingLogs] to [finalStorageDir].
* In the resulting directory, logs will be divided into files by groups.
*/
class FusLogsSaver(private val finalStorageDir: Path, private val allowedGroups: List<String>? = null) : LogsSaver {
private val temporaryFusLogsDirectory: Path = PathManager.getSystemPath().toNioPathOrNull()!! / "completion-fus-logs"
init {
if (temporaryFusLogsDirectory.exists()) {
clearTemporaryStorage()
}
temporaryFusLogsDirectory.createDirectory()
require(finalStorageDir.exists())
}
override fun <T> invokeRememberingLogs(action: () -> T): T {
val logFilter: (LogEvent) -> Boolean = if (allowedGroups != null) { log -> log.group.id in allowedGroups }
else { _ -> true }
val (actionResult, fusLogs) = collectingFusLogs(logFilter, action)
val fusLogsByGroup = fusLogs.groupBy { it.group }
for ((eventGroup, groupLogs) in fusLogsByGroup) {
val serialisedGroupLogs = groupLogs.joinToString("\n") { LogEventSerializer.toString(it) }
val fusGroupPath = temporaryFusLogsDirectory / eventGroup.id
fusGroupPath.toFile().appendText(serialisedGroupLogs + "\n")
}
return actionResult
}
@OptIn(ExperimentalPathApi::class)
override fun save(languageName: String, trainingPercentage: Int) {
try {
for (sessionTemporaryRelativePath in temporaryFusLogsDirectory.walk()) {
val sessionTemporaryPath = temporaryFusLogsDirectory / sessionTemporaryRelativePath
val sessionId = sessionTemporaryPath.fileName
val sessionFinalPath = obtainFinalLogsDirectory(languageName) / sessionId
Files.move(sessionTemporaryPath, sessionFinalPath)
}
}
finally {
clearTemporaryStorage()
}
}
private fun obtainFinalLogsDirectory(languageName: String): Path {
val fusFinalDirectory = finalStorageDir / languageName
if (!fusFinalDirectory.exists()) {
fusFinalDirectory.createDirectory()
}
else {
require(fusFinalDirectory.isDirectory())
}
return fusFinalDirectory
}
private fun clearTemporaryStorage() {
require(temporaryFusLogsDirectory.exists())
temporaryFusLogsDirectory.delete(true)
}
}

View File

@@ -0,0 +1,80 @@
package com.intellij.cce.fus
import com.intellij.internal.statistic.eventLog.*
import com.intellij.openapi.Disposable
import com.intellij.openapi.extensions.impl.ExtensionPointImpl
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.use
import com.jetbrains.fus.reporting.model.lion3.LogEvent
import java.util.concurrent.CompletableFuture
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.Executor
object MockFUSCollector {
fun <T> collectLogEvents(parentDisposable: Disposable,
action: () -> T): Pair<List<LogEvent>, T> {
val mockLoggerProvider = MockStatisticsEventLoggerProvider("ML")
(StatisticsEventLoggerProvider.EP_NAME.point as ExtensionPointImpl<StatisticsEventLoggerProvider>)
.maskAll(listOf(mockLoggerProvider), parentDisposable, true)
val actionResult = action()
return mockLoggerProvider.getLoggedEvents() to actionResult
}
}
private class MockStatisticsEventLoggerProvider(recorderId: String) : StatisticsEventLoggerProvider(recorderId,
1,
DEFAULT_SEND_FREQUENCY_MS,
DEFAULT_MAX_FILE_SIZE_BYTES,
false,
true) {
override val logger: MockStatisticsEventLogger = MockStatisticsEventLogger()
override fun isRecordEnabled(): Boolean = true
override fun isSendEnabled(): Boolean = false
fun getLoggedEvents(): List<LogEvent> = logger.logged
class MockStatisticsEventLogger(private val session: String = "mockSession",
private val build: String = "999.999",
private val bucket: String = "1",
private val recorderVersion: String = "1") : StatisticsEventLogger {
val logged = CopyOnWriteArrayList<LogEvent>()
override fun logAsync(group: EventLogGroup, eventId: String, data: Map<String, Any>, isState: Boolean): CompletableFuture<Void> {
val eventTime = System.currentTimeMillis()
val event = newLogEvent(session, build, bucket, eventTime, group.id, group.version.toString(), recorderVersion, eventId, isState,
data)
.escape()
logged.add(event)
return CompletableFuture.completedFuture(null)
}
override fun logAsync(group: EventLogGroup,
eventId: String,
dataProvider: () -> Map<String, Any>?,
isState: Boolean): CompletableFuture<Void> {
val data = dataProvider() ?: return CompletableFuture.completedFuture(null)
return logAsync(group, eventId, data, isState)
}
override fun computeAsync(computation: (backgroundThreadExecutor: Executor) -> Unit) {
}
override fun getActiveLogFile(): EventLogFile? = null
override fun getLogFilesProvider(): EventLogFilesProvider = EmptyEventLogFilesProvider
override fun cleanup() {}
override fun rollOver() {}
}
}
fun <T> collectingFusLogs(logEventFilter: (LogEvent) -> Boolean, action: () -> T): Pair<T, List<LogEvent>> = Disposer.newDisposable().use { lifetime ->
val (allFusLogs, result) = MockFUSCollector.collectLogEvents(lifetime, action)
val mlFusLogs: List<LogEvent> = allFusLogs.filter { logEventFilter(it) }
result to mlFusLogs
}

View File

@@ -0,0 +1,78 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.cce.interpreter
import com.intellij.cce.actions.*
import com.intellij.cce.core.Session
import com.intellij.cce.util.FileTextUtil.computeChecksum
import com.intellij.cce.util.FileTextUtil.getDiff
import java.nio.file.Paths
import kotlin.random.Random
class ActionInvokingInterpreter(private val invokersFactory: InvokersFactory,
private val handler: InterpretationHandler,
private val filter: InterpretFilter,
private val order: InterpretationOrder,
private val projectPath: String?) : Interpreter {
override fun interpret(fileActions: FileActions, sessionHandler: (Session) -> Unit): List<Session> {
val actionsInvoker = invokersFactory.createActionsInvoker()
val featureInvoker = invokersFactory.createFeatureInvoker()
val sessions = mutableListOf<Session>()
val filePath = if (projectPath == null) fileActions.path else Paths.get(projectPath).resolve(fileActions.path).toString()
val needToClose = !actionsInvoker.isOpen(filePath)
val text = actionsInvoker.openFile(filePath)
if (fileActions.checksum != computeChecksum(text)) {
handler.onErrorOccurred(IllegalStateException("File $filePath has been modified."), fileActions.sessionsCount)
return emptyList()
}
var shouldCompleteToken = filter.shouldCompleteToken()
var isCanceled = false
val actions = fileActions.actions.reorder(order)
for (action in actions) {
handler.onActionStarted(action)
when (action) {
is MoveCaret -> {
actionsInvoker.moveCaret(action.offset)
}
is CallFeature -> {
if (shouldCompleteToken) {
val session = featureInvoker.callFeature(action.expectedText, action.offset, action.nodeProperties)
sessions.add(session)
sessionHandler(session)
}
isCanceled = handler.onSessionFinished(fileActions.path)
shouldCompleteToken = filter.shouldCompleteToken()
}
is Rename -> actionsInvoker.rename(action.newName)
is PrintText -> actionsInvoker.printText(action.text)
is DeleteRange -> actionsInvoker.deleteRange(action.begin, action.end)
is SelectRange -> actionsInvoker.selectRange(action.begin, action.end)
is Delay -> actionsInvoker.delay(action.seconds)
}
if (isCanceled) break
}
actionsInvoker.save()
val resultText = actionsInvoker.getText()
if (text != resultText) {
actionsInvoker.deleteRange(0, resultText.length)
actionsInvoker.printText(text)
if (needToClose) actionsInvoker.closeFile(filePath)
throw IllegalStateException("Text before and after interpretation doesn't match. Diff:\n${getDiff(text, resultText)}")
}
if (needToClose) actionsInvoker.closeFile(filePath)
handler.onFileProcessed(fileActions.path)
return sessions.sortedBy { it.offset }
}
private fun List<Action>.reorder(order: InterpretationOrder): List<Action> {
val groups = groupBy { it.sessionId }.values
return when (order) {
InterpretationOrder.LINEAR -> groups.flatten()
InterpretationOrder.REVERSED -> groups.reversed().flatten()
InterpretationOrder.RANDOM -> groups.shuffled(ORDER_RANDOM).flatten()
}
}
}
private val ORDER_RANDOM = Random(42)

View File

@@ -3,76 +3,7 @@ package com.intellij.cce.interpreter
import com.intellij.cce.actions.*
import com.intellij.cce.core.Session
import com.intellij.cce.util.FileTextUtil.computeChecksum
import com.intellij.cce.util.FileTextUtil.getDiff
import java.nio.file.Paths
import kotlin.random.Random
class Interpreter(private val invokersFactory: InvokersFactory,
private val handler: InterpretationHandler,
private val filter: InterpretFilter,
private val order: InterpretationOrder,
private val projectPath: String?) {
fun interpret(fileActions: FileActions, sessionHandler: (Session) -> Unit): List<Session> {
val actionsInvoker = invokersFactory.createActionsInvoker()
val featureInvoker = invokersFactory.createFeatureInvoker()
val sessions = mutableListOf<Session>()
val filePath = if (projectPath == null) fileActions.path else Paths.get(projectPath).resolve(fileActions.path).toString()
val needToClose = !actionsInvoker.isOpen(filePath)
val text = actionsInvoker.openFile(filePath)
if (fileActions.checksum != computeChecksum(text)) {
handler.onErrorOccurred(IllegalStateException("File $filePath has been modified."), fileActions.sessionsCount)
return emptyList()
}
var shouldCompleteToken = filter.shouldCompleteToken()
var isCanceled = false
val actions = fileActions.actions.reorder(order)
for (action in actions) {
handler.onActionStarted(action)
when (action) {
is MoveCaret -> {
actionsInvoker.moveCaret(action.offset)
}
is CallFeature -> {
if (shouldCompleteToken) {
val session = featureInvoker.callFeature(action.expectedText, action.offset, action.nodeProperties)
sessions.add(session)
sessionHandler(session)
}
isCanceled = handler.onSessionFinished(fileActions.path)
shouldCompleteToken = filter.shouldCompleteToken()
}
is Rename -> actionsInvoker.rename(action.newName)
is PrintText -> actionsInvoker.printText(action.text)
is DeleteRange -> actionsInvoker.deleteRange(action.begin, action.end)
is SelectRange -> actionsInvoker.selectRange(action.begin, action.end)
is Delay -> actionsInvoker.delay(action.seconds)
}
if (isCanceled) break
}
actionsInvoker.save()
val resultText = actionsInvoker.getText()
if (text != resultText) {
actionsInvoker.deleteRange(0, resultText.length)
actionsInvoker.printText(text)
if (needToClose) actionsInvoker.closeFile(filePath)
throw IllegalStateException("Text before and after interpretation doesn't match. Diff:\n${getDiff(text, resultText)}")
}
if (needToClose) actionsInvoker.closeFile(filePath)
handler.onFileProcessed(fileActions.path)
return sessions.sortedBy { it.offset }
}
private fun List<Action>.reorder(order: InterpretationOrder): List<Action> {
val groups = groupBy { it.sessionId }.values
return when (order) {
InterpretationOrder.LINEAR -> groups.flatten()
InterpretationOrder.REVERSED -> groups.reversed().flatten()
InterpretationOrder.RANDOM -> groups.shuffled(ORDER_RANDOM).flatten()
}
}
interface Interpreter {
fun interpret(fileActions: FileActions, sessionHandler: (Session) -> Unit): List<Session>
}
private val ORDER_RANDOM = Random(42)

View File

@@ -0,0 +1,15 @@
package com.intellij.cce.interpreter
import com.intellij.cce.actions.FileActions
import com.intellij.cce.core.Session
import com.intellij.cce.workspace.storages.LogsSaver
class LoggingInterpreterWrapper(private val baseInterpreter: Interpreter, private val logsSaver: LogsSaver) : Interpreter {
override fun interpret(fileActions: FileActions, sessionHandler: (Session) -> Unit): List<Session> {
return logsSaver.invokeRememberingLogs {
baseInterpreter.interpret(fileActions, sessionHandler)
}
}
}
fun Interpreter.wrapLogging(logsSaver: LogsSaver): Interpreter = LoggingInterpreterWrapper(this, logsSaver)

View File

@@ -83,6 +83,7 @@ data class Config private constructor(
val sessionSeed: Long?,
val order: InterpretationOrder,
val saveLogs: Boolean,
val saveFusLogs: Boolean,
val saveFeatures: Boolean,
val saveContent: Boolean,
val logLocationAndItemText: Boolean,
@@ -122,6 +123,7 @@ data class Config private constructor(
var outputDir: String = Paths.get(projectPath, "completion-evaluation").toAbsolutePath().toString()
var strategy: EvaluationStrategy = EvaluationStrategy.defaultStrategy
var saveLogs = false
var saveFusLogs = false
var saveFeatures = true
var saveContent = false
var logLocationAndItemText = false
@@ -147,6 +149,7 @@ data class Config private constructor(
evaluationRoots.addAll(config.actions.evaluationRoots)
ignoreFileNames.addAll(config.actions.ignoreFileNames)
saveLogs = config.interpret.saveLogs
saveFusLogs = config.interpret.saveFusLogs
saveFeatures = config.interpret.saveFeatures
saveContent = config.interpret.saveContent
logLocationAndItemText = config.interpret.logLocationAndItemText
@@ -195,6 +198,7 @@ data class Config private constructor(
sessionSeed,
order,
saveLogs,
saveFusLogs,
saveFeatures,
saveContent,
logLocationAndItemText,

View File

@@ -88,6 +88,7 @@ object ConfigFactory {
builder.order = InterpretationOrder.valueOf(map.getAs<String>("order"))
}
builder.saveLogs = map.getAs("saveLogs")
builder.saveFusLogs = map.getAs("saveFusLogs")
if (map.containsKey("saveFeatures")) {
builder.saveFeatures = map.getAs("saveFeatures")
}

View File

@@ -4,6 +4,7 @@ package com.intellij.cce.workspace
import com.google.gson.Gson
import com.intellij.cce.evaluable.EvaluationStrategy
import com.intellij.cce.evaluable.StrategySerializer
import com.intellij.cce.fus.FusLogsSaver
import com.intellij.cce.workspace.storages.*
import java.io.FileWriter
import java.nio.file.Files
@@ -12,25 +13,25 @@ import java.nio.file.Paths
import java.text.SimpleDateFormat
import java.util.*
class EvaluationWorkspace private constructor(private val basePath: Path) {
class EvaluationWorkspace private constructor(private val basePath: Path,
statsLogsPath: Path) {
companion object {
private const val DEFAULT_REPORT_TYPE = "html"
private val gson = Gson()
private val formatter = SimpleDateFormat("yyyy-MM-dd_HH-mm-ss")
fun open(workspaceDir: String): EvaluationWorkspace {
return EvaluationWorkspace(Paths.get(workspaceDir).toAbsolutePath())
fun open(workspaceDir: String, statsLogsPath: Path): EvaluationWorkspace {
return EvaluationWorkspace(Paths.get(workspaceDir).toAbsolutePath(), statsLogsPath)
}
fun create(config: Config): EvaluationWorkspace {
val workspace = EvaluationWorkspace(Paths.get(config.outputDir).toAbsolutePath().resolve(formatter.format(Date())))
fun create(config: Config, statsLogsPath: Path): EvaluationWorkspace {
val workspace = EvaluationWorkspace(Paths.get(config.outputDir).toAbsolutePath().resolve(formatter.format(Date())), statsLogsPath)
workspace.writeConfig(config)
return workspace
}
}
private val sessionsDir = subdir("data")
private val logsDir = subdir("logs")
private val fullLineLogsDir = subdir("full-line-logs")
private val featuresDir = subdir("features")
private val actionsDir = subdir("actions")
@@ -45,7 +46,9 @@ class EvaluationWorkspace private constructor(private val basePath: Path) {
val errorsStorage: FileErrorsStorage = FileErrorsStorage(errorsDir.toString())
val logsStorage: LogsStorage = LogsStorage(logsDir.toString())
val statLogsSaver: StatLogsSaver = StatLogsSaver(statsLogsPath, subdir("logs"))
val fusLogsSaver: LogsSaver = FusLogsSaver(subdir("fus-logs"))
val featuresStorage: FeaturesStorage = FeaturesStorageImpl(featuresDir.toString())

View File

@@ -0,0 +1,41 @@
package com.intellij.cce.workspace.storages
interface LogsSaver {
fun <T> invokeRememberingLogs(action: () -> T): T
fun save(languageName: String, trainingPercentage: Int)
}
class NoLogsSaver : LogsSaver {
override fun <T> invokeRememberingLogs(action: () -> T): T = action()
override fun save(languageName: String, trainingPercentage: Int) = Unit
}
fun logsSaverIf(condition: Boolean, createSaver: () -> LogsSaver): LogsSaver = if (condition) createSaver() else NoLogsSaver()
private fun makeNestingCollector(saverA: LogsSaver,
saverB: LogsSaver
) = object : LogsSaver {
override fun <T> invokeRememberingLogs(action: () -> T): T {
return saverA.invokeRememberingLogs {
saverB.invokeRememberingLogs(action)
}
}
override fun save(languageName: String, trainingPercentage: Int) {
try {
saverA.save(languageName, trainingPercentage)
}
finally {
saverB.save(languageName, trainingPercentage)
}
}
}
fun Collection<LogsSaver>.asCompositeLogsSaver(): LogsSaver {
val meaningfulSavers = this@asCompositeLogsSaver.filter { it !is NoLogsSaver }
val meaningfulLogsSavers: List<LogsSaver> = meaningfulSavers.ifEmpty { listOf(NoLogsSaver()) }
return meaningfulLogsSavers.reduce(::makeNestingCollector)
}

View File

@@ -0,0 +1,2 @@
package com.intellij.cce.workspace.storages

View File

@@ -2,31 +2,33 @@
package com.intellij.cce.workspace.storages
import java.io.BufferedWriter
import java.io.File
import java.io.FileWriter
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.text.SimpleDateFormat
import java.util.*
import java.util.zip.GZIPInputStream
import kotlin.io.path.div
class LogsStorage(private val storageDir: String) {
class StatLogsSaver(private val logsTemporaryStoragePath: Path, private val finalStorageDir: Path) : LogsSaver {
private val formatter = SimpleDateFormat("dd_MM_yyyy")
private val sessionIds = linkedSetOf<String>()
val path = storageDir
override fun <T> invokeRememberingLogs(action: () -> T): T = action()
fun save(logsPath: String, languageName: String, trainingPercentage: Int) {
val logsDir = File(logsPath)
override fun save(languageName: String, trainingPercentage: Int) {
val logsDir = logsTemporaryStoragePath.toFile()
if (!logsDir.exists()) return
val outputDir = Paths.get(storageDir, languageName)
require(logsDir.isDirectory)
val outputDir = finalStorageDir / languageName
Files.createDirectories(outputDir)
FileWriter(Paths.get(outputDir.toString(), "full.log").toString()).use { writer ->
for (logChunk in (logsDir.listFiles() ?: emptyArray())
.filter { it.name.startsWith("chunk") }
.map { it.name.toString() }
.sortedBy { it.substringAfterLast('_').substringBefore(".gz").toInt() }) {
val chunkPath = Paths.get(logsPath, logChunk)
val chunkPath = logsTemporaryStoragePath / logChunk
if (Files.exists(chunkPath)) {
val log = GZIPInputStream(chunkPath.toFile().readBytes().inputStream()).reader().readText()
sessionIds.addAll(log.split("\n").filter { it.isNotBlank() }.map { getSessionId(it) })
@@ -36,7 +38,7 @@ class LogsStorage(private val storageDir: String) {
}
}
saveLogs(outputDir.toString(), trainingPercentage)
File(storageDir).compress()
finalStorageDir.toFile().compress()
}
private fun saveLogs(outputDir: String, trainingPercentage: Int) {

View File

@@ -13,5 +13,7 @@
<orderEntry type="module" module-name="intellij.platform.ide" />
<orderEntry type="module" module-name="intellij.platform.lang.impl" />
<orderEntry type="module" module-name="intellij.evaluationPlugin.core" />
<orderEntry type="module" module-name="intellij.platform.statistics" />
<orderEntry type="module" module-name="intellij.platform.statistics.uploader" />
</component>
</module>

View File

@@ -15,6 +15,7 @@ import com.intellij.cce.evaluable.StrategySerializer
import com.intellij.cce.evaluation.BackgroundStepFactory
import com.intellij.cce.evaluation.EvaluationProcess
import com.intellij.cce.evaluation.EvaluationRootInfo
import com.intellij.cce.evaluation.step.SetupStatsCollectorStep
import com.intellij.cce.util.ExceptionsUtil.stackTraceToString
import com.intellij.cce.workspace.ConfigFactory
import com.intellij.cce.workspace.EvaluationWorkspace
@@ -109,7 +110,7 @@ internal class CompletionEvaluationStarter : ApplicationStarter {
override fun run() {
val feature = EvaluableFeature.forFeature(featureName) ?: throw Exception("No support for the $featureName")
val config = loadConfig(Paths.get(configPath), feature.getStrategySerializer())
val workspace = EvaluationWorkspace.create(config)
val workspace = EvaluationWorkspace.create(config, SetupStatsCollectorStep.statsCollectorLogsDirectory)
runPreliminarySteps(feature, workspace)
loadAndApply(config.projectPath) { project ->
val stepFactory = BackgroundStepFactory(feature, config, project, null, EvaluationRootInfo(true))
@@ -148,7 +149,7 @@ internal class CompletionEvaluationStarter : ApplicationStarter {
override fun run() {
val feature = EvaluableFeature.forFeature(featureName) ?: throw Exception("No support for the feature")
val workspace = EvaluationWorkspace.open(workspacePath)
val workspace = EvaluationWorkspace.open(workspacePath, SetupStatsCollectorStep.statsCollectorLogsDirectory)
val config = workspace.readConfig(feature.getStrategySerializer())
runPreliminarySteps(feature, workspace)
loadAndApply(config.projectPath) { project ->
@@ -170,11 +171,11 @@ internal class CompletionEvaluationStarter : ApplicationStarter {
override fun run() {
val workspacesToCompare = getWorkspaces()
val feature = EvaluableFeature.forFeature(featureName) ?: throw Exception("No support for the feature")
val config = workspacesToCompare.map { EvaluationWorkspace.open(it) }.buildMultipleEvaluationsConfig(
val config = workspacesToCompare.map { EvaluationWorkspace.open(it, SetupStatsCollectorStep.statsCollectorLogsDirectory) }.buildMultipleEvaluationsConfig(
feature.getStrategySerializer(),
"COMPARING",
)
val outputWorkspace = EvaluationWorkspace.create(config)
val outputWorkspace = EvaluationWorkspace.create(config, SetupStatsCollectorStep.statsCollectorLogsDirectory)
loadAndApply(config.projectPath) { project ->
val process = EvaluationProcess.build({
shouldGenerateReports = true
@@ -207,12 +208,12 @@ internal class CompletionEvaluationStarter : ApplicationStarter {
override fun run() {
val workspacesToMerge = readWorkspacesFromDirectory(root)
val feature = EvaluableFeature.forFeature(featureName) ?: throw Exception("No support for the feature")
val config = workspacesToMerge.map { EvaluationWorkspace.open(it) }.buildMultipleEvaluationsConfig(
val config = workspacesToMerge.map { EvaluationWorkspace.open(it, SetupStatsCollectorStep.statsCollectorLogsDirectory) }.buildMultipleEvaluationsConfig(
feature.getStrategySerializer()
)
val outputWorkspace = EvaluationWorkspace.create(config)
val outputWorkspace = EvaluationWorkspace.create(config, SetupStatsCollectorStep.statsCollectorLogsDirectory)
for (workspacePath in workspacesToMerge) {
val workspace = EvaluationWorkspace.open(workspacePath)
val workspace = EvaluationWorkspace.open(workspacePath, SetupStatsCollectorStep.statsCollectorLogsDirectory)
val sessionFiles = workspace.sessionsStorage.getSessionFiles()
for (sessionFile in sessionFiles) {
outputWorkspace.sessionsStorage.saveSessions(workspace.sessionsStorage.getSessions(sessionFile.first))

View File

@@ -14,6 +14,7 @@ import com.intellij.cce.evaluable.common.getEditorSafe
import com.intellij.cce.evaluable.completion.BaseCompletionActionsInvoker
import com.intellij.cce.evaluation.*
import com.intellij.cce.evaluation.step.ActionsGenerationStep
import com.intellij.cce.evaluation.step.SetupStatsCollectorStep
import com.intellij.cce.filter.EvaluationFilter
import com.intellij.cce.filter.EvaluationFilterReader
import com.intellij.cce.interpreter.FeatureInvoker
@@ -55,7 +56,7 @@ internal class ContextCollectionEvaluationCommand : CompletionEvaluationStarter.
override fun run() {
val feature = EvaluableFeature.forFeature(featureName) ?: error("There is no support for the $featureName")
val config = loadConfig(Paths.get(configPath), feature.getStrategySerializer())
val workspace = EvaluationWorkspace.create(config)
val workspace = EvaluationWorkspace.create(config, SetupStatsCollectorStep.statsCollectorLogsDirectory)
val evaluationRootInfo = EvaluationRootInfo(true)
loadAndApply(config.projectPath) { project ->
val stepFactory = object : StepFactory by BackgroundStepFactory(

View File

@@ -2,10 +2,7 @@
package com.intellij.cce.evaluation
import com.intellij.cce.evaluation.step.SetupStatsCollectorStep
import com.intellij.cce.interpreter.InterpretFilter
import com.intellij.cce.interpreter.InterpretationHandlerImpl
import com.intellij.cce.interpreter.Interpreter
import com.intellij.cce.interpreter.InvokersFactory
import com.intellij.cce.interpreter.*
import com.intellij.cce.util.ExceptionsUtil
import com.intellij.cce.util.FilesHelper
import com.intellij.cce.util.Progress
@@ -15,6 +12,9 @@ import com.intellij.cce.workspace.EvaluationWorkspace
import com.intellij.cce.workspace.info.FileErrorInfo
import com.intellij.cce.workspace.info.FileSessionsInfo
import com.intellij.cce.workspace.storages.FeaturesStorage
import com.intellij.cce.workspace.storages.LogsSaver
import com.intellij.cce.workspace.storages.asCompositeLogsSaver
import com.intellij.cce.workspace.storages.logsSaverIf
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import kotlin.math.roundToInt
@@ -26,10 +26,16 @@ class ActionsInterpretationHandler(
private val language: String,
private val invokersFactory: InvokersFactory,
private val project: Project) : TwoWorkspaceHandler {
companion object {
val LOG = Logger.getInstance(ActionsInterpretationHandler::class.java)
}
private fun createLogsSaver(workspace: EvaluationWorkspace): LogsSaver = listOf(
logsSaverIf(config.interpret.saveLogs) { workspace.statLogsSaver },
logsSaverIf(config.interpret.saveFusLogs) { workspace.fusLogsSaver }
).asCompositeLogsSaver()
override fun invoke(workspace1: EvaluationWorkspace, workspace2: EvaluationWorkspace, indicator: Progress) {
var sessionsCount: Int
val computingTime = measureTimeMillis {
@@ -37,12 +43,14 @@ class ActionsInterpretationHandler(
}
LOG.info("Computing of sessions count took $computingTime ms")
val interpretationConfig = config.interpret
val logsSaver = createLogsSaver(workspace2)
val handler = InterpretationHandlerImpl(indicator, sessionsCount, interpretationConfig.sessionsLimit)
val filter =
if (interpretationConfig.sessionProbability < 1)
RandomInterpretFilter(interpretationConfig.sessionProbability, interpretationConfig.sessionSeed)
else InterpretFilter.default()
val interpreter = Interpreter(invokersFactory, handler, filter, config.interpret.order, project.basePath)
val interpreter = ActionInvokingInterpreter(invokersFactory, handler, filter, config.interpret.order, project.basePath)
.wrapLogging(logsSaver)
val featuresStorage = if (interpretationConfig.saveFeatures) workspace2.featuresStorage else FeaturesStorage.EMPTY
LOG.info("Start interpreting actions")
if (interpretationConfig.sessionProbability < 1) {
@@ -75,7 +83,7 @@ class ActionsInterpretationHandler(
}
if (handler.isCancelled() || handler.isLimitExceeded()) break
}
if (interpretationConfig.saveLogs) workspace2.logsStorage.save(SetupStatsCollectorStep.statsCollectorLogsDirectory(), language, interpretationConfig.trainTestSplit)
logsSaver.save(language, config.interpret.trainTestSplit)
SetupStatsCollectorStep.deleteLogs()
workspace2.saveMetadata()
LOG.info("Interpreting actions completed")

View File

@@ -36,7 +36,7 @@ class BackgroundStepFactory(
ActionsInterpretationStep(config, config.language, invokersFactory, project)
override fun generateReportStep(): EvaluationStep =
ReportGenerationStep(inputWorkspacePaths?.map { EvaluationWorkspace.open(it) },
ReportGenerationStep(inputWorkspacePaths?.map { EvaluationWorkspace.open(it, SetupStatsCollectorStep.statsCollectorLogsDirectory) },
config.reports.sessionsFilters, config.reports.comparisonFilters, project, feature)
override fun interpretActionsOnNewWorkspaceStep(): EvaluationStep =

View File

@@ -16,7 +16,7 @@ import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator
import com.intellij.openapi.project.Project
import com.intellij.util.concurrency.FutureResult
abstract class BackgroundEvaluationStep(protected val project: Project) : EvaluationStep {
abstract class BackgroundEvaluationStep(val project: Project) : EvaluationStep {
protected companion object {
val LOG = Logger.getInstance(BackgroundEvaluationStep::class.java)
}

View File

@@ -13,7 +13,7 @@ abstract class CreateWorkspaceStep(
project: Project) : BackgroundEvaluationStep(project) {
override fun runInBackground(workspace: EvaluationWorkspace, progress: Progress): EvaluationWorkspace {
val newWorkspace = EvaluationWorkspace.create(config)
val newWorkspace = EvaluationWorkspace.create(config, SetupStatsCollectorStep.statsCollectorLogsDirectory)
handler.invoke(workspace, newWorkspace, progress)
return newWorkspace
}

View File

@@ -13,27 +13,27 @@ import com.intellij.ide.plugins.PluginManagerCore
import com.intellij.lang.Language
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.PathManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.diagnostic.runAndLogException
import com.intellij.openapi.diagnostic.thisLogger
import com.intellij.openapi.extensions.PluginId
import com.intellij.serviceContainer.ComponentManagerImpl
import com.intellij.stats.completion.sender.StatisticSender
import com.intellij.stats.completion.storage.FilePathProvider
import com.intellij.stats.completion.storage.UniqueFilesProvider
import java.io.File
import java.nio.file.Path
import java.nio.file.Paths
class SetupStatsCollectorStep(private val experimentGroup: Int?,
private val logLocationAndTextItem: Boolean) : UndoableEvaluationStep {
companion object {
private val LOG = Logger.getInstance(SetupStatsCollectorStep::class.java)
private val LOG = thisLogger()
private const val SEND_LOGS_KEY = "completion.stats.send.logs"
private const val STATS_COLLECTOR_ID = "com.intellij.stats.completion"
private const val COLLECT_LOGS_HEADLESS_KEY = "completion.evaluation.headless"
fun statsCollectorLogsDirectory(): String = Paths.get(PathManager.getSystemPath(), "completion-stats-data").toString()
val statsCollectorLogsDirectory: Path = Paths.get(PathManager.getSystemPath(), "completion-stats-data")
fun deleteLogs() {
val logsDirectory = File(statsCollectorLogsDirectory())
val logsDirectory = statsCollectorLogsDirectory.toFile()
if (logsDirectory.exists()) {
logsDirectory.deleteRecursively()
}