001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase; 019 020import java.io.PrintWriter; 021import java.io.StringWriter; 022import java.lang.management.LockInfo; 023import java.lang.management.ManagementFactory; 024import java.lang.management.MonitorInfo; 025import java.lang.management.ThreadInfo; 026import java.lang.management.ThreadMXBean; 027import java.time.Instant; 028import java.time.ZoneId; 029import java.time.format.DateTimeFormatter; 030import java.util.Locale; 031import java.util.Map; 032 033/** 034 * JUnit run listener which prints full thread dump into System.err in case a test is failed due to 035 * timeout. 036 */ 037public final class TimedOutTestsThreadDumpHelper { 038 039 private static final DateTimeFormatter TIMESTAMP_FORMATTER = 040 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss,SSS Z").withZone(ZoneId.systemDefault()); 041 042 private static final String INDENT = " "; 043 044 private TimedOutTestsThreadDumpHelper() { 045 } 046 047 public static String buildThreadDiagnosticString() { 048 StringWriter sw = new StringWriter(); 049 PrintWriter output = new PrintWriter(sw); 050 051 output.println(String.format("Timestamp: %s", TIMESTAMP_FORMATTER.format(Instant.now()))); 052 output.println(); 053 output.println(buildThreadDump()); 054 055 String deadlocksInfo = buildDeadlockInfo(); 056 if (deadlocksInfo != null) { 057 output.println("====> DEADLOCKS DETECTED <===="); 058 output.println(); 059 output.println(deadlocksInfo); 060 } 061 062 return sw.toString(); 063 } 064 065 private static String buildThreadDump() { 066 StringBuilder dump = new StringBuilder(); 067 Map<Thread, StackTraceElement[]> stackTraces = Thread.getAllStackTraces(); 068 for (Map.Entry<Thread, StackTraceElement[]> e : stackTraces.entrySet()) { 069 Thread thread = e.getKey(); 070 dump.append(String.format("\"%s\" %s prio=%d tid=%d %s\njava.lang.Thread.State: %s", 071 thread.getName(), (thread.isDaemon() ? "daemon" : ""), thread.getPriority(), thread.getId(), 072 Thread.State.WAITING.equals(thread.getState()) 073 ? "in Object.wait()" 074 : thread.getState().name().toLowerCase(Locale.ROOT), 075 Thread.State.WAITING.equals(thread.getState()) 076 ? "WAITING (on object monitor)" 077 : thread.getState())); 078 for (StackTraceElement stackTraceElement : e.getValue()) { 079 dump.append("\n at "); 080 dump.append(stackTraceElement); 081 } 082 dump.append("\n"); 083 } 084 return dump.toString(); 085 } 086 087 private static String buildDeadlockInfo() { 088 ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); 089 long[] threadIds = threadBean.findMonitorDeadlockedThreads(); 090 if (threadIds != null && threadIds.length > 0) { 091 StringWriter stringWriter = new StringWriter(); 092 PrintWriter out = new PrintWriter(stringWriter); 093 094 ThreadInfo[] infos = threadBean.getThreadInfo(threadIds, true, true); 095 for (ThreadInfo ti : infos) { 096 printThreadInfo(ti, out); 097 printLockInfo(ti.getLockedSynchronizers(), out); 098 out.println(); 099 } 100 101 out.close(); 102 return stringWriter.toString(); 103 } else { 104 return null; 105 } 106 } 107 108 private static void printThreadInfo(ThreadInfo ti, PrintWriter out) { 109 // print thread information 110 printThread(ti, out); 111 112 // print stack trace with locks 113 StackTraceElement[] stacktrace = ti.getStackTrace(); 114 MonitorInfo[] monitors = ti.getLockedMonitors(); 115 for (int i = 0; i < stacktrace.length; i++) { 116 StackTraceElement ste = stacktrace[i]; 117 out.println(INDENT + "at " + ste.toString()); 118 for (MonitorInfo mi : monitors) { 119 if (mi.getLockedStackDepth() == i) { 120 out.println(INDENT + " - locked " + mi); 121 } 122 } 123 } 124 out.println(); 125 } 126 127 private static void printThread(ThreadInfo ti, PrintWriter out) { 128 out.print( 129 "\"" + ti.getThreadName() + "\"" + " Id=" + ti.getThreadId() + " in " + ti.getThreadState()); 130 if (ti.getLockName() != null) { 131 out.print(" on lock=" + ti.getLockName()); 132 } 133 if (ti.isSuspended()) { 134 out.print(" (suspended)"); 135 } 136 if (ti.isInNative()) { 137 out.print(" (running in native)"); 138 } 139 out.println(); 140 if (ti.getLockOwnerName() != null) { 141 out.println(INDENT + " owned by " + ti.getLockOwnerName() + " Id=" + ti.getLockOwnerId()); 142 } 143 } 144 145 private static void printLockInfo(LockInfo[] locks, PrintWriter out) { 146 out.println(INDENT + "Locked synchronizers: count = " + locks.length); 147 for (LockInfo li : locks) { 148 out.println(INDENT + " - " + li); 149 } 150 out.println(); 151 } 152 153}