// Copyright 2017 Pants project contributors (see CONTRIBUTORS.md). // Licensed under the Apache License, Version 2.0 (see LICENSE). package com.twitter.intellij.pants.metrics; import com.google.common.base.Stopwatch; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * This class captures the indexing duration on the fly, which is independent from the pipeline where * pants export, project loading, and indexing happen in sequence. * <p> * My current understanding about how indexing works, aka project being dumb, is that multiple * components such as python and scala plugins can submit tasks to the framework, from which time to they * are completed, the project is in dumb mode. Also, indexing time can happen in any time and order. * <p> * For example: * <p> * When a project opens, both python and scala plugins found that some .py and .scala files have been modified, * they may submit jobs to update the indexing caches simultaneously. Assuming each job will take 5 seconds and there is * only a single CPU, `PantsDumbModeListener` will report 10 seconds. * <p> * In some other circumstances where files in IDE are not in sync with their counterparts on the disk, there * can be a delay where python plugin detects changes first, and one moment later, scala plugin does the same. * In this case, it is possible `PantsDumbModeListener` will report 5 seconds twice and separately. */ public class LivePantsMetrics { static class PantsDumbModeListener implements DumbService.DumbModeListener { private AtomicInteger count = new AtomicInteger(0); Stopwatch indexWatch = Stopwatch.createUnstarted(); @Override public void enteredDumbMode() { if (count.getAndIncrement() == 0) { indexWatch.start(); } } @Override public void exitDumbMode() { if (count.decrementAndGet() == 0) { indexWatch.stop(); PantsExternalMetricsListenerManager.getInstance().logIndexingDuration(indexWatch.elapsed(TimeUnit.MILLISECONDS)); indexWatch.reset(); } } } private static final ConcurrentHashMap<Project, PantsDumbModeListener> projectListener = new ConcurrentHashMap<>(); /** * Register project to subscribe the event of hopping in and out of Dumb mode. Being in dumb mode means the IDE is indexing. * This is only functional in the actual GUI setting. * * @param project project of interest. */ public static void registerDumbModeListener(@NotNull Project project) { PantsDumbModeListener listener = projectListener.computeIfAbsent(project, k -> new PantsDumbModeListener()); project.getMessageBus().connect().subscribe(DumbService.DUMB_MODE, listener); } public static void unregisterDumbModeListener(@NotNull Project project) { projectListener.remove(project); } }