This project has retired. For details please refer to its Attic page.
Source code
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}