blob: 85f287f3d046b0e618e1c364adc176971cc1f65a [file] [log] [blame]
/*
* Copyright (c) 2004, 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package sun.jvm.hotspot.runtime;
import sun.jvm.hotspot.debugger.*;
import sun.jvm.hotspot.memory.*;
import sun.jvm.hotspot.oops.*;
import java.io.*;
import java.util.*;
import java.util.Map.Entry;
/** Prints information about Java-level deadlocks in supplied 'tty'. */
public class DeadlockDetector {
public static void print(PrintStream tty) {
print(tty, true);
}
/** prints zero or more deadlocks into 'tty' taking current
snapshot of Java threads and locks */
public static void print(PrintStream tty, boolean concurrentLocks) {
tty.println("Deadlock Detection:");
tty.println();
int globalDfn = 0, thisDfn;
int numberOfDeadlocks = 0;
JavaThread currentThread = null, previousThread = null;
ObjectMonitor waitingToLockMonitor = null;
Oop waitingToLockBlocker = null;
threads = VM.getVM().getThreads();
heap = VM.getVM().getObjectHeap();
createThreadTable();
Iterator i = threadTable.entrySet().iterator();
while (i.hasNext()) {
Entry e = (Entry)i.next();
if (dfn(e) >= 0) {
// this thread was already visited
continue;
}
thisDfn = globalDfn;
JavaThread thread = (JavaThread)e.getKey();
previousThread = thread;
// When there is a deadlock, all the monitors involved in the dependency
// cycle must be contended and heavyweight. So we only care about the
// heavyweight monitor a thread is waiting to lock.
try {
waitingToLockMonitor = thread.getCurrentPendingMonitor();
} catch (RuntimeException re) {
tty.println("This version of HotSpot VM doesn't support deadlock detection.");
return;
}
Klass abstractOwnableSyncKlass = null;
if (concurrentLocks) {
waitingToLockBlocker = thread.getCurrentParkBlocker();
SystemDictionary sysDict = VM.getVM().getSystemDictionary();
abstractOwnableSyncKlass = sysDict.getAbstractOwnableSynchronizerKlass();
}
while (waitingToLockMonitor != null ||
waitingToLockBlocker != null) {
if (waitingToLockMonitor != null) {
currentThread = threads.owningThreadFromMonitor(waitingToLockMonitor);
} else {
if (concurrentLocks) {
if (waitingToLockBlocker.isA(abstractOwnableSyncKlass)) {
Oop threadOop = OopUtilities.abstractOwnableSynchronizerGetOwnerThread(waitingToLockBlocker);
if (threadOop != null) {
currentThread = OopUtilities.threadOopGetJavaThread(threadOop);
}
}
}
}
if (currentThread == null) {
// No dependency on another thread
break;
}
if (dfn(currentThread) < 0) {
// First visit to this thread
threadTable.put(currentThread, new Integer(globalDfn++));
} else if (dfn(currentThread) < thisDfn) {
// Thread already visited, and not on a (new) cycle
break;
} else if (currentThread == previousThread) {
// Self-loop, ignore
break;
} else {
// We have a (new) cycle
numberOfDeadlocks ++;
printOneDeadlock(tty, currentThread, concurrentLocks);
break;
}
previousThread = currentThread;
waitingToLockMonitor = (ObjectMonitor)currentThread.getCurrentPendingMonitor();
if (concurrentLocks) {
waitingToLockBlocker = currentThread.getCurrentParkBlocker();
}
}
}
switch (numberOfDeadlocks) {
case 0:
tty.println("No deadlocks found.");
break;
case 1:
tty.println("Found a total of 1 deadlock.");
break;
default:
tty.println("Found a total of " + numberOfDeadlocks + " deadlocks.");
break;
}
tty.println();
}
//-- Internals only below this point
private static Threads threads;
private static ObjectHeap heap;
private static HashMap threadTable;
private static void createThreadTable() {
threadTable = new HashMap();
for (JavaThread cur = threads.first(); cur != null; cur = cur.next()) {
// initialize dfn for each thread to -1
threadTable.put(cur, new Integer(-1));
}
}
private static int dfn(JavaThread thread) {
Object obj = threadTable.get(thread);
if (obj != null) {
return ((Integer)obj).intValue();
}
return -1;
}
private static int dfn(Entry e) {
return ((Integer)e.getValue()).intValue();
}
private static void printOneDeadlock(PrintStream tty, JavaThread thread,
boolean concurrentLocks) {
tty.println("Found one Java-level deadlock:");
tty.println("=============================");
ObjectMonitor waitingToLockMonitor = null;
Oop waitingToLockBlocker = null;
JavaThread currentThread = thread;
do {
tty.println();
tty.println("\"" + currentThread.getThreadName() + "\":");
waitingToLockMonitor = currentThread.getCurrentPendingMonitor();
if (waitingToLockMonitor != null) {
tty.print(" waiting to lock Monitor@" + waitingToLockMonitor.getAddress());
OopHandle obj = waitingToLockMonitor.object();
Oop oop = heap.newOop(obj);
if (obj != null) {
tty.print(" (Object@");
Oop.printOopAddressOn(oop, tty);
tty.print(", a " + oop.getKlass().getName().asString() + ")" );
tty.print(",\n which is held by");
} else {
// No Java object associated - a raw monitor
tty.print(" (raw monitor),\n which is held by");
}
currentThread = threads.owningThreadFromMonitor(waitingToLockMonitor);
tty.print(" \"" + currentThread.getThreadName() + "\"");
} else if (concurrentLocks) {
waitingToLockBlocker = currentThread.getCurrentParkBlocker();
tty.print(" waiting for ownable synchronizer ");
Oop.printOopAddressOn(waitingToLockBlocker, tty);
tty.print(", (a " + waitingToLockBlocker.getKlass().getName().asString() + ")" );
Oop threadOop = OopUtilities.abstractOwnableSynchronizerGetOwnerThread(waitingToLockBlocker);
currentThread = OopUtilities.threadOopGetJavaThread(threadOop);
tty.print(",\n which is held by");
tty.print(" \"" + currentThread.getThreadName() + "\"");
}
} while (!currentThread.equals(thread));
tty.println();
tty.println();
}
}