IJPL-234844 Dump WSM to log directory on every WSM update IJ-CR-193116

(cherry picked from commit b854cb31be3b9f76ba91765833487b6ddefe5bfa)

GitOrigin-RevId: 094b577e4a261e24e9b3eecfe98e98c5af0d2cf9
This commit is contained in:
Liudmila Kornilova
2026-02-18 18:02:31 +01:00
committed by intellij-monorepo-bot
parent 23813d84e5
commit 24f2ff56aa
5 changed files with 49 additions and 19 deletions

View File

@@ -28,8 +28,8 @@ import com.intellij.workspaceModel.ide.impl.GlobalWorkspaceModel
import com.intellij.workspaceModel.ide.impl.WorkspaceModelImpl
import com.intellij.workspaceModel.ide.impl.jps.serialization.JpsProjectModelSynchronizer
import com.intellij.workspaceModel.ide.impl.jpsMetrics
import com.intellij.workspaceModel.ide.impl.jsonDump.DumpWorkspaceEntitiesWsmChangeListener
import com.intellij.workspaceModel.ide.impl.legacyBridge.library.ProjectLibraryTableBridgeImpl
import com.intellij.workspaceModel.ide.impl.legacyBridge.project.ProjectRootManagerBridge
import com.intellij.workspaceModel.ide.legacyBridge.ModuleDependencyIndex
import io.opentelemetry.api.metrics.Meter
import kotlinx.coroutines.CompletableDeferred
@@ -57,6 +57,7 @@ private fun setupOpenTelemetryReporting(meter: Meter) {
class ModuleBridgeLoaderService : InitProjectActivity {
override suspend fun run(project: Project) {
coroutineScope {
project.serviceAsync<DumpWorkspaceEntitiesWsmChangeListener>()
val projectModelSynchronizer = project.serviceAsync<JpsProjectModelSynchronizer>()
val workspaceModel = project.serviceAsync<WorkspaceModel>() as WorkspaceModelImpl

View File

@@ -3,8 +3,18 @@ package com.intellij.workspaceModel.ide.impl.jsonDump
import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.project.DumbAwareAction
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.registry.Registry
import com.intellij.platform.backend.workspace.WorkspaceModelChangeListener
import com.intellij.platform.backend.workspace.WorkspaceModelTopics
import com.intellij.platform.backend.workspace.workspaceModel
import com.intellij.platform.workspace.storage.VersionedStorageChange
import com.intellij.util.PathUtil
import com.intellij.workspaceModel.ide.impl.WorkspaceModelImpl
import kotlinx.coroutines.CoroutineScope
import org.jetbrains.annotations.ApiStatus
@ApiStatus.Internal
@@ -20,6 +30,25 @@ class DumpWorkspaceEntitiesToClipboardAction : DumbAwareAction() {
}
}
@ApiStatus.Internal
@Service(Service.Level.PROJECT)
class DumpWorkspaceEntitiesWsmChangeListener(val project: Project, val scope: CoroutineScope) {
init {
project.messageBus.connect(project).subscribe(WorkspaceModelTopics.CHANGED, object : WorkspaceModelChangeListener {
override fun changed(event: VersionedStorageChange) {
if (!Registry.`is`("ide.workspace.model.dump.on.every.wsm.update")){
return
}
val version = (project.workspaceModel as WorkspaceModelImpl).entityStorage.version
val fileName = "${System.currentTimeMillis()}-workspace-model-dump-version-${PathUtil.suggestFileName(project.name)}.${project.locationHash}-$version"
project.service<WorkspaceModelJsonDumpService>()
.dumpWorkspaceEntitiesToLogFileAsJson(fileName, event.storageAfter, openFileInEditor = false)
}
})
}
}
@ApiStatus.Internal
class DumpWorkspaceEntitiesToLogAction : DumbAwareAction() {
override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT

View File

@@ -20,6 +20,8 @@ import com.intellij.openapi.vfs.writeText
import com.intellij.platform.backend.workspace.workspaceModel
import com.intellij.platform.ide.progress.withBackgroundProgress
import com.intellij.platform.util.progress.reportSequentialProgress
import com.intellij.platform.workspace.storage.EntityStorage
import com.intellij.platform.workspace.storage.WorkspaceEntityWithSymbolicId
import com.intellij.workspaceModel.ide.impl.WorkspaceModelIdeBundle
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -47,13 +49,12 @@ class WorkspaceModelJsonDumpService(private val project: Project, private val co
prettyPrintIndent = " "
}
private val logFileName = "workspace-model-dump.json"
suspend fun getWorkspaceEntitiesAsJsonArray(): JsonArray {
val snapshot = project.workspaceModel.currentSnapshot
private val logFileName = "workspace-model-dump"
suspend fun getWorkspaceEntitiesAsJsonArray(snapshot: EntityStorage? = null): JsonArray {
val snapshot = snapshot ?: project.workspaceModel.currentSnapshot
val wsmSerializers = WorkspaceModelSerializers()
val rootEntities = snapshot.allUniqueEntities().rootEntitiesClasses().toList()
val rootEntities = snapshot.allUniqueEntities().rootEntitiesClasses().toList().sortedBy { it.simpleName }
return withBackgroundProgress(project, WorkspaceModelIdeBundle.message("progress.title.dumping.workspace.entities.json.to.clipboard")) {
reportSequentialProgress(rootEntities.size) { reporter ->
@@ -62,7 +63,7 @@ class WorkspaceModelJsonDumpService(private val project: Project, private val co
reporter.itemStep(rootEntityClass.name)
addJsonObject {
put("rootEntityName", rootEntityClass.simpleName)
val entities = snapshot.entities(rootEntityClass).toList()
val entities = snapshot.entities(rootEntityClass).toList().sortedBy { if (it is WorkspaceEntityWithSymbolicId) it.symbolicId.presentableName else it.toString() }
put("rootEntitiesCount", entities.size)
putJsonArray("entities") {
for ((i, entity) in entities.withIndex()) {
@@ -97,7 +98,8 @@ class WorkspaceModelJsonDumpService(private val project: Project, private val co
}
}
fun dumpWorkspaceEntitiesToLogFileAsJson() {
fun dumpWorkspaceEntitiesToLogFileAsJson(name: String = logFileName, snapshot: EntityStorage? = null, openFileInEditor: Boolean = true) {
val fileName = "$name.json"
coroutineScope.launch(Dispatchers.IO) {
val logDirectory2 = LoggerFactory.getLogFilePath().parent?.let {
LocalFileSystem.getInstance().refreshAndFindFileByNioFile(it)
@@ -108,14 +110,16 @@ class WorkspaceModelJsonDumpService(private val project: Project, private val co
Notifications.Bus.notify(Notification(groupAndTitle, groupAndTitle, content, NotificationType.INFORMATION))
return@launch
}
val jsonEntities = getWorkspaceEntitiesAsJsonArray()
val jsonEntities = getWorkspaceEntitiesAsJsonArray(snapshot)
val serializedEntitiesString = json.encodeToString(jsonEntities)
val wsmDumpFile = edtWriteAction {
logDirectory2.findChild(logFileName)?.delete(this)
val wsmDumpFile = logDirectory2.createChildData(this, logFileName)
logDirectory2.findChild(fileName)?.delete(this)
val wsmDumpFile = logDirectory2.createChildData(this, fileName)
wsmDumpFile.writeText(serializedEntitiesString)
VfsUtil.markDirtyAndRefresh(true, false, false, wsmDumpFile)
openDumpInEditor(wsmDumpFile)
if (openFileInEditor) {
openDumpInEditor(wsmDumpFile)
}
wsmDumpFile
}
LOG.info("Workspace model was dumped to ${wsmDumpFile.canonicalPath}")

View File

@@ -45,6 +45,8 @@
description="Enable workspace model checking for accessing bridges from WSM listeners"/>
<registryKey key="ide.workspace.model.per.environment.model.separation" defaultValue="true" restartRequired="true"
description="Enable existence of multiple workspace models, one per each isolated environment"/>
<registryKey key="ide.workspace.model.dump.on.every.wsm.update" defaultValue="false" restartRequired="false"
description="Dump workspace model to log directory on every WSM update"/>
<registryKey key="use.workspace.file.index.for.partial.scanning" defaultValue="true" restartRequired="true"
description="If enabled, relies on events from WorkspaceFileIndex for partial scanning"/>
<registryKey key="create.coroutines.for.wfi.events.processing" defaultValue="true" restartRequired="false"

View File

@@ -337,13 +337,7 @@ open class WorkspaceModelImpl : WorkspaceModelInternal {
updatesCounter.incrementAndGet()
}
log.debug("Project model updated silently to version ${entityStorage.pointer.version} in $generalTime ms: $description")
if (generalTime > 1000) {
log.info("Project model update details: Updater code: $updateTimeMillis ms, To snapshot: $toSnapshotTimeMillis m")
}
else {
log.debug { "Project model update details: Updater code: $updateTimeMillis ms, To snapshot: $toSnapshotTimeMillis m" }
}
log.info("Project model updated silently to version ${entityStorage.pointer.version} in $generalTime ms: $description")
}
/**