getOptionList() {
+ return PsiTreeUtil.getChildrenOfTypeAsList(this, IJPerfOption.class);
+ }
+
+}
diff --git a/plugins/performanceTesting/gen/com/jetbrains/performancePlugin/lang/psi/impl/IJPerfSimpleOptionImpl.java b/plugins/performanceTesting/gen/com/jetbrains/performancePlugin/lang/psi/impl/IJPerfSimpleOptionImpl.java
new file mode 100644
index 000000000000..0991e188b9f6
--- /dev/null
+++ b/plugins/performanceTesting/gen/com/jetbrains/performancePlugin/lang/psi/impl/IJPerfSimpleOptionImpl.java
@@ -0,0 +1,30 @@
+// This is a generated file. Not intended for manual editing.
+package com.jetbrains.performancePlugin.lang.psi.impl;
+
+import java.util.List;
+import org.jetbrains.annotations.*;
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiElementVisitor;
+import com.intellij.psi.util.PsiTreeUtil;
+import static com.jetbrains.performancePlugin.lang.psi.IJPerfElementTypes.*;
+import com.intellij.extapi.psi.ASTWrapperPsiElement;
+import com.jetbrains.performancePlugin.lang.psi.*;
+
+public class IJPerfSimpleOptionImpl extends ASTWrapperPsiElement implements IJPerfSimpleOption {
+
+ public IJPerfSimpleOptionImpl(@NotNull ASTNode node) {
+ super(node);
+ }
+
+ public void accept(@NotNull IJPerfVisitor visitor) {
+ visitor.visitSimpleOption(this);
+ }
+
+ @Override
+ public void accept(@NotNull PsiElementVisitor visitor) {
+ if (visitor instanceof IJPerfVisitor) accept((IJPerfVisitor)visitor);
+ else super.accept(visitor);
+ }
+
+}
diff --git a/plugins/performanceTesting/gen/com/jetbrains/performancePlugin/lang/psi/impl/IJPerfStatementImpl.java b/plugins/performanceTesting/gen/com/jetbrains/performancePlugin/lang/psi/impl/IJPerfStatementImpl.java
new file mode 100644
index 000000000000..a2de16678850
--- /dev/null
+++ b/plugins/performanceTesting/gen/com/jetbrains/performancePlugin/lang/psi/impl/IJPerfStatementImpl.java
@@ -0,0 +1,36 @@
+// This is a generated file. Not intended for manual editing.
+package com.jetbrains.performancePlugin.lang.psi.impl;
+
+import java.util.List;
+import org.jetbrains.annotations.*;
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiElementVisitor;
+import com.intellij.psi.util.PsiTreeUtil;
+import static com.jetbrains.performancePlugin.lang.psi.IJPerfElementTypes.*;
+import com.intellij.extapi.psi.ASTWrapperPsiElement;
+import com.jetbrains.performancePlugin.lang.psi.*;
+
+public class IJPerfStatementImpl extends ASTWrapperPsiElement implements IJPerfStatement {
+
+ public IJPerfStatementImpl(@NotNull ASTNode node) {
+ super(node);
+ }
+
+ public void accept(@NotNull IJPerfVisitor visitor) {
+ visitor.visitStatement(this);
+ }
+
+ @Override
+ public void accept(@NotNull PsiElementVisitor visitor) {
+ if (visitor instanceof IJPerfVisitor) accept((IJPerfVisitor)visitor);
+ else super.accept(visitor);
+ }
+
+ @Override
+ @Nullable
+ public IJPerfCommandLine getCommandLine() {
+ return findChildByClass(IJPerfCommandLine.class);
+ }
+
+}
diff --git a/plugins/performanceTesting/intellij.performanceTesting.iml b/plugins/performanceTesting/intellij.performanceTesting.iml
new file mode 100644
index 000000000000..3a3fee1028bf
--- /dev/null
+++ b/plugins/performanceTesting/intellij.performanceTesting.iml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/META-INF/plugin.xml b/plugins/performanceTesting/resources/META-INF/plugin.xml
new file mode 100644
index 000000000000..f1f7ed3691f0
--- /dev/null
+++ b/plugins/performanceTesting/resources/META-INF/plugin.xml
@@ -0,0 +1,81 @@
+
+ com.jetbrains.performancePlugin
+ Performance Testing
+ JetBrains
+ Other Tools
+ messages.PerformanceTestingBundle
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/InspectCodeExCommand.html b/plugins/performanceTesting/resources/commandDescriptions/InspectCodeExCommand.html
new file mode 100644
index 000000000000..5bbedef0821b
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/InspectCodeExCommand.html
@@ -0,0 +1,13 @@
+
+
+ Command runs code inspection using custom tool
+
+
+
+ Syntax: %InspectCodeEx [parameters]
+
+
+ Example: %InspectCodeEx -directory app -toolShortName RubyResolve
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/addFileCommand.html b/plugins/performanceTesting/resources/commandDescriptions/addFileCommand.html
new file mode 100644
index 000000000000..d05e178fc20e
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/addFileCommand.html
@@ -0,0 +1,11 @@
+
+Command to add file to project
+
+
+ Create file in project. File name and path to project get from parameters.
+
+
+ Example: %addFile C:\Users\username\intellij, fileName.java
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/altEnterCommand.html b/plugins/performanceTesting/resources/commandDescriptions/altEnterCommand.html
new file mode 100644
index 000000000000..bd901bc39b29
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/altEnterCommand.html
@@ -0,0 +1,14 @@
+
+Command invokes alt+enter combination
+
+
+ Simulate invocation of alt+enter and pressing item in the list defined by parameter.
+
+
+ Syntax: %altEnter [parameters]
+
+
+ Example: %altEnter Find cause
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/buildProjectCommand.html b/plugins/performanceTesting/resources/commandDescriptions/buildProjectCommand.html
new file mode 100644
index 000000000000..aafbf399a820
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/buildProjectCommand.html
@@ -0,0 +1,14 @@
+
+Command builds all modules in a project
+
+
+ builds all modules in a project. There are two modes: BUILD and REBUILD
+
+
+ Syntax: %buildProject [mode]
+
+
+ Example: %buildProject REBUILD
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/checkOnRedCodeCommand.html b/plugins/performanceTesting/resources/commandDescriptions/checkOnRedCodeCommand.html
new file mode 100644
index 000000000000..40bfeadab36e
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/checkOnRedCodeCommand.html
@@ -0,0 +1,14 @@
+
+Command checks whether an opened document in the editor has errors
+
+
+ Simulate analyzing of document on errors (red code). The command finds only ERROR severity
+
+
+ Syntax: %checkOnRedCode
+
+
+ Example: %checkOnRedCode
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/cleanCachesCommand.html b/plugins/performanceTesting/resources/commandDescriptions/cleanCachesCommand.html
new file mode 100644
index 000000000000..3bbf4d248295
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/cleanCachesCommand.html
@@ -0,0 +1,8 @@
+
+Command invalidates indexes
+
+
+ Simulate invocation of 'Invalidate caches' action
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/compareIndicesCommand.html b/plugins/performanceTesting/resources/commandDescriptions/compareIndicesCommand.html
new file mode 100644
index 000000000000..0e15ff80bf68
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/compareIndicesCommand.html
@@ -0,0 +1,9 @@
+
+Command compares stored indices with indices on the disk
+
+
+ Compares the stored indices with the last created indices, which are in the current moment on the disk.
+ The folder with the stored indices should be defined by the parameter '-Dcompare.indices.command.stored.indexes.directory'.
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/delayTypeCommand.html b/plugins/performanceTesting/resources/commandDescriptions/delayTypeCommand.html
new file mode 100644
index 000000000000..bbac07ae26c1
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/delayTypeCommand.html
@@ -0,0 +1,14 @@
+
+Command types text with some delay between typing
+
+
+ Text and delay are being set as parameters
+
+
+ Syntax: %delayType <delay in ms>|<Text to type>
+
+
+ Example: %delayType 150|Sample text for typing scenario
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/deleteFileCommand.html b/plugins/performanceTesting/resources/commandDescriptions/deleteFileCommand.html
new file mode 100644
index 000000000000..b2b91d5afbb5
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/deleteFileCommand.html
@@ -0,0 +1,11 @@
+
+Command to delete file from project
+
+
+ Delete file from project if file exists. File name and path to project get from parameters.
+
+
+ Example: %deleteFile C:\Users\username\intellij, fileName.java
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/doCompleteCommand.html b/plugins/performanceTesting/resources/commandDescriptions/doCompleteCommand.html
new file mode 100644
index 000000000000..e2615c469c3d
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/doCompleteCommand.html
@@ -0,0 +1,5 @@
+
+Command invokes simple completion
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/doHighlightingCommand.html b/plugins/performanceTesting/resources/commandDescriptions/doHighlightingCommand.html
new file mode 100644
index 000000000000..ba2cd633115b
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/doHighlightingCommand.html
@@ -0,0 +1,9 @@
+
+Command runs highlighting pass
+
+ Runs all highlighting passes. Can't be invoked multiple times in a row since it drops caches before run.
+
+ Example: %doHighlighting
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/doLocalInspectionCommand.html b/plugins/performanceTesting/resources/commandDescriptions/doLocalInspectionCommand.html
new file mode 100644
index 000000000000..14375c3c882a
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/doLocalInspectionCommand.html
@@ -0,0 +1,8 @@
+
+Command runs local inspection
+
+
+ Runs local inspection using DaemonCodeAnalyzer
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/exitAppCommand.html b/plugins/performanceTesting/resources/commandDescriptions/exitAppCommand.html
new file mode 100644
index 000000000000..4040eaccab3d
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/exitAppCommand.html
@@ -0,0 +1,8 @@
+
+Command exits app
+
+
+ Closes IDE
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/expandProjectMenuCommand.html b/plugins/performanceTesting/resources/commandDescriptions/expandProjectMenuCommand.html
new file mode 100644
index 000000000000..4629c3158f3d
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/expandProjectMenuCommand.html
@@ -0,0 +1,10 @@
+
+Command to test how long does it take to open context menu in project view
+
+Command to test how long does it take to open context menu in project view.
+The popup won't be shown but all heavy methods that may cause context menu long load will be invoked
+
+ Example: %expandProjectMenu
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/findUsagesCommand.html b/plugins/performanceTesting/resources/commandDescriptions/findUsagesCommand.html
new file mode 100644
index 000000000000..892a1fd3c931
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/findUsagesCommand.html
@@ -0,0 +1,13 @@
+
+
+ Command looks for psi element usages
+
+
+
+ Looks for and count psi usages of element under caret
+
+
+ Example: %findUsages
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/gitCheckoutCommand.html b/plugins/performanceTesting/resources/commandDescriptions/gitCheckoutCommand.html
new file mode 100644
index 000000000000..48b055efb8ca
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/gitCheckoutCommand.html
@@ -0,0 +1,16 @@
+
+
+ Command switches to another branch
+
+
+
+ Call from the project dir git checkout command in command line
+
+
+ Syntax: %gitCheckout [branch]
+
+
+ Example: %gitCheckout master
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/gotoCommand.html b/plugins/performanceTesting/resources/commandDescriptions/gotoCommand.html
new file mode 100644
index 000000000000..0ba2a74143b3
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/gotoCommand.html
@@ -0,0 +1,16 @@
+
+
+ Command puts caret to the line and column
+
+
+
+ Puts the caret to the location defined by parameters
+
+
+ Syntax: %goto <line> <column>
+
+
+ Example: %goto 25 17
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/inspectCodeCommand.html b/plugins/performanceTesting/resources/commandDescriptions/inspectCodeCommand.html
new file mode 100644
index 000000000000..9cb682ade138
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/inspectCodeCommand.html
@@ -0,0 +1,16 @@
+
+
+ Command runs 'Inspect code' action
+
+
+
+ Run 'Inspect code' command in context defined by parameters
+
+
+ Syntax: %inspectCode [directory]
+
+
+ Example: %inspectCode src
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/memoryDumpCommand.html b/plugins/performanceTesting/resources/commandDescriptions/memoryDumpCommand.html
new file mode 100644
index 000000000000..9559ca945e28
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/memoryDumpCommand.html
@@ -0,0 +1,8 @@
+
+
+ Command dumps memory
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/openFileCommand.html b/plugins/performanceTesting/resources/commandDescriptions/openFileCommand.html
new file mode 100644
index 000000000000..d36cb752f893
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/openFileCommand.html
@@ -0,0 +1,10 @@
+
+
+ Command opens file
+
+
+
+ Example: %openFile MyClass.php
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/openProjectCommand.html b/plugins/performanceTesting/resources/commandDescriptions/openProjectCommand.html
new file mode 100644
index 000000000000..7ea6c2d457a1
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/openProjectCommand.html
@@ -0,0 +1,22 @@
+
+Command to open a new project
+
+To open a project in the current frame closing the current one:
+
+ Example: %openProject C:\Users\username\intellij
+
+
+
+To open a project in a new frame and don't close the current one:
+
+ Example: %openProject C:\Users\username\intellij false
+
+
+If you do the following:
+%openProject /tmp/a false
+%openProject /tmp/b false
+%openProject /tmp/a false
+
+In the end, the same project a will be active and there will be 2 window frames.
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/pressKeyCommand.html b/plugins/performanceTesting/resources/commandDescriptions/pressKeyCommand.html
new file mode 100644
index 000000000000..fbd84611217e
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/pressKeyCommand.html
@@ -0,0 +1,16 @@
+
+
+ Command simulates pressing a keyboard key
+
+
+
+ Only defined set of key is supported for now: "ENTER", "BACKSPACE", "TAB" and "ESCAPE"
+
+
+ Syntax: %pressKey <KEY>
+
+
+ Example: %pressKey ENTER
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/projectCommand.html b/plugins/performanceTesting/resources/commandDescriptions/projectCommand.html
new file mode 100644
index 000000000000..3039099d0641
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/projectCommand.html
@@ -0,0 +1,15 @@
+
+Command declares project used for test
+
+Name of project that is being used for test
+
+ Command must be followed by project name and optionally by project archive.
+
+
+ Syntax: %%project <projetName> [projectArchive]
+
+
+ Example: %%project mpdf mpdf-5.6.zip
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/reformatCommand.html b/plugins/performanceTesting/resources/commandDescriptions/reformatCommand.html
new file mode 100644
index 000000000000..166d08c22337
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/reformatCommand.html
@@ -0,0 +1,8 @@
+
+
+ Command reformats project
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/reloadFilesCommand.html b/plugins/performanceTesting/resources/commandDescriptions/reloadFilesCommand.html
new file mode 100644
index 000000000000..edddfe9f015b
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/reloadFilesCommand.html
@@ -0,0 +1,11 @@
+
+Command to reload project files from disk
+
+
+ Command to reload project files from disk.
+
+
+ Example: %reloadFiles
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/replaceTextCommand.html b/plugins/performanceTesting/resources/commandDescriptions/replaceTextCommand.html
new file mode 100644
index 000000000000..072616bf7e27
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/replaceTextCommand.html
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/runConfigurationCommand.html b/plugins/performanceTesting/resources/commandDescriptions/runConfigurationCommand.html
new file mode 100644
index 000000000000..b10b8c08a84e
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/runConfigurationCommand.html
@@ -0,0 +1,17 @@
+
+
+ Command runs specified configuration
+
+
+
+ Command runs specified configuration if configuration is absent prints all available for the project.
+ There are two modes: TILL_STARTED and TILL_TERMINATED
+
+
+ Syntax: %runConfiguration [mode] <configurationName>
+
+
+ Example: %runConfiguration TILL_STARTED IDEA
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/runSingleInspectionCommand.html b/plugins/performanceTesting/resources/commandDescriptions/runSingleInspectionCommand.html
new file mode 100644
index 000000000000..cef9f78fd04e
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/runSingleInspectionCommand.html
@@ -0,0 +1,13 @@
+
+
+ Command runs single inspection
+
+
+
+ Runs single inspection by short name
+
+
+ Syntax: %runSingleInspection <name>
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/searchEverywhereCommand.html b/plugins/performanceTesting/resources/commandDescriptions/searchEverywhereCommand.html
new file mode 100644
index 000000000000..30c6772e59f3
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/searchEverywhereCommand.html
@@ -0,0 +1,14 @@
+
+Command calls search everywhere with the given text and tab
+
+
+ Simulate calling search everywhere action with a given text and specified tab.
+
+
+ Syntax: %searchEverywhere [tab]|[text]
+
+
+ Example: %searchEverywhere symbol|EditorIm
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/selectFileInProjectViewCommand.html b/plugins/performanceTesting/resources/commandDescriptions/selectFileInProjectViewCommand.html
new file mode 100644
index 000000000000..643cb5f776c5
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/selectFileInProjectViewCommand.html
@@ -0,0 +1,9 @@
+
+Command to select file in project view
+
+Command to select file in project view.
+
+ Example: %selectFileInProjectView MyClass.php
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/startPowerSaveCommand.html b/plugins/performanceTesting/resources/commandDescriptions/startPowerSaveCommand.html
new file mode 100644
index 000000000000..072616bf7e27
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/startPowerSaveCommand.html
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/startProfileCommand.html b/plugins/performanceTesting/resources/commandDescriptions/startProfileCommand.html
new file mode 100644
index 000000000000..9abdbfbb4476
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/startProfileCommand.html
@@ -0,0 +1,17 @@
+
+Command starts async or yourkit profiler
+
+
+ Command starts async or yourkit profiler based on system property integrationTests.profiler.
+
+
+ Command must be followed by activity name and optionally by parameters
+
+
+ Syntax: %startProfile <activityName> [parameters]
+
+
+ Example: %startProfile magento_inspection event=alloc
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/stopPowerSaveCommand.html b/plugins/performanceTesting/resources/commandDescriptions/stopPowerSaveCommand.html
new file mode 100644
index 000000000000..072616bf7e27
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/stopPowerSaveCommand.html
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/stopProfileCommand.html b/plugins/performanceTesting/resources/commandDescriptions/stopProfileCommand.html
new file mode 100644
index 000000000000..93ec95dc6b64
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/stopProfileCommand.html
@@ -0,0 +1,17 @@
+
+Command stops profiler
+
+
+ Command stops async or yourkit profiler based on system property integrationTests.profiler.
+
+
+ In case when yourkit profiler was used parameters are ignored
+
+
+ Syntax: %stopProfile [parameters]
+
+
+ Example: %stopProfile collapsed,flamegraph,traces=5000
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/storeIndicesCommand.html b/plugins/performanceTesting/resources/commandDescriptions/storeIndicesCommand.html
new file mode 100644
index 000000000000..41431bc625a5
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/storeIndicesCommand.html
@@ -0,0 +1,8 @@
+
+Command stores created indices
+
+
+ Stores created during indexing indices in the defined by the parameter '-Dstore.indices.command.stored.indexes.dump.directory' directory.
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/takeScreenshotCommand.html b/plugins/performanceTesting/resources/commandDescriptions/takeScreenshotCommand.html
new file mode 100644
index 000000000000..4960b04b453c
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/takeScreenshotCommand.html
@@ -0,0 +1,16 @@
+
+
+ Command takes screenshot
+
+
+
+ Takes JPG screenshot of current screen to specified file.
+
+
+ Syntax: %takeScreenshot <fullPathToFile>
+
+
+ Example: %takeScreenshot ./myScreenshot.jpg
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/commandDescriptions/waitForSmartCommand.html b/plugins/performanceTesting/resources/commandDescriptions/waitForSmartCommand.html
new file mode 100644
index 000000000000..072616bf7e27
--- /dev/null
+++ b/plugins/performanceTesting/resources/commandDescriptions/waitForSmartCommand.html
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/performanceTesting/resources/messages/PerformanceTestingBundle.properties b/plugins/performanceTesting/resources/messages/PerformanceTestingBundle.properties
new file mode 100644
index 000000000000..b431251430b7
--- /dev/null
+++ b/plugins/performanceTesting/resources/messages/PerformanceTestingBundle.properties
@@ -0,0 +1,79 @@
+profiling.compress.snapshot.progress=Compressing snapshot
+profiling.capture.snapshot.success=Captured snapshot ''{0}'' is placed in user home directory.
+profiling.capture.snapshot.error=Failed to capture snapshot: {0}
+profiling.capture.snapshot.action.showInFolder=Show in {0}
+profiling.capture.snapshot.action.open=Open
+
+command.goto.finish=GoTo was executed
+command.inspection.extension=No files with extension:
+command.inspection.finish=Inspection was executed
+command.inspection.finish.result=with {0} found problems
+command.file.not.found=File not found: {0}
+command.file.opened=File opened successfully: {0}
+command.reformat.finish=Reformat was executed
+command.start.error=Start profile should contain key: %startProfile
+command.build.finish=Build finished successfully
+command.build.finish.with.errors=Build finished with errors
+command.build.finish.with.compilation.errors=Build finished with compilation errors
+
+startup.noscript=Script file {0} doesn''t exist.
+startup.silent=Performance Plugin is in silent mode
+startup.script.read.error=Script file can\'t be read.
+
+callback.stop=Performance Plugin was stopped
+
+summary.action=&Open Support Request
+summary.link=https://intellij-support.jetbrains.com/anonymous_requests/new
+summary.link.rider=https://rider-support.jetbrains.com/hc/en-us/requests/new
+
+finish.path=Snapshot path is: {0}
+finish.title=Snapshot Was Captured
+finish.open=&Open Snapshot in {0}
+
+delay.notification.title=Performance Plugin finished execution
+delay.notification.message=Total time: {0} ms \n Average delay: {1} ms Max delay: {2} ms
+
+command.singleInspection.noinspection=Cannot run inspection, inspection doesn''t exists {0}
+
+select.scenario=Please select &scenario:
+executor.title=Performance Issue Reporter
+execute.script.button=&Execute
+typing.scenario=Typing
+formatting.scenario=Formatting
+local.inspection.scenario=Local Inspection
+input.below.parameters.for.async.profiler=Input below parameters for async profiler
+
+action.performancePlugin.ExecuteScriptAction.text=Execute Performance Script\u2026
+action.performancePlugin.ExecuteScriptAction.description=Execute performance test script
+action.performancePlugin.OpenIndexingDiagnosticsAction.text=Open Indexing Diagnostics
+action.performancePlugin.OpenIndexingDiagnosticsAction.description=Open Indexing Diagnostics for the current project
+
+comparing.indexes=Comparing Indexes\u2026
+storing.indexes=Storing indexes\u2026
+flushing.indexes=Flushing indexes\u2026
+storing.indexes.copying=Copying current indexes\u2026
+checking.shared.indexes=Checking Shared Indexes\u2026
+checking.shared.indexes.collecting.shared.files=Collecting files available in shared indexes\u2026
+checking.shared.indexes.collecting.all.indexed.files=Collecting all indexed files\u2026
+compare.indexes.comparing.index=Comparing current and stored indexes of {0}\u2026
+compare.indexes.comparing.forward.index=Comparing forward index of the current and stored indexes of {0}\u2026
+compare.indexes.comparing.inverted.index=Comparing inverted index of the current and stored indexes of {0}\u2026
+compare.indexes.comparing.inverted.index.collecting.keys=Collecting keys of the inverted index\u2026
+compare.indexes.resolving.files=Resolving files for comparison\u2026
+compare.indexes.preparing.indexes=Preparing indexes\u2026
+button.text.cancel=Not Now
+
+filetype.ijperformance.test.description=Script of integration performance test
+filetype.ijperformance.test.display.name=ijperf script
+
+task.title.executing.performance.script=Executing performance script
+
+dumping.project.files=Dumping Project Files\u2026
+comparing.project.files=Comparing Project Files\u2026
+comparing.project.files.for.0=Comparing Project Files for {0}\u2026
+
+intention.add.stopcommand=Add `%stopProfile` command
+inspection.message.activity.started.but.never.closed.with.stopprofile=`{0}` activity started, but never closed with `%stopProfile`
+inspection.message.two.startprofile.commands.cant.follow.each.other=`%startProfile` commands can''t follow each other. Add `%stopProfile` before `{0}`
+notification.group.performance.plugin=IDE self-profiling
+
diff --git a/plugins/performanceTesting/src/com/jetbrains/performancePlugin/BaseCommandProvider.java b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/BaseCommandProvider.java
new file mode 100644
index 000000000000..9f2aae0836e1
--- /dev/null
+++ b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/BaseCommandProvider.java
@@ -0,0 +1,70 @@
+package com.jetbrains.performancePlugin;
+
+import com.jetbrains.performancePlugin.commands.*;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Map;
+
+public final class BaseCommandProvider implements CommandProvider {
+ @Override
+ public @NotNull Map getCommands() {
+ return Map.ofEntries(
+ Map.entry(RunConfigurationCommand.PREFIX, RunConfigurationCommand::new),
+ Map.entry(TakeScreenshotCommand.PREFIX, TakeScreenshotCommand::new),
+ Map.entry(OpenFileCommand.PREFIX, OpenFileCommand::new),
+ Map.entry(StartProfileCommand.PREFIX, StartProfileCommand::new),
+ Map.entry(StopProfileCommand.PREFIX, StopProfileCommand::new),
+ Map.entry(InspectionCommand.PREFIX, InspectionCommand::new),
+ Map.entry(InspectionCommandEx.PREFIX, InspectionCommandEx::new),
+ Map.entry(ReformatCommand.PREFIX, ReformatCommand::new),
+ Map.entry(GoToCommand.PREFIX, GoToCommand::new),
+ Map.entry(DoLocalInspection.PREFIX, DoLocalInspection::new),
+ Map.entry(CompletionCommand.PREFIX, CompletionCommand::new),
+ Map.entry(DelayTypeCommand.PREFIX, DelayTypeCommand::new),
+ Map.entry(ReplaceTextCommand.PREFIX, ReplaceTextCommand::new),
+ Map.entry(ExitAppCommand.PREFIX, ExitAppCommand::new),
+ Map.entry(ExitAppWithTimeoutCommand.PREFIX, ExitAppWithTimeoutCommand::new),
+ Map.entry(OpenFileWithTerminateCommand.PREFIX, OpenFileWithTerminateCommand::new),
+ Map.entry(WaitForSmartCommand.PREFIX, WaitForSmartCommand::new),
+ Map.entry(WaitForVcsLogCommand.PREFIX, WaitForVcsLogCommand::new),
+ Map.entry(WaitForAsyncRefreshCommand.PREFIX, WaitForAsyncRefreshCommand::new),
+ Map.entry(SingleInspectionCommand.PREFIX, SingleInspectionCommand::new),
+ Map.entry(StartPowerSave.PREFIX, StartPowerSave::new),
+ Map.entry(StopPowerSave.PREFIX, StopPowerSave::new),
+ Map.entry(MemoryDumpCommand.PREFIX, MemoryDumpCommand::new),
+ Map.entry(CleanCaches.PREFIX, CleanCaches::new),
+ Map.entry(FindUsagesCommand.PREFIX, FindUsagesCommand::new),
+ Map.entry(EnterCommand.PREFIX, EnterCommand::new),
+ Map.entry(ShowAltEnter.PREFIX, ShowAltEnter::new),
+ Map.entry(SelectCommand.PREFIX, SelectCommand::new),
+ Map.entry(CompareIndicesKt.PREFIX, CompareIndices::new),
+ Map.entry(StoreIndices.PREFIX, StoreIndices::new),
+ Map.entry(GitCheckoutCommand.PREFIX, GitCheckoutCommand::new),
+ Map.entry(OpenProjectView.PREFIX, OpenProjectView::new),
+ Map.entry(MoveDirectoryCommand.PREFIX, MoveDirectoryCommand::new),
+ Map.entry(RunClassInPlugin.PREFIX, RunClassInPlugin::new),
+ Map.entry(SetupProjectSdkCommand.PREFIX, SetupProjectSdkCommand::new),
+ Map.entry(OpenProjectCommand.PREFIX, OpenProjectCommand::new),
+ Map.entry(CodeAnalysisCommand.PREFIX, CodeAnalysisCommand::new),
+ Map.entry(DumpProjectFiles.PREFIX, DumpProjectFiles::new),
+ Map.entry(CompareProjectFiles.PREFIX, CompareProjectFiles::new),
+ Map.entry(RecordCounterCollectorBaselinesCommand.PREFIX, RecordCounterCollectorBaselinesCommand::new),
+ Map.entry(RecordStateCollectorsCommand.PREFIX, RecordStateCollectorsCommand::new),
+ Map.entry(CreateAllServicesAndExtensionsCommand.PREFIX, CreateAllServicesAndExtensionsCommand::new),
+ Map.entry(RecoveryActionCommand.PREFIX, RecoveryActionCommand::new),
+ Map.entry(CorruptIndexesCommand.PREFIX, CorruptIndexesCommand::new),
+ Map.entry(FlushIndexesCommand.PREFIX, FlushIndexesCommand::new),
+ Map.entry(SearchEverywhereCommand.PREFIX, SearchEverywhereCommand::new),
+ Map.entry(SelectFileInProjectViewCommand.PREFIX, SelectFileInProjectViewCommand::new),
+ Map.entry(ExpandProjectMenuCommand.PREFIX, ExpandProjectMenuCommand::new),
+ Map.entry(DoHighlighting.PREFIX, DoHighlighting::new),
+ Map.entry(ReloadFilesCommand.PREFIX, ReloadFilesCommand::new),
+ Map.entry(AddFileCommand.PREFIX, AddFileCommand::new),
+ Map.entry(DeleteFileCommand.PREFIX, DeleteFileCommand::new),
+ Map.entry(TestTipsAndFeaturesIntegrationCommand.PREFIX, TestTipsAndFeaturesIntegrationCommand::new),
+ Map.entry(ExpandMainMenuCommand.PREFIX, ExpandMainMenuCommand::new),
+ Map.entry(ExpandEditorMenuCommand.PREFIX, ExpandEditorMenuCommand::new),
+ Map.entry(OpenRandomFileCommand.PREFIX, OpenRandomFileCommand::new)
+ );
+ }
+}
diff --git a/plugins/performanceTesting/src/com/jetbrains/performancePlugin/BaseRunCallbackHandler.java b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/BaseRunCallbackHandler.java
new file mode 100644
index 000000000000..e78a1ce1dadc
--- /dev/null
+++ b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/BaseRunCallbackHandler.java
@@ -0,0 +1,11 @@
+package com.jetbrains.performancePlugin;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.ActionCallback;
+
+public class BaseRunCallbackHandler implements RunCallbackHandler {
+
+ @Override
+ public void patchCommandCallback(Project project, ActionCallback callback) { }
+
+}
diff --git a/plugins/performanceTesting/src/com/jetbrains/performancePlugin/CommandLogger.java b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/CommandLogger.java
new file mode 100644
index 000000000000..edd58d9bd74f
--- /dev/null
+++ b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/CommandLogger.java
@@ -0,0 +1,23 @@
+package com.jetbrains.performancePlugin;
+
+import com.intellij.openapi.application.ex.ApplicationManagerEx;
+import com.intellij.openapi.ui.playback.PlaybackContext;
+import com.intellij.openapi.ui.playback.PlaybackRunner.StatusCallback;
+import org.jetbrains.annotations.Nullable;
+
+@SuppressWarnings("UseOfSystemOutOrSystemErr")
+public class CommandLogger implements StatusCallback {
+
+ @Override
+ public void message(@Nullable PlaybackContext context, String text, Type type) {
+ if (type == Type.error) {
+ System.err.println(text);
+ if (System.getProperty("testscript.filename") != null) {
+ ApplicationManagerEx.getApplicationEx().exit(true, true);
+ }
+ }
+ else {
+ System.out.println(text);
+ }
+ }
+}
diff --git a/plugins/performanceTesting/src/com/jetbrains/performancePlugin/CommandProvider.java b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/CommandProvider.java
new file mode 100644
index 000000000000..694da1161343
--- /dev/null
+++ b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/CommandProvider.java
@@ -0,0 +1,30 @@
+package com.jetbrains.performancePlugin;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+public interface CommandProvider {
+ ExtensionPointName EP_NAME = new ExtensionPointName<>("com.jetbrains.performancePlugin.commandProvider");
+
+ @NotNull Map<@NonNls String, CreateCommand> getCommands();
+
+ static @NotNull List<@NonNls String> getAllCommandNames() {
+ return EP_NAME.getExtensionList().stream()
+ .flatMap(e -> e.getCommands().keySet().stream()).toList();
+ }
+
+ static @Nullable CreateCommand findCommandCreator(@NonNls @NotNull String commandName) {
+ return EP_NAME.getExtensionList().stream()
+ .map(provider -> provider.getCommands().get(commandName))
+ .filter(Objects::nonNull)
+ .findFirst()
+ .orElse(null);
+ }
+}
diff --git a/plugins/performanceTesting/src/com/jetbrains/performancePlugin/CommandsRunner.java b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/CommandsRunner.java
new file mode 100644
index 000000000000..7f45527098f7
--- /dev/null
+++ b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/CommandsRunner.java
@@ -0,0 +1,35 @@
+package com.jetbrains.performancePlugin;
+
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.util.ActionCallback;
+import org.jetbrains.annotations.NotNull;
+
+import static com.jetbrains.performancePlugin.ProjectLoaded.runScript;
+
+public class CommandsRunner {
+
+ private static CommandsRunner myCommandsRunner;
+
+ private ActionCallback startActionCallback;
+
+ synchronized static CommandsRunner getInstance() {
+ if (myCommandsRunner == null) {
+ myCommandsRunner = new CommandsRunner();
+ }
+ return myCommandsRunner;
+ }
+
+ //TODO: add methode with project in parameter? AT-114
+ public static void doRunScript(@NotNull String text) {
+ runScript(ProjectManager.getInstance().getOpenProjects()[0], text);
+ }
+
+ public static void setStartActionCallback(ActionCallback actionCallback) {
+ getInstance().startActionCallback = actionCallback;
+ }
+
+ public static boolean areCommandsAfterStartFinished() {
+ if (getInstance().startActionCallback == null) return false;
+ return getInstance().startActionCallback.isDone();
+ }
+}
diff --git a/plugins/performanceTesting/src/com/jetbrains/performancePlugin/CreateCommand.java b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/CreateCommand.java
new file mode 100644
index 000000000000..657a2fd7a712
--- /dev/null
+++ b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/CreateCommand.java
@@ -0,0 +1,9 @@
+package com.jetbrains.performancePlugin;
+
+import com.intellij.openapi.ui.playback.PlaybackCommand;
+import org.jetbrains.annotations.NotNull;
+
+public interface CreateCommand {
+ @NotNull
+ PlaybackCommand invoke(@NotNull String command, int line);
+}
diff --git a/plugins/performanceTesting/src/com/jetbrains/performancePlugin/OnEndListener.java b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/OnEndListener.java
new file mode 100644
index 000000000000..4c3595d7b51b
--- /dev/null
+++ b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/OnEndListener.java
@@ -0,0 +1,6 @@
+package com.jetbrains.performancePlugin;
+
+public interface OnEndListener {
+
+ void onEnd(T t);
+}
diff --git a/plugins/performanceTesting/src/com/jetbrains/performancePlugin/PerformanceTestSpan.java b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/PerformanceTestSpan.java
new file mode 100644
index 000000000000..2532d8fc3a7e
--- /dev/null
+++ b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/PerformanceTestSpan.java
@@ -0,0 +1,29 @@
+package com.jetbrains.performancePlugin;
+
+import com.intellij.diagnostic.telemetry.IJTracer;
+import com.intellij.diagnostic.telemetry.TraceManager;
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.context.Context;
+import io.opentelemetry.context.Scope;
+
+public class PerformanceTestSpan {
+ public static final String SPAN_NAME = "performance_test";
+ public final static IJTracer TRACER = TraceManager.INSTANCE.getTracer("performance-plugin");
+ private static Span performanceTestSpan;
+ private static Scope performanceScope;
+
+ static void startSpan() {
+ performanceTestSpan = TRACER.spanBuilder(SPAN_NAME).setNoParent().startSpan();
+ performanceScope = performanceTestSpan.makeCurrent();
+ }
+
+ static void endSpan() {
+ if (performanceScope != null) performanceScope.close();
+ if (performanceTestSpan != null) performanceTestSpan.end();
+ }
+
+ public static Context getContext(){
+ if (performanceTestSpan != null) return Context.current().with(performanceTestSpan);
+ return null;
+ }
+}
diff --git a/plugins/performanceTesting/src/com/jetbrains/performancePlugin/PerformanceTestTotalTimeTimer.java b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/PerformanceTestTotalTimeTimer.java
new file mode 100644
index 000000000000..b93acf77f8bf
--- /dev/null
+++ b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/PerformanceTestTotalTimeTimer.java
@@ -0,0 +1,27 @@
+package com.jetbrains.performancePlugin;
+
+import com.intellij.ide.AppLifecycleListener;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.startup.StartupActivity;
+import com.intellij.util.messages.MessageBusConnection;
+import org.jetbrains.annotations.NotNull;
+
+public final class PerformanceTestTotalTimeTimer implements StartupActivity.DumbAware {
+ public static final String TOTAL_TEST_TIMER_NAME = "test";
+
+ @Override
+ public void runActivity(@NotNull Project project) {
+ if (ProjectLoaded.TEST_SCRIPT_FILE_PATH != null) {
+ Timer myTimer = new Timer();
+ myTimer.start(TOTAL_TEST_TIMER_NAME, true);
+ MessageBusConnection connection = ApplicationManager.getApplication().getMessageBus().connect();
+ connection.subscribe(AppLifecycleListener.TOPIC, new AppLifecycleListener() {
+ @Override
+ public void appWillBeClosed(boolean isRestart) {
+ myTimer.stop();
+ }
+ });
+ }
+ }
+}
diff --git a/plugins/performanceTesting/src/com/jetbrains/performancePlugin/PerformanceTestingBundle.java b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/PerformanceTestingBundle.java
new file mode 100644
index 000000000000..fc228079e700
--- /dev/null
+++ b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/PerformanceTestingBundle.java
@@ -0,0 +1,26 @@
+package com.jetbrains.performancePlugin;
+
+import com.intellij.DynamicBundle;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.PropertyKey;
+
+import java.util.function.Supplier;
+
+public final class PerformanceTestingBundle extends DynamicBundle {
+ @NonNls private static final String BUNDLE = "messages.PerformanceTestingBundle";
+ private static final PerformanceTestingBundle INSTANCE = new PerformanceTestingBundle();
+
+ private PerformanceTestingBundle() { super(BUNDLE); }
+
+ @NotNull
+ public static @Nls String message(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key, Object @NotNull ... params) {
+ return INSTANCE.getMessage(key, params);
+ }
+
+ @NotNull
+ public static Supplier<@Nls String> messagePointer(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key, Object @NotNull ... params) {
+ return INSTANCE.getLazyMessage(key, params);
+ }
+}
\ No newline at end of file
diff --git a/plugins/performanceTesting/src/com/jetbrains/performancePlugin/PerformanceTroubleInfoCollector.java b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/PerformanceTroubleInfoCollector.java
new file mode 100644
index 000000000000..1ffc47080128
--- /dev/null
+++ b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/PerformanceTroubleInfoCollector.java
@@ -0,0 +1,19 @@
+package com.jetbrains.performancePlugin;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.troubleshooting.TroubleInfoCollector;
+import com.jetbrains.performancePlugin.utils.StatisticCollector;
+import org.jetbrains.annotations.NotNull;
+
+public class PerformanceTroubleInfoCollector implements TroubleInfoCollector {
+ @NotNull
+ @Override
+ public String collectInfo(@NotNull Project project) {
+ return "=====PERFORMANCE SUMMARY=====\n" + new StatisticCollector(project).collectMetrics(false);
+ }
+
+ @Override
+ public String toString() {
+ return "Performance";
+ }
+}
diff --git a/plugins/performanceTesting/src/com/jetbrains/performancePlugin/PlaybackRunnerExtended.java b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/PlaybackRunnerExtended.java
new file mode 100644
index 000000000000..ba3c15a9cb2d
--- /dev/null
+++ b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/PlaybackRunnerExtended.java
@@ -0,0 +1,102 @@
+package com.jetbrains.performancePlugin;
+
+import com.intellij.ide.plugins.CannotUnloadPluginException;
+import com.intellij.ide.plugins.DynamicPluginListener;
+import com.intellij.ide.plugins.IdeaPluginDescriptor;
+import com.intellij.openapi.extensions.PluginId;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.playback.PlaybackCommand;
+import com.intellij.openapi.ui.playback.PlaybackRunner;
+import com.intellij.openapi.ui.playback.commands.AbstractCommand;
+import com.intellij.openapi.util.ActionCallback;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.registry.Registry;
+import com.intellij.util.messages.MessageBusConnection;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+
+public final class PlaybackRunnerExtended extends PlaybackRunner {
+ public static final String NOTIFICATION_GROUP = "PerformancePlugin";
+
+ private Project myProject;
+ private boolean myStopped;
+ private final DynamicPluginListener myDynamicPluginListener;
+
+ public PlaybackRunnerExtended(String script, StatusCallback callback, @NotNull Project project) {
+ super(script, callback, Registry.is("performance.plugin.playback.runner.useDirectActionCall", false), false, true);
+ setProject(project);
+ myDynamicPluginListener = new DynamicPluginListener() {
+ @Override
+ public void checkUnloadPlugin(@NotNull IdeaPluginDescriptor pluginDescriptor) throws CannotUnloadPluginException {
+ if (PluginId.getId("com.jetbrains.performancePlugin").equals(pluginDescriptor.getPluginId())) {
+ throw new CannotUnloadPluginException("Cannot unload plugin during playback execution");
+ }
+ }
+ };
+ }
+
+ @Override
+ public void setProject(@Nullable Project project) {
+ myProject = project;
+
+ if (project != null && !project.isDefault()) {
+ Disposer.register(project, () -> {
+ if (project == myProject) {
+ Disposer.dispose(myOnStop);
+ }
+ });
+ }
+ }
+
+ @Override
+ @Nullable
+ public Project getProject() {
+ return myProject;
+ }
+
+ @Override
+ protected void subscribeListeners(MessageBusConnection connection) {
+ super.subscribeListeners(connection);
+ connection.subscribe(DynamicPluginListener.TOPIC, myDynamicPluginListener);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ myStopped = true;
+ }
+
+ @Nullable
+ @Override
+ protected PlaybackCommand createCommand(@NotNull String _command, int line, @NotNull File scriptDir) {
+ String command = _command.replaceAll("[\r\n]+$", "");
+ String[] cmdline = command.split("\\s+");
+ PlaybackCommand playbackCommand = null;
+ if (cmdline.length > 0) {
+ String commandName = cmdline[0];
+ CreateCommand createCommand = CommandProvider.findCommandCreator(commandName);
+ if (createCommand != null) {
+ playbackCommand = createCommand.invoke(command, line);
+ if (playbackCommand instanceof AbstractCommand) {
+ ((AbstractCommand)playbackCommand).setScriptDir(scriptDir);
+ }
+ }
+ }
+ if (playbackCommand == null) {
+ playbackCommand = super.createCommand(command, line, scriptDir);
+ }
+ return playbackCommand;
+ }
+
+ @Override
+ public ActionCallback run() {
+ if (myStopped) {
+ throw new IllegalStateException("PlaybackRunnerExtended can be run only once.");
+ }
+ ActionCallback callback = super.run();
+ RunCallbackHandler.applyPatchesToCommandCallback(myProject, callback);
+ return callback;
+ }
+}
diff --git a/plugins/performanceTesting/src/com/jetbrains/performancePlugin/ProjectIndexingComponent.java b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/ProjectIndexingComponent.java
new file mode 100644
index 000000000000..a15171dd8a21
--- /dev/null
+++ b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/ProjectIndexingComponent.java
@@ -0,0 +1,69 @@
+package com.jetbrains.performancePlugin;
+
+import com.intellij.ide.util.PropertiesComponent;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.project.DumbService;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.Alarm;
+import com.intellij.util.AlarmFactory;
+import com.jetbrains.performancePlugin.profilers.Profiler;
+import com.jetbrains.performancePlugin.profilers.ProfilersController;
+import com.jetbrains.performancePlugin.ui.FinishScriptDialog;
+import com.jetbrains.performancePlugin.utils.ActionCallbackProfilerStopper;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+
+public final class ProjectIndexingComponent implements DumbService.DumbModeListener {
+ public static final String PROFILE_INDEXING_COMPONENT = "performancePlugin.isProfileIndexing";
+ public static final String PROFILE_WITH_ASYNC = "performancePlugin.isProfileIndexingWithAsync";
+ @NotNull private final Project project;
+ private static final int TIMEOUT = 500;
+ private final Alarm myAlarm = AlarmFactory.getInstance().create();
+
+ ProjectIndexingComponent(@NotNull Project project) {
+ this.project = project;
+ }
+
+ @Override
+ public void enteredDumbMode() {
+ try {
+ if (isProfilingEnabled()) {
+ PropertiesComponent.getInstance(project).setValue(PROFILE_INDEXING_COMPONENT, false);
+ PropertiesComponent.getInstance(project).setValue(PROFILE_WITH_ASYNC, false);
+ Profiler.getCurrentProfilerHandler(project).startProfiling(project.getName(), new ArrayList<>());
+ DumbService.getInstance(project).runReadActionInSmartMode(()->{
+ runScriptAfterDumb(project);
+ });
+ }
+ }
+ catch (Exception e) {
+ new ActionCallbackProfilerStopper().reject(e.getMessage());
+ }
+ }
+
+ private void runScriptAfterDumb(Project project) {
+ DumbService.getInstance(project).smartInvokeLater(() -> myAlarm.addRequest(() -> {
+ if (DumbService.isDumb(project)) {
+ runScriptAfterDumb(project);
+ }
+ else {
+ ProfilersController.getInstance();
+ if (Profiler.isAnyProfilingStarted()) {
+ try {
+ ProfilersController.getInstance().getCurrentProfilerHandler().stopProfiling(new ArrayList<>());
+ ProfilersController.getInstance().setStoppedByScript(true);
+ }
+ catch (Exception e) {
+ new ActionCallbackProfilerStopper().setRejected();
+ }
+ ApplicationManager.getApplication().invokeLater(() -> new FinishScriptDialog(project).show());
+ }
+ }
+ }, TIMEOUT));
+ }
+
+ private boolean isProfilingEnabled() {
+ return PropertiesComponent.getInstance(project).getBoolean(PROFILE_INDEXING_COMPONENT);
+ }
+}
diff --git a/plugins/performanceTesting/src/com/jetbrains/performancePlugin/ProjectLoaded.java b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/ProjectLoaded.java
new file mode 100644
index 000000000000..0197e97083a6
--- /dev/null
+++ b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/ProjectLoaded.java
@@ -0,0 +1,501 @@
+package com.jetbrains.performancePlugin;
+
+import com.intellij.diagnostic.AbstractMessage;
+import com.intellij.diagnostic.MessagePool;
+import com.intellij.diagnostic.ThreadDumper;
+import com.intellij.diagnostic.VMOptions;
+import com.intellij.diagnostic.startUpPerformanceReporter.StartUpPerformanceReporter;
+import com.intellij.ide.AppLifecycleListener;
+import com.intellij.ide.ApplicationInitializedListener;
+import com.intellij.ide.actions.RevealFileAction;
+import com.intellij.ide.lightEdit.LightEditService;
+import com.intellij.ide.lightEdit.LightEditorInfo;
+import com.intellij.ide.lightEdit.LightEditorListener;
+import com.intellij.idea.LoggerFactory;
+import com.intellij.notification.Notification;
+import com.intellij.notification.NotificationType;
+import com.intellij.notification.Notifications;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.application.ex.ApplicationManagerEx;
+import com.intellij.openapi.diagnostic.Attachment;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.extensions.ExtensionNotApplicableException;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.impl.CoreProgressManager;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.DumbService;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.startup.InitProjectActivityJavaShim;
+import com.intellij.openapi.ui.playback.PlaybackRunner;
+import com.intellij.openapi.util.ActionCallback;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.io.FileUtilRt;
+import com.intellij.util.Alarm;
+import com.intellij.util.AlarmFactory;
+import com.intellij.util.ConcurrencyUtil;
+import com.intellij.util.SystemProperties;
+import com.intellij.util.concurrency.AppExecutorUtil;
+import com.jetbrains.performancePlugin.commands.OpenProjectCommand;
+import com.jetbrains.performancePlugin.commands.TakeScreenshotCommand;
+import com.jetbrains.performancePlugin.profilers.ProfilersController;
+import io.opentelemetry.context.Context;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Stream;
+
+@SuppressWarnings({"UseOfSystemOutOrSystemErr", "SpellCheckingInspection"})
+public final class ProjectLoaded extends InitProjectActivityJavaShim implements ApplicationInitializedListener {
+ private static final Logger LOG = Logger.getInstance("PerformancePlugin");
+
+ private static final int TIMEOUT = 500;
+ public static final String TEST_SCRIPT_FILE_PATH = System.getProperty("testscript.filename");
+
+ /**
+ * If an IDE error occurs and this flag is true, a failure of a TeamCity test will be printed
+ * to stdout using TeamCity Service Messages (see {@link #reportTeamCityFailedTestAndBuildProblem(String, String, String)}).
+ * The name of the failed test will be inferred from the name of the script file
+ * (its name without extension, see {@link #getTeamCityFailedTestName()}).
+ */
+ private static final boolean MUST_REPORT_TEAMCITY_TEST_FAILURE_ON_IDE_ERROR = Boolean.parseBoolean(
+ System.getProperty("testscript.must.report.teamcity.test.failure.on.error", "true")
+ );
+
+ /**
+ * If an IDE error occurs during test script execution, the IDE will exit.
+ * This flag determines whether the status code of the exiting process will be 0 (if false) or 1 (if true).
+ */
+ private static final boolean MUST_EXIT_PROCESS_WITH_NON_SUCCESS_CODE_ON_IDE_ERROR = Boolean.parseBoolean(
+ System.getProperty("testscript.must.exist.process.with.non.success.code.on.ide.error", "false")
+ );
+
+ private static final String INDEXING_PROFILER_PREFIX = "%%profileIndexing";
+ private static ScheduledExecutorService screenshotExecutor;
+ private final Alarm myAlarm = AlarmFactory.getInstance().create();
+
+ private static boolean ourScriptStarted;
+
+ @SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod")
+ @Override
+ public void runActivity(@NotNull Project project) {
+ if (TEST_SCRIPT_FILE_PATH != null && !ourScriptStarted) {
+ ourScriptStarted = true;
+ if (System.getProperty("ide.performance.screenshot") != null) {
+ registerScreenshotTaking(project, System.getProperty("ide.performance.screenshot"));
+ }
+ LOG.info("Start Execution");
+ PerformanceTestSpan.startSpan();
+ Pair> profilerSettings = initializeProfilerSettingsForIndexing();
+ if (profilerSettings != null) {
+ try {
+ ProfilersController.getInstance().getCurrentProfilerHandler()
+ .startProfiling(profilerSettings.first, profilerSettings.second);
+ }
+ catch (Exception e) {
+ System.err.println("Start profile failed: " + e.getMessage());
+ ApplicationManagerEx.getApplicationEx().exit(true, true);
+ }
+ }
+ if (OpenProjectCommand.Companion.shouldOpenInSmartMode(project)) {
+ runScriptAfterDumb(project);
+ }
+ else if (SystemProperties.getBooleanProperty("performance.execute.script.after.scanning", false)) {
+ runScriptDuringIndexing(project);
+ }
+ else {
+ runScriptFromFile(project);
+ }
+ }
+ else {
+ if (!ApplicationManager.getApplication().isUnitTestMode()) {
+ LOG.info(PerformanceTestingBundle.message("startup.silent"));
+ }
+ }
+ }
+
+ private static void registerScreenshotTaking(@NotNull Project project, String fileName) {
+ screenshotExecutor = ConcurrencyUtil.newSingleScheduledThreadExecutor("Performance plugin screenshoter");
+ screenshotExecutor.scheduleWithFixedDelay(()-> {
+ TakeScreenshotCommand.takeScreenshotOfFrame(project, fileName);
+ }, 0, 1, TimeUnit.MINUTES);
+ }
+
+ private static Pair> initializeProfilerSettingsForIndexing() {
+ try {
+ List lines = FileUtil.loadLines(getTestFile());
+ for (String line : lines) {
+ if (line.startsWith(INDEXING_PROFILER_PREFIX)) {
+ String[] command = line.substring(INDEXING_PROFILER_PREFIX.length()).trim().split("\\s+", 2);
+ String indexingActivity = command[0];
+ List profilingParameters = command.length > 1 ? Arrays.asList(command[1].trim().split(","))
+ : new ArrayList<>();
+ return new Pair<>(indexingActivity, profilingParameters);
+ }
+ }
+ }
+ catch (IOException ignored) {
+ System.err.println(PerformanceTestingBundle.message("startup.script.read.error"));
+ ApplicationManagerEx.getApplicationEx().exit(true, true);
+ }
+ return null;
+ }
+
+ private static File getTestFile() {
+ File file = new File(TEST_SCRIPT_FILE_PATH);
+ if (!file.isFile()) {
+ System.err.println(PerformanceTestingBundle.message("startup.noscript", file.getAbsolutePath()));
+ ApplicationManagerEx.getApplicationEx().exit(true, true);
+ }
+
+ return file;
+ }
+
+
+ public static void reportErrorsFromMessagePool() {
+ MessagePool messagePool = MessagePool.getInstance();
+ List ideErrors = messagePool.getFatalErrors(false, true);
+ for (AbstractMessage message : ideErrors) {
+ try {
+ reportScriptError(message);
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
+ finally {
+ message.setRead(true);
+ }
+ }
+ }
+
+ static String generifyErrorMessage(String originalMessage) {
+ return originalMessage
+ // text@3ba5aac, text => text, text
+ .replaceAll("[$@#][A-Za-z0-9-_]+", "")
+
+ // java-design-patterns-master.db451f59 => java-design-patterns-master.
+ .replaceAll("[.]([A-Za-z]+[0-9]|[0-9]+[A-Za-z])[A-Za-z0-9]*", ".")
+
+ // 0x01 =>
+ .replaceAll("0x[0-9a-fA-F]+", "")
+
+ // text1234text => texttext
+ .replaceAll("[0-9]+", "");
+ }
+
+ private static void reportScriptError(@NotNull AbstractMessage errorMessage) throws IOException {
+ Throwable throwable = errorMessage.getThrowable();
+
+ Throwable cause = throwable;
+ String causeMessage = "";
+ while (cause.getCause() != null) {
+ cause = cause.getCause();
+ causeMessage = cause.getMessage();
+ }
+ if (causeMessage == null || causeMessage.isEmpty()) {
+ causeMessage = errorMessage.getMessage();
+ if (causeMessage == null || causeMessage.isEmpty()) {
+ String throwableMessage = getNonEmptyThrowableMessage(throwable);
+ int index = throwableMessage.indexOf("\tat ");
+ if (index != -1) {
+ causeMessage = throwableMessage.substring(0, index);
+ }
+ else {
+ causeMessage = throwableMessage;
+ }
+ }
+ }
+
+ Path scriptErrorsDir = Paths.get(PathManager.getLogPath(), "script-errors");
+ Files.createDirectories(scriptErrorsDir);
+
+ try (Stream stream = Files.walk(scriptErrorsDir)) {
+ String finalCauseMessage = causeMessage;
+ boolean isDuplicated = stream.filter(path -> path.getFileName().toString().equals("message.txt"))
+ .anyMatch(path -> {
+ try {
+ return Files.readString(path).equals(finalCauseMessage);
+ }
+ catch (IOException ex) {
+ LOG.error(ex.getMessage());
+ return false;
+ }
+ });
+ if (isDuplicated) {
+ return;
+ }
+ }
+
+ for (int i = 1; i < 1000; i++) {
+ Path errorDir = scriptErrorsDir.resolve("error-" + i);
+ if (Files.exists(errorDir)) {
+ continue;
+ }
+ Files.createDirectories(errorDir);
+ Files.writeString(errorDir.resolve("message.txt"), causeMessage);
+ Files.writeString(errorDir.resolve("stacktrace.txt"), errorMessage.getThrowableText());
+
+ List attachments = errorMessage.getAllAttachments();
+ for (int j = 0; j < attachments.size(); j++) {
+
+ Attachment attachment = attachments.get(j);
+ writeAttachmentToErrorDir(attachment, errorDir.resolve(j + "-" + attachment.getName()));
+ }
+ return;
+ }
+ LOG.error("Too many errors have been reported during script execution. See " + scriptErrorsDir);
+ }
+
+ private static void writeAttachmentToErrorDir(Attachment attachment, Path path) {
+ var executor = AppExecutorUtil.getAppScheduledExecutorService();
+ var counter = new AtomicInteger(0);
+ var isDone = new AtomicBoolean(false);
+
+ Path attachmentPath = Paths.get(attachment.getPath());
+ try {
+ Files.writeString(path, attachment.getDisplayText(), StandardOpenOption.APPEND, StandardOpenOption.CREATE);
+ Files.writeString(path, System.lineSeparator(), StandardOpenOption.APPEND, StandardOpenOption.CREATE);
+ }
+ catch (Exception e) {
+ LOG.warn("Failed to write attachment `display text`", e);
+ }
+
+ var task = new Runnable() {
+ @Override
+ public void run() {
+ if (isDone.get()) return;
+
+ try (FileChannel channel = FileChannel.open(attachmentPath, StandardOpenOption.READ)) {
+ channel.lock();
+
+ int bufferSize = 1024;
+ if (bufferSize > channel.size()) {
+ bufferSize = (int)channel.size();
+ }
+ ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
+
+ try (var writer = Files.newByteChannel(path)) {
+ while (channel.read(buffer) > 0) {
+ writer.write(buffer);
+ buffer.clear();
+ }
+ }
+
+ isDone.set(true);
+ }
+ catch (Exception e) {
+ LOG.warn("Failed to store attachment", e);
+ }
+ finally {
+ if (counter.getAndIncrement() >= 5) {
+ LOG.warn(String.format("Unable to store attachment %s %s to path %s",
+ attachment.getName(), attachment.getPath(), path.toString()));
+ isDone.set(true);
+ }
+ }
+ }
+ };
+
+ executor.scheduleWithFixedDelay(task, 0, 200, TimeUnit.MILLISECONDS);
+ }
+
+ @NotNull
+ private static String getNonEmptyThrowableMessage(@NotNull Throwable throwable) {
+ if (throwable.getMessage() != null && !throwable.getMessage().isEmpty()) {
+ return throwable.getMessage();
+ }
+ return throwable.getClass().getName();
+ }
+
+ @NotNull
+ private static String getTeamCityFailedTestName() {
+ return FileUtilRt.getNameWithoutExtension(getTestFile().getName());
+ }
+
+ private static void reportTeamCityFailedTestAndBuildProblem(@NotNull String testName,
+ @NotNull String failureMessage,
+ @NotNull String failureDetails) {
+ testName = generifyErrorMessage(testName);
+
+ System.out.printf("##teamcity[testFailed name='%s' message='%s' details='%s']\n",
+ encodeStringForTC(testName),
+ encodeStringForTC(failureMessage),
+ encodeStringForTC(failureDetails));
+
+ System.out.printf("##teamcity[buildProblem description='%s' identity='%s'] ",
+ encodeStringForTC(failureMessage),
+ encodeStringForTC(testName));
+ }
+
+ private static @NotNull String encodeStringForTC(@NotNull String line) {
+ final int MAX_DESCRIPTION_LENGTH = 7_500;
+ return line.substring(0, Math.min(MAX_DESCRIPTION_LENGTH, line.length())).
+ replaceAll("\\|", "||").
+ replaceAll("\\[", "|[").
+ replaceAll("]", "|]").
+ replaceAll("\n", "|n").
+ replaceAll("'", "|'").
+ replaceAll("\r", "|r");
+ }
+
+ @Override
+ public void componentsInitialized() {
+ if (ApplicationManagerEx.getApplicationEx().isLightEditMode()) {
+ LightEditService.getInstance().getEditorManager().addListener(new LightEditorListener() {
+ @Override
+ public void afterSelect(@Nullable LightEditorInfo editorInfo) {
+ StartUpPerformanceReporter.Companion.logStats("LightEditor");
+ runActivity(Objects.requireNonNull(LightEditService.getInstance().getProject()));
+ }
+ });
+ }
+ }
+
+ private void runScriptAfterDumb(Project project) {
+ DumbService.getInstance(project).smartInvokeLater(Context.current().wrap(() -> {
+ myAlarm.addRequest(Context.current().wrap(() -> {
+ if (DumbService.isDumb(project) || CoreProgressManager.getCurrentIndicators().size() != 0) {
+ runScriptAfterDumb(project);
+ }
+ else {
+ runScriptFromFile(project);
+ }
+ }), TIMEOUT);
+ }));
+ }
+
+ private void runScriptDuringIndexing(Project project) {
+ ApplicationManager.getApplication().executeOnPooledThread(Context.current().wrap(() -> myAlarm.addRequest(Context.current().wrap(() -> {
+ List indicators = CoreProgressManager.getCurrentIndicators();
+ boolean indexingInProgress = false;
+ for (ProgressIndicator indicator : indicators) {
+ String indicatorText = indicator.getText();
+ if (indicatorText != null && indicatorText.contains("Indexing")) {
+ indexingInProgress = true;
+ break;
+ }
+ }
+ if (indexingInProgress) {
+ runScriptFromFile(project);
+ }
+ else {
+ runScriptDuringIndexing(project);
+ }
+ }), TIMEOUT)));
+ }
+
+ public static void runScript(Project project, String script) {
+ PlaybackRunner playback = new PlaybackRunnerExtended(script, new CommandLogger(), project);
+ ActionCallback scriptCallback = playback.run();
+ runScript(scriptCallback, project);
+ }
+
+ private static void runScriptFromFile(Project project) {
+ PlaybackRunner playback = new PlaybackRunnerExtended("%include " + getTestFile(), new CommandLogger(), project);
+ playback.setScriptDir(getTestFile().getParentFile());
+ ActionCallback scriptCallback = playback.run();
+ CommandsRunner.setStartActionCallback(scriptCallback);
+ runScript(scriptCallback, project);
+ }
+
+ private static void runScript(ActionCallback scriptCallback, Project project) {
+ scriptCallback
+ .doWhenDone(() -> {
+ LOG.info("Execution of the script has been finished successfully");
+ })
+ .doWhenRejected(errorMessage -> {
+ String message = "IDE will be terminated because some errors are detected while running the startup script: " + errorMessage;
+ if (MUST_REPORT_TEAMCITY_TEST_FAILURE_ON_IDE_ERROR) {
+ String testName = getTeamCityFailedTestName();
+ reportTeamCityFailedTestAndBuildProblem(testName, message, "");
+ }
+
+ if (SystemProperties.getBooleanProperty("startup.performance.framework", false)) {
+ storeFailureToFile(errorMessage);
+ }
+
+ LOG.error(message);
+
+ String threadDump = "Thread dump before IDE termination:\n" + ThreadDumper.dumpThreadsToString();
+ LOG.info(threadDump);
+
+ if (System.getProperty("ide.performance.screenshot.on.failure") != null) {
+ TakeScreenshotCommand.takeScreenshotOfFrame(project, System.getProperty("ide.performance.screenshot.before.kill"));
+ }
+
+ if (MUST_EXIT_PROCESS_WITH_NON_SUCCESS_CODE_ON_IDE_ERROR) {
+ System.exit(1);
+ }
+ else {
+ ApplicationManagerEx.getApplicationEx().exit(true, true);
+ }
+ });
+ }
+
+ private static void storeFailureToFile(String errorMessage) {
+ try {
+ Path logDir = Path.of(PathManager.getLogPath());
+ String ideaLogContent = Files.readString(logDir.resolve(LoggerFactory.LOG_FILE_NAME));
+ String substringBegin = ideaLogContent.substring(ideaLogContent.indexOf(errorMessage));
+ Timestamp timestamp = new Timestamp(System.currentTimeMillis());
+ String date = timestamp.toString().substring(0, 10);
+ int endIndex = substringBegin.indexOf(date);
+ String errorMessageFromLog;
+ if (endIndex != -1) {
+ errorMessageFromLog = substringBegin.substring(0, endIndex);
+ }
+ else {
+ errorMessageFromLog = substringBegin;
+ }
+ Path failureCause = logDir.resolve("failure_cause.txt");
+ Files.writeString(failureCause, errorMessageFromLog);
+ }
+ catch (Exception ex) {
+ LOG.error(ex.getMessage());
+ }
+ }
+
+ static final class MyAppLifecycleListener implements AppLifecycleListener {
+ MyAppLifecycleListener() {
+ if (TEST_SCRIPT_FILE_PATH == null) {
+ throw ExtensionNotApplicableException.create();
+ }
+ }
+
+ @Override
+ public void appFrameCreated(@NotNull List commandLineArgs) {
+ MessagePool messagePool = MessagePool.getInstance();
+ LOG.info("Error watcher has started");
+ messagePool.addListener(ProjectLoaded::reportErrorsFromMessagePool);
+ }
+
+ @Override
+ public void appClosing() {
+ var executor = screenshotExecutor;
+ if (executor != null) {
+ executor.shutdown();
+ }
+ PerformanceTestSpan.endSpan();
+ reportErrorsFromMessagePool();
+ }
+ }
+}
diff --git a/plugins/performanceTesting/src/com/jetbrains/performancePlugin/RunCallbackHandler.java b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/RunCallbackHandler.java
new file mode 100644
index 000000000000..887e634d9bfb
--- /dev/null
+++ b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/RunCallbackHandler.java
@@ -0,0 +1,17 @@
+package com.jetbrains.performancePlugin;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.ActionCallback;
+
+public interface RunCallbackHandler {
+ ExtensionPointName EP_NAME = ExtensionPointName.create("com.jetbrains.performancePlugin.runCallbackHandler");
+
+ void patchCommandCallback(Project project, ActionCallback callback);
+
+ static void applyPatchesToCommandCallback(Project project, ActionCallback callback) {
+ EP_NAME.getExtensionList().forEach(extension ->
+ extension.patchCommandCallback(project, callback)
+ );
+ }
+}
diff --git a/plugins/performanceTesting/src/com/jetbrains/performancePlugin/SpanBuilderWithSystemInfoAttributes.java b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/SpanBuilderWithSystemInfoAttributes.java
new file mode 100644
index 000000000000..424fd7c72225
--- /dev/null
+++ b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/SpanBuilderWithSystemInfoAttributes.java
@@ -0,0 +1,86 @@
+package com.jetbrains.performancePlugin;
+
+import com.jetbrains.performancePlugin.utils.JvmRuntimeUtils;
+import io.opentelemetry.api.common.AttributeKey;
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.api.trace.SpanBuilder;
+import io.opentelemetry.api.trace.SpanContext;
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.context.Context;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.concurrent.TimeUnit;
+
+public class SpanBuilderWithSystemInfoAttributes implements SpanBuilder {
+ private final SpanBuilder spanBuilder;
+
+ public SpanBuilderWithSystemInfoAttributes(SpanBuilder spanBuilder) {
+ this.spanBuilder = spanBuilder;
+ }
+
+ @Override
+ public SpanBuilder setParent(@NotNull Context context) {
+ return spanBuilder.setParent(context);
+ }
+
+ @Override
+ public SpanBuilder setNoParent() {
+ return spanBuilder.setNoParent();
+ }
+
+ @Override
+ public SpanBuilder addLink(@NotNull SpanContext spanContext) {
+ return spanBuilder.addLink(spanContext);
+ }
+
+ @Override
+ public SpanBuilder addLink(@NotNull SpanContext spanContext, @NotNull Attributes attributes) {
+ return spanBuilder.addLink(spanContext, attributes);
+ }
+
+ @Override
+ public SpanBuilder setAttribute(@NotNull String key, @NotNull String value) {
+ return spanBuilder.setAttribute(key, value);
+ }
+
+ @Override
+ public SpanBuilder setAttribute(@NotNull String key, long value) {
+ return spanBuilder.setAttribute(key, value);
+ }
+
+ @Override
+ public SpanBuilder setAttribute(@NotNull String key, double value) {
+ return spanBuilder.setAttribute(key, value);
+ }
+
+ @Override
+ public SpanBuilder setAttribute(@NotNull String key, boolean value) {
+ return spanBuilder.setAttribute(key, value);
+ }
+
+ @Override
+ public SpanBuilder setAttribute(@NotNull AttributeKey key, @NotNull T value) {
+ return spanBuilder.setAttribute(key, value);
+ }
+
+ @Override
+ public SpanBuilder setSpanKind(@NotNull SpanKind spanKind) {
+ return spanBuilder.setSpanKind(spanKind);
+ }
+
+ @Override
+ public SpanBuilder setStartTimestamp(long startTimestamp, @NotNull TimeUnit unit) {
+ return spanBuilder.setStartTimestamp(startTimestamp, unit);
+ }
+
+ @Override
+ public Span startSpan() {
+ long jitTime = JvmRuntimeUtils.INSTANCE.getJitTime();
+ long gcTime = JvmRuntimeUtils.INSTANCE.getGCTime();
+ return new SpanWithOnEndListener(spanBuilder.startSpan(), span -> {
+ span.setAttribute("jitTime", JvmRuntimeUtils.INSTANCE.getJitTime() - jitTime);
+ span.setAttribute("gcTime", JvmRuntimeUtils.INSTANCE.getGCTime() - gcTime);
+ });
+ }
+}
diff --git a/plugins/performanceTesting/src/com/jetbrains/performancePlugin/SpanWithOnEndListener.java b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/SpanWithOnEndListener.java
new file mode 100644
index 000000000000..e76fa5168940
--- /dev/null
+++ b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/SpanWithOnEndListener.java
@@ -0,0 +1,72 @@
+package com.jetbrains.performancePlugin;
+
+import io.opentelemetry.api.common.AttributeKey;
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.api.trace.SpanContext;
+import io.opentelemetry.api.trace.StatusCode;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.concurrent.TimeUnit;
+
+public class SpanWithOnEndListener implements Span {
+ private final Span span;
+ private final OnEndListener super Span> listener;
+
+ public SpanWithOnEndListener(Span span, OnEndListener super Span> listener) {
+ this.span = span;
+ this.listener = listener;
+ }
+
+ @Override
+ public Span setAttribute(@NotNull AttributeKey key, @NotNull T value) {
+ return span.setAttribute(key, value);
+ }
+
+ @Override
+ public Span addEvent(@NotNull String name, @NotNull Attributes attributes) {
+ return span.addEvent(name, attributes);
+ }
+
+ @Override
+ public Span addEvent(@NotNull String name, @NotNull Attributes attributes, long timestamp, @NotNull TimeUnit unit) {
+ return span.addEvent(name, attributes, timestamp, unit);
+ }
+
+ @Override
+ public Span setStatus(@NotNull StatusCode statusCode, @NotNull String description) {
+ return span.setStatus(statusCode, description);
+ }
+
+ @Override
+ public Span recordException(@NotNull Throwable exception, @NotNull Attributes additionalAttributes) {
+ return span.recordException(exception, additionalAttributes);
+ }
+
+ @Override
+ public Span updateName(@NotNull String name) {
+ return span.updateName(name);
+ }
+
+ @Override
+ public void end() {
+ listener.onEnd(this);
+ span.end();
+ }
+
+ @Override
+ public void end(long timestamp, @NotNull TimeUnit unit) {
+ listener.onEnd(this);
+ span.end(timestamp, unit);
+ }
+
+ @Override
+ public SpanContext getSpanContext() {
+ return span.getSpanContext();
+ }
+
+ @Override
+ public boolean isRecording() {
+ return span.isRecording();
+ }
+}
diff --git a/plugins/performanceTesting/src/com/jetbrains/performancePlugin/Timer.java b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/Timer.java
new file mode 100644
index 000000000000..1ca3ba386f33
--- /dev/null
+++ b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/Timer.java
@@ -0,0 +1,165 @@
+package com.jetbrains.performancePlugin;
+
+import com.google.common.util.concurrent.AtomicDouble;
+import com.intellij.util.ConcurrencyUtil;
+import com.sun.management.OperatingSystemMXBean;
+import io.opentelemetry.api.trace.Span;
+import oshi.SystemInfo;
+import oshi.software.os.OSProcess;
+
+import javax.swing.*;
+import java.lang.management.ManagementFactory;
+import java.lang.reflect.InvocationTargetException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.LongAccumulator;
+
+public class Timer {
+ public static final Timer instance = new Timer();
+
+ private String myActivityName = "default";
+ private volatile long myStartTime;
+ private volatile long myStopTime;
+ private LongAccumulator myLongestDelay;
+ private volatile double myHighestCPULoad;
+ private volatile long myHighestRAMUsage;
+ private final AtomicLong myTotalDelay = new AtomicLong();
+ private final AtomicDouble myTotalCPULoad = new AtomicDouble();
+ private final AtomicLong myTotalRAMUsage = new AtomicLong();
+ private final AtomicLong myCounter = new AtomicLong();
+ private final AtomicLong myHWCounter = new AtomicLong();
+ private Span mySpan;
+ private ScheduledExecutorService executor;
+ private long totalTime;
+
+ public void start() {
+ start("default", true);
+ }
+
+ public void start(String activityName, boolean withSpan) {
+ myActivityName = activityName;
+ myStartTime = System.currentTimeMillis();
+ myLongestDelay = new LongAccumulator(Math::max, 0);
+ myHighestCPULoad = 0;
+ myHighestRAMUsage = 0;
+ myTotalRAMUsage.set(0);
+ myTotalCPULoad.set(0);
+ myTotalDelay.set(0);
+ myCounter.set(0);
+ myHWCounter.set(0);
+ executor = ConcurrencyUtil.newSingleScheduledThreadExecutor("Performance plugin timer");
+
+ SystemInfo info = new SystemInfo();
+ int processId = info.getOperatingSystem().getProcessId();
+ OSProcess process = info.getOperatingSystem().getProcess(processId);
+ executor.scheduleWithFixedDelay(() -> {
+ long before = System.currentTimeMillis();
+ try {
+ SwingUtilities.invokeAndWait(() -> {
+ });
+ long after = System.currentTimeMillis();
+ long delay = after - before;
+ myTotalDelay.addAndGet(delay);
+ myCounter.incrementAndGet();
+ myLongestDelay.accumulate(delay);
+ }
+ catch (InterruptedException | InvocationTargetException ignored) {
+ }
+ }, 0, 50, TimeUnit.MILLISECONDS);
+
+ OperatingSystemMXBean bean = (OperatingSystemMXBean)ManagementFactory.getOperatingSystemMXBean();
+ executor.scheduleWithFixedDelay(() -> {
+ myHWCounter.incrementAndGet();
+ double cpuLoad = bean.getProcessCpuLoad();
+ if (cpuLoad > 0 && cpuLoad <= 1) {
+ myTotalCPULoad.addAndGet(cpuLoad);
+ if (myHighestCPULoad < cpuLoad) myHighestCPULoad = cpuLoad;
+ }
+
+ long ramUsage = process.getResidentSetSize();
+ myTotalRAMUsage.addAndGet(ramUsage);
+ if (myHighestRAMUsage < ramUsage) myHighestRAMUsage = ramUsage;
+ }, 0, 1, TimeUnit.SECONDS);
+
+ if(withSpan) {
+ String spanName = activityName.equals("default") ? "timer" : activityName;
+ // We need to set parent or null since it's part of general startup activity
+ //noinspection DataFlowIssue Parent can be null.
+ mySpan = PerformanceTestSpan.TRACER.spanBuilder(spanName).setParent(PerformanceTestSpan.getContext()).startSpan();
+ }
+ }
+
+ public void stop() {
+ if(mySpan != null) {
+ mySpan.setAttribute("max_awt_delay", getLongestDelay());
+ mySpan.setAttribute("average_awt_delay", getAverageDelay());
+ mySpan.end();
+ }
+ myStopTime = System.currentTimeMillis();
+ totalTime = myStopTime - myStartTime;
+ executor.shutdownNow();
+ }
+
+ public void reportToTeamCity() {
+ logValue(myActivityName, totalTime);
+ logValue(myActivityName + " | Total Time Execution", totalTime);
+ logValue(myActivityName + " | Responsiveness", getLongestDelay());
+ logValue(myActivityName + " | Average Responsiveness", getAverageDelay());
+ logValue(myActivityName + " | Average RAM Usage", getAverageRAMUsage());
+ logValue(myActivityName + " | Average CPU Usage", getAverageCPULoad());
+ logValue(myActivityName + " | Max RAM Usage", myHighestRAMUsage);
+ logValue(myActivityName + " | Max CPU Usage", myHighestCPULoad);
+ }
+
+ public long getLongestDelay() {
+ return myLongestDelay.get();
+ }
+
+ public long getTotalTime() {
+ return totalTime;
+ }
+
+ public long getStopTime() {
+ return myStopTime;
+ }
+
+ public double getAverageCPULoad() {
+ long counterValue = myHWCounter.get();
+ return counterValue != 0 ? myTotalCPULoad.get() / counterValue : 0.0;
+ }
+
+ public int getAverageRAMUsage() {
+ long counterValue = myHWCounter.get();
+ return counterValue != 0 ? (int)(myTotalRAMUsage.get() / counterValue) : 0;
+ }
+
+ public long getAverageDelay() {
+ long counterValue = myCounter.get();
+ return counterValue != 0 ? myTotalDelay.get() / counterValue : 0;
+ }
+
+ public String getActivityName() {
+ return myActivityName;
+ }
+
+ public boolean isStarted() {
+ return executor != null && !executor.isShutdown();
+ }
+
+ @SuppressWarnings("UseOfSystemOutOrSystemErr")
+ private static void logValue(String key, long value) {
+ if (value >= 0L) {
+ System.out.printf("##teamcity[buildStatisticValue key='%s' value='%d']", key, value);
+ System.out.println();
+ }
+ }
+
+ @SuppressWarnings("UseOfSystemOutOrSystemErr")
+ private static void logValue(String key, double value) {
+ if (value >= 0L) {
+ System.out.printf("##teamcity[buildStatisticValue key='%s' value='%f']", key, value);
+ System.out.println();
+ }
+ }
+}
diff --git a/plugins/performanceTesting/src/com/jetbrains/performancePlugin/actions/ExecuteScriptAction.java b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/actions/ExecuteScriptAction.java
new file mode 100644
index 000000000000..6c2c46ac859a
--- /dev/null
+++ b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/actions/ExecuteScriptAction.java
@@ -0,0 +1,33 @@
+package com.jetbrains.performancePlugin.actions;
+
+import com.intellij.openapi.actionSystem.ActionUpdateThread;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import com.jetbrains.performancePlugin.ui.ExecuteScriptDialog;
+import org.jetbrains.annotations.NotNull;
+
+
+public class ExecuteScriptAction extends AnAction implements DumbAware {
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ Project project = e.getProject();
+ if (project == null) {
+ return;
+ }
+
+ ExecuteScriptDialog executeScriptDialog = new ExecuteScriptDialog(project);
+ executeScriptDialog.show();
+ }
+
+ @Override
+ public @NotNull ActionUpdateThread getActionUpdateThread() {
+ return ActionUpdateThread.BGT;
+ }
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ e.getPresentation().setEnabledAndVisible(e.getProject() != null);
+ }
+}
diff --git a/plugins/performanceTesting/src/com/jetbrains/performancePlugin/actions/OpenIndexingDiagnosticsAction.java b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/actions/OpenIndexingDiagnosticsAction.java
new file mode 100644
index 000000000000..80c179144011
--- /dev/null
+++ b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/actions/OpenIndexingDiagnosticsAction.java
@@ -0,0 +1,34 @@
+package com.jetbrains.performancePlugin.actions;
+
+import com.intellij.ide.BrowserUtil;
+import com.intellij.openapi.actionSystem.ActionUpdateThread;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.indexing.diagnostic.IndexDiagnosticDumper;
+import org.jetbrains.annotations.NotNull;
+
+import java.nio.file.Path;
+
+final class OpenIndexingDiagnosticsAction extends AnAction implements DumbAware {
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ Project project = e.getProject();
+ if (project == null) {
+ return;
+ }
+ Path reportHtml = IndexDiagnosticDumper.Companion.getProjectDiagnosticDirectory(project).resolve("report.html");
+ BrowserUtil.browse(reportHtml);
+ }
+
+ @Override
+ public @NotNull ActionUpdateThread getActionUpdateThread() {
+ return ActionUpdateThread.BGT;
+ }
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ e.getPresentation().setEnabledAndVisible(e.getProject() != null);
+ }
+}
diff --git a/plugins/performanceTesting/src/com/jetbrains/performancePlugin/commands/AddFileCommand.kt b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/commands/AddFileCommand.kt
new file mode 100644
index 000000000000..35053323546c
--- /dev/null
+++ b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/commands/AddFileCommand.kt
@@ -0,0 +1,54 @@
+package com.jetbrains.performancePlugin.commands
+
+import com.intellij.openapi.application.WriteAction
+import com.intellij.openapi.diagnostic.Logger
+import com.intellij.openapi.ui.playback.PlaybackContext
+import com.intellij.openapi.util.ActionCallback
+import com.intellij.openapi.vfs.VirtualFile
+import com.intellij.openapi.vfs.VirtualFileManager
+import com.jetbrains.performancePlugin.utils.AbstractCallbackBasedCommand
+import org.jetbrains.annotations.NonNls
+import java.io.IOException
+import java.nio.file.Path
+import java.nio.file.Paths
+
+abstract class AbstractFileCommand(text: String, line: Int) : AbstractCallbackBasedCommand(text, line, true) {
+ protected fun execute(path: Path, callback: ActionCallback, f: (VirtualFile) -> Any) {
+ val virtualFile = VirtualFileManager.getInstance().findFileByNioPath(path)
+ if (virtualFile == null) {
+ callback.reject("Directory or file by ${path} path from parameters was not found.")
+ return
+ }
+ WriteAction.runAndWait {
+ f(virtualFile)
+ }
+ callback.setDone()
+ }
+
+ protected fun isCommandParametersRight(input: ArrayList): Boolean {
+ return !(input.size < 2 || input.size > 2 || input[0] == "" || input[1] == "")
+ }
+}
+
+class AddFileCommand(text: String, line: Int) : AbstractFileCommand(text, line) {
+ @Throws(Exception::class)
+ override fun execute(callback: ActionCallback,
+ context: PlaybackContext) {
+ val input = extractCommandList(PREFIX, ",")
+ if (!isCommandParametersRight(input)) {
+ callback.reject("Command $PREFIX should have 2 non empty parameters (1 - path to directory, 2 - file name) with delimiter ','.")
+ return
+ }
+ if (VirtualFileManager.getInstance().findFileByNioPath(Paths.get(input[0], input[1])) != null) {
+ callback.reject("File by ${Paths.get(input[0], input[1])} path from parameters already exists.")
+ return
+ }
+ LOG.info("Add file ${input[1]} ${input[0]}")
+ super.execute(Paths.get(input[0]), callback) { virtualFile: VirtualFile -> virtualFile.createChildData(null, input[1]) }
+ }
+
+ companion object {
+ private val LOG = Logger.getInstance(AddFileCommand::class.java)
+ const val PREFIX: @NonNls String = CMD_PREFIX + "addFile"
+ }
+}
\ No newline at end of file
diff --git a/plugins/performanceTesting/src/com/jetbrains/performancePlugin/commands/CleanCaches.java b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/commands/CleanCaches.java
new file mode 100644
index 000000000000..cf9334da3259
--- /dev/null
+++ b/plugins/performanceTesting/src/com/jetbrains/performancePlugin/commands/CleanCaches.java
@@ -0,0 +1,39 @@
+package com.jetbrains.performancePlugin.commands;
+
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.playback.PlaybackContext;
+import com.intellij.openapi.ui.playback.commands.AbstractCommand;
+import com.intellij.openapi.util.ActionCallback;
+import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS;
+import com.intellij.openapi.vfs.newvfs.persistent.PersistentFSImpl;
+import com.intellij.psi.PsiManager;
+import com.intellij.psi.stubs.StubIndex;
+import com.intellij.psi.stubs.StubIndexEx;
+import com.jetbrains.performancePlugin.utils.ActionCallbackProfilerStopper;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.concurrency.Promise;
+import org.jetbrains.concurrency.Promises;
+
+public class CleanCaches extends AbstractCommand {
+
+ public static final String PREFIX = CMD_PREFIX + "cleanCaches";
+
+ public CleanCaches(@NotNull String text, int line) {
+ super(text, line);
+ }
+
+ @Override
+ protected @NotNull Promise