flatter directory structure for thread dumps, and delete the old ones more eagerly (IDEA-164176)

This commit is contained in:
peter
2017-02-08 20:49:47 +01:00
parent 05686c30b2
commit 29d38b3072
@@ -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<StackTraceElement> 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())) {