/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ 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.RuntimeMXBean; 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; import java.util.List; import java.util.Set; import java.util.TreeSet; 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.managed.api.ManagedOperation.Impact; import org.jboss.managed.api.annotation.ManagementComponent; import org.jboss.managed.api.annotation.ManagementObject; import org.jboss.managed.api.annotation.ManagementOperation; import org.jboss.managed.api.annotation.ManagementParameter; import org.jboss.managed.api.annotation.ManagementProperty; import org.jboss.managed.api.annotation.ViewUse; import org.jboss.util.platform.Java; /** * An MBean that provides a rich view of system information for the JBoss * server in which it is deployed. * * @author <a href="mailto:rickard.oberg@telkel.com">Rickard Oberg</a> * @author <a href="mailto:Scott.Stark@jboss.org">Scott Stark</a> * @author <a href="mailto:hiram.chirino@jboss.org">Hiram Chirino</a> * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> * @author <a href="mailto:marc.fleury@jboss.org">Marc Fleury</a> * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a> * @author <a href="mailto:galder.zamarreno@jboss.com">Galder Zamarreno</a> * @version $Revision: 87850 $ */ @ManagementObject( name="jboss.system:type=ServerInfo", isRuntime=true, description="provides a view of system information for the JBoss server in which it is deployed", componentType=@ManagementComponent(type="MCBean", subtype="ServerInfo") ) public class ServerInfo implements ServerInfoMBean, MBeanRegistration { /** Class logger. */ private static final Logger log = Logger.getLogger(ServerInfo.class); /** Zero */ private static final Integer ZERO = new Integer(0); /** Empty parameter signature for reflective calls */ private static final Class[] NO_PARAMS_SIG = new Class[0]; /** Empty paramater list for reflective calls */ private static final Object[] NO_PARAMS = new Object[0]; /** 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 jdk6+ ThreadMXBean methods */ private boolean isObjectMonitorUsageSupported; private boolean isSynchronizerUsageSupported; private Method findDeadlockedThreads; private Method dumpAllThreads; private Method getThreadInfoWithSyncInfo; /** The cached jdk6+ ThreadInfo methods */ private Method getLockInfo; private Method getLockedMonitors; private Method getLockedSynchronizers; /** The cached jdk6+ LockInfo methods */ private Method getClassName; private Method getIdentityHashCode; /** The cached jdk6+ MonitorInfo methods */ private Method from; private Method getLockedStackDepth; private Method getLockedStackFrame; /////////////////////////////////////////////////////////////////////////// // JMX Hooks // /////////////////////////////////////////////////////////////////////////// public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception { // Dump out basic JVM & OS info as INFO priority msgs log.info("Java version: " + System.getProperty("java.version") + "," + System.getProperty("java.vendor")); log.info("Java Runtime: " + System.getProperty("java.runtime.name") + " (build " + System.getProperty("java.runtime.version") + ")"); log.info("Java VM: " + System.getProperty("java.vm.name") + " " + System.getProperty("java.vm.version") + "," + System.getProperty("java.vm.vendor")); log.info("OS-System: " + System.getProperty("os.name") + " " + System.getProperty("os.version") + "," + System.getProperty("os.arch")); log.info("VM arguments: " + getVMArguments()); // Dump out the entire system properties log.debug("Full System Properties Dump"); Enumeration names = System.getProperties().propertyNames(); while (names.hasMoreElements()) { String pname = (String)names.nextElement(); log.debug(" " + pname + ": " + System.getProperty(pname)); } if (Java.isCompatible(Java.VERSION_1_6)) { try { ClassLoader cl = Thread.currentThread().getContextClassLoader(); Class clazz = ThreadMXBean.class; Method method = clazz.getMethod("isObjectMonitorUsageSupported", NO_PARAMS_SIG); isObjectMonitorUsageSupported = (Boolean)method.invoke(threadMXBean, NO_PARAMS); method = clazz.getMethod("isSynchronizerUsageSupported", NO_PARAMS_SIG); isSynchronizerUsageSupported = (Boolean)method.invoke(threadMXBean, NO_PARAMS); 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}); 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); 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) { log.debug("Cannot access platform ThreadMXBean", e); } } return name == null ? new ObjectName(OBJECT_NAME_STR) : name; } public void postRegister(Boolean registrationDone) { // empty } public void preDeregister() throws Exception { // empty } public void postDeregister() { // empty } /////////////////////////////////////////////////////////////////////////// // Server Information // /////////////////////////////////////////////////////////////////////////// @ManagementProperty(use={ViewUse.RUNTIME}, readOnly=true) public String getJavaVersion() { return System.getProperty("java.version"); } @ManagementProperty(use={ViewUse.RUNTIME}, readOnly=true) public String getJavaVendor() { return System.getProperty("java.vendor"); } @ManagementProperty(use={ViewUse.RUNTIME}, readOnly=true) public String getJavaVMName() { return System.getProperty("java.vm.name"); } @ManagementProperty(use={ViewUse.RUNTIME}, readOnly=true) public String getJavaVMVersion() { return System.getProperty("java.vm.version"); } @ManagementProperty(use={ViewUse.RUNTIME}, readOnly=true) public String getJavaVMVendor() { return System.getProperty("java.vm.vendor"); } @ManagementProperty(use={ViewUse.RUNTIME}, readOnly=true) public String getOSName() { return System.getProperty("os.name"); } @ManagementProperty(use={ViewUse.RUNTIME}, readOnly=true) public String getOSVersion() { return System.getProperty("os.version"); } @ManagementProperty(use={ViewUse.RUNTIME}, readOnly=true) public String getOSArch() { return System.getProperty("os.arch"); } @ManagementProperty(use={ViewUse.STATISTIC}, readOnly=true) public Long getTotalMemory() { return new Long(Runtime.getRuntime().totalMemory()); } @ManagementProperty(use={ViewUse.STATISTIC}, readOnly=true) public Long getFreeMemory() { return new Long(Runtime.getRuntime().freeMemory()); } /** * Returns <tt>Runtime.getRuntime().maxMemory()<tt> on * JDK 1.4 vms or -1 on previous versions. */ @ManagementProperty(use={ViewUse.STATISTIC}, readOnly=true) public Long getMaxMemory() { if (Java.isCompatible(Java.VERSION_1_4)) { // Uncomment when JDK 1.4 is the base JVM // return new Long(Runtime.getRuntime().maxMemory()); // until then use reflection to do the job try { Runtime rt = Runtime.getRuntime(); Method m = rt.getClass().getMethod("maxMemory", NO_PARAMS_SIG); return (Long)m.invoke(rt, NO_PARAMS); } catch (Exception e) { log.error("Operation failed", e); } } return new Long(-1); } /** * Returns <tt>Runtime.getRuntime().availableProcessors()</tt> on * JDK 1.4 vms or -1 on previous versions. */ @ManagementProperty(use={ViewUse.STATISTIC}, readOnly=true) public Integer getAvailableProcessors() { if (Java.isCompatible(Java.VERSION_1_4)) { // Uncomment when JDK 1.4 is the base JVM // return new Integer(Runtime.getRuntime().availableProcessors()); // until then use reflection to do the job try { Runtime rt = Runtime.getRuntime(); Method m = rt.getClass().getMethod("availableProcessors", NO_PARAMS_SIG); return (Integer)m.invoke(rt, NO_PARAMS); } catch (Exception e) { log.error("Operation failed", e); } } return new Integer(-1); } /** * Returns InetAddress.getLocalHost().getHostName(); */ @ManagementProperty(use={ViewUse.STATISTIC}, readOnly=true) public String getHostName() { if (hostName == null) { try { hostName = java.net.InetAddress.getLocalHost().getHostName(); } catch (java.net.UnknownHostException e) { log.error("Error looking up local hostname", e); hostName = "<unknown>"; } } return hostName; } /** * Returns InetAddress.getLocalHost().getHostAddress(); */ @ManagementProperty(use={ViewUse.STATISTIC}, readOnly=true) public String getHostAddress() { if (hostAddress == null) { try { hostAddress = java.net.InetAddress.getLocalHost().getHostAddress(); } catch (java.net.UnknownHostException e) { log.error("Error looking up local address", e); hostAddress = "<unknown>"; } } return hostAddress; } /** * Return a listing of the thread pools on jdk5+. * * @param fancy produce a text-based graph when true */ @ManagementOperation(description="Return a listing of the thread pools on jdk5+", impact=Impact.ReadOnly, params={@ManagementParameter(name="fancy", description="produce a text-based graph when true")}) public String listMemoryPools(boolean fancy) { 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) { // 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) { 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/>"); 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/>"); } return sbuf.toString(); } @ManagementProperty(use={ViewUse.STATISTIC}, readOnly=true) public Integer getActiveThreadCount() { return new Integer(getRootThreadGroup().activeCount()); } @ManagementProperty(use={ViewUse.STATISTIC}, readOnly=true) public Integer getActiveThreadGroupCount() { return new Integer(getRootThreadGroup().activeGroupCount()); } /** * Return a listing of the active threads and thread groups. */ @ManagementOperation(description="Return a listing of the active threads and thread groups", impact=Impact.ReadOnly) public String listThreadDump() { ThreadGroup root = getRootThreadGroup(); // Count the threads/groups during our traversal // rather than use the often inaccurate ThreadGroup // activeCount() and activeGroupCount() ThreadGroupCount count = new ThreadGroupCount(); StringBuffer rc = new StringBuffer(); // 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/>" + rc.toString(); return threadDump; } /** * Return a listing of the active threads and thread groups. */ @ManagementOperation(description="Return a listing of the active threads and thread groups", impact=Impact.ReadOnly) public String listThreadCpuUtilization() { Set threads = getThreadCpuUtilization(); if (threads == null) { return("Thread cpu utilization requires J2SE5+"); } else { long totalCPU = 0; StringBuffer buffer = new StringBuffer(); buffer.append("<table><tr><th>Thread Name</th><th>CPU (milliseconds)</th></tr>"); for (Iterator i = threads.iterator(); i.hasNext();) { ThreadCPU thread = (ThreadCPU) i.next(); buffer.append("<tr><td>").append(thread.name).append("</td><td>"); buffer.append(thread.cpuTime).append("</td></tr>"); totalCPU += thread.cpuTime; } buffer.append("<tr><td> </td><td> </td></tr><tr><td>Total</td><td>"); buffer.append(totalCPU).append("</td></tr></table>"); return buffer.toString(); } } /////////////////////////////////////////////////////////////////////////// // Private // /////////////////////////////////////////////////////////////////////////// /** * Get the Thread cpu utilization * * @return an ordered */ private Set<ThreadCPU> getThreadCpuUtilization() { TreeSet<ThreadCPU> result = new TreeSet<ThreadCPU>(); long[] threads = threadMXBean.getAllThreadIds(); 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) { String name = threadInfo.getThreadName(); result.add(new ThreadCPU(name, cpuTime.longValue())); } } return result; } /* * Traverse to the root thread group */ private ThreadGroup getRootThreadGroup() { ThreadGroup group = Thread.currentThread().getThreadGroup(); while (group.getParent() != null) { group = group.getParent(); } return group; } /* * Recurse inside ThreadGroups to create the thread dump */ private void getThreadGroupInfo(ThreadGroup group, ThreadGroupCount count, StringBuffer rc) { 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("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); for (int i= 0; i < threads.length && threads[i] != null; i++) { // Visit one more thread count.threads++; rc.append("<b>"); 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/> outputJdk5ThreadMXBeanInfo(rc, threads[i]); } ThreadGroup groups[]= new ThreadGroup[group.activeGroupCount()]; group.enumerate(groups, false); for (int i= 0; i < groups.length && groups[i] != null; i++) { 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>"); } /* * Complete the output of thread info, with optional stuff * when running under jdk5+, or just change line. */ private void outputJdk5ThreadMXBeanInfo(StringBuffer sbuf, Thread thread) { // 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) { // 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) { 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); 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++) { sbuf.append(stackTrace[i]).append("<br/>"); for (Object monitor : monitors) { int lockedStackDepth = (Integer)getLockedStackDepth.invoke(monitor, NO_PARAMS); if (lockedStackDepth == i) { printLockName(sbuf, "locked", monitor.toString()); } } } Object[] synchronizers = (Object[])getLockedSynchronizers.invoke(threadInfo, NO_PARAMS); if (synchronizers.length > 0) { 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; } 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) { 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); } } catch(Exception ignore) { log.debug("Exception to be ignored", ignore); } } private String getVMArguments() { String args = ""; RuntimeMXBean rmBean = ManagementFactory.getRuntimeMXBean(); List<String> inputArguments = rmBean.getInputArguments(); for (String arg : inputArguments) { args += arg + " "; } return args; } /** * Display the java.lang.Package info for the pkgName */ public String displayPackageInfo(String pkgName) { Package pkg = Package.getPackage(pkgName); if( pkg == null ) return "<h2>Package:"+pkgName+" Not Found!</h2>"; StringBuffer info = new StringBuffer("<h2>Package: "+pkgName+"</h2>"); displayPackageInfo(pkg, info); return info.toString(); } private void displayPackageInfo(Package pkg, StringBuffer info) { info.append("<pre>\n"); info.append("SpecificationTitle: "+pkg.getSpecificationTitle()); info.append("\nSpecificationVersion: "+pkg.getSpecificationVersion()); info.append("\nSpecificationVendor: "+pkg.getSpecificationVendor()); info.append("\nImplementationTitle: "+pkg.getImplementationTitle()); info.append("\nImplementationVersion: "+pkg.getImplementationVersion()); info.append("\nImplementationVendor: "+pkg.getImplementationVendor()); info.append("\nisSealed: "+pkg.isSealed()); info.append("</pre>\n"); } /////////////////////////////////////////////////////////////////////////// // Inner // /////////////////////////////////////////////////////////////////////////// /* * Inner Helper class for fancy text graphs * * @author dimitris@jboss.org */ private static class TextGraphHelper { // number conversions static final DecimalFormat formatter = new DecimalFormat("#.##"); static final long KILO = 1024; static final long MEGA = 1024 * 1024; static final long GIGA = 1024 * 1024 * 1024; // how many dashes+pipe is 100% static final int factor = 70; static char[] fixedline; static char[] baseline; static char[] barline; static char[] spaces; static { // cache a couple of Strings StringBuffer sbuf0 = new StringBuffer(); StringBuffer sbuf1 = new StringBuffer(); StringBuffer sbuf2 = new StringBuffer(); StringBuffer sbuf3 = new StringBuffer(); sbuf0.append('+'); sbuf1.append('|'); sbuf2.append('|'); for (int i = 1; i < factor; i++) { sbuf0.append('-'); sbuf1.append('-'); sbuf2.append('/'); sbuf3.append(' '); } sbuf0.append('+'); fixedline = sbuf0.toString().toCharArray(); baseline = sbuf1.toString().toCharArray(); barline = sbuf2.toString().toCharArray(); spaces = sbuf3.toString().toCharArray(); } private TextGraphHelper() { // do not instantiate } /* * Make a text graph of a memory pool usage: * * +---------------------------| committed:10Mb * +-------------------------------------------------+ * |//////////////// | | max:20Mb * +-------------------------------------------------+ * +---------------| used:3Mb * * When max is unknown assume max == committed * * |-------------------------------------------------| committed:10Mb * +-------------------------------------------------+ * |//////////////// | max:-1 * +-------------------------------------------------+ * |---------------| used:3Mb */ public static void poolUsage(StringBuffer sbuf, long used, long committed, long max) { // there is a chance that max is not provided (-1) long assumedMax = (max == -1) ? committed : max; // find out bar lengths int localUsed = (int)(factor * used / assumedMax); 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/>"); // the difficult part sbuf.append(barline, 0, localUsed); if (localUsed < localCommitted) { sbuf.append(localUsed > 0 ? '/' : '|'); sbuf.append(spaces, 0, localCommitted - localUsed - 1); } sbuf.append('|'); if (localCommitted < localMax) { sbuf.append(spaces, 0, localMax - localCommitted - 1); sbuf.append('|'); } sbuf.append(" max:").append(outputNumber(max)).append("<br/>"); sbuf.append(fixedline).append("<br/>"); sbuf.append(baseline, 0, localUsed).append("| used:").append(outputNumber(used)); sbuf.append("</blockquote>"); } private static String outputNumber(long value) { if (value >= GIGA) { return formatter.format((double)value / GIGA) + "Gb"; } else if (value >= MEGA) { return formatter.format((double)value / MEGA) + "Mb"; } else if (value >= KILO) { return formatter.format((double)value / KILO) + "Kb"; } else if (value >= 0) { return value + "b"; } else { return Long.toString(value); } } } private static class ThreadCPU implements Comparable { public String name; public long cpuTime; public ThreadCPU(String name, long cpuTime) { this.name = name; this.cpuTime = cpuTime / 1000000; // convert to millis } public int compareTo(Object o) { ThreadCPU other = (ThreadCPU) o; long value = cpuTime - other.cpuTime; if (value > 0) return -1; else if (value < 0) return +1; else return name.compareTo(other.name); } } /* * Simple data holder */ private static class ThreadGroupCount { public int threads; public int groups; } }