/* * Copyright (c) 2010, 2013, 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. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * 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 com.sun.tools.visualvm.jmx.impl; import com.sun.management.HotSpotDiagnosticMXBean; import com.sun.management.VMOption; import com.sun.tools.visualvm.application.jvm.HeapHistogram; import com.sun.tools.visualvm.tools.jmx.JmxModel; import com.sun.tools.visualvm.tools.jmx.JmxModel.ConnectionState; import com.sun.tools.visualvm.tools.jmx.JvmMXBeans; import com.sun.tools.visualvm.tools.jmx.JvmMXBeansFactory; import java.io.IOException; import java.lang.management.LockInfo; import java.lang.management.ManagementFactory; import java.lang.management.MonitorInfo; 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.Properties; import java.util.logging.Level; import java.util.logging.Logger; import javax.management.InstanceNotFoundException; import javax.management.MBeanException; import javax.management.MBeanInfo; import javax.management.MBeanOperationInfo; import javax.management.MBeanServerConnection; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import javax.management.ReflectionException; import org.openide.ErrorManager; import org.openide.util.Exceptions; /** * * @author Tomas Hurka */ public class JmxSupport { private final static Logger LOGGER = Logger.getLogger(JmxSupport.class.getName()); private static final String HOTSPOT_DIAGNOSTIC_MXBEAN_NAME = "com.sun.management:type=HotSpotDiagnostic"; // NOI18N private static final String DIAGNOSTIC_COMMAND_MXBEAN_NAME = "com.sun.management:type=DiagnosticCommand"; // NOI18N private static final String ALL_OBJECTS_OPTION = "-all"; // NOI18N private static final String HISTOGRAM_COMMAND = "gcClassHistogram"; // NOI18N private JvmMXBeans mxbeans; private JmxModel jmxModel; // HotspotDiagnostic private boolean hotspotDiagnosticInitialized; private final Object hotspotDiagnosticLock = new Object(); private HotSpotDiagnosticMXBean hotspotDiagnosticMXBean; private final Object readOnlyConnectionLock = new Object(); private Boolean readOnlyConnection; private Boolean hasDumpAllThreads; private final Object hasDumpAllThreadsLock = new Object(); JmxSupport(JmxModel jmx) { jmxModel = jmx; } private RuntimeMXBean getRuntime() { JvmMXBeans jmx = getJvmMXBeans(); if (jmx != null) { return jmx.getRuntimeMXBean(); } return null; } private synchronized JvmMXBeans getJvmMXBeans() { if (mxbeans == null) { if (jmxModel.getConnectionState() == ConnectionState.CONNECTED) { mxbeans = JvmMXBeansFactory.getJvmMXBeans(jmxModel); } } return mxbeans; } Properties getSystemProperties() { try { RuntimeMXBean runtime = getRuntime(); if (runtime != null) { Properties prop = new Properties(); prop.putAll(runtime.getSystemProperties()); return prop; } return null; } catch (Exception e) { LOGGER.throwing(JmxSupport.class.getName(), "getSystemProperties", e); // NOI18N return null; } } synchronized boolean isReadOnlyConnection() { synchronized (readOnlyConnectionLock) { if (readOnlyConnection == null) { readOnlyConnection = Boolean.FALSE; ThreadMXBean threads = getThreadBean(); if (threads != null) { try { threads.getThreadInfo(1); } catch (SecurityException ex) { readOnlyConnection = Boolean.TRUE; } } } return readOnlyConnection.booleanValue(); } } ThreadMXBean getThreadBean() { JvmMXBeans jmx = getJvmMXBeans(); if (jmx != null) { return jmx.getThreadMXBean(); } return null; } HotSpotDiagnosticMXBean getHotSpotDiagnostic() { synchronized (hotspotDiagnosticLock) { if (hotspotDiagnosticInitialized) { return hotspotDiagnosticMXBean; } JvmMXBeans jmx = getJvmMXBeans(); if (jmx != null) { try { hotspotDiagnosticMXBean = jmx.getMXBean( ObjectName.getInstance(HOTSPOT_DIAGNOSTIC_MXBEAN_NAME), HotSpotDiagnosticMXBean.class); } catch (MalformedObjectNameException e) { ErrorManager.getDefault().log(ErrorManager.WARNING, "Couldn't find HotSpotDiagnosticMXBean: " + // NOI18N e.getLocalizedMessage()); } catch (IllegalArgumentException ex) { ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex); } } hotspotDiagnosticInitialized = true; return hotspotDiagnosticMXBean; } } String takeThreadDump(long[] threadIds) { ThreadMXBean threadMXBean = getThreadBean(); if (threadMXBean == null) { return null; } ThreadInfo[] threads; StringBuilder sb = new StringBuilder(4096); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // NOI18N if (hasDumpAllThreads()) { threads = threadMXBean.getThreadInfo(threadIds, true, true); } else { threads = threadMXBean.getThreadInfo(threadIds, Integer.MAX_VALUE); } sb.append(df.format(new Date()) + "\n"); // NOI18N printThreads(sb, threadMXBean, threads); return sb.toString(); } String takeThreadDump() { try { ThreadMXBean threadMXBean = getThreadBean(); if (threadMXBean == null) { return null; } ThreadInfo[] threads; Properties prop = getSystemProperties(); StringBuilder sb = new StringBuilder(4096); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // NOI18N sb.append(df.format(new Date()) + "\n"); sb.append("Full thread dump " + prop.getProperty("java.vm.name") + // NOI18N " (" + prop.getProperty("java.vm.version") + " " + // NOI18N prop.getProperty("java.vm.info") + "):\n"); // NOI18N if (hasDumpAllThreads()) { threads = threadMXBean.dumpAllThreads(true, true); } else { long[] threadIds = threadMXBean.getAllThreadIds(); threads = threadMXBean.getThreadInfo(threadIds, Integer.MAX_VALUE); } printThreads(sb, threadMXBean, threads); return sb.toString(); } catch (Exception e) { LOGGER.log(Level.INFO,"takeThreadDump", e); // NOI18N return null; } } private void printThreads(final StringBuilder sb, final ThreadMXBean threadMXBean, ThreadInfo[] threads) { boolean jdk16 = hasDumpAllThreads(); for (ThreadInfo thread : threads) { if (thread != null) { if (jdk16) { print16Thread(sb, threadMXBean, thread); } else { print15Thread(sb, thread); } } } } private void print16Thread(final StringBuilder sb, final ThreadMXBean threadMXBean, final ThreadInfo thread) { MonitorInfo[] monitors = null; if (threadMXBean.isObjectMonitorUsageSupported()) { monitors = thread.getLockedMonitors(); } sb.append("\n\"" + thread.getThreadName() + // NOI18N "\" - Thread t@" + thread.getThreadId() + "\n"); // NOI18N sb.append(" java.lang.Thread.State: " + thread.getThreadState()); // NOI18N sb.append("\n"); // NOI18N int index = 0; for (StackTraceElement st : thread.getStackTrace()) { LockInfo lock = thread.getLockInfo(); String lockOwner = thread.getLockOwnerName(); sb.append("\tat " + st.toString() + "\n"); // NOI18N if (index == 0) { if ("java.lang.Object".equals(st.getClassName()) && // NOI18N "wait".equals(st.getMethodName())) { // NOI18N if (lock != null) { sb.append("\t- waiting on "); // NOI18N printLock(sb,lock); sb.append("\n"); // NOI18N } } else if (lock != null) { if (lockOwner == null) { sb.append("\t- parking to wait for "); // NOI18N printLock(sb,lock); sb.append("\n"); // NOI18N } else { sb.append("\t- waiting to lock "); // NOI18N printLock(sb,lock); sb.append(" owned by \""+lockOwner+"\" t@"+thread.getLockOwnerId()+"\n"); // NOI18N } } } printMonitors(sb, monitors, index); index++; } StringBuilder jnisb = new StringBuilder(); printMonitors(jnisb, monitors, -1); if (jnisb.length() > 0) { sb.append(" JNI locked monitors:\n"); sb.append(jnisb); } if (threadMXBean.isSynchronizerUsageSupported()) { sb.append("\n Locked ownable synchronizers:"); // NOI18N LockInfo[] synchronizers = thread.getLockedSynchronizers(); if (synchronizers == null || synchronizers.length == 0) { sb.append("\n\t- None\n"); // NOI18N } else { for (LockInfo li : synchronizers) { sb.append("\n\t- locked "); // NOI18N printLock(sb,li); sb.append("\n"); // NOI18N } } } } private void printMonitors(final StringBuilder sb, final MonitorInfo[] monitors, final int index) { if (monitors != null) { for (MonitorInfo mi : monitors) { if (mi.getLockedStackDepth() == index) { sb.append("\t- locked "); // NOI18N printLock(sb,mi); sb.append("\n"); // NOI18N } } } } private void print15Thread(final StringBuilder sb, final ThreadInfo thread) { sb.append("\n\"" + thread.getThreadName() + // NOI18N "\" - Thread t@" + thread.getThreadId() + "\n"); // NOI18N sb.append(" java.lang.Thread.State: " + thread.getThreadState()); // NOI18N if (thread.getLockName() != null) { sb.append(" on " + thread.getLockName()); // NOI18N if (thread.getLockOwnerName() != null) { sb.append(" owned by: " + thread.getLockOwnerName()); // NOI18N } } sb.append("\n"); // NOI18N for (StackTraceElement st : thread.getStackTrace()) { sb.append(" at " + st.toString() + "\n"); // NOI18N } } private void printLock(StringBuilder sb,LockInfo lock) { String id = Integer.toHexString(lock.getIdentityHashCode()); String className = lock.getClassName(); sb.append("<"+id+"> (a "+className+")"); // NOI18N } boolean takeHeapDump(String fileName) { HotSpotDiagnosticMXBean hsDiagnostic = getHotSpotDiagnostic(); if (hsDiagnostic != null) { try { hsDiagnostic.dumpHeap(fileName,true); } catch (IOException ex) { LOGGER.log(Level.INFO,"takeHeapDump", ex); // NOI18N return false; } return true; } return false; } String getFlagValue(String name) { HotSpotDiagnosticMXBean hsDiagnostic = getHotSpotDiagnostic(); if (hsDiagnostic != null) { VMOption option = hsDiagnostic.getVMOption(name); if (option != null) { return option.getValue(); } } return null; } HeapHistogram takeHeapHistogram() { if (jmxModel.getConnectionState() == ConnectionState.CONNECTED) { MBeanServerConnection conn = jmxModel.getMBeanServerConnection(); try { ObjectName diagCommName = new ObjectName(DIAGNOSTIC_COMMAND_MXBEAN_NAME); if (conn.isRegistered(diagCommName)) { Object histo = conn.invoke(diagCommName, HISTOGRAM_COMMAND, new Object[] {new String[] {ALL_OBJECTS_OPTION}}, new String[] {String[].class.getName()} ); if (histo instanceof String) { return new HeapHistogramImpl((String)histo); } } } catch (MalformedObjectNameException ex) { Exceptions.printStackTrace(ex); } catch (IOException ex) { LOGGER.log(Level.INFO,"takeHeapHistogram", ex); // NOI18N } catch (InstanceNotFoundException ex) { Exceptions.printStackTrace(ex); } catch (MBeanException ex) { Exceptions.printStackTrace(ex); } catch (ReflectionException ex) { Exceptions.printStackTrace(ex); } } return null; } void setFlagValue(String name, String value) { HotSpotDiagnosticMXBean hsDiagnostic = getHotSpotDiagnostic(); if (hsDiagnostic != null) { hsDiagnostic.setVMOption(name,value); } } private boolean hasDumpAllThreads() { synchronized (hasDumpAllThreadsLock) { if (hasDumpAllThreads == null) { hasDumpAllThreads = Boolean.FALSE; try { ObjectName threadObjName = new ObjectName(ManagementFactory.THREAD_MXBEAN_NAME); MBeanInfo threadInfo = jmxModel.getMBeanServerConnection().getMBeanInfo(threadObjName); if (threadInfo != null) { for (MBeanOperationInfo op : threadInfo.getOperations()) { if ("dumpAllThreads".equals(op.getName())) { hasDumpAllThreads = Boolean.TRUE; } } } } catch (Exception ex) { LOGGER.log(Level.INFO,"hasDumpAllThreads", ex); // NOI18N } } return hasDumpAllThreads.booleanValue(); } } }