// Copyright 2011 The Bazel Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.testing.junit.runner.internal; import java.io.PrintStream; import java.lang.management.LockInfo; import java.lang.management.ManagementFactory; import java.lang.management.MonitorInfo; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * Utilities for stack traces. */ public class StackTraces { /** * Prints all stack traces to the given stream. * * @param out Stream to print to */ public static void printAll(PrintStream out) { out.println("Starting full thread dump ...\n"); ThreadMXBean mb = ManagementFactory.getThreadMXBean(); // ThreadInfo has comprehensive information such as locks. ThreadInfo[] threadInfos = mb.dumpAllThreads(true, true); // But we can know whether a thread is daemon only from Thread Set<Thread> threads = Thread.getAllStackTraces().keySet(); Map<Long, Thread> threadMap = new HashMap<>(); for (Thread thread : threads) { threadMap.put(thread.getId(), thread); } // Dump non-daemon threads first for (ThreadInfo threadInfo : threadInfos) { Thread thread = threadMap.get(threadInfo.getThreadId()); if (thread != null && !thread.isDaemon()) { dumpThreadInfo(threadInfo, thread, out); } } // Dump daemon threads for (ThreadInfo threadInfo : threadInfos) { Thread thread = threadMap.get(threadInfo.getThreadId()); if (thread != null && thread.isDaemon()) { dumpThreadInfo(threadInfo, thread, out); } } long[] deadlockedThreads = mb.findDeadlockedThreads(); if (deadlockedThreads != null) { out.println("Detected deadlocked threads: " + Arrays.toString(deadlockedThreads)); } long[] monitorDeadlockedThreads = mb.findMonitorDeadlockedThreads(); if (monitorDeadlockedThreads != null) { out.println("Detected monitor deadlocked threads: " + Arrays.toString(monitorDeadlockedThreads)); } out.println("\nDone full thread dump."); out.flush(); } // Adopted from ThreadInfo.toString(), without MAX_FRAMES limit private static void dumpThreadInfo(ThreadInfo t, Thread thread, PrintStream out) { out.print("\"" + t.getThreadName() + "\"" + " Id=" + t.getThreadId() + " " + t.getThreadState()); if (t.getLockName() != null) { out.print(" on " + t.getLockName()); } if (t.getLockOwnerName() != null) { out.print(" owned by \"" + t.getLockOwnerName() + "\" Id=" + t.getLockOwnerId()); } if (t.isSuspended()) { out.print(" (suspended)"); } if (t.isInNative()) { out.print(" (in native)"); } if (thread.isDaemon()) { out.print(" (daemon)"); } out.print('\n'); StackTraceElement[] stackTrace = t.getStackTrace(); MonitorInfo[] lockedMonitors = t.getLockedMonitors(); for (int i = 0; i < stackTrace.length; i++) { StackTraceElement ste = stackTrace[i]; out.print("\tat " + ste.toString()); out.print('\n'); if (i == 0 && t.getLockInfo() != null) { Thread.State ts = t.getThreadState(); switch (ts) { case BLOCKED: out.print("\t- blocked on " + t.getLockInfo()); out.print('\n'); break; case WAITING: out.print("\t- waiting on " + t.getLockInfo()); out.print('\n'); break; case TIMED_WAITING: out.print("\t- waiting on " + t.getLockInfo()); out.print('\n'); break; default: } } for (MonitorInfo mi : lockedMonitors) { if (mi.getLockedStackDepth() == i) { out.print("\t- locked " + mi); out.print('\n'); } } } LockInfo[] locks = t.getLockedSynchronizers(); if (locks.length > 0) { out.print("\n\tNumber of locked synchronizers = " + locks.length); out.print('\n'); for (LockInfo li : locks) { out.print("\t- " + li); out.print('\n'); } } out.print('\n'); } }