Showing the changes to java/pkg/sun/tools/jconsole/ThreadTab.java (1 of 18352 files). Show all
Unified · Split
| @@ -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 | } | ||