[jboss-cvs] JBossAS SVN: r74143 - trunk/system/src/main/org/jboss/system/server.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Thu Jun 5 11:00:54 EDT 2008


Author: galder.zamarreno at jboss.com
Date: 2008-06-05 11:00:54 -0400 (Thu, 05 Jun 2008)
New Revision: 74143

Modified:
   trunk/system/src/main/org/jboss/system/server/ServerInfo.java
Log:
[JBAS-5163] listThreadDump now takes advantage of new JDK 6 APIs, if available, providing more detailed thread dumps. Besides, thread dumps now print deadlocks, if detected any. JDK5 reflection calls have been removed as trunk is now based in JDK5. Finally, I changed <br> so that it's properly closed, i.e. <br/>.

Modified: trunk/system/src/main/org/jboss/system/server/ServerInfo.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/ServerInfo.java	2008-06-05 14:34:47 UTC (rev 74142)
+++ trunk/system/src/main/org/jboss/system/server/ServerInfo.java	2008-06-05 15:00:54 UTC (rev 74143)
@@ -21,9 +21,16 @@
  */
 package org.jboss.system.server;
 
+import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryPoolMXBean;
+import java.lang.management.MemoryType;
+import java.lang.management.MemoryUsage;
+import java.lang.management.ThreadInfo;
+import java.lang.management.ThreadMXBean;
 import java.lang.reflect.Method;
 import java.text.DecimalFormat;
 import java.text.SimpleDateFormat;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.Enumeration;
 import java.util.Iterator;
@@ -34,6 +41,7 @@
 import javax.management.MBeanRegistration;
 import javax.management.MBeanServer;
 import javax.management.ObjectName;
+import javax.management.openmbean.CompositeData;
 
 import org.jboss.logging.Logger;
 import org.jboss.util.platform.Java;
@@ -48,6 +56,7 @@
  * @author <a href="mailto:jason at planet57.com">Jason Dillon</a>
  * @author <a href="mailto:marc.fleury at jboss.org">Marc Fleury</a>
  * @author <a href="mailto:dimitris at jboss.org">Dimitris Andreadis</a>
+ * @author <a href="mailto:galder.zamarreno at jboss.com">Galder Zamarreno</a>
  * @version $Revision$
  */
 public class ServerInfo
@@ -67,46 +76,37 @@
    
    /** used for formating timestamps (date attribute) */
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
+
+   /** Entry point for the management of the thread system */
+   private static final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
    
    /** The cached host name for the server. */
    private String hostName;
    
    /** The cached host address for the server. */
    private String hostAddress;
-
-   /** The cached jdk5+ ThreadMXBean instance */
-   private Object threadMXBean;
    
-   /** The cached jdk5+ ManagementFactory.getMemoryPoolMXBeans() method */
-   private Method getMemoryPoolMXBeans;
+   /** The cached jdk6+ ThreadMXBean methods */
+   private boolean isObjectMonitorUsageSupported;
+   private boolean isSynchronizerUsageSupported;
+   private Method findDeadlockedThreads;
+   private Method dumpAllThreads;
+   private Method getThreadInfoWithSyncInfo;
    
-   /** The cached jdk5+ MemoryPoolMXBean methods */
-   private Method getName;
-   private Method getType;
-   private Method getUsage;
-   private Method getPeakUsage;
+   /** The cached jdk6+ ThreadInfo methods */
+   private Method getLockInfo;
+   private Method getLockedMonitors;
+   private Method getLockedSynchronizers;
    
-   /** The cached jdk5+ MemoryUsage methods */
-   private Method getInit;
-   private Method getUsed;
-   private Method getCommitted;
-   private Method getMax;
+   /** The cached jdk6+ LockInfo methods */
+   private Method getClassName;
+   private Method getIdentityHashCode;
    
-   /** The cached jdk5+ ThreadMXBean.getThreadInfo() method */
-   private Method getThreadInfo;
-   private Method getAllThreadIds;
-   private Method getThreadCpuTime;
-
-   /** The cached jdk5+ ThreadInfo methods */
-   private Method getThreadName;
-   private Method getThreadState;
-   private Method getLockName;
-   //private Method getLockOwnerId;
-   //private Method getLockOwnerName;   
-   private Method getStackTrace;
+   /** The cached jdk6+ MonitorInfo methods */
+   private Method from;
+   private Method getLockedStackDepth;
+   private Method getLockedStackFrame;
    
