1 /*
2 * Copyright (c) 1998-2004 The Jgroup Team.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 *
17 */
18
19 package jgroup.util;
20
21 import java.util.Collections;
22 import java.util.HashSet;
23 import java.util.Iterator;
24 import java.util.Map;
25 import java.util.Set;
26
27 import org.apache.log4j.Logger;
28
29 /**
30 * Monitor threads to detect thread failures, and perform corrective action
31 * in case some thread has crashed. <p>
32 *
33 * This class is helpful with respect to debugging deadlock problems, especially
34 * when <code>DUMP_ALL_THREADS</code> is set to true. This will, at regular
35 * intervals, dump the stack trace of all threads allowing the developer to
36 * identify threads that might have entered into a deadlock situation awaiting
37 * a lock being held by another thread. <p>
38 *
39 * This class requires JDK 5.0, but is not mandatory for general use of Jgroup/ARM,
40 * and as such use of this class may be disabled.
41 *
42 * @author Hein Meling
43 * @since Jgroup 2.1
44 */
45 public class ThreadMonitor
46 extends Thread
47 {
48
49 ////////////////////////////////////////////////////////////////////////////////////////////
50 // Logger
51 ////////////////////////////////////////////////////////////////////////////////////////////
52
53 /** Obtain logger for this class */
54 private static final Logger log = Logger.getLogger(ThreadMonitor.class);
55
56
57 ////////////////////////////////////////////////////////////////////////////////////////////
58 // Fields
59 ////////////////////////////////////////////////////////////////////////////////////////////
60
61 /* The default delay to wait between checking thread liveness */
62 private static final int THREAD_DUMP_DELAY = 5000;
63
64 /*
65 * Option to dump the stack trace of all threads (true),
66 * rather than just the trace for the registered threads (false).
67 */
68 private static boolean DUMP_ALL_THREADS = true;
69
70 /* Singleton instance of the thread monitor object */
71 private static ThreadMonitor threadMonitor = null;
72
73 /* The list of threads to watch for failures */
74 private final Set<Thread> threadSet = Collections.synchronizedSet(new HashSet<Thread>(5));
75
76
77 ////////////////////////////////////////////////////////////////////////////////////////////
78 // Constructors
79 ////////////////////////////////////////////////////////////////////////////////////////////
80
81 private ThreadMonitor(String name)
82 {
83 super(name);
84 DUMP_ALL_THREADS = Boolean.getBoolean("jgroup.log.dump.threads");
85 }
86
87
88 ////////////////////////////////////////////////////////////////////////////////////////////
89 // Methods
90 ////////////////////////////////////////////////////////////////////////////////////////////
91
92 /**
93 * Add the given thread to the monitor.
94 */
95 public static void add(Thread thread)
96 {
97 if (threadMonitor == null) {
98 threadMonitor = new ThreadMonitor("ThreadMonitor");
99 threadMonitor.setDaemon(true);
100 threadMonitor.start();
101 }
102 threadMonitor.threadSet.add(thread);
103 }
104
105
106 /**
107 * Remove the given thread from being monitored.
108 */
109 public static void remove(Thread thread)
110 {
111 if (threadMonitor == null) {
112 throw new IllegalStateException("Cannot remove entry from monitor; not initialized");
113 }
114 threadMonitor.threadSet.remove(thread);
115 }
116
117 public static void dumpAllThreads()
118 {
119 Set traceSet = Thread.getAllStackTraces().entrySet();
120 synchronized (traceSet) {
121 for (Iterator iter = traceSet.iterator(); iter.hasNext();) {
122 Map.Entry element = (Map.Entry) iter.next();
123 StackTraceElement[] arr = (StackTraceElement[]) element.getValue();
124 Thread thread = (Thread) element.getKey();
125 log.debug("------------- " + thread.getName()
126 + "." + thread.getId()
127 + ", state=" + thread.getState());
128 for (int i = 0; i < arr.length; i++) {
129 log.debug(" " + arr[i]);
130 }
131 }
132 log.debug("*************************");
133 }
134 }
135
136
137 /* (non-Javadoc)
138 * @see java.lang.Runnable#run()
139 */
140 public void run()
141 {
142 while (true) {
143 try {
144 sleep(THREAD_DUMP_DELAY);
145 } catch (InterruptedException e) { }
146
147 if (DUMP_ALL_THREADS) {
148 if (log.isDebugEnabled()) {
149 dumpAllThreads();
150 }
151 } else {
152 boolean noFailedThreads = true;
153 synchronized (threadSet) {
154 for (Iterator iter = threadSet.iterator(); noFailedThreads && iter.hasNext();) {
155 Thread thread = (Thread) iter.next();
156 if (log.isDebugEnabled()) {
157 StackTraceElement[] trace = thread.getStackTrace();
158 log.debug("--------------------- " + thread.getName());
159 for (int i = 0; i < trace.length; i++) {
160 log.debug(" " + trace[i]);
161 }
162 }
163 noFailedThreads &= thread.isAlive();
164 }
165 if (log.isDebugEnabled())
166 log.debug("*************************");
167 }
168 if (noFailedThreads == false) {
169 /* One or more threads have failed */
170 if (log.isDebugEnabled())
171 log.debug("One or more threads have failed...");
172 }
173 }
174 }
175 }
176
177 } // END of ThreadMonitor