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