-   /** The cached jdk5+ Thread.getId() method */
-   private Method getThreadId;
    
    ///////////////////////////////////////////////////////////////////////////
    //                               JMX Hooks                               //
@@ -139,54 +139,36 @@
             log.debug("    " + pname + ": " + System.getProperty(pname));
       }
       
-      // cache a reference to the platform ThreadMXBean
-      // and related Thread/ThreadInfo methods, if available
-      if (Java.isCompatible(Java.VERSION_1_5))
+      if (Java.isCompatible(Java.VERSION_1_6))
       {
          try
          {
-            ClassLoader cl = Thread.currentThread().getContextClassLoader();            
-            Class clazz = cl.loadClass("java.lang.management.ManagementFactory");
-
-            // cache ThreadMXBean instance
-            Method method = clazz.getMethod("getThreadMXBean", NO_PARAMS_SIG);
-            this.threadMXBean = method.invoke(null, NO_PARAMS);
+            ClassLoader cl = Thread.currentThread().getContextClassLoader();
             
-            // cache ManagementFactory.getMemoryPoolMXBeans() method
-            this.getMemoryPoolMXBeans = clazz.getMethod("getMemoryPoolMXBeans", NO_PARAMS_SIG);
+            Class clazz = ThreadMXBean.class;
+            Method method = clazz.getMethod("isObjectMonitorUsageSupported", NO_PARAMS_SIG);
+            isObjectMonitorUsageSupported = (Boolean)method.invoke(threadMXBean, NO_PARAMS);
             
-            // cache MemoryPoolMXBean methods
-            clazz = cl.loadClass("java.lang.management.MemoryPoolMXBean");
-            this.getName = clazz.getMethod("getName", NO_PARAMS_SIG);
-            this.getType = clazz.getMethod("getType", NO_PARAMS_SIG);
-            this.getUsage = clazz.getMethod("getUsage", NO_PARAMS_SIG);
-            this.getPeakUsage = clazz.getMethod("getPeakUsage", NO_PARAMS_SIG);
+            method = clazz.getMethod("isSynchronizerUsageSupported", NO_PARAMS_SIG);
+            isSynchronizerUsageSupported = (Boolean)method.invoke(threadMXBean, NO_PARAMS);
             
-            // cache MemoryUsage methods
-            clazz = cl.loadClass("java.lang.management.MemoryUsage");
-            this.getInit = clazz.getMethod("getInit", NO_PARAMS_SIG);
-            this.getUsed = clazz.getMethod("getUsed", NO_PARAMS_SIG);
-            this.getCommitted = clazz.getMethod("getCommitted", NO_PARAMS_SIG);
-            this.getMax = clazz.getMethod("getMax", NO_PARAMS_SIG);
+            this.findDeadlockedThreads = clazz.getMethod("findDeadlockedThreads", NO_PARAMS_SIG);
+            this.dumpAllThreads = clazz.getMethod("dumpAllThreads", new Class[] { Boolean.TYPE, Boolean.TYPE } );
+            this.getThreadInfoWithSyncInfo = clazz.getMethod("getThreadInfo", new Class[] { long[].class , Boolean.TYPE, Boolean.TYPE});
             
-            // cache ThreadMXBean.getThreadInfo() method
-            clazz = cl.loadClass("java.lang.management.ThreadMXBean");
-            this.getThreadInfo = clazz.getMethod("getThreadInfo", new Class[] { Long.TYPE, Integer.TYPE } );
-            this.getAllThreadIds = clazz.getMethod("getAllThreadIds", NO_PARAMS_SIG );
-            this.getThreadCpuTime = clazz.getMethod("getThreadCpuTime", new Class[] { Long.TYPE } );
-
-            // cache ThreadInfo methods
-            clazz = cl.loadClass("java.lang.management.ThreadInfo");
-            this.getThreadName = clazz.getMethod("getThreadName", NO_PARAMS_SIG);
-            this.getThreadState = clazz.getMethod("getThreadState", NO_PARAMS_SIG);
-            this.getLockName = clazz.getMethod("getLockName", NO_PARAMS_SIG);
-            //this.getLockOwnerId = clazz.getMethod("getLockOwnerId", NO_PARAMS_SIG);
-            //this.getLockOwnerName = clazz.getMethod("getLockOwnerName", NO_PARAMS_SIG);            
-            this.getStackTrace = clazz.getMethod("getStackTrace", NO_PARAMS_SIG);
+            clazz = ThreadInfo.class;
+            this.getLockInfo = clazz.getMethod("getLockInfo", NO_PARAMS_SIG);
+            this.getLockedMonitors = clazz.getMethod("getLockedMonitors", NO_PARAMS_SIG);
+            this.getLockedSynchronizers = clazz.getMethod("getLockedSynchronizers", NO_PARAMS_SIG);
             
-            // cache Thread.getId() method
-            clazz = Thread.class;
-            this.getThreadId = clazz.getMethod("getId", NO_PARAMS_SIG);
+            clazz = cl.loadClass("java.lang.management.LockInfo");
+            this.getClassName = clazz.getMethod("getClassName", NO_PARAMS_SIG);
+            this.getIdentityHashCode = clazz.getMethod("getIdentityHashCode", NO_PARAMS_SIG);
+            
+            clazz = cl.loadClass("java.lang.management.MonitorInfo");            
+            this.from = clazz.getMethod("from", new Class[] { CompositeData.class });
+            this.getLockedStackDepth = clazz.getMethod("getLockedStackDepth", NO_PARAMS_SIG);
+            this.getLockedStackFrame = clazz.getMethod("getLockedStackFrame", NO_PARAMS_SIG);
          }
          catch (Exception e)
          {
@@ -364,81 +346,66 @@
     */
    public String listMemoryPools(boolean fancy)
    {
-      if (getMemoryPoolMXBeans != null)
+      StringBuffer sbuf = new StringBuffer(4196);
+      
+      // get the pools
+      List<MemoryPoolMXBean> poolList = ManagementFactory.getMemoryPoolMXBeans();
+      sbuf.append("<b>Total Memory Pools:</b> ").append(poolList.size());
+      sbuf.append("<blockquote>");
+      for (MemoryPoolMXBean pool : poolList)
       {
-         // running under jdk5+
-         StringBuffer sbuf = new StringBuffer(4196);
-         try
+         // MemoryPoolMXBean instance
+         String name = pool.getName();
+         // enum MemoryType
+         MemoryType type = pool.getType();
+         sbuf.append("<b>Pool: ").append(name);
+         sbuf.append("</b> (").append(type).append(")");
+
+         // PeakUsage/CurrentUsage
+         MemoryUsage peakUsage = pool.getPeakUsage();
+         MemoryUsage usage = pool.getUsage();
+         
+         sbuf.append("<blockquote>");
+         if (usage != null && peakUsage != null)
          {
-            // get the pools
-            List poolList = (List)getMemoryPoolMXBeans.invoke(null, NO_PARAMS);
-            sbuf.append("<b>Total Memory Pools:</b> ").append(poolList.size());
-            sbuf.append("<blockquote>");
-            for (Iterator i = poolList.iterator(); i.hasNext(); )
-            {
-               // MemoryPoolMXBean instance
-               Object pool = i.next();
-               String name = (String)getName.invoke(pool, NO_PARAMS);
-               // enum MemoryType
-               Object type = getType.invoke(pool, NO_PARAMS);
-               sbuf.append("<b>Pool: ").append(name);
-               sbuf.append("</b> (").append(type).append(")");
-               
-               // PeakUsage/CurrentUsage
-               Object peakUsage = getPeakUsage.invoke(pool, NO_PARAMS);
-               Object usage = getUsage.invoke(pool, NO_PARAMS);
-               
-               sbuf.append("<blockquote>");
-               if (usage != null && peakUsage != null)
-               {
-                  Long init = (Long)getInit.invoke(peakUsage, NO_PARAMS);
-                  Long used = (Long)getUsed.invoke(peakUsage, NO_PARAMS);
-                  Long committed = (Long)getCommitted.invoke(peakUsage, NO_PARAMS);
-                  Long max = (Long)getMax.invoke(peakUsage, NO_PARAMS);
+            Long init = peakUsage.getInit();
+            Long used = peakUsage.getUsed();
+            Long committed = peakUsage.getCommitted();
+            Long max = peakUsage.getMax();
+            
+            sbuf.append("Peak Usage    : ");
+            sbuf.append("init:").append(init);
+            sbuf.append(", used:").append(used);
+            sbuf.append(", committed:").append(committed);
+            sbuf.append(", max:").append(max);
+            sbuf.append("<br/>");
 
-                  sbuf.append("Peak Usage    : ");
-                  sbuf.append("init:").append(init);
-                  sbuf.append(", used:").append(used);
-                  sbuf.append(", committed:").append(committed);
-                  sbuf.append(", max:").append(max);
-                  sbuf.append("<br>");
-                  
-                  init = (Long)getInit.invoke(usage, NO_PARAMS);
-                  used = (Long)getUsed.invoke(usage, NO_PARAMS);
-                  committed = (Long)getCommitted.invoke(usage, NO_PARAMS);
-                  max = (Long)getMax.invoke(usage, NO_PARAMS);
+            init = usage.getInit();
+            used = usage.getUsed();
+            committed = usage.getCommitted();
+            max = usage.getMax();
 
-                  sbuf.append("Current Usage : ");
-                  sbuf.append("init:").append(init);
-                  sbuf.append(", used:").append(used);
-                  sbuf.append(", committed:").append(committed);
-                  sbuf.append(", max:").append(max);
-                  
-                  if (fancy)
-                  {
-                     TextGraphHelper.poolUsage(sbuf, used.longValue(), committed.longValue(), max.longValue());
-                  }
-               }
-               else
-               {
-                  sbuf.append("Memory pool NOT valid!");
-               }
-               sbuf.append("</blockquote><br>");
+            sbuf.append("Current Usage : ");
+            sbuf.append("init:").append(init);
+            sbuf.append(", used:").append(used);
+            sbuf.append(", committed:").append(committed);
+            sbuf.append(", max:").append(max);
+
+            if (fancy)
+            {
+               TextGraphHelper.poolUsage(sbuf, used.longValue(), committed.longValue(), max.longValue());
             }
-            sbuf.append("</blockquote>");
          }
-         catch (Exception e)
+         else
          {
-            // ignore
+            sbuf.append("Memory pool NOT valid!");
          }
-         return sbuf.toString();
+         sbuf.append("</blockquote><br/>");
       }
-      else
-      {
-         return "<b>Memory pool information available only under a JDK5+ compatible JVM!</b>";
-      }
+      
+      return sbuf.toString();      
    }
-   
+      
    public Integer getActiveThreadCount()
    {
       return new Integer(getRootThreadGroup().activeCount());
@@ -461,15 +428,20 @@
       // activeCount() and activeGroupCount()
       ThreadGroupCount count = new ThreadGroupCount();
 
-      // traverse
-      String threadGroupInfo = getThreadGroupInfo(root, count);
+      StringBuffer rc = new StringBuffer();
       
-      // attach counters
+      // Find deadlocks, if there're any first, so that they're visible first thing
+      findDeadlockedThreads(rc);
+      
+      // Traverse thread dump
+      getThreadGroupInfo(root, count, rc);
+            
+      // Attach counters
       String threadDump =
-         "<b>Total Threads:</b> " + count.threads + "<br>" +
-         "<b>Total Thread Groups:</b> " + count.groups + "<br>" +
-         "<b>Timestamp:</b> " + dateFormat.format(new Date()) + "<br>" +
-         threadGroupInfo;
+         "<b>Total Threads:</b> " + count.threads + "<br/>" +
+         "<b>Total Thread Groups:</b> " + count.groups + "<br/>" +
+         "<b>Timestamp:</b> " + dateFormat.format(new Date()) + "<br/>" +
+         rc.toString();
       
       return threadDump;
    }
@@ -514,33 +486,23 @@
     */
    private Set<ThreadCPU> getThreadCpuUtilization()
    {
-      if (threadMXBean == null)
-         return null;
+      TreeSet<ThreadCPU> result = new TreeSet<ThreadCPU>();
       
-      try
+      long[] threads = threadMXBean.getAllThreadIds();
+      for (int i = 0; i < threads.length; ++i)
       {
-         TreeSet<ThreadCPU> result = new TreeSet<ThreadCPU>();
-         long[] threads = (long[]) getAllThreadIds.invoke(threadMXBean, NO_PARAMS);
-         for (int i = 0; i < threads.length; ++i)
+         Long id = new Long(threads[i]);
+         Long cpuTime = threadMXBean.getThreadCpuTime(id);
+         ThreadInfo threadInfo = threadMXBean.getThreadInfo(id, ZERO );
+         if (threadInfo != null)
          {
-            Long id = new Long(threads[i]);
-            Long cpuTime = (Long) getThreadCpuTime.invoke(threadMXBean, new Object[] { id });
-            Object threadInfo = getThreadInfo.invoke(threadMXBean, new Object[] { id, ZERO });
-            if (threadInfo != null)
-            {
-               String name = (String) getThreadName.invoke(threadInfo, NO_PARAMS);
-               result.add(new ThreadCPU(name, cpuTime.longValue()));
-            }
+            String name = threadInfo.getThreadName();
+            result.add(new ThreadCPU(name, cpuTime.longValue()));
          }
-         return result;
       }
-      catch (Exception e)
-      {
-         log.warn("Error retrieving thread cpu utiliation", e);
-         return null;
-      }
+      return result;
    }
-   
+      
    /*
     * Traverse to the root thread group
     */
@@ -558,14 +520,32 @@
    /*
     * Recurse inside ThreadGroups to create the thread dump
     */
-   private String getThreadGroupInfo(ThreadGroup group, ThreadGroupCount count)
+   private void getThreadGroupInfo(ThreadGroup group, ThreadGroupCount count, StringBuffer rc)
    {
-      StringBuffer rc = new StringBuffer();
-      
+      if (Java.isCompatible(Java.VERSION_1_6) && 
+            (isObjectMonitorUsageSupported || isSynchronizerUsageSupported))
+      {
+         /* We're running JDK6+ and either object monitor or ownable 
+          * synchronizers are supported by the JVM */
+         log.debug("Generate a thread dump [show monitors = " + isObjectMonitorUsageSupported + ", show ownable synchronizers = " + isSynchronizerUsageSupported + "]");
+         getThreadGroupInfoWithLocks(group, count, rc);
+      }
+      else
+      {
+         /* If we're running JDK5, or JDK6 but neither monitor nor 
+          * synchronisers cannot be retrieved, we use standard thread dump 
+          * format. */
+         log.debug("Generate a thread dump without locks.");
+         getThreadGroupInfoWithoutLocks(group, count, rc);
+      }      
+   }
+   
+   private void getThreadGroupInfoWithoutLocks(ThreadGroup group, ThreadGroupCount count, StringBuffer rc)
+   {
       // Visit one more group
       count.groups++;
       
-      rc.append("<br><b>");
+      rc.append("<br/><b>");
       rc.append("Thread Group: " + group.getName());
       rc.append("</b> : ");
       rc.append("max priority:" + group.getMaxPriority() +
@@ -583,8 +563,8 @@
          rc.append("Thread: " + threads[i].getName());
          rc.append("</b> : ");
          rc.append("priority:" + threads[i].getPriority() +
-         ", demon:" + threads[i].isDaemon());
-         // Output extra info with jdk5+, or just <br>
+         ", demon:" + threads[i].isDaemon() + ", ");
+         // Output extra info with jdk5+, or just <br/>
          outputJdk5ThreadMXBeanInfo(rc, threads[i]);
       }
       
@@ -592,71 +572,267 @@
       group.enumerate(groups, false);
       for (int i= 0; i < groups.length && groups[i] != null; i++)
       {
-         rc.append(getThreadGroupInfo(groups[i], count));
+         getThreadGroupInfoWithoutLocks(groups[i], count, rc);
       }
+      rc.append("</blockquote>");      
+   }
+   
+   private void getThreadGroupInfoWithLocks(ThreadGroup group, ThreadGroupCount count, StringBuffer rc)
+   {
+      // Visit one more group
+      count.groups++;
+      
+      rc.append("<br/><b>");
+      rc.append("Thread Group: " + group.getName());
+      rc.append("</b> : ");
+      rc.append("max priority:" + group.getMaxPriority() +
+                ", demon:" + group.isDaemon());
+      
+      rc.append("<blockquote>");
+      Thread threads[]= new Thread[group.activeCount()];
+      group.enumerate(threads, false);
+      
+      long[] idsTmp = new long[threads.length];
+      int numberNonNullThreads = 0;
+      for (int i= 0; i < threads.length && threads[i] != null; i++)
+      {
+         if (log.isTraceEnabled())
+         {
+            log.trace("Adding " + threads[i] + " with id=" + threads[i].getId());
+         }
+         idsTmp[i] = threads[i].getId();
+         numberNonNullThreads++;
+      }
+      
+      long[] ids = new long[numberNonNullThreads];
+      System.arraycopy(idsTmp, 0, ids, 0, numberNonNullThreads);
+      
+      if (log.isTraceEnabled())
+      {
+         log.trace("List of ids after trimming " + Arrays.toString(ids));
+      }    
+      
+      try
+      {
+         ThreadInfo[] infos = (ThreadInfo[])getThreadInfoWithSyncInfo.invoke(threadMXBean, 
+               new Object[] {ids, isObjectMonitorUsageSupported, isSynchronizerUsageSupported});
+         
+         for (int i= 0; i < infos.length && threads[i] != null; i++)
+         {
+            // Visit one more thread
+            count.threads++;
+            
+            rc.append("<b>");
+            rc.append("Thread: " + infos[i].getThreadName());
+            rc.append("</b> : ");
+            rc.append("priority:" + threads[i].getPriority() +
+            ", demon:" + threads[i].isDaemon() + ", ");
+            // Output extra info with jdk6+
+            outputJdk6ThreadMXBeanInfo(rc, infos[i]);
+         }
+         
+         ThreadGroup groups[]= new ThreadGroup[group.activeGroupCount()];
+         group.enumerate(groups, false);
+         for (int i= 0; i < groups.length && groups[i] != null; i++)
+         {
+            getThreadGroupInfoWithLocks(groups[i], count, rc);
+         }         
+      }
+      catch(Exception ignore)
+      {
+         log.debug("Exception to be ignored", ignore);
+      }
+      
       rc.append("</blockquote>");
-      
-      return rc.toString();
    }
 
+
    /*
     * Complete the output of thread info, with optional stuff
     * when running under jdk5+, or just change line.
     */
    private void outputJdk5ThreadMXBeanInfo(StringBuffer sbuf, Thread thread)
    {
-      // if ThreadMXBean has been found, we run under jdk5+
-      if (threadMXBean != null)
+      // Get the threadId
+      Long threadId = thread.getId();
+
+      // Get the ThreadInfo object for that threadId, max StackTraceElement depth
+      ThreadInfo threadInfo = threadMXBean.getThreadInfo(threadId, new Integer(Integer.MAX_VALUE));
+      
+      outputJdk5ThreadMXBeanInfo(sbuf, threadInfo);      
+   }
+   
+   /*
+    * Complete the output of thread info, with optional stuff when running 
+    * under jdk5+ or jdk6 without object monitor usage and object synchronizer 
+    * usage capabilities, or just change line.
+    */
+   private void outputJdk5ThreadMXBeanInfo(StringBuffer sbuf, ThreadInfo threadInfo)
+   {
+      // JBAS-3838, thread might not be alive
+      if (threadInfo != null)
       {
-         // use reflection all the way, until we base on jdk5
-         try
+         // get misc info from ThreadInfo
+         Thread.State threadState = threadInfo.getThreadState(); // enum
+         String lockName = threadInfo.getLockName();
+         StackTraceElement[] stackTrace = threadInfo.getStackTrace();
+
+         Long threadId = threadInfo.getThreadId();
+         sbuf.append("threadId:").append(threadId);
+         sbuf.append(", threadState:").append(threadState);
+         sbuf.append("<br/>");
+         if (stackTrace.length > 0)
          {
-            // get the threadId
-            Long threadId = (Long)getThreadId.invoke(thread, NO_PARAMS);
-            sbuf.append(", threadId:").append(threadId);
+            sbuf.append("<blockquote>");
+            
+            printLockName(sbuf, "waiting on", lockName);
+            
+            for (int i = 0; i < stackTrace.length; i++)
+            {
+               sbuf.append(stackTrace[i]).append("<br/>");
+            }
+            sbuf.append("</blockquote>");
+         }
+      }
+      else
+      {
+         sbuf.append("<br/>");
+      }      
+   }
+   
+   /*
+    * Complete the output of thread info, with optional stuff
+    * when running under jdk6+, or just change line.
+    */
+   private void outputJdk6ThreadMXBeanInfo(StringBuffer sbuf, ThreadInfo threadInfo) throws Exception
+   {
+      // get the threadId
+      Long threadId = threadInfo.getThreadId();
+      sbuf.append("threadId:").append(threadId);
 
-            // get the ThreadInfo object for that threadId, max StackTraceElement depth
-            Object threadInfo = getThreadInfo.invoke(threadMXBean,
-                  new Object[] { threadId, new Integer(Integer.MAX_VALUE) });
-            // JBAS-3838, thread might not be alive
-            if (threadInfo != null)
+      if (threadInfo != null)
+      {
+         // get misc info from ThreadInfo
+         Thread.State threadState = threadInfo.getThreadState(); // enum
+         String lockName = threadInfo.getLockName();
+         StackTraceElement[] stackTrace = threadInfo.getStackTrace();         
+         Object[] monitors = (Object[])getLockedMonitors.invoke(threadInfo, NO_PARAMS);
+
+         sbuf.append(", threadState:").append(threadState);
+         sbuf.append("<br/>");
+         if (stackTrace.length > 0)
+         {
+            sbuf.append("<blockquote>");
+
+            printLockName(sbuf, "waiting on", lockName);
+            
+            for (int i = 0; i < stackTrace.length; i++)
             {
-               // get misc info from ThreadInfo
-               Object threadState = getThreadState.invoke(threadInfo, NO_PARAMS); // enum
-               String lockName = (String)getLockName.invoke(threadInfo, NO_PARAMS);
-               //Long lockOwnerId = (Long)getLockOwnerId.invoke(threadInfo, NO_PARAMS);
-               //String lockOwnerName = (String)getLockOwnerName.invoke(threadInfo, NO_PARAMS);
-               Object[] stackTrace = (Object[])getStackTrace.invoke(threadInfo, NO_PARAMS);
-
-               sbuf.append(", threadState:").append(threadState);
-               sbuf.append(", lockName:").append(lockName);
-               //sbuf.append(", lockOwnerId:").append(lockOwnerId);
-               //sbuf.append(", lockOwnerName:").append(lockOwnerName);
-               sbuf.append("<br>");
-               if (stackTrace.length > 0)
+               sbuf.append(stackTrace[i]).append("<br/>");
+               for (Object monitor : monitors)
                {
-                  sbuf.append("<blockquote>");
-                  for (int i = 0; i < stackTrace.length; i++)
+                  int lockedStackDepth = (Integer)getLockedStackDepth.invoke(monitor, NO_PARAMS);
+                  if (lockedStackDepth == i)
                   {
-                     sbuf.append(stackTrace[i]).append("<br>");
+                     printLockName(sbuf, "locked", monitor.toString()); 
                   }
-                  sbuf.append("</blockquote>");
                }
             }
-            else
+            
+            Object[] synchronizers = (Object[])getLockedSynchronizers.invoke(threadInfo, NO_PARAMS);
+            if (synchronizers.length > 0)
             {
-               sbuf.append("<br>");
+               sbuf.append("<br/>").append("<b>Locked synchronizers</b> : ").append("<br/>");
+               for (Object synchronizer : synchronizers)
+               {
+                  printLockName(sbuf, "locked", synchronizer.toString());
+               }
             }
+            
+            sbuf.append("</blockquote>");
+         }         
+      }
+      else
+      {
+         sbuf.append("<br/>");
+      }
+   }
+   
+   private void printLockName(StringBuffer sbuf, String status, String lockName)
+   {
+      if (lockName != null)
+      {
+         String[] lockInfo = lockName.split("@");
+         sbuf.append("- " + status + " <0x" + lockInfo[1] + "> (a " + lockInfo[0] + ")").append("<br/>");
+      }
+   }
+
+   private void findDeadlockedThreads(StringBuffer rc)
+   {
+      if (Java.isCompatible(Java.VERSION_1_6) && isSynchronizerUsageSupported)
+      {
+         findDeadlockedThreadsMonitorsOrSynchronisers(rc);
+      }
+      else
+      {
+         findDeadlockedThreadsOnlyMonitors(rc);
+      }
+   }
+   
+   private void findDeadlockedThreadsMonitorsOrSynchronisers(StringBuffer sb)
+   {
+      try
+      {
+         long[] ids = (long[])findDeadlockedThreads.invoke(threadMXBean, NO_PARAMS);
+         if (ids == null)
+         {
+            return;
          }
-         catch (Exception ignore)
+         
+         ThreadInfo[] threadsInfo = (ThreadInfo[])getThreadInfoWithSyncInfo.invoke(threadMXBean, 
+               new Object[] {ids, isObjectMonitorUsageSupported, isSynchronizerUsageSupported});
+         
+         sb.append("<br/><b>Found deadlock(s)</b> : <br/><br/>");
+         
+         for (ThreadInfo threadInfo : threadsInfo)
          {
-            // empty
+            sb.append("<b>");
+            sb.append("Thread: " + threadInfo.getThreadName());
+            sb.append("</b> : ");
+            outputJdk6ThreadMXBeanInfo(sb, threadInfo);
+         }         
+      }
+      catch(Exception ignore)
+      {
+         log.debug("Exception to be ignored", ignore);
+      }
+   }
+   
+   private void findDeadlockedThreadsOnlyMonitors(StringBuffer sb)
+   {
+      try
+      {
+         long[] ids = threadMXBean.findMonitorDeadlockedThreads();
+         if (ids == null)
+         {
+            return;
          }
+         
+         ThreadInfo[] threadsInfo = threadMXBean.getThreadInfo(ids, Integer.MAX_VALUE);
+         
+         sb.append("<br/><b>Found deadlock(s)</b> : <br/><br/>");
+         
+         for (ThreadInfo threadInfo : threadsInfo)
+         {
+            sb.append("<b>");
+            sb.append("Thread: " + threadInfo.getThreadName());
+            sb.append("</b> : ");
+            outputJdk5ThreadMXBeanInfo(sb, threadInfo);
+         }
       }
-      else
+      catch(Exception ignore)
       {
-         // no jdk5+ info to add, just change line
-         sbuf.append("<br>");
+         log.debug("Exception to be ignored", ignore);
       }
    }
    
@@ -765,9 +941,9 @@
          int localCommitted = (int)(factor * committed / assumedMax);
          int localMax = factor;
 
-         sbuf.append("<blockquote><br>");
-         sbuf.append(baseline, 0, localCommitted).append("| committed:").append(outputNumber(committed)).append("<br>");
-         sbuf.append(fixedline).append("<br>");
+         sbuf.append("<blockquote><br/>");
+         sbuf.append(baseline, 0, localCommitted).append("| committed:").append(outputNumber(committed)).append("<br/>");
+         sbuf.append(fixedline).append("<br/>");
          
          // the difficult part
          sbuf.append(barline, 0, localUsed);
@@ -782,9 +958,9 @@
             sbuf.append(spaces, 0, localMax - localCommitted - 1);            
             sbuf.append('|');
          }
-         sbuf.append(" max:").append(outputNumber(max)).append("<br>");
+         sbuf.append(" max:").append(outputNumber(max)).append("<br/>");
          
-         sbuf.append(fixedline).append("<br>");
+         sbuf.append(fixedline).append("<br/>");
          sbuf.append(baseline, 0, localUsed).append("| used:").append(outputNumber(used));
          sbuf.append("</blockquote>");
       }




More information about the jboss-cvs-commits mailing list