From 130372f9041f84a1bc4e4087ca6dee31fa56fc1b Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Fri, 1 Feb 2019 14:38:56 +0300 Subject: [PATCH] print stacktraces before/after wait for leaking thread to die, print all other threads dump to help debug Zip Handler Janitor --- .../diagnostic/PerformanceWatcher.java | 2 +- .../intellij/testFramework/ThreadTracker.java | 28 +++++++++++++------ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/platform/core-impl/src/com/intellij/diagnostic/PerformanceWatcher.java b/platform/core-impl/src/com/intellij/diagnostic/PerformanceWatcher.java index f9449d183397..1cb44559a13c 100644 --- a/platform/core-impl/src/com/intellij/diagnostic/PerformanceWatcher.java +++ b/platform/core-impl/src/com/intellij/diagnostic/PerformanceWatcher.java @@ -181,7 +181,7 @@ public class PerformanceWatcher implements Disposable { @NotNull public static String printStacktrace(@NotNull String headerMsg, @NotNull Thread thread, @NotNull StackTraceElement[] stackTrace) { @SuppressWarnings("NonConstantStringShouldBeStringBuffer") - String trace = headerMsg + ": "+thread + "; " + thread.getState() + " (" + thread.isAlive() + ")\n--- its stacktrace:\n"; + String trace = headerMsg + thread + " (" + (thread.isAlive() ? "alive" : "dead") + ") " + thread.getState() + "\n--- its stacktrace:\n"; for (final StackTraceElement stackTraceElement : stackTrace) { trace += " at "+stackTraceElement +"\n"; } diff --git a/platform/testFramework/src/com/intellij/testFramework/ThreadTracker.java b/platform/testFramework/src/com/intellij/testFramework/ThreadTracker.java index ae42c50c140b..30a1a88bfb89 100644 --- a/platform/testFramework/src/com/intellij/testFramework/ThreadTracker.java +++ b/platform/testFramework/src/com/intellij/testFramework/ThreadTracker.java @@ -2,7 +2,6 @@ package com.intellij.testFramework; import com.intellij.diagnostic.PerformanceWatcher; -import com.intellij.diagnostic.ThreadDumper; import com.intellij.execution.process.ProcessIOExecutorService; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.Application; @@ -24,8 +23,6 @@ import org.jetbrains.annotations.TestOnly; import org.jetbrains.io.NettyUtil; import org.junit.Assert; -import java.io.StringWriter; -import java.io.Writer; import java.lang.reflect.Method; import java.util.*; import java.util.concurrent.ForkJoinWorkerThread; @@ -150,7 +147,9 @@ public class ThreadTracker { //if (thread.isAlive()) { // System.err.println("waiting for " + thread + "\n" + ThreadDumper.dumpThreadsToString()); //} - while (!shouldIgnore(thread, thread.getStackTrace()) && System.currentTimeMillis() < start + 10_000) { + StackTraceElement[] traceBeforeWait = thread.getStackTrace(); + int WAIT_SEC = 10; + while (!shouldIgnore(thread, thread.getStackTrace()) && System.currentTimeMillis() < start + WAIT_SEC*1_000) { UIUtil.dispatchAllInvocationEvents(); // give blocked thread opportunity to die if it's stuck doing invokeAndWait() // afters some time the submitted task can finish and the thread become idle pool } @@ -165,8 +164,15 @@ public class ThreadTracker { if (shouldIgnore(thread, stackTrace)) continue; - String trace = PerformanceWatcher.printStacktrace("Thread leaked", thread, stackTrace); - Assert.fail(trace + "\n\nFull thread dump:\n"+dumpThreadsToString(after, stackTraces)); + all.keySet().removeAll(after.keySet()); + Map otherStackTraces = ContainerUtil.map2Map(all.values(), t -> Pair.create(t, t.getStackTrace())); + + String trace = PerformanceWatcher.printStacktrace("", thread, stackTrace); + String traceBefore = PerformanceWatcher.printStacktrace("", thread, traceBeforeWait); + + Assert.fail("Thread leaked: " +traceBefore + (trace.equals(traceBefore) ? "" : "(its trace after "+WAIT_SEC+" seconds wait:) "+trace)+ + "\n\nLeaking threads dump:\n" + dumpThreadsToString(after, stackTraces) + + "\n----\nAll other threads dump:\n" + dumpThreadsToString(all, otherStackTraces)); } } finally { @@ -175,8 +181,14 @@ public class ThreadTracker { } private static String dumpThreadsToString(Map after, Map stackTraces) { - Writer f = new StringWriter(); - after.values().forEach(thread -> ThreadDumper.dumpCallStack(thread, f, stackTraces.get(thread))); + StringBuilder f = new StringBuilder(); + after.forEach((name, thread) -> { + f.append("\"" + name + "\" " + " (" + (thread.isAlive() ? "alive" : "dead") + ") " + thread.getState() + "\n"); + for (StackTraceElement element : stackTraces.get(thread)) { + f.append("\tat " + element + "\n"); + } + f.append("\n"); + }); return f.toString(); }