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, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.reef.util; 020 021import java.lang.management.LockInfo; 022import java.lang.management.MonitorInfo; 023import java.lang.management.ThreadInfo; 024import java.util.Map; 025import java.util.TreeMap; 026import java.util.logging.Level; 027import java.util.logging.Logger; 028 029/** 030 * Methods to log the currently active set of threads with their stack traces. This is useful to log abnormal 031 * process exit situations, for instance the Driver timeout in the tests. 032 */ 033public final class ThreadLogger { 034 035 /** 036 * This is a utility class that shall not be instantiated. 037 */ 038 private ThreadLogger() { 039 } 040 041 /** 042 * Same as <code>logThreads(logger, level, prefix, "\n\t", "\n\t\t")</code>. 043 */ 044 public static void logThreads(final Logger logger, final Level level, final String prefix) { 045 logThreads(logger, level, prefix, "\n\t", "\n\t\t"); 046 } 047 048 /** 049 * Logs the currently active threads and their stack trace to the given Logger and Level. 050 * 051 * @param logger the Logger instance to log to. 052 * @param level the Level to log into. 053 * @param prefix a prefix of the log message. 054 * @param threadPrefix logged before each thread, e.g. "\n\t" to create an indented list. 055 * @param stackElementPrefix logged before each stack trace element, e.g. "\n\t\t" to create an indented list. 056 */ 057 public static void logThreads( 058 final Logger logger, final Level level, final String prefix, 059 final String threadPrefix, final String stackElementPrefix) { 060 061 if (logger.isLoggable(level)) { 062 logger.log(level, getFormattedThreadList(prefix, threadPrefix, stackElementPrefix)); 063 } 064 } 065 066 /** 067 * Produces a String representation of the currently running threads. 068 * 069 * @param prefix The prefix of the string returned. 070 * @param threadPrefix Printed before each thread, e.g. "\n\t" to create an indented list. 071 * @param stackElementPrefix Printed before each stack trace element, e.g. "\n\t\t" to create an indented list. 072 * @return a String representation of the currently running threads. 073 */ 074 public static String getFormattedThreadList( 075 final String prefix, final String threadPrefix, final String stackElementPrefix) { 076 077 // Sort by thread name 078 final TreeMap<String, StackTraceElement[]> threadNames = new TreeMap<>(); 079 for (final Map.Entry<Thread, StackTraceElement[]> entry : Thread.getAllStackTraces().entrySet()) { 080 081 final Thread t = entry.getKey(); 082 final String tg = t.getThreadGroup() == null ? null : t.getThreadGroup().getName(); 083 084 if (!"system".equals(tg)) { 085 threadNames.put(String.format("TG %s THREAD %s :: %s, %s, %s, %s", 086 tg, t.getName(), t.getState(), 087 t.isAlive() ? "Alive" : "NOT alive", 088 t.isInterrupted() ? "Interrupted" : "NOT interrupted", 089 t.isDaemon() ? "Daemon" : "NOT daemon"), entry.getValue()); 090 } 091 } 092 093 final StringBuilder message = new StringBuilder(prefix); 094 for (final Map.Entry<String, StackTraceElement[]> entry : threadNames.entrySet()) { 095 message.append(threadPrefix).append(entry.getKey()); 096 for (final StackTraceElement element : entry.getValue()) { 097 message.append(stackElementPrefix).append(element); 098 } 099 } 100 101 return message.toString(); 102 } 103 104 /** 105 * Same as <code>getFormattedThreadList(prefix, "\n\t", "\n\t\t")</code>. 106 */ 107 public static String getFormattedThreadList(final String prefix) { 108 return getFormattedThreadList(prefix, "\n\t", "\n\t\t"); 109 } 110 111 /** 112 * Produces a String representation of threads that are deadlocked, including lock information. 113 * @param prefix The prefix of the string returned. 114 * @param threadPrefix Printed before each thread, e.g. "\n\t" to create an indented list. 115 * @param stackElementPrefix Printed before each stack trace element, e.g. "\n\t\t" to create an indented list. 116 * @return a String representation of threads that are deadlocked, including lock information 117 */ 118 public static String getFormattedDeadlockInfo( 119 final String prefix, final String threadPrefix, final String stackElementPrefix) { 120 final StringBuilder message = new StringBuilder(prefix); 121 122 final DeadlockInfo deadlockInfo = new DeadlockInfo(); 123 124 final ThreadInfo[] deadlockedThreads = deadlockInfo.getDeadlockedThreads(); 125 126 if (0 == deadlockedThreads.length) { 127 message.append(" none "); 128 return message.toString(); 129 } 130 131 for (final ThreadInfo threadInfo : deadlockedThreads) { 132 message.append(threadPrefix).append("Thread '").append(threadInfo.getThreadName()) 133 .append("' with state ").append(threadInfo.getThreadState()); 134 135 boolean firstElement = true; 136 for (final StackTraceElement stackTraceElement : threadInfo.getStackTrace()) { 137 message.append(stackElementPrefix).append("at ").append(stackTraceElement); 138 if (firstElement) { 139 final String waitingLockString = deadlockInfo.getWaitingLockString(threadInfo); 140 if (waitingLockString != null) { 141 message.append(stackElementPrefix).append("- waiting to lock: ").append(waitingLockString); 142 } 143 firstElement = false; 144 } 145 for (final MonitorInfo info : deadlockInfo.getMonitorLockedElements(threadInfo, stackTraceElement)) { 146 message.append(stackElementPrefix).append("- locked: ").append(info); 147 } 148 } 149 for (final LockInfo lockInfo : threadInfo.getLockedSynchronizers()) { 150 message.append(stackElementPrefix).append("* holds locked synchronizer: ").append(lockInfo); 151 } 152 } 153 154 return message.toString(); 155 } 156 157 /** 158 * Same as <code>getFormattedDeadlockInfo(prefix, "\n\t", "\n\t\t")</code>. 159 */ 160 public static String getFormattedDeadlockInfo(final String prefix) { 161 return getFormattedDeadlockInfo(prefix, "\n\t", "\n\t\t"); 162 } 163 164 /** 165 * An example how to use the above methods. 166 * 167 * @param args ignored. 168 */ 169 public static void main(final String[] args) { 170 logThreads(Logger.getAnonymousLogger(), Level.INFO, "Threads active:"); 171 } 172}