View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase;
19  
20  import java.io.PrintWriter;
21  import java.io.StringWriter;
22  import java.lang.management.LockInfo;
23  import java.lang.management.ManagementFactory;
24  import java.lang.management.MonitorInfo;
25  import java.lang.management.ThreadInfo;
26  import java.lang.management.ThreadMXBean;
27  import java.text.DateFormat;
28  import java.text.SimpleDateFormat;
29  import java.util.Date;
30  import java.util.Locale;
31  import java.util.Map;
32  
33  import org.junit.runner.notification.Failure;
34  import org.junit.runner.notification.RunListener;
35  
36  /**
37   * JUnit run listener which prints full thread dump into System.err
38   * in case a test is failed due to timeout.
39   */
40  public class TimedOutTestsListener extends RunListener {
41  
42    static final String TEST_TIMED_OUT_PREFIX = "test timed out after";
43    
44    private static String INDENT = "    ";
45  
46    private final PrintWriter output;
47    
48    public TimedOutTestsListener() {
49      this.output = new PrintWriter(System.err);
50    }
51    
52    public TimedOutTestsListener(PrintWriter output) {
53      this.output = output;
54    }
55  
56    @Override
57    public void testFailure(Failure failure) throws Exception {
58      if (failure != null && failure.getMessage() != null 
59          && failure.getMessage().startsWith(TEST_TIMED_OUT_PREFIX)) {
60        output.println("====> TEST TIMED OUT. PRINTING THREAD DUMP. <====");
61        output.println();
62        output.print(buildThreadDiagnosticString());
63      }
64    }
65    
66    public static String buildThreadDiagnosticString() {
67      StringWriter sw = new StringWriter();
68      PrintWriter output = new PrintWriter(sw);
69      
70      DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss,SSS");
71      output.println(String.format("Timestamp: %s", dateFormat.format(new Date())));
72      output.println();
73      output.println(buildThreadDump());
74      
75      String deadlocksInfo = buildDeadlockInfo();
76      if (deadlocksInfo != null) {
77        output.println("====> DEADLOCKS DETECTED <====");
78        output.println();
79        output.println(deadlocksInfo);
80      }
81  
82      return sw.toString();
83    }
84  
85    static String buildThreadDump() {
86      StringBuilder dump = new StringBuilder();
87      Map<Thread, StackTraceElement[]> stackTraces = Thread.getAllStackTraces();
88      for (Map.Entry<Thread, StackTraceElement[]> e : stackTraces.entrySet()) {
89        Thread thread = e.getKey();
90        dump.append(String.format(
91            "\"%s\" %s prio=%d tid=%d %s\njava.lang.Thread.State: %s",
92            thread.getName(),
93            (thread.isDaemon() ? "daemon" : ""),
94            thread.getPriority(),
95            thread.getId(),
96            Thread.State.WAITING.equals(thread.getState()) ? 
97                "in Object.wait()" : thread.getState().name().toLowerCase(Locale.ROOT),
98            Thread.State.WAITING.equals(thread.getState()) ? 
99                "WAITING (on object monitor)" : thread.getState()));
100       for (StackTraceElement stackTraceElement : e.getValue()) {
101         dump.append("\n        at ");
102         dump.append(stackTraceElement);
103       }
104       dump.append("\n");
105     }
106     return dump.toString();
107   }
108   
109   static String buildDeadlockInfo() {
110     ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
111     long[] threadIds = threadBean.findMonitorDeadlockedThreads();
112     if (threadIds != null && threadIds.length > 0) {
113       StringWriter stringWriter = new StringWriter();
114       PrintWriter out = new PrintWriter(stringWriter);
115       
116       ThreadInfo[] infos = threadBean.getThreadInfo(threadIds, true, true);
117       for (ThreadInfo ti : infos) {
118         printThreadInfo(ti, out);
119         printLockInfo(ti.getLockedSynchronizers(), out);
120         out.println();
121       }
122       
123       out.close();
124       return stringWriter.toString();
125     } else {
126       return null;
127     }
128   }
129   
130   private static void printThreadInfo(ThreadInfo ti, PrintWriter out) {
131     // print thread information
132     printThread(ti, out);
133 
134     // print stack trace with locks
135     StackTraceElement[] stacktrace = ti.getStackTrace();
136     MonitorInfo[] monitors = ti.getLockedMonitors();
137     for (int i = 0; i < stacktrace.length; i++) {
138       StackTraceElement ste = stacktrace[i];
139       out.println(INDENT + "at " + ste.toString());
140       for (MonitorInfo mi : monitors) {
141         if (mi.getLockedStackDepth() == i) {
142           out.println(INDENT + "  - locked " + mi);
143         }
144       }
145     }
146     out.println();
147   }
148 
149   private static void printThread(ThreadInfo ti, PrintWriter out) {
150     out.print("\"" + ti.getThreadName() + "\"" + " Id="
151         + ti.getThreadId() + " in " + ti.getThreadState());
152     if (ti.getLockName() != null) {
153       out.print(" on lock=" + ti.getLockName());
154     }
155     if (ti.isSuspended()) {
156       out.print(" (suspended)");
157     }
158     if (ti.isInNative()) {
159       out.print(" (running in native)");
160     }
161     out.println();
162     if (ti.getLockOwnerName() != null) {
163       out.println(INDENT + " owned by " + ti.getLockOwnerName() + " Id="
164           + ti.getLockOwnerId());
165     }
166   }
167 
168   private static void printLockInfo(LockInfo[] locks, PrintWriter out) {
169     out.println(INDENT + "Locked synchronizers: count = " + locks.length);
170     for (LockInfo li : locks) {
171       out.println(INDENT + "  - " + li);
172     }
173     out.println();
174   }
175   
176 }