diff --git a/platform/core-impl/src/com/intellij/diagnostic/PerformanceWatcher.java b/platform/core-impl/src/com/intellij/diagnostic/PerformanceWatcher.java index d9503dc46a30..27c3850ffafa 100644 --- a/platform/core-impl/src/com/intellij/diagnostic/PerformanceWatcher.java +++ b/platform/core-impl/src/com/intellij/diagnostic/PerformanceWatcher.java @@ -59,11 +59,11 @@ import java.util.concurrent.TimeUnit; public class PerformanceWatcher extends ApplicationComponent.Adapter implements Disposable { private static final Logger LOG = Logger.getInstance("#com.intellij.diagnostic.PerformanceWatcher"); private static final int TOLERABLE_LATENCY = 100; + private static final String THREAD_DUMPS_PREFIX = "threadDumps-"; private final ScheduledFuture myThread; private final ThreadMXBean myThreadMXBean; private final DateFormat myDateFormat = new SimpleDateFormat("yyyyMMdd-HHmmss"); - private final File mySessionLogDir; - private File myCurHangLogDir; + private final File myLogDir = new File(PathManager.getLogPath()); private List myStacktraceCommonPart; private final IdePerformanceListener myPublisher; @@ -80,14 +80,13 @@ public class PerformanceWatcher extends ApplicationComponent.Adapter implements private int UNRESPONSIVE_THRESHOLD_SECONDS = 5; private int UNRESPONSIVE_INTERVAL_SECONDS = 5; private static final int SAMPLING_INTERVAL_MS = 1000; + private static final long ourIdeStart = System.currentTimeMillis(); public static PerformanceWatcher getInstance() { return ApplicationManager.getApplication().getComponent(PerformanceWatcher.class); } public PerformanceWatcher() { - myCurHangLogDir = mySessionLogDir = new File(PathManager.getLogPath() + "/threadDumps-" + myDateFormat.format(new Date()) - + "-" + ApplicationInfo.getInstance().getBuild().asString()); myPublisher = ApplicationManager.getApplication().getMessageBus().syncPublisher(IdePerformanceListener.TOPIC); myThreadMXBean = ManagementFactory.getThreadMXBean(); myThread = JobScheduler.getScheduler().scheduleWithFixedDelay(new Runnable() { @@ -121,7 +120,7 @@ public class PerformanceWatcher extends ApplicationComponent.Adapter implements ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { @Override public void run() { - deleteOldThreadDumps(); + cleanOldFiles(myLogDir, 0); } }); @@ -157,24 +156,30 @@ public class PerformanceWatcher extends ApplicationComponent.Adapter implements }, null, null); } - private static void deleteOldThreadDumps() { - File allLogsDir = new File(PathManager.getLogPath()); - if (allLogsDir.isDirectory()) { - final String[] dirs = allLogsDir.list(new FilenameFilter() { - @Override - public boolean accept(@NotNull final File dir, @NotNull final String name) { - return name.startsWith("threadDumps-"); - } - }); - if (dirs != null) { - Arrays.sort(dirs); - for (int i = 0; i < dirs.length - 11; i++) { - FileUtil.delete(new File(allLogsDir, dirs [i])); - } + private static void cleanOldFiles(File dir, final int level) { + File[] children = dir.listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return level > 0 || name.startsWith(THREAD_DUMPS_PREFIX); + } + }); + if (children == null) return; + + Arrays.sort(children); + for (int i = 0; i < children.length; i++) { + File child = children[i]; + if (i < children.length - 100 || ageInDays(child) > 10) { + FileUtil.delete(child); + } else if (level < 3) { + cleanOldFiles(child, level + 1); } } } + private static long ageInDays(File file) { + return TimeUnit.DAYS.convert(System.currentTimeMillis() - file.lastModified(), TimeUnit.MILLISECONDS); + } + @Override public void dispose() { if (myThread != null) { @@ -218,45 +223,60 @@ public class PerformanceWatcher extends ApplicationComponent.Adapter implements myFreezeStart = myLastAliveEdt; myPublisher.uiFreezeStarted(); } - if (myCurHangLogDir == mySessionLogDir) { - //System.out.println("EDT is not responding at " + myPrintDateFormat.format(new Date())); - myCurHangLogDir = new File(mySessionLogDir, myDateFormat.format(new Date())); - } - dumpThreads("", false); + dumpThreads(getFreezeFolderName(myFreezeStart) + "/", false); } } + @NotNull + private String getFreezeFolderName(long freezeStartMs) { + return THREAD_DUMPS_PREFIX + "freeze-" + formatTime(freezeStartMs) + "-" + buildName(); + } + + private static String buildName() { + return ApplicationInfo.getInstance().getBuild().asString(); + } + + private String formatTime(long timeMs) { + return myDateFormat.format(new Date(timeMs)); + } + private void edtResponds(long currentMillis) { if (myFreezeStart != 0) { - if (myCurHangLogDir != mySessionLogDir && myCurHangLogDir.exists()) { - int unresponsiveDuration = (int)(currentMillis - myFreezeStart) / 1000; + int unresponsiveDuration = (int)(currentMillis - myFreezeStart) / 1000; + File dir = new File(myLogDir, getFreezeFolderName(myFreezeStart)); + if (dir.exists()) { //noinspection ResultOfMethodCallIgnored - myCurHangLogDir.renameTo(new File(mySessionLogDir, getLogDirForHang(unresponsiveDuration))); - myPublisher.uiFreezeFinished(unresponsiveDuration); + dir.renameTo(new File(myLogDir, dir.getName() + "-" + unresponsiveDuration + "sec" + getFreezePlaceSuffix())); } + myPublisher.uiFreezeFinished(unresponsiveDuration); myFreezeStart = 0; - myCurHangLogDir = mySessionLogDir; myStacktraceCommonPart = null; } } - private String getLogDirForHang(int unresponsiveDuration) { - StringBuilder name = new StringBuilder("freeze-" + myCurHangLogDir.getName()); - name.append("-").append(unresponsiveDuration); + private String getFreezePlaceSuffix() { if (myStacktraceCommonPart != null && !myStacktraceCommonPart.isEmpty()) { final StackTraceElement element = myStacktraceCommonPart.get(0); - name.append("-").append(StringUtil.getShortName(element.getClassName())).append(".").append(element.getMethodName()); + return "-" + StringUtil.getShortName(element.getClassName()) + "." + element.getMethodName(); } - return name.toString(); + return ""; } @Nullable public File dumpThreads(@NotNull String pathPrefix, boolean millis) { if (!shouldWatch()) return null; - String suffix = millis ? "-" + System.currentTimeMillis() : ""; - File file = new File(myCurHangLogDir, pathPrefix + "threadDump-" + myDateFormat.format(new Date()) + suffix + ".txt"); + if (!pathPrefix.contains("/")) { + pathPrefix = THREAD_DUMPS_PREFIX + "-" + pathPrefix + "-" + formatTime(ourIdeStart) + "-" + buildName() + "/"; + } + else if (!pathPrefix.startsWith(THREAD_DUMPS_PREFIX)) { + pathPrefix = THREAD_DUMPS_PREFIX + pathPrefix; + } + + long now = System.currentTimeMillis(); + String suffix = millis ? "-" + now : ""; + File file = new File(myLogDir, pathPrefix + "threadDump-" + formatTime(now) + suffix + ".txt"); File dir = file.getParentFile(); if (!(dir.isDirectory() || dir.mkdirs())) {