1 /*
   2  * Copyright (c) 2004, 2006, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 package sun.jvm.hotspot.runtime;
  26 
  27 import sun.jvm.hotspot.debugger.*;
  28 import sun.jvm.hotspot.memory.*;
  29 import sun.jvm.hotspot.oops.*;
  30 import java.io.*;
  31 import java.util.*;
  32 import java.util.Map.Entry;
  33 
  34 /** Prints information about Java-level deadlocks in supplied 'tty'. */
  35 
  36 public class DeadlockDetector {
  37 
  38     public static void print(PrintStream tty) {
  39         print(tty, true);
  40     }
  41 
  42     /** prints zero or more deadlocks into 'tty' taking current
  43      snapshot of Java threads and locks */
  44     public static void print(PrintStream tty, boolean concurrentLocks) {
  45         tty.println("Deadlock Detection:");
  46         tty.println();
  47 
  48         int globalDfn = 0, thisDfn;
  49         int numberOfDeadlocks = 0;
  50         JavaThread currentThread = null, previousThread = null;
  51         ObjectMonitor waitingToLockMonitor = null;
  52         Oop waitingToLockBlocker = null;
  53 
  54         threads = VM.getVM().getThreads();
  55         heap = VM.getVM().getObjectHeap();
  56         createThreadTable();
  57 
  58         Iterator i = threadTable.entrySet().iterator();
  59         while (i.hasNext()) {
  60             Entry e = (Entry)i.next();
  61             if (dfn(e) >= 0) {
  62                 // this thread was already visited
  63                 continue;
  64             }
  65 
  66             thisDfn = globalDfn;
  67             JavaThread thread = (JavaThread)e.getKey();
  68             previousThread = thread;
  69 
  70             // When there is a deadlock, all the monitors involved in the dependency
  71             // cycle must be contended and heavyweight. So we only care about the
  72             // heavyweight monitor a thread is waiting to lock.
  73             try {
  74                 waitingToLockMonitor = thread.getCurrentPendingMonitor();
  75             } catch (RuntimeException re) {
  76                 tty.println("This version of HotSpot VM doesn't support deadlock detection.");
  77                 return;
  78             }
  79 
  80             Klass abstractOwnableSyncKlass = null;
  81             if (concurrentLocks) {
  82                 waitingToLockBlocker = thread.getCurrentParkBlocker();
  83                 SystemDictionary sysDict = VM.getVM().getSystemDictionary();
  84                 abstractOwnableSyncKlass = sysDict.getAbstractOwnableSynchronizerKlass();
  85             }
  86 
  87             while (waitingToLockMonitor != null ||
  88                    waitingToLockBlocker != null) {
  89                 if (waitingToLockMonitor != null) {
  90                     currentThread = threads.owningThreadFromMonitor(waitingToLockMonitor);
  91                 } else {
  92                     if (concurrentLocks) {
  93                         if (waitingToLockBlocker.isA(abstractOwnableSyncKlass)) {
  94                             Oop threadOop = OopUtilities.abstractOwnableSynchronizerGetOwnerThread(waitingToLockBlocker);
  95                             if (threadOop != null) {
  96                                 currentThread = OopUtilities.threadOopGetJavaThread(threadOop);
  97                             }
  98                         }
  99                     }
 100                 }
 101                 if (currentThread == null) {
 102                     // No dependency on another thread
 103                     break;
 104                 }
 105                 if (dfn(currentThread) < 0) {
 106                     // First visit to this thread
 107                     threadTable.put(currentThread, new Integer(globalDfn++));
 108                 } else if (dfn(currentThread) < thisDfn) {
 109                     // Thread already visited, and not on a (new) cycle
 110                     break;
 111                 } else if (currentThread == previousThread) {
 112                     // Self-loop, ignore
 113                     break;
 114                 } else {
 115                     // We have a (new) cycle
 116                     numberOfDeadlocks ++;
 117                     printOneDeadlock(tty, currentThread, concurrentLocks);
 118                     break;
 119                 }
 120                 previousThread = currentThread;
 121                 waitingToLockMonitor = (ObjectMonitor)currentThread.getCurrentPendingMonitor();
 122                 if (concurrentLocks) {
 123                     waitingToLockBlocker = currentThread.getCurrentParkBlocker();
 124                 }
 125             }
 126         }
 127 
 128         switch (numberOfDeadlocks) {
 129             case 0:
 130                 tty.println("No deadlocks found.");
 131                 break;
 132             case 1:
 133                 tty.println("Found a total of 1 deadlock.");
 134                 break;
 135             default:
 136                 tty.println("Found a total of " + numberOfDeadlocks + " deadlocks.");
 137                 break;
 138         }
 139         tty.println();
 140     }
 141 
 142     //-- Internals only below this point
 143     private static Threads threads;
 144     private static ObjectHeap heap;
 145     private static HashMap threadTable;
 146 
 147     private static void createThreadTable() {
 148         threadTable = new HashMap();
 149         for (JavaThread cur = threads.first(); cur != null; cur = cur.next()) {
 150             // initialize dfn for each thread to -1
 151             threadTable.put(cur, new Integer(-1));
 152         }
 153     }
 154 
 155     private static int dfn(JavaThread thread) {
 156         Object obj = threadTable.get(thread);
 157         if (obj != null) {
 158             return ((Integer)obj).intValue();
 159         }
 160         return -1;
 161     }
 162 
 163     private static int dfn(Entry e) {
 164         return ((Integer)e.getValue()).intValue();
 165     }
 166 
 167     private static void printOneDeadlock(PrintStream tty, JavaThread thread,
 168                                          boolean concurrentLocks) {
 169         tty.println("Found one Java-level deadlock:");
 170         tty.println("=============================");
 171         ObjectMonitor waitingToLockMonitor = null;
 172         Oop waitingToLockBlocker = null;
 173         JavaThread currentThread = thread;
 174         do {
 175             tty.println();
 176             tty.println("\"" + currentThread.getThreadName() + "\":");
 177             waitingToLockMonitor = currentThread.getCurrentPendingMonitor();
 178             if (waitingToLockMonitor != null) {
 179                 tty.print("  waiting to lock Monitor@" + waitingToLockMonitor.getAddress());
 180                 OopHandle obj = waitingToLockMonitor.object();
 181                 Oop oop = heap.newOop(obj);
 182                 if (obj != null) {
 183                     tty.print(" (Object@");
 184                     Oop.printOopAddressOn(oop, tty);
 185                     tty.print(", a " + oop.getKlass().getName().asString() + ")" );
 186                     tty.print(",\n  which is held by");
 187                 } else {
 188                     // No Java object associated - a raw monitor
 189                     tty.print(" (raw monitor),\n  which is held by");
 190                 }
 191                 currentThread = threads.owningThreadFromMonitor(waitingToLockMonitor);
 192                 tty.print(" \"" + currentThread.getThreadName() + "\"");
 193             } else if (concurrentLocks) {
 194                 waitingToLockBlocker = currentThread.getCurrentParkBlocker();
 195                 tty.print(" waiting for ownable synchronizer ");
 196                 Oop.printOopAddressOn(waitingToLockBlocker, tty);
 197                 tty.print(", (a " + waitingToLockBlocker.getKlass().getName().asString() + ")" );
 198                 Oop threadOop = OopUtilities.abstractOwnableSynchronizerGetOwnerThread(waitingToLockBlocker);
 199                 currentThread = OopUtilities.threadOopGetJavaThread(threadOop);
 200                 tty.print(",\n which is held by");
 201                 tty.print(" \"" + currentThread.getThreadName() + "\"");
 202             }
 203         } while (!currentThread.equals(thread));
 204         tty.println();
 205         tty.println();
 206     }
 207 }