Path: blob/master/src/jdk.jconsole/share/classes/sun/tools/jconsole/ThreadTab.java
40948 views
/*1* Copyright (c) 2004, 2014, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.tools.jconsole;2627import java.awt.*;28import java.awt.event.*;29import java.io.*;30import java.lang.management.*;31import java.lang.reflect.*;3233import javax.swing.*;34import javax.swing.border.*;35import javax.swing.event.*;363738import java.util.*;39import java.util.concurrent.*;40import java.util.List;4142import static sun.tools.jconsole.Utilities.*;434445@SuppressWarnings("serial")46class ThreadTab extends Tab implements ActionListener, DocumentListener, ListSelectionListener {47PlotterPanel threadMeter;48TimeComboBox timeComboBox;49JTabbedPane threadListTabbedPane;50DefaultListModel<Long> listModel;51JTextField filterTF;52JLabel messageLabel;53JSplitPane threadsSplitPane;54HashMap<Long, String> nameCache = new HashMap<Long, String>();5556private ThreadOverviewPanel overviewPanel;57private boolean plotterListening = false;585960private static final String threadCountKey = "threadCount";61private static final String peakKey = "peak";6263private static final Color threadCountColor = Plotter.defaultColor;64private static final Color peakColor = Color.red;6566private static final Border thinEmptyBorder = new EmptyBorder(2, 2, 2, 2);6768/*69Hierarchy of panels and layouts for this tab:7071ThreadTab (BorderLayout)7273North: topPanel (BorderLayout)7475Center: controlPanel (FlowLayout)76timeComboBox7778Center: plotterPanel (BorderLayout)7980Center: plotter8182*/838485public static String getTabName() {86return Messages.THREADS;87}8889public ThreadTab(VMPanel vmPanel) {90super(vmPanel, getTabName());9192setLayout(new BorderLayout(0, 0));93setBorder(new EmptyBorder(4, 4, 3, 4));9495JPanel topPanel = new JPanel(new BorderLayout());96JPanel plotterPanel = new JPanel(new VariableGridLayout(0, 1, 4, 4, true, true));9798add(topPanel, BorderLayout.NORTH);99add(plotterPanel, BorderLayout.CENTER);100101JPanel controlPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 5));102topPanel.add(controlPanel, BorderLayout.CENTER);103104threadMeter = new PlotterPanel(Messages.NUMBER_OF_THREADS,105Plotter.Unit.NONE, true);106threadMeter.plotter.createSequence(threadCountKey, Messages.LIVE_THREADS, threadCountColor, true);107threadMeter.plotter.createSequence(peakKey, Messages.PEAK, peakColor, true);108setAccessibleName(threadMeter.plotter,109Messages.THREAD_TAB_THREAD_PLOTTER_ACCESSIBLE_NAME);110111plotterPanel.add(threadMeter);112113timeComboBox = new TimeComboBox(threadMeter.plotter);114controlPanel.add(new LabeledComponent(Messages.TIME_RANGE_COLON,115Resources.getMnemonicInt(Messages.TIME_RANGE_COLON),116timeComboBox));117118listModel = new DefaultListModel<Long>();119120JTextArea textArea = new JTextArea();121textArea.setBorder(thinEmptyBorder);122textArea.setEditable(false);123setAccessibleName(textArea,124Messages.THREAD_TAB_THREAD_INFO_ACCESSIBLE_NAME);125ThreadJList list = new ThreadJList(listModel, textArea);126127Dimension di = new Dimension(super.getPreferredSize());128di.width = Math.min(di.width, 200);129130JScrollPane threadlistSP = new JScrollPane(list);131threadlistSP.setPreferredSize(di);132threadlistSP.setBorder(null);133134JScrollPane textAreaSP = new JScrollPane(textArea);135textAreaSP.setBorder(null);136137threadListTabbedPane = new JTabbedPane(JTabbedPane.TOP);138threadsSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,139threadlistSP, textAreaSP);140threadsSplitPane.setOneTouchExpandable(true);141threadsSplitPane.setBorder(null);142143JPanel firstTabPanel = new JPanel(new BorderLayout());144firstTabPanel.setOpaque(false);145146JPanel firstTabToolPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 2));147firstTabToolPanel.setOpaque(false);148149filterTF = new PromptingTextField("Filter", 20);150filterTF.getDocument().addDocumentListener(this);151firstTabToolPanel.add(filterTF);152153JSeparator separator = new JSeparator(JSeparator.VERTICAL);154separator.setPreferredSize(new Dimension(separator.getPreferredSize().width,155filterTF.getPreferredSize().height));156firstTabToolPanel.add(separator);157158JButton detectDeadlockButton = new JButton(Messages.DETECT_DEADLOCK);159detectDeadlockButton.setMnemonic(Resources.getMnemonicInt(Messages.DETECT_DEADLOCK));160detectDeadlockButton.setActionCommand("detectDeadlock");161detectDeadlockButton.addActionListener(this);162detectDeadlockButton.setToolTipText(Messages.DETECT_DEADLOCK_TOOLTIP);163firstTabToolPanel.add(detectDeadlockButton);164165messageLabel = new JLabel();166firstTabToolPanel.add(messageLabel);167168firstTabPanel.add(threadsSplitPane, BorderLayout.CENTER);169firstTabPanel.add(firstTabToolPanel, BorderLayout.SOUTH);170threadListTabbedPane.addTab(Messages.THREADS, firstTabPanel);171172plotterPanel.add(threadListTabbedPane);173}174175private long oldThreads[] = new long[0];176177public SwingWorker<?, ?> newSwingWorker() {178final ProxyClient proxyClient = vmPanel.getProxyClient();179180if (!plotterListening) {181proxyClient.addWeakPropertyChangeListener(threadMeter.plotter);182plotterListening = true;183}184185return new SwingWorker<Boolean, Object>() {186private int tlCount;187private int tpCount;188private long ttCount;189private long[] threads;190private long timeStamp;191192public Boolean doInBackground() {193try {194ThreadMXBean threadMBean = proxyClient.getThreadMXBean();195196tlCount = threadMBean.getThreadCount();197tpCount = threadMBean.getPeakThreadCount();198if (overviewPanel != null) {199ttCount = threadMBean.getTotalStartedThreadCount();200} else {201ttCount = 0L;202}203204threads = threadMBean.getAllThreadIds();205for (long newThread : threads) {206if (nameCache.get(newThread) == null) {207ThreadInfo ti = threadMBean.getThreadInfo(newThread);208if (ti != null) {209String name = ti.getThreadName();210if (name != null) {211nameCache.put(newThread, name);212}213}214}215}216timeStamp = System.currentTimeMillis();217return true;218} catch (IOException e) {219return false;220} catch (UndeclaredThrowableException e) {221return false;222}223}224225protected void done() {226try {227if (!get()) {228return;229}230} catch (InterruptedException ex) {231return;232} catch (ExecutionException ex) {233if (JConsole.isDebug()) {234ex.printStackTrace();235}236return;237}238239threadMeter.plotter.addValues(timeStamp, tlCount, tpCount);240threadMeter.setValueLabel(tlCount+"");241242if (overviewPanel != null) {243overviewPanel.updateThreadsInfo(tlCount, tpCount, ttCount, timeStamp);244}245246String filter = filterTF.getText().toLowerCase(Locale.ENGLISH);247boolean doFilter = (filter.length() > 0);248249ArrayList<Long> l = new ArrayList<Long>();250for (long t : threads) {251l.add(t);252}253Iterator<Long> iterator = l.iterator();254while (iterator.hasNext()) {255long newThread = iterator.next();256String name = nameCache.get(newThread);257if (doFilter && name != null &&258name.toLowerCase(Locale.ENGLISH).indexOf(filter) < 0) {259260iterator.remove();261}262}263long[] newThreads = threads;264if (l.size() < threads.length) {265newThreads = new long[l.size()];266for (int i = 0; i < newThreads.length; i++) {267newThreads[i] = l.get(i);268}269}270271272for (long oldThread : oldThreads) {273boolean found = false;274for (long newThread : newThreads) {275if (newThread == oldThread) {276found = true;277break;278}279}280if (!found) {281listModel.removeElement(oldThread);282if (!doFilter) {283nameCache.remove(oldThread);284}285}286}287288// Threads are in reverse chronological order289for (int i = newThreads.length - 1; i >= 0; i--) {290long newThread = newThreads[i];291boolean found = false;292for (long oldThread : oldThreads) {293if (newThread == oldThread) {294found = true;295break;296}297}298if (!found) {299listModel.addElement(newThread);300}301}302oldThreads = newThreads;303}304};305}306307long lastSelected = -1;308309public void valueChanged(ListSelectionEvent ev) {310ThreadJList list = (ThreadJList)ev.getSource();311final JTextArea textArea = list.textArea;312313Long selected = list.getSelectedValue();314if (selected == null) {315if (lastSelected != -1) {316selected = lastSelected;317}318} else {319lastSelected = selected;320}321textArea.setText("");322if (selected != null) {323final long threadID = selected;324workerAdd(new Runnable() {325public void run() {326ProxyClient proxyClient = vmPanel.getProxyClient();327StringBuilder sb = new StringBuilder();328try {329ThreadMXBean threadMBean = proxyClient.getThreadMXBean();330ThreadInfo ti = null;331MonitorInfo[] monitors = null;332if (proxyClient.isLockUsageSupported() &&333threadMBean.isObjectMonitorUsageSupported()) {334// VMs that support the monitor usage monitoring335ThreadInfo[] infos = threadMBean.dumpAllThreads(true, false);336for (ThreadInfo info : infos) {337if (info.getThreadId() == threadID) {338ti = info;339monitors = info.getLockedMonitors();340break;341}342}343} else {344// VM doesn't support monitor usage monitoring345ti = threadMBean.getThreadInfo(threadID, Integer.MAX_VALUE);346}347if (ti != null) {348if (ti.getLockName() == null) {349sb.append(Resources.format(Messages.NAME_STATE,350ti.getThreadName(),351ti.getThreadState().toString()));352} else if (ti.getLockOwnerName() == null) {353sb.append(Resources.format(Messages.NAME_STATE_LOCK_NAME,354ti.getThreadName(),355ti.getThreadState().toString(),356ti.getLockName()));357} else {358sb.append(Resources.format(Messages.NAME_STATE_LOCK_NAME_LOCK_OWNER,359ti.getThreadName(),360ti.getThreadState().toString(),361ti.getLockName(),362ti.getLockOwnerName()));363}364sb.append(Resources.format(Messages.BLOCKED_COUNT_WAITED_COUNT,365ti.getBlockedCount(),366ti.getWaitedCount()));367sb.append(Messages.STACK_TRACE);368int index = 0;369for (StackTraceElement e : ti.getStackTrace()) {370sb.append(e).append('\n');371if (monitors != null) {372for (MonitorInfo mi : monitors) {373if (mi.getLockedStackDepth() == index) {374sb.append(Resources.format(Messages.MONITOR_LOCKED, mi.toString()));375}376}377}378index++;379}380}381} catch (IOException ex) {382// Ignore383} catch (UndeclaredThrowableException e) {384proxyClient.markAsDead();385}386final String text = sb.toString();387SwingUtilities.invokeLater(new Runnable() {388public void run() {389textArea.setText(text);390textArea.setCaretPosition(0);391}392});393}394});395}396}397398private void doUpdate() {399workerAdd(new Runnable() {400public void run() {401update();402}403});404}405406407private void detectDeadlock() {408workerAdd(new Runnable() {409public void run() {410try {411final Long[][] deadlockedThreads = getDeadlockedThreadIds();412413if (deadlockedThreads == null || deadlockedThreads.length == 0) {414// Display message for 30 seconds. Do it on a separate thread so415// the sleep won't hold up the worker queue.416// This will be replaced later by separate statusbar logic.417new Thread() {418public void run() {419try {420SwingUtilities.invokeAndWait(new Runnable() {421public void run() {422String msg = Messages.NO_DEADLOCK_DETECTED;423messageLabel.setText(msg);424threadListTabbedPane.revalidate();425}426});427sleep(30 * 1000);428} catch (InterruptedException ex) {429// Ignore430} catch (InvocationTargetException ex) {431// Ignore432}433SwingUtilities.invokeLater(new Runnable() {434public void run() {435messageLabel.setText("");436}437});438}439}.start();440return;441}442443SwingUtilities.invokeLater(new Runnable() {444public void run() {445// Remove old deadlock tabs446while (threadListTabbedPane.getTabCount() > 1) {447threadListTabbedPane.removeTabAt(1);448}449450if (deadlockedThreads != null) {451for (int i = 0; i < deadlockedThreads.length; i++) {452DefaultListModel<Long> listModel = new DefaultListModel<Long>();453JTextArea textArea = new JTextArea();454textArea.setBorder(thinEmptyBorder);455textArea.setEditable(false);456setAccessibleName(textArea,457Messages.THREAD_TAB_THREAD_INFO_ACCESSIBLE_NAME);458ThreadJList list = new ThreadJList(listModel, textArea);459JScrollPane threadlistSP = new JScrollPane(list);460JScrollPane textAreaSP = new JScrollPane(textArea);461threadlistSP.setBorder(null);462textAreaSP.setBorder(null);463JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,464threadlistSP, textAreaSP);465splitPane.setOneTouchExpandable(true);466splitPane.setBorder(null);467splitPane.setDividerLocation(threadsSplitPane.getDividerLocation());468String tabName;469if (deadlockedThreads.length > 1) {470tabName = Resources.format(Messages.DEADLOCK_TAB_N, i+1);471} else {472tabName = Messages.DEADLOCK_TAB;473}474threadListTabbedPane.addTab(tabName, splitPane);475476for (long t : deadlockedThreads[i]) {477listModel.addElement(t);478}479}480threadListTabbedPane.setSelectedIndex(1);481}482}483});484} catch (IOException e) {485// Ignore486} catch (UndeclaredThrowableException e) {487vmPanel.getProxyClient().markAsDead();488}489}490});491}492493494// Return deadlocked threads or null495public Long[][] getDeadlockedThreadIds() throws IOException {496ProxyClient proxyClient = vmPanel.getProxyClient();497ThreadMXBean threadMBean = proxyClient.getThreadMXBean();498499long[] ids = proxyClient.findDeadlockedThreads();500if (ids == null) {501return null;502}503ThreadInfo[] infos = threadMBean.getThreadInfo(ids, Integer.MAX_VALUE);504505List<Long[]> dcycles = new ArrayList<Long[]>();506List<Long> cycle = new ArrayList<Long>();507508// keep track of which thread is visited509// one thread can only be in one cycle510boolean[] visited = new boolean[ids.length];511512int deadlockedThread = -1; // Index into arrays513while (true) {514if (deadlockedThread < 0) {515if (cycle.size() > 0) {516// a cycle found517dcycles.add(cycle.toArray(new Long[0]));518cycle = new ArrayList<Long>();519}520// start a new cycle from a non-visited thread521for (int j = 0; j < ids.length; j++) {522if (!visited[j]) {523deadlockedThread = j;524visited[j] = true;525break;526}527}528if (deadlockedThread < 0) {529// done530break;531}532}533534cycle.add(ids[deadlockedThread]);535long nextThreadId = infos[deadlockedThread].getLockOwnerId();536for (int j = 0; j < ids.length; j++) {537ThreadInfo ti = infos[j];538if (ti.getThreadId() == nextThreadId) {539if (visited[j]) {540deadlockedThread = -1;541} else {542deadlockedThread = j;543visited[j] = true;544}545break;546}547}548}549return dcycles.toArray(new Long[0][0]);550}551552553554555556// ActionListener interface557public void actionPerformed(ActionEvent evt) {558String cmd = ((AbstractButton)evt.getSource()).getActionCommand();559560if (cmd == "detectDeadlock") {561messageLabel.setText("");562detectDeadlock();563}564}565566567568// DocumentListener interface569570public void insertUpdate(DocumentEvent e) {571doUpdate();572}573574public void removeUpdate(DocumentEvent e) {575doUpdate();576}577578public void changedUpdate(DocumentEvent e) {579doUpdate();580}581582583584private class ThreadJList extends JList<Long> {585private JTextArea textArea;586587ThreadJList(DefaultListModel<Long> listModel, JTextArea textArea) {588super(listModel);589590this.textArea = textArea;591592setBorder(thinEmptyBorder);593594setSelectionMode(ListSelectionModel.SINGLE_SELECTION);595textArea.setText(Messages.THREAD_TAB_INITIAL_STACK_TRACE_MESSAGE);596addListSelectionListener(ThreadTab.this);597setCellRenderer(new DefaultListCellRenderer() {598public Component getListCellRendererComponent(JList<?> list, Object value, int index,599boolean isSelected, boolean cellHasFocus) {600super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);601602if (value != null) {603String name = nameCache.get(value);604if (name == null) {605name = value.toString();606}607setText(name);608}609return this;610}611});612}613614public Dimension getPreferredSize() {615Dimension d = super.getPreferredSize();616d.width = Math.max(d.width, 100);617return d;618}619}620621private class PromptingTextField extends JTextField implements FocusListener {622private String prompt;623boolean promptRemoved = false;624Color fg;625626public PromptingTextField(String prompt, int columns) {627super(prompt, columns);628629this.prompt = prompt;630updateForeground();631addFocusListener(this);632setAccessibleName(this, prompt);633}634635@Override636public void revalidate() {637super.revalidate();638updateForeground();639}640641private void updateForeground() {642this.fg = UIManager.getColor("TextField.foreground");643if (promptRemoved) {644setForeground(fg);645} else {646setForeground(Color.gray);647}648}649650public String getText() {651if (!promptRemoved) {652return "";653} else {654return super.getText();655}656}657658public void focusGained(FocusEvent e) {659if (!promptRemoved) {660setText("");661setForeground(fg);662promptRemoved = true;663}664}665666public void focusLost(FocusEvent e) {667if (promptRemoved && getText().isEmpty()) {668setText(prompt);669setForeground(Color.gray);670promptRemoved = false;671}672}673674}675676OverviewPanel[] getOverviewPanels() {677if (overviewPanel == null) {678overviewPanel = new ThreadOverviewPanel();679}680return new OverviewPanel[] { overviewPanel };681}682683684private static class ThreadOverviewPanel extends OverviewPanel {685ThreadOverviewPanel() {686super(Messages.THREADS, threadCountKey, Messages.LIVE_THREADS, null);687}688689private void updateThreadsInfo(long tlCount, long tpCount, long ttCount, long timeStamp) {690getPlotter().addValues(timeStamp, tlCount);691getInfoLabel().setText(Resources.format(Messages.THREAD_TAB_INFO_LABEL_FORMAT, tlCount, tpCount, ttCount));692}693}694}695696697