History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/tools/jconsole/ThreadTab.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/tools/jconsole/ThreadTab.java+702−0
@@ -0,0 +1,702 @@
1+/*
2+ * Copyright (c) 2004, 2007, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package sun.tools.jconsole;
27+
28+import java.awt.*;
29+import java.awt.event.*;
30+import java.io.*;
31+import java.lang.management.*;
32+import java.lang.reflect.*;
33+
34+import javax.swing.*;
35+import javax.swing.border.*;
36+import javax.swing.event.*;
37+
38+import java.util.*;
39+import java.util.concurrent.*;
40+import java.util.List;
41+
42+import sun.awt.*;
43+
44+import static sun.tools.jconsole.OverviewPanel.*;
45+import static sun.tools.jconsole.Resources.*;
46+import static sun.tools.jconsole.Utilities.*;
47+
48+
49+@SuppressWarnings("serial")
50+class ThreadTab extends Tab implements ActionListener, DocumentListener, ListSelectionListener {
51+ PlotterPanel threadMeter;
52+ TimeComboBox timeComboBox;
53+ JTabbedPane threadListTabbedPane;
54+ DefaultListModel listModel;
55+ JTextField filterTF;
56+ JLabel messageLabel;
57+ JSplitPane threadsSplitPane;
58+ HashMap<Long, String> nameCache = new HashMap<Long, String>();
59+
60+ private ThreadOverviewPanel overviewPanel;
61+ private boolean plotterListening = false;
62+
63+
64+ private static final String threadCountKey = "threadCount";
65+ private static final String peakKey = "peak";
66+
67+ private static final String threadCountName = Resources.getText("Live Threads");
68+ private static final String peakName = Resources.getText("Peak");
69+
70+ private static final Color threadCountColor = Plotter.defaultColor;
71+ private static final Color peakColor = Color.red;
72+
73+ private static final Border thinEmptyBorder = new EmptyBorder(2, 2, 2, 2);
74+
75+ private static final String infoLabelFormat = "ThreadTab.infoLabelFormat";
76+
77+
78+ /*
79+ Hierarchy of panels and layouts for this tab:
80+
81+ ThreadTab (BorderLayout)
82+
83+ North: topPanel (BorderLayout)
84+
85+ Center: controlPanel (FlowLayout)
86+ timeComboBox
87+
88+ Center: plotterPanel (BorderLayout)
89+
90+ Center: plotter
91+
92+ */
93+
94+
95+ public static String getTabName() {
96+ return Resources.getText("Threads");
97+ }
98+
99+ public ThreadTab(VMPanel vmPanel) {
100+ super(vmPanel, getTabName());
101+
102+ setLayout(new BorderLayout(0, 0));
103+ setBorder(new EmptyBorder(4, 4, 3, 4));
104+
105+ JPanel topPanel = new JPanel(new BorderLayout());
106+ JPanel plotterPanel = new JPanel(new VariableGridLayout(0, 1, 4, 4, true, true));
107+
108+ add(topPanel, BorderLayout.NORTH);
109+ add(plotterPanel, BorderLayout.CENTER);
110+
111+ JPanel controlPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 5));
112+ topPanel.add(controlPanel, BorderLayout.CENTER);
113+
114+ threadMeter = new PlotterPanel(Resources.getText("Number of Threads"),
115+ Plotter.Unit.NONE, true);
116+ threadMeter.plotter.createSequence(threadCountKey, threadCountName, threadCountColor, true);
117+ threadMeter.plotter.createSequence(peakKey, peakName, peakColor, true);
118+ setAccessibleName(threadMeter.plotter,
119+ getText("ThreadTab.threadPlotter.accessibleName"));
120+
121+ plotterPanel.add(threadMeter);
122+
123+ timeComboBox = new TimeComboBox(threadMeter.plotter);
124+ controlPanel.add(new LabeledComponent(Resources.getText("Time Range:"),
125+ getMnemonicInt("Time Range:"),
126+ timeComboBox));
127+
128+ listModel = new DefaultListModel();
129+
130+ JTextArea textArea = new JTextArea();
131+ textArea.setBorder(thinEmptyBorder);
132+ textArea.setEditable(false);
133+ setAccessibleName(textArea,
134+ getText("ThreadTab.threadInfo.accessibleName"));
135+ JList list = new ThreadJList(listModel, textArea);
136+
137+ Dimension di = new Dimension(super.getPreferredSize());
138+ di.width = Math.min(di.width, 200);
139+
140+ JScrollPane threadlistSP = new JScrollPane(list);
141+ threadlistSP.setPreferredSize(di);
142+ threadlistSP.setBorder(null);
143+
144+ JScrollPane textAreaSP = new JScrollPane(textArea);
145+ textAreaSP.setBorder(null);
146+
147+ threadListTabbedPane = new JTabbedPane(JTabbedPane.TOP);
148+ threadsSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
149+ threadlistSP, textAreaSP);
150+ threadsSplitPane.setOneTouchExpandable(true);
151+ threadsSplitPane.setBorder(null);
152+
153+ JPanel firstTabPanel = new JPanel(new BorderLayout());
154+ firstTabPanel.setOpaque(false);
155+
156+ JPanel firstTabToolPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 2));
157+ firstTabToolPanel.setOpaque(false);
158+
159+ filterTF = new PromptingTextField("Filter", 20);
160+ filterTF.getDocument().addDocumentListener(this);
161+ firstTabToolPanel.add(filterTF);
162+
163+ JSeparator separator = new JSeparator(JSeparator.VERTICAL);
164+ separator.setPreferredSize(new Dimension(separator.getPreferredSize().width,
165+ filterTF.getPreferredSize().height));
166+ firstTabToolPanel.add(separator);
167+
168+ JButton detectDeadlockButton = new JButton(Resources.getText("Detect Deadlock"));
169+ detectDeadlockButton.setMnemonic(getMnemonicInt("Detect Deadlock"));
170+ detectDeadlockButton.setActionCommand("detectDeadlock");
171+ detectDeadlockButton.addActionListener(this);
172+ detectDeadlockButton.setToolTipText(getText("Detect Deadlock.toolTip"));
173+ firstTabToolPanel.add(detectDeadlockButton);
174+
175+ messageLabel = new JLabel();
176+ firstTabToolPanel.add(messageLabel);
177+
178+ firstTabPanel.add(threadsSplitPane, BorderLayout.CENTER);
179+ firstTabPanel.add(firstTabToolPanel, BorderLayout.SOUTH);
180+ threadListTabbedPane.addTab(Resources.getText("Threads"), firstTabPanel);
181+
182+ plotterPanel.add(threadListTabbedPane);
183+ }
184+
185+ private long oldThreads[] = new long[0];
186+
187+ public SwingWorker<?, ?> newSwingWorker() {
188+ final ProxyClient proxyClient = vmPanel.getProxyClient();
189+
190+ if (!plotterListening) {
191+ proxyClient.addWeakPropertyChangeListener(threadMeter.plotter);
192+ plotterListening = true;
193+ }
194+
195+ return new SwingWorker<Boolean, Object>() {
196+ private int tlCount;
197+ private int tpCount;
198+ private long ttCount;
199+ private long[] threads;
200+ private long timeStamp;
201+
202+ public Boolean doInBackground() {
203+ try {
204+ ThreadMXBean threadMBean = proxyClient.getThreadMXBean();
205+
206+ tlCount = threadMBean.getThreadCount();
207+ tpCount = threadMBean.getPeakThreadCount();
208+ if (overviewPanel != null) {
209+ ttCount = threadMBean.getTotalStartedThreadCount();
210+ } else {
211+ ttCount = 0L;
212+ }
213+
214+ threads = threadMBean.getAllThreadIds();
215+ for (long newThread : threads) {
216+ if (nameCache.get(newThread) == null) {
217+ ThreadInfo ti = threadMBean.getThreadInfo(newThread);
218+ if (ti != null) {
219+ String name = ti.getThreadName();
220+ if (name != null) {
221+ nameCache.put(newThread, name);
222+ }
223+ }
224+ }
225+ }
226+ timeStamp = System.currentTimeMillis();
227+ return true;
228+ } catch (IOException e) {
229+ return false;
230+ } catch (UndeclaredThrowableException e) {
231+ return false;
232+ }
233+ }
234+
235+ protected void done() {
236+ try {
237+ if (!get()) {
238+ return;
239+ }
240+ } catch (InterruptedException ex) {
241+ return;
242+ } catch (ExecutionException ex) {
243+ if (JConsole.isDebug()) {
244+ ex.printStackTrace();
245+ }
246+ return;
247+ }
248+
249+ threadMeter.plotter.addValues(timeStamp, tlCount, tpCount);
250+ threadMeter.setValueLabel(tlCount+"");
251+
252+ if (overviewPanel != null) {
253+ overviewPanel.updateThreadsInfo(tlCount, tpCount, ttCount, timeStamp);
254+ }
255+
256+ String filter = filterTF.getText().toLowerCase(Locale.ENGLISH);
257+ boolean doFilter = (filter.length() > 0);
258+
259+ ArrayList<Long> l = new ArrayList<Long>();
260+ for (long t : threads) {
261+ l.add(t);
262+ }
263+ Iterator<Long> iterator = l.iterator();
264+ while (iterator.hasNext()) {
265+ long newThread = iterator.next();
266+ String name = nameCache.get(newThread);
267+ if (doFilter && name != null &&
268+ name.toLowerCase(Locale.ENGLISH).indexOf(filter) < 0) {
269+
270+ iterator.remove();
271+ }
272+ }
273+ long[] newThreads = threads;
274+ if (l.size() < threads.length) {
275+ newThreads = new long[l.size()];
276+ for (int i = 0; i < newThreads.length; i++) {
277+ newThreads[i] = l.get(i);
278+ }
279+ }
280+
281+
282+ for (long oldThread : oldThreads) {
283+ boolean found = false;
284+ for (long newThread : newThreads) {
285+ if (newThread == oldThread) {
286+ found = true;
287+ break;
288+ }
289+ }
290+ if (!found) {
291+ listModel.removeElement(oldThread);
292+ if (!doFilter) {
293+ nameCache.remove(oldThread);
294+ }
295+ }
296+ }
297+
298+ // Threads are in reverse chronological order
299+ for (int i = newThreads.length - 1; i >= 0; i--) {
300+ long newThread = newThreads[i];
301+ boolean found = false;
302+ for (long oldThread : oldThreads) {
303+ if (newThread == oldThread) {
304+ found = true;
305+ break;
306+ }
307+ }
308+ if (!found) {
309+ listModel.addElement(newThread);
310+ }
311+ }
312+ oldThreads = newThreads;
313+ }
314+ };
315+ }
316+
317+ long lastSelected = -1;
318+
319+ public void valueChanged(ListSelectionEvent ev) {
320+ ThreadJList list = (ThreadJList)ev.getSource();
321+ final JTextArea textArea = list.textArea;
322+
323+ Long selected = (Long)list.getSelectedValue();
324+ if (selected == null) {
325+ if (lastSelected != -1) {
326+ selected = lastSelected;
327+ }
328+ } else {
329+ lastSelected = selected;
330+ }
331+ textArea.setText("");
332+ if (selected != null) {
333+ final long threadID = selected;
334+ workerAdd(new Runnable() {
335+ public void run() {
336+ ProxyClient proxyClient = vmPanel.getProxyClient();
337+ StringBuilder sb = new StringBuilder();
338+ try {
339+ ThreadMXBean threadMBean = proxyClient.getThreadMXBean();
340+ ThreadInfo ti = null;
341+ MonitorInfo[] monitors = null;
342+ if (proxyClient.isLockUsageSupported() &&
343+ threadMBean.isObjectMonitorUsageSupported()) {
344+ // VMs that support the monitor usage monitoring
345+ ThreadInfo[] infos = threadMBean.dumpAllThreads(true, false);
346+ for (ThreadInfo info : infos) {
347+ if (info.getThreadId() == threadID) {
348+ ti = info;
349+ monitors = info.getLockedMonitors();
350+ break;
351+ }
352+ }
353+ } else {
354+ // VM doesn't support monitor usage monitoring
355+ ti = threadMBean.getThreadInfo(threadID, Integer.MAX_VALUE);
356+ }
357+ if (ti != null) {
358+ if (ti.getLockName() == null) {
359+ sb.append(Resources.getText("Name State",
360+ ti.getThreadName(),
361+ ti.getThreadState().toString()));
362+ } else if (ti.getLockOwnerName() == null) {
363+ sb.append(Resources.getText("Name State LockName",
364+ ti.getThreadName(),
365+ ti.getThreadState().toString(),
366+ ti.getLockName()));
367+ } else {
368+ sb.append(Resources.getText("Name State LockName LockOwner",
369+ ti.getThreadName(),
370+ ti.getThreadState().toString(),
371+ ti.getLockName(),
372+ ti.getLockOwnerName()));
373+ }
374+ sb.append(Resources.getText("BlockedCount WaitedCount",
375+ ti.getBlockedCount(),
376+ ti.getWaitedCount()));
377+ sb.append(Resources.getText("Stack trace"));
378+ int index = 0;
379+ for (StackTraceElement e : ti.getStackTrace()) {
380+ sb.append(e.toString()+"\n");
381+ if (monitors != null) {
382+ for (MonitorInfo mi : monitors) {
383+ if (mi.getLockedStackDepth() == index) {
384+ sb.append(Resources.getText("Monitor locked", mi.toString()));
385+ }
386+ }
387+ }
388+ index++;
389+ }
390+ }
391+ } catch (IOException ex) {
392+ // Ignore
393+ } catch (UndeclaredThrowableException e) {
394+ proxyClient.markAsDead();
395+ }
396+ final String text = sb.toString();
397+ SwingUtilities.invokeLater(new Runnable() {
398+ public void run() {
399+ textArea.setText(text);
400+ textArea.setCaretPosition(0);
401+ }
402+ });
403+ }
404+ });
405+ }
406+ }
407+
408+ private void doUpdate() {
409+ workerAdd(new Runnable() {
410+ public void run() {
411+ update();
412+ }
413+ });
414+ }
415+
416+
417+ private void detectDeadlock() {
418+ workerAdd(new Runnable() {
419+ public void run() {
420+ try {
421+ final Long[][] deadlockedThreads = getDeadlockedThreadIds();
422+
423+ if (deadlockedThreads == null || deadlockedThreads.length == 0) {
424+ // Display message for 30 seconds. Do it on a separate thread so
425+ // the sleep won't hold up the worker queue.
426+ // This will be replaced later by separate statusbar logic.
427+ new Thread() {
428+ public void run() {
429+ try {
430+ SwingUtilities.invokeAndWait(new Runnable() {
431+ public void run() {
432+ String msg = Resources.getText("No deadlock detected");
433+ messageLabel.setText(msg);
434+ threadListTabbedPane.revalidate();
435+ }
436+ });
437+ sleep(30 * 1000);
438+ } catch (InterruptedException ex) {
439+ // Ignore
440+ } catch (InvocationTargetException ex) {
441+ // Ignore
442+ }
443+ SwingUtilities.invokeLater(new Runnable() {
444+ public void run() {
445+ messageLabel.setText("");
446+ }
447+ });
448+ }
449+ }.start();
450+ return;
451+ }
452+
453+ SwingUtilities.invokeLater(new Runnable() {
454+ public void run() {
455+ // Remove old deadlock tabs
456+ while (threadListTabbedPane.getTabCount() > 1) {
457+ threadListTabbedPane.removeTabAt(1);
458+ }
459+
460+ if (deadlockedThreads != null) {
461+ for (int i = 0; i < deadlockedThreads.length; i++) {
462+ DefaultListModel listModel = new DefaultListModel();
463+ JTextArea textArea = new JTextArea();
464+ textArea.setBorder(thinEmptyBorder);
465+ textArea.setEditable(false);
466+ setAccessibleName(textArea,
467+ getText("ThreadTab.threadInfo.accessibleName"));
468+ JList list = new ThreadJList(listModel, textArea);
469+ JScrollPane threadlistSP = new JScrollPane(list);
470+ JScrollPane textAreaSP = new JScrollPane(textArea);
471+ threadlistSP.setBorder(null);
472+ textAreaSP.setBorder(null);
473+ JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
474+ threadlistSP, textAreaSP);
475+ splitPane.setOneTouchExpandable(true);
476+ splitPane.setBorder(null);
477+ splitPane.setDividerLocation(threadsSplitPane.getDividerLocation());
478+ String tabName;
479+ if (deadlockedThreads.length > 1) {
480+ tabName = Resources.getText("deadlockTabN", i+1);
481+ } else {
482+ tabName = Resources.getText("deadlockTab");
483+ }
484+ threadListTabbedPane.addTab(tabName, splitPane);
485+
486+ for (long t : deadlockedThreads[i]) {
487+ listModel.addElement(t);
488+ }
489+ }
490+ threadListTabbedPane.setSelectedIndex(1);
491+ }
492+ }
493+ });
494+ } catch (IOException e) {
495+ // Ignore
496+ } catch (UndeclaredThrowableException e) {
497+ vmPanel.getProxyClient().markAsDead();
498+ }
499+ }
500+ });
501+ }
502+
503+
504+ // Return deadlocked threads or null
505+ public Long[][] getDeadlockedThreadIds() throws IOException {
506+ ProxyClient proxyClient = vmPanel.getProxyClient();
507+ ThreadMXBean threadMBean = proxyClient.getThreadMXBean();
508+
509+ long[] ids = proxyClient.findDeadlockedThreads();
510+ if (ids == null) {
511+ return null;
512+ }
513+ ThreadInfo[] infos = threadMBean.getThreadInfo(ids, Integer.MAX_VALUE);
514+
515+ List<Long[]> dcycles = new ArrayList<Long[]>();
516+ List<Long> cycle = new ArrayList<Long>();
517+
518+ // keep track of which thread is visited
519+ // one thread can only be in one cycle
520+ boolean[] visited = new boolean[ids.length];
521+
522+ int deadlockedThread = -1; // Index into arrays
523+ while (true) {
524+ if (deadlockedThread < 0) {
525+ if (cycle.size() > 0) {
526+ // a cycle found
527+ dcycles.add(cycle.toArray(new Long[0]));
528+ cycle = new ArrayList<Long>();
529+ }
530+ // start a new cycle from a non-visited thread
531+ for (int j = 0; j < ids.length; j++) {
532+ if (!visited[j]) {
533+ deadlockedThread = j;
534+ visited[j] = true;
535+ break;
536+ }
537+ }
538+ if (deadlockedThread < 0) {
539+ // done
540+ break;
541+ }
542+ }
543+
544+ cycle.add(ids[deadlockedThread]);
545+ long nextThreadId = infos[deadlockedThread].getLockOwnerId();
546+ for (int j = 0; j < ids.length; j++) {
547+ ThreadInfo ti = infos[j];
548+ if (ti.getThreadId() == nextThreadId) {
549+ if (visited[j]) {
550+ deadlockedThread = -1;
551+ } else {
552+ deadlockedThread = j;
553+ visited[j] = true;
554+ }
555+ break;
556+ }
557+ }
558+ }
559+ return dcycles.toArray(new Long[0][0]);
560+ }
561+
562+
563+
564+
565+
566+ // ActionListener interface
567+ public void actionPerformed(ActionEvent evt) {
568+ String cmd = ((AbstractButton)evt.getSource()).getActionCommand();
569+
570+ if (cmd == "detectDeadlock") {
571+ messageLabel.setText("");
572+ detectDeadlock();
573+ }
574+ }
575+
576+
577+
578+ // DocumentListener interface
579+
580+ public void insertUpdate(DocumentEvent e) {
581+ doUpdate();
582+ }
583+
584+ public void removeUpdate(DocumentEvent e) {
585+ doUpdate();
586+ }
587+
588+ public void changedUpdate(DocumentEvent e) {
589+ doUpdate();
590+ }
591+
592+
593+
594+ private class ThreadJList extends JList {
595+ private JTextArea textArea;
596+
597+ ThreadJList(DefaultListModel listModel, JTextArea textArea) {
598+ super(listModel);
599+
600+ this.textArea = textArea;
601+
602+ setBorder(thinEmptyBorder);
603+
604+ addListSelectionListener(ThreadTab.this);
605+ setCellRenderer(new DefaultListCellRenderer() {
606+ public Component getListCellRendererComponent(JList list, Object value, int index,
607+ boolean isSelected, boolean cellHasFocus) {
608+ super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
609+
610+ if (value != null) {
611+ String name = nameCache.get(value);
612+ if (name == null) {
613+ name = value.toString();
614+ }
615+ setText(name);
616+ }
617+ return this;
618+ }
619+ });
620+ }
621+
622+ public Dimension getPreferredSize() {
623+ Dimension d = super.getPreferredSize();
624+ d.width = Math.max(d.width, 100);
625+ return d;
626+ }
627+ }
628+
629+ private class PromptingTextField extends JTextField implements FocusListener {
630+ private String prompt;
631+ boolean promptRemoved = false;
632+ Color fg;
633+
634+ public PromptingTextField(String prompt, int columns) {
635+ super(prompt, columns);
636+
637+ this.prompt = prompt;
638+ updateForeground();
639+ addFocusListener(this);
640+ setAccessibleName(this, prompt);
641+ }
642+
643+ @Override
644+ public void revalidate() {
645+ super.revalidate();
646+ updateForeground();
647+ }
648+
649+ private void updateForeground() {
650+ this.fg = UIManager.getColor("TextField.foreground");
651+ if (promptRemoved) {
652+ setForeground(fg);
653+ } else {
654+ setForeground(Color.gray);
655+ }
656+ }
657+
658+ public String getText() {
659+ if (!promptRemoved) {
660+ return "";
661+ } else {
662+ return super.getText();
663+ }
664+ }
665+
666+ public void focusGained(FocusEvent e) {
667+ if (!promptRemoved) {
668+ setText("");
669+ setForeground(fg);
670+ promptRemoved = true;
671+ }
672+ }
673+
674+ public void focusLost(FocusEvent e) {
675+ if (promptRemoved && getText().equals("")) {
676+ setText(prompt);
677+ setForeground(Color.gray);
678+ promptRemoved = false;
679+ }
680+ }
681+
682+ }
683+
684+ OverviewPanel[] getOverviewPanels() {
685+ if (overviewPanel == null) {
686+ overviewPanel = new ThreadOverviewPanel();
687+ }
688+ return new OverviewPanel[] { overviewPanel };
689+ }
690+
691+
692+ private static class ThreadOverviewPanel extends OverviewPanel {
693+ ThreadOverviewPanel() {
694+ super(getText("Threads"), threadCountKey, threadCountName, null);
695+ }
696+
697+ private void updateThreadsInfo(long tlCount, long tpCount, long ttCount, long timeStamp) {
698+ getPlotter().addValues(timeStamp, tlCount);
699+ getInfoLabel().setText(getText(infoLabelFormat, tlCount, tpCount, ttCount));
700+ }
701+ }
702+}