@Override
  public void actionPerformed(@NotNull AnActionEvent e) {
    JTextArea textArea = new JTextArea(12, 100);
    textArea.setText(CommonBundle.getLoadingTreeNodeText());
    ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
    List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
    CompilationMXBean jitBean = ManagementFactory.getCompilationMXBean();
    OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
    Method getProcessCpuTime = Objects.requireNonNull(ReflectionUtil.getMethod(osBean.getClass().getInterfaces()[0], "getProcessCpuTime"));
    ScheduledFuture<?> future = AppExecutorUtil.getAppScheduledExecutorService().scheduleWithFixedDelay(new Runnable() {
      final Long2LongMap lastThreadTimes = new Long2LongOpenHashMap();
      final Object2LongMap<String> subsystemToSamples = new Object2LongOpenHashMap<>();
      long lastGcTime = totalGcTime();
      long lastProcessTime = totalProcessTime();
      long lastJitTime = jitBean.getTotalCompilationTime();
      long lastUiUpdate = System.currentTimeMillis();

      private final Map<String, String> classToSubsystem = new HashMap<>();

      private @NotNull String calcSubSystemName(String className) {
        String pkg = StringUtil.getPackageName(className);
        if (pkg.isEmpty()) pkg = className;

        String prefix = getMeaninglessPrefix(pkg);
        if (!prefix.isEmpty()) {
          pkg = pkg.substring(prefix.length()) + " (in " + StringUtil.trimEnd(prefix, ".") + ")";
        }

        IdeaPluginDescriptor plugin = PluginManagerCore.getPlugin(PluginManager.getPluginByClassNameAsNoAccessToClass(className));
        return plugin == null ? pkg : "Plugin " + plugin.getName() + ": " + pkg;
      }

      private String getMeaninglessPrefix(String qname) {
        String result = findPrefix(qname, MEANINGLESS_PREFIXES_1);
        if (!result.isEmpty()) {
          result += findPrefix(qname.substring(result.length()), MEANINGLESS_PREFIXES_2);
        }
        return result;
      }

      private static String findPrefix(String qname, String[] prefixes) {
        for (String prefix : prefixes) {
          if (qname.startsWith(prefix)) {
            return prefix;
          }
        }
        return "";
      }

      private long totalGcTime() {
        return gcBeans.stream().mapToLong(GarbageCollectorMXBean::getCollectionTime).sum();
      }

      private long totalProcessTime() {
        try {
          return (long)getProcessCpuTime.invoke(osBean);
        }
        catch (Exception ex) {
          return 0;
        }
      }

      private @NotNull String getSubsystemName(long threadId) {
        if (threadId == Thread.currentThread().getId()) {
          return "<Activity Monitor>";
        }

        ThreadInfo info = threadBean.getThreadInfo(threadId, Integer.MAX_VALUE);
        if (info == null) return "<unidentified: thread finished>";

        boolean runnable = info.getThreadState() == Thread.State.RUNNABLE;
        if (runnable) {
          for (StackTraceElement element : info.getStackTrace()) {
            String className = element.getClassName();
            if (!isInfrastructureClass(className)) {
              return classToSubsystem.computeIfAbsent(className, this::calcSubSystemName);
            }
          }
        }
        return (runnable ? "<infrastructure: " : "<unidentified: ") + getCommonThreadName(info) + ">";
      }

      private static String getCommonThreadName(ThreadInfo info) {
        String name = info.getThreadName();
        if (ThreadDumper.isEDT(name)) return "UI thread";

        int numberStart = CharArrayUtil.shiftBackward(name, name.length() - 1, "0123456789/ ") + 1;
        if (numberStart > 0) return name.substring(0, numberStart);
        return name;
      }

      private static boolean isInfrastructureClass(String className) {
        return ContainerUtil.exists(INFRASTRUCTURE_PREFIXES, className::startsWith);
      }

      @Override
      public void run() {
        for (long id : threadBean.getAllThreadIds()) {
          long cpuTime = threadBean.getThreadCpuTime(id);
          long prev = lastThreadTimes.put(id, cpuTime);
          if (prev != 0 && cpuTime > prev) {
            String subsystem = getSubsystemName(id);
            subsystemToSamples.put(subsystem, subsystemToSamples.getLong(subsystem) + cpuTime - prev);
          }
        }
        long now = System.currentTimeMillis();
        long sinceLastUpdate = now - lastUiUpdate;
        if (sinceLastUpdate > 2_000) {
          lastUiUpdate = now;
          scheduleUiUpdate(sinceLastUpdate);
        }
      }

      private void scheduleUiUpdate(long sinceLastUpdate) {
        List<Pair<String, Long>> times = takeSnapshot();
        String text = " %CPU  Subsystem\n\n" +
                      StreamEx.of(times)
                        .filter(p -> p.second > 10)
                        .sorted(Comparator.comparing((Pair<String, Long> p) -> p.second).reversed())
                        .limit(8)
                        .map(p -> String.format("%5.1f  %s", (double) p.second*100 / sinceLastUpdate, p.first))
                        .joining("\n");
        ApplicationManager.getApplication().invokeLater(() -> {
          textArea.setText(text);
          textArea.setCaretPosition(0);
        }, ModalityState.any());
      }

      private @NotNull List<Pair<String, Long>> takeSnapshot() {
        List<Pair<String, Long>> times = new ArrayList<>();
        for (Object2LongMap.Entry<String> entry : subsystemToSamples.object2LongEntrySet()) {
          times.add(new Pair<>(entry.getKey(), TimeUnit.NANOSECONDS.toMillis(entry.getLongValue())));
        }
        subsystemToSamples.clear();

        long gcTime = totalGcTime();
        if (gcTime != lastGcTime) {
          times.add(Pair.create("<Garbage collection>", gcTime - lastGcTime));
          lastGcTime = gcTime;
        }

        long jitTime = jitBean.getTotalCompilationTime();
        if (jitTime != lastJitTime) {
          times.add(Pair.create("<JIT compiler>", jitTime - lastJitTime));
          lastJitTime = jitTime;
        }

        long processTime = totalProcessTime();
        if (processTime != lastProcessTime) {
          times.add(Pair.create("<Process total CPU usage>", TimeUnit.NANOSECONDS.toMillis(processTime - lastProcessTime)));
          lastProcessTime = processTime;
        }

        return times;
      }

    }, 0, 20, TimeUnit.MILLISECONDS);
    DialogWrapper dialog = new DialogWrapper(false) {
      {
        init();
      }

      @Override
      protected JComponent createCenterPanel() {
        JBScrollPane pane = new JBScrollPane(textArea);
        pane.setPreferredSize(textArea.getPreferredSize());
        return pane;
      }

      @Override
      protected String getDimensionServiceKey() {
        return "Performance.Activity.Monitor";
      }

      @Override
      protected Action @NotNull [] createActions() {
        return new Action[]{getOKAction()};
      }
    };
    dialog.setTitle(IdeBundle.message("dialog.title.activity.monitor"));
    dialog.setModal(false);
    Disposer.register(dialog.getDisposable(), () -> future.cancel(false));
    dialog.show();
  }
@Override
  public void actionPerformed(@NotNull AnActionEvent e)