Author: rjung Date: Mon Apr 29 10:58:01 2013 New Revision: 1476959 URL: http://svn.apache.org/r1476959 Log: First draft of Diagnostics class.
Added: tomcat/trunk/java/org/apache/tomcat/util/Diagnostics.java (with props) Added: tomcat/trunk/java/org/apache/tomcat/util/Diagnostics.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/Diagnostics.java?rev=1476959&view=auto ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/Diagnostics.java (added) +++ tomcat/trunk/java/org/apache/tomcat/util/Diagnostics.java Mon Apr 29 10:58:01 2013 @@ -0,0 +1,429 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// XXX TODO: Source code line length +// XXX TODO: StringManager +// XXX TODO: Sort logger names and system property keys in getVMInfo() +// XXX TODO: Add memory and GC MBeans to getVMInfo() +// XXX Optional: Wire setters to the manager: +// log level setter, verbose class loading setter, +// and threadMXBean setters. + +package org.apache.tomcat.util; + +import java.lang.management.ClassLoadingMXBean; +import java.lang.management.CompilationMXBean; +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.LockInfo; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.MemoryManagerMXBean; +import java.lang.management.MemoryPoolMXBean; +import java.lang.management.MonitorInfo; +import java.lang.management.OperatingSystemMXBean; +import java.lang.management.RuntimeMXBean; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.logging.LogManager; +import java.util.logging.LoggingMXBean; + +public class Diagnostics { + + private static final String INDENT1 = " "; + private static final String INDENT2 = "\t"; + private static final String INDENT3 = " "; + private static final String CRLF = "\r\n"; + private static final String vminfoSystemProperty = "java.vm.info"; + + private static final org.apache.juli.logging.Log log= + org.apache.juli.logging.LogFactory.getLog(Diagnostics.class); + + private static final SimpleDateFormat timeformat = + new SimpleDateFormat("yyyy-MM-DD HH:mm:ss.SSS"); + + /* Some platform MBeans */ + private static final ClassLoadingMXBean classLoadingMXBean = + ManagementFactory.getClassLoadingMXBean(); + private static final CompilationMXBean compilationMXBean = + ManagementFactory.getCompilationMXBean(); + private static final OperatingSystemMXBean operatingSystemMXBean = + ManagementFactory.getOperatingSystemMXBean(); + private static final RuntimeMXBean runtimeMXBean = + ManagementFactory.getRuntimeMXBean(); + private static final ThreadMXBean threadMXBean = + ManagementFactory.getThreadMXBean(); + + // XXX Not sure whether the following MBeans should better + // be retrieved on demand, i.e. whether they can change + // dynamically in the MBeanServer. + private static final LoggingMXBean loggingMXBean = + LogManager.getLoggingMXBean(); + private static final MemoryMXBean memoryMXBeans = + ManagementFactory.getMemoryMXBean(); + private static final List<GarbageCollectorMXBean> garbageCollectorMXBean = + ManagementFactory.getGarbageCollectorMXBeans(); + private static final List<MemoryManagerMXBean> memoryManagerMXBeans = + ManagementFactory.getMemoryManagerMXBeans(); + private static final List<MemoryPoolMXBean> memoryPoolMXBeans = + ManagementFactory.getMemoryPoolMXBeans(); + + /** + * Check whether thread contention monitoring is enabled. + * + * @return true if thread contention monitoring is enabled + */ + public static boolean isThreadContentionMonitoringEnabled() { + return threadMXBean.isThreadContentionMonitoringEnabled(); + } + + /** + * Enable or disable thread contention monitoring via the ThreadMxMXBean. + * + * @param enable whether to enable thread contention monitoring + */ + public static void setThreadContentionMonitoringEnabled(boolean enable) { + threadMXBean.setThreadContentionMonitoringEnabled(enable); + boolean checkValue = threadMXBean.isThreadContentionMonitoringEnabled(); + if (enable != checkValue) { + log.error("Could not set threadContentionMonitoringEnabled to " + + enable + ", got " + checkValue + " instead"); + } + } + + /** + * Check whether thread cpu time measurement is enabled. + * + * @return true if thread cpu time measurement is enabled + */ + public static boolean isThreadCpuTimeEnabled() { + return threadMXBean.isThreadCpuTimeEnabled(); + } + + /** + * Enable or disable thread cpu time measurement via the ThreadMxMXBean. + * + * @param enable whether to enable thread cpu time measurement + */ + public static void setThreadCpuTimeEnabled(boolean enable) { + threadMXBean.setThreadCpuTimeEnabled(enable); + boolean checkValue = threadMXBean.isThreadCpuTimeEnabled(); + if (enable != checkValue) { + log.error("Could not set threadCpuTimeEnabled to " + enable + + ", got " + checkValue + " instead"); + } + } + + /** + * Reset peak thread count in ThreadMXBean + */ + public static void resetPeakThreadCount() { + threadMXBean.resetPeakThreadCount(); + } + + /** + * Set verbose class loading + * + * @param verbose whether to enable verbose class loading + */ + public static void setVerboseClassLoading(boolean verbose) { + classLoadingMXBean.setVerbose(verbose); + boolean checkValue = classLoadingMXBean.isVerbose(); + if (verbose != checkValue) { + log.error("Could not set verbose class loading to " + verbose + + ", got " + checkValue + " instead"); + } + } + + /** + * Set logger level + * + * @param loggerName the name of the logger + * @param levelName the level to set + */ + public static void setLoggerLevel(String loggerName, String levelName) { + loggingMXBean.setLoggerLevel(loggerName, levelName); + String checkValue = loggingMXBean.getLoggerLevel(loggerName); + if (!checkValue.equals(levelName)) { + log.error("Could not set logger level for logger '" + + loggerName + "' to '" + levelName + + "', got '" + checkValue + "' instead"); + } + } + + /** + * Formats the thread dump header for one thread. + * + * @param ti the ThreadInfo describing the thread + * @return the formatted thread dump header + */ + private static String getThreadDumpHeader(ThreadInfo ti) { + StringBuilder sb = new StringBuilder("\"" + ti.getThreadName() + "\""); + sb.append(" Id=" + ti.getThreadId()); + sb.append(" cpu=" + threadMXBean.getThreadCpuTime(ti.getThreadId()) + + " ns"); + sb.append(" usr=" + threadMXBean.getThreadUserTime(ti.getThreadId()) + + " ns"); + sb.append(" blocked " + ti.getBlockedCount() + " for " + + ti.getBlockedTime() + " ms"); + sb.append(" waited " + ti.getWaitedCount() + " for " + + ti.getWaitedTime() + " ms"); + + if (ti.isSuspended()) { + sb.append(" (suspended)"); + } + if (ti.isInNative()) { + sb.append(" (running in native)"); + } + sb.append(CRLF); + sb.append(INDENT3 + "java.lang.Thread.State: " + ti.getThreadState()); + sb.append(CRLF); + return sb.toString(); + } + + /** + * Formats the thread dump for one thread. + * + * @param ti the ThreadInfo describing the thread + * @return the formatted thread dump + */ + private static String getThreadDump(ThreadInfo ti) { + StringBuilder sb = new StringBuilder(getThreadDumpHeader(ti)); + for (LockInfo li : ti.getLockedSynchronizers()) { + sb.append(INDENT2 + "locks " + + li.toString() + CRLF); + } + boolean start = true; + StackTraceElement[] stes = ti.getStackTrace(); + Object[] monitorDepths = new Object[stes.length]; + MonitorInfo[] mis = ti.getLockedMonitors(); + for (int i = 0; i < mis.length; i++) { + monitorDepths[mis[i].getLockedStackDepth()] = mis[i]; + } + for (int i = 0; i < stes.length; i++) { + StackTraceElement ste = stes[i]; + sb.append(INDENT2 + + "at " + ste.toString() + CRLF); + if (start) { + if (ti.getLockName() != null) { + sb.append(INDENT2 + "- waiting on (a " + + ti.getLockName() + ")"); + if (ti.getLockOwnerName() != null) { + sb.append(" owned by " + ti.getLockOwnerName() + + " Id=" + ti.getLockOwnerId()); + } + sb.append(CRLF); + } + start = false; + } + if (monitorDepths[i] != null) { + MonitorInfo mi = (MonitorInfo)monitorDepths[i]; + sb.append(INDENT2 + + "- locked (a " + mi.toString() + ")"+ + " index " + mi.getLockedStackDepth() + + " frame " + mi.getLockedStackFrame().toString()); + sb.append(CRLF); + + } + } + return sb.toString(); + } + + /** + * Formats the thread dump for a list of threads. + * + * @param tinfos the ThreadInfo array describing the thread list + * @return the formatted thread dump + */ + private static String getThreadDump(ThreadInfo[] tinfos) { + StringBuilder sb = new StringBuilder(); + for (ThreadInfo tinfo : tinfos) { + sb.append(getThreadDump(tinfo)); + sb.append(CRLF); + } + return sb.toString(); + } + + /** + * Check if any threads are deadlocked. If any, print + * the thread dump for those threads. + * + * @return a deadlock message and the formatted thread dump + * of the deadlocked threads + */ + public static String findDeadlock() { + ThreadInfo[] tinfos = null; + long[] ids = threadMXBean.findDeadlockedThreads(); + if (ids != null) { + tinfos = threadMXBean.getThreadInfo(threadMXBean.findDeadlockedThreads(), + true, true); + if (tinfos != null) { + StringBuilder sb = + new StringBuilder("Deadlock found between the following threads:"); + sb.append(CRLF); + sb.append(getThreadDump(tinfos)); + return sb.toString(); + } + } + return ""; + } + + /** + * Retrieve a formatted JVM thread dump. + * @return the thread dump + */ + public static String getThreadDump() { + StringBuilder sb = new StringBuilder(); + + synchronized(timeformat) { + sb.append(timeformat.format(new Date())); + } + sb.append(CRLF); + + sb.append("Full thread dump "); + sb.append(runtimeMXBean.getVmName()); + sb.append(" ("); + sb.append(runtimeMXBean.getVmVersion()); + String vminfo = System.getProperty(vminfoSystemProperty); + if (vminfo != null) { + sb.append(" " + vminfo); + } + sb.append("):" + CRLF); + sb.append(CRLF); + + ThreadInfo[] tis = threadMXBean.dumpAllThreads(true, true); + sb.append(getThreadDump(tis)); + + sb.append(findDeadlock()); + return sb.toString(); + } + + /** + * Retrieve a formatted JVM thread dump. + * @return the thread dump + */ + public static String getVMInfo() { + StringBuilder sb = new StringBuilder(); + + synchronized(timeformat) { + sb.append(timeformat.format(new Date())); + } + sb.append(CRLF); + + sb.append("Runtime information:" + CRLF); + sb.append(INDENT1 + "vmName: " + runtimeMXBean.getVmName() + CRLF); + sb.append(INDENT1 + "vmVersion: " + runtimeMXBean.getVmVersion() + CRLF); + sb.append(INDENT1 + "vmVendor: " + runtimeMXBean.getVmVendor() + CRLF); + sb.append(INDENT1 + "specName: " + runtimeMXBean.getSpecName() + CRLF); + sb.append(INDENT1 + "specVersion: " + runtimeMXBean.getSpecVersion() + CRLF); + sb.append(INDENT1 + "specVendor: " + runtimeMXBean.getSpecVendor() + CRLF); + sb.append(INDENT1 + "managementSpecVersion: " + + runtimeMXBean.getManagementSpecVersion() + CRLF); + sb.append(INDENT1 + "name: " + runtimeMXBean.getName() + CRLF); + sb.append(INDENT1 + "startTime: " + runtimeMXBean.getStartTime() + CRLF); + sb.append(INDENT1 + "uptime: " + runtimeMXBean.getUptime() + CRLF); + sb.append(INDENT1 + "isBootClassPathSupported: " + + runtimeMXBean.isBootClassPathSupported() + CRLF); + sb.append(CRLF); + + sb.append("OS information:" + CRLF); + sb.append(INDENT1 + "name: " + operatingSystemMXBean.getName() + CRLF); + sb.append(INDENT1 + "version: " + operatingSystemMXBean.getVersion() + CRLF); + sb.append(INDENT1 + "architecture: " + operatingSystemMXBean.getArch() + CRLF); + sb.append(INDENT1 + "availableProcessors: " + + operatingSystemMXBean.getAvailableProcessors() + CRLF); + sb.append(INDENT1 + "systemLoadAverage: " + + operatingSystemMXBean.getSystemLoadAverage() + CRLF); + sb.append(CRLF); + + sb.append("ThreadMXBean capabilities:" + CRLF); + sb.append(INDENT1 + "isCurrentThreadCpuTimeSupported: " + + threadMXBean.isCurrentThreadCpuTimeSupported() + CRLF); + sb.append(INDENT1 + "isThreadCpuTimeSupported: " + + threadMXBean.isThreadCpuTimeSupported() + CRLF); + sb.append(INDENT1 + "isThreadCpuTimeEnabled: " + + threadMXBean.isThreadCpuTimeEnabled() + CRLF); + sb.append(INDENT1 + "isObjectMonitorUsageSupported: " + + threadMXBean.isObjectMonitorUsageSupported() + CRLF); + sb.append(INDENT1 + "isSynchronizerUsageSupported: " + + threadMXBean.isSynchronizerUsageSupported() + CRLF); + sb.append(INDENT1 + "isThreadContentionMonitoringSupported: " + + threadMXBean.isThreadContentionMonitoringSupported() + CRLF); + sb.append(INDENT1 + "isThreadContentionMonitoringEnabled: " + + threadMXBean.isThreadContentionMonitoringEnabled() + CRLF); + sb.append(CRLF); + + sb.append("Thread counts:" + CRLF); + sb.append(INDENT1 + "daemon: " + threadMXBean.getDaemonThreadCount() + CRLF); + sb.append(INDENT1 + "total: " + threadMXBean.getThreadCount() + CRLF); + sb.append(INDENT1 + "peak: " + threadMXBean.getPeakThreadCount() + CRLF); + sb.append(INDENT1 + "totalStarted: " + + threadMXBean.getTotalStartedThreadCount() + CRLF); + sb.append(CRLF); + + sb.append("Class loading:" + CRLF); + sb.append(INDENT1 + "loaded: " + + classLoadingMXBean.getLoadedClassCount() + CRLF); + sb.append(INDENT1 + "unloaded: " + + classLoadingMXBean.getUnloadedClassCount() + CRLF); + sb.append(INDENT1 + "totalLoaded: " + + classLoadingMXBean.getTotalLoadedClassCount() + CRLF); + sb.append(INDENT1 + "isVerbose: " + + classLoadingMXBean.isVerbose() + CRLF); + sb.append(CRLF); + + sb.append("Class compilation:" + CRLF); + sb.append(INDENT1 + "name: " + compilationMXBean.getName() + CRLF); + sb.append(INDENT1 + "totalCompilationTime: " + + compilationMXBean.getTotalCompilationTime() + CRLF); + sb.append(INDENT1 + "isCompilationTimeMonitoringSupported: " + + compilationMXBean.isCompilationTimeMonitoringSupported() + CRLF); + sb.append(CRLF); + + sb.append("Path information:" + CRLF); + sb.append(INDENT1 + "bootClassPath: " + runtimeMXBean.getBootClassPath() + CRLF); + sb.append(INDENT1 + "classPath: " + runtimeMXBean.getClassPath() + CRLF); + sb.append(INDENT1 + "libraryPath: " + runtimeMXBean.getLibraryPath() + CRLF); + sb.append(CRLF); + + sb.append("Startup arguments:" + CRLF); + for (String arg: runtimeMXBean.getInputArguments()) { + sb.append(INDENT1 + arg + CRLF); + } + sb.append(CRLF); + + sb.append("System properties:" + CRLF); + Map<String,String> props = runtimeMXBean.getSystemProperties(); + for (String prop: props.keySet()) { + sb.append(INDENT1 + prop + ": " + props.get(prop) + CRLF); + } + sb.append(CRLF); + + sb.append("Logger information:" + CRLF); + for (String logger: loggingMXBean.getLoggerNames()) { + sb.append(INDENT1 + logger + + ": level=" + loggingMXBean.getLoggerLevel(logger) + + ", parent=" + loggingMXBean.getParentLoggerName(logger) + CRLF); + } + sb.append(CRLF); + + return sb.toString(); + } +} Propchange: tomcat/trunk/java/org/apache/tomcat/util/Diagnostics.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: tomcat/trunk/java/org/apache/tomcat/util/Diagnostics.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org