/* * Copyright (c) 2007, 2011, 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.application.views.threads; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.logging.Logger; import javax.swing.SwingUtilities; import org.netbeans.lib.profiler.client.MonitoredData; import org.netbeans.lib.profiler.global.CommonConstants; import org.netbeans.lib.profiler.wireprotocol.MonitoredNumbersResponse; import org.openide.util.RequestProcessor; /** * * @author Tomas Hurka */ class ThreadMXBeanDataManager extends VisualVMThreadsDataManager { private static final long[] dummyLong = new long[0]; private static final Logger LOGGER = Logger.getLogger(ThreadMXBeanDataManager.class.getName()); static final String DEADLOCK_PROP = "Deadlock"; // NOI18N private ThreadMXBean threadBean; private Set<Long> threadIdSet = new HashSet(); private boolean refreshRunning; private DeadlockDetector deadlockDetector; private PropertyChangeSupport changeSupport; private long[] deadlockThreadIds; ThreadMXBeanDataManager(ThreadMXBean tb) { threadBean = tb; deadlockDetector = new DeadlockDetector(tb); changeSupport = new PropertyChangeSupport(this); } // Non-blocking call for general usage void refreshThreadsAsync() { synchronized (this) { if (refreshRunning) return; refreshRunning = true; } RequestProcessor.getDefault().post(new Runnable() { public void run() { long[] oldDeadlockThreadIds = deadlockThreadIds; refreshThreadsSync(); deadlockThreadIds = deadlockDetector.detectDeadlock(); if (deadlockThreadIds != null && !Arrays.equals(oldDeadlockThreadIds,deadlockThreadIds)) { changeSupport.firePropertyChange(DEADLOCK_PROP,oldDeadlockThreadIds,deadlockThreadIds); } synchronized (ThreadMXBeanDataManager.this) { refreshRunning = false; } } }); } // Blocking call used to save application snapshot for not opened application void refreshThreadsSync() { try { ThreadMonitoredDataResponse resp = new ThreadMonitoredDataResponse(); resp.fillInThreadData(); final MonitoredData monitoredData = MonitoredData.getMonitoredData(resp); SwingUtilities.invokeLater(new Runnable() { public void run() { // must run in AWT processData(monitoredData); } }); } catch (Exception ex) { LOGGER.throwing(ThreadMXBeanDataManager.class.getName(), "refreshThreads", ex); // NOI18N } } int getDaemonThreadCount() { return threadBean.getDaemonThreadCount(); } int getThreadCount() { return threadBean.getThreadCount(); } void addPropertyChangeListener(PropertyChangeListener l) { changeSupport.addPropertyChangeListener(l); } void removePropertyChangeListener(PropertyChangeListener l) { changeSupport.removePropertyChangeListener(l); } class ThreadMonitoredDataResponse extends MonitoredNumbersResponse { ThreadMonitoredDataResponse() { super(dummyLong, CommonConstants.SERVER_RUNNING, CommonConstants.SERVER_PROGRESS_INDETERMINATE); setGCstartFinishData(dummyLong, dummyLong); } private void fillInThreadData() { long[] currentThreadIds = threadBean.getAllThreadIds(); ThreadInfo[] threadInfos = threadBean.getThreadInfo(currentThreadIds, 1); Set<Long> currentIdSet = new HashSet(currentThreadIds.length * 4 / 3); int nThreads = 0; long timeStamps[] = {System.currentTimeMillis()}; int maxThreads = currentThreadIds.length + threadIdSet.size(); int tids[] = new int[maxThreads]; byte states[] = new byte[maxThreads]; int nNewThreads = 0; int newThreadsId[] = new int[currentThreadIds.length]; String[] newThreadsNames = new String[currentThreadIds.length]; String[] newThreadsClasses = new String[currentThreadIds.length]; for (int i = 0; i < currentThreadIds.length; i++) { ThreadInfo tinfo = threadInfos[i]; long threadId = currentThreadIds[i]; Long threadIdLong; if (tinfo == null) { continue; } threadIdLong = Long.valueOf(threadId); currentIdSet.add(threadIdLong); tids[nThreads] = (int) threadId; states[nThreads] = getState(tinfo); nThreads++; if (!threadIdSet.remove(threadIdLong)) { // New Thread newThreadsId[nNewThreads] = (int) threadId; newThreadsNames[nNewThreads] = tinfo.getThreadName(); newThreadsClasses[nNewThreads] = ""; nNewThreads++; } } // set remaining threads as terminated for (Iterator it = threadIdSet.iterator(); it.hasNext();) { Long elem = (Long) it.next(); tids[nThreads] = elem.intValue(); states[nThreads] = CommonConstants.THREAD_STATUS_ZOMBIE; nThreads++; } threadIdSet = currentIdSet; setDataOnNewThreads(nNewThreads, newThreadsId, newThreadsNames, newThreadsClasses); setDataOnThreads(nThreads, timeStamps.length, tids, timeStamps, states); } byte getState(ThreadInfo threadInfo) { Thread.State state = threadInfo.getThreadState(); switch (state) { case BLOCKED: return CommonConstants.THREAD_STATUS_MONITOR; case RUNNABLE: return CommonConstants.THREAD_STATUS_RUNNING; case TIMED_WAITING: case WAITING: StackTraceElement[] stack = threadInfo.getStackTrace(); if (stack.length>0) { StackTraceElement el = stack[0]; if (isSleeping(el)) return CommonConstants.THREAD_STATUS_SLEEPING; if (isParked(el)) return CommonConstants.THREAD_STATUS_PARK; } return CommonConstants.THREAD_STATUS_WAIT; case TERMINATED: case NEW: return CommonConstants.THREAD_STATUS_ZOMBIE; } return CommonConstants.THREAD_STATUS_UNKNOWN; } boolean isSleeping(StackTraceElement element) { return Thread.class.getName().equals(element.getClassName()) && "sleep".equals(element.getMethodName()); // NOI18N } boolean isParked(StackTraceElement element) { return "sun.misc.Unsafe".equals(element.getClassName()) && // NOI18N "park".equals(element.getMethodName()); // NOI18N } } }