Path: blob/master/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/JavaThreadsPanel.java
41161 views
/*1* Copyright (c) 2000, 2020, 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.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*22*/2324package sun.jvm.hotspot.ui;2526import java.awt.BorderLayout;27import java.awt.Dimension;2829import java.awt.event.*;3031import java.io.*;32import java.util.*;3334import javax.swing.*;35import javax.swing.event.ListSelectionEvent;36import javax.swing.event.ListSelectionListener;37import javax.swing.table.*;3839import sun.jvm.hotspot.debugger.*;40import sun.jvm.hotspot.runtime.*;41import sun.jvm.hotspot.types.*;4243import sun.jvm.hotspot.ui.action.*;4445import com.sun.java.swing.ui.*;46import com.sun.java.swing.action.*;47import sun.jvm.hotspot.utilities.Observable;48import sun.jvm.hotspot.utilities.Observer;4950/**51* This panel contains a JTable which displays the list of Java52* threads as their native thread identifiers combined with their53* Java names. It allows selection and examination of any of the54* threads.55*/56public class JavaThreadsPanel extends SAPanel implements ActionListener {57private JavaThreadsTableModel dataModel;58private StatusBar statusBar;59private JTable threadTable;60private java.util.List<CachedThread> cachedThreads = new ArrayList<>();61private static AddressField crashThread;626364static {65VM.registerVMInitializedObserver(66(o, a) -> initialize(VM.getVM().getTypeDataBase()));67}6869private static void initialize(TypeDataBase db) {70crashThread = db.lookupType("VMError").getAddressField("_thread");71}7273/** Constructor assumes the threads panel is created while the VM is74suspended. Subsequent resume and suspend operations of the VM75will cause the threads panel to clear and fill itself back in,76respectively. */77public JavaThreadsPanel() {78VM.getVM().registerVMResumedObserver(new Observer() {79public void update(Observable o, Object data) {80decache();81}82});8384VM.getVM().registerVMSuspendedObserver(new Observer() {85public void update(Observable o, Object data) {86cache();87}88});8990cache();9192setLayout(new BorderLayout());9394dataModel = new JavaThreadsTableModel(cachedThreads);95statusBar = new StatusBar();9697threadTable = new JTable(dataModel, new JavaThreadsColumnModel());98threadTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);99threadTable.addMouseListener(new MouseAdapter() {100public void mouseClicked(MouseEvent evt) {101if (evt.getClickCount() == 2) {102// double clicking will display the oop inspector.103fireShowThreadOopInspector();104}105}106});107108add(new JavaThreadsToolBar(statusBar), BorderLayout.NORTH);109add(new ThreadPanel(threadTable), BorderLayout.CENTER);110add(statusBar, BorderLayout.SOUTH);111112registerActions();113}114115/**116* A splitpane panel which contains the thread table and the Thread Info.117* the thread info is toggleable118*/119private class ThreadPanel extends JPanel {120121private JSplitPane splitPane;122private JTable threadTable;123private ThreadInfoPanel threadInfo;124private int dividerSize;125private int dividerLocation = -1;126private boolean actionsEnabled = false;127128public ThreadPanel(JTable table) {129setLayout(new BorderLayout());130this.threadInfo = new ThreadInfoPanel();131this.threadTable = table;132133splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);134splitPane.setOneTouchExpandable(true);135splitPane.setTopComponent(new JScrollPane(table));136137// Set the size of the divider to 0 but save it so it can be restored138dividerSize = splitPane.getDividerSize();139splitPane.setDividerSize(0);140141add(splitPane, BorderLayout.CENTER);142143// Register an ItemListener on the LogViewerAction which toggles144// the apearance of the ThreadInfoPanel145ActionManager manager = HSDBActionManager.getInstance();146StateChangeAction action = manager.getStateChangeAction(ThreadInfoAction.VALUE_COMMAND);147if (action != null) {148action.setItemListener(new ItemListener() {149public void itemStateChanged(ItemEvent evt) {150if (evt.getStateChange() == ItemEvent.SELECTED) {151showOutputPane();152} else {153hideOutputPane();154}155}156});157}158159// A listener is added to listen to changes in row selection160// and changes the contents of the ThreadInfoPanel.161ListSelectionModel selModel = table.getSelectionModel();162selModel.addListSelectionListener(new ListSelectionListener() {163public void valueChanged(ListSelectionEvent evt) {164if (evt.getValueIsAdjusting() == false) {165setActionsEnabled(true);166if (isInfoVisible()) {167showCurrentThreadInfo();168}169}170}171});172}173174/**175* Returns a flag to indicate if the thread info is visible176*/177private boolean isInfoVisible() {178return (splitPane.getBottomComponent() != null);179}180181private void showOutputPane() {182if (splitPane.getBottomComponent() == null) {183splitPane.setBottomComponent(threadInfo);184185if (dividerLocation == -1) {186// Calculate the divider location from the pref size.187Dimension pSize = this.getSize();188dividerLocation = pSize.height / 2;189}190191splitPane.setDividerSize(dividerSize);192splitPane.setDividerLocation(dividerLocation);193showCurrentThreadInfo();194}195}196197private void hideOutputPane() {198dividerLocation = splitPane.getDividerLocation();199splitPane.remove(threadInfo);200splitPane.setDividerSize(0);201}202203private void showCurrentThreadInfo() {204int row = threadTable.getSelectedRow();205if (row >= 0) {206threadInfo.setJavaThread(dataModel.getJavaThread(row));207}208}209210private void setActionsEnabled(boolean enabled) {211if (actionsEnabled != enabled) {212ActionManager manager = ActionManager.getInstance();213manager.setActionEnabled(InspectAction.VALUE_COMMAND, enabled);214manager.setActionEnabled(MemoryAction.VALUE_COMMAND, enabled);215manager.setActionEnabled(JavaStackTraceAction.VALUE_COMMAND, enabled);216actionsEnabled = enabled;217}218}219220} // end ThreadPanel221222private class JavaThreadsToolBar extends CommonToolBar {223public JavaThreadsToolBar(StatusBar status) {224super(HSDBActionManager.getInstance(), status);225}226227protected void addComponents() {228addButton(manager.getAction(InspectAction.VALUE_COMMAND));229addButton(manager.getAction(MemoryAction.VALUE_COMMAND));230addButton(manager.getAction(JavaStackTraceAction.VALUE_COMMAND));231232addToggleButton(manager.getStateChangeAction(ThreadInfoAction.VALUE_COMMAND));233addButton(manager.getAction(FindCrashesAction.VALUE_COMMAND));234}235}236237private class JavaThreadsColumnModel extends DefaultTableColumnModel {238private String[] columnNames = { "OS Thread ID", "Java Thread Name" };239240public JavaThreadsColumnModel() {241// Should actually get the line metrics for242int PREF_WIDTH = 80;243int MAX_WIDTH = 100;244int HUGE_WIDTH = 140;245246TableColumn column;247248// Thread ID249column = new TableColumn(0, MAX_WIDTH);250column.setHeaderValue(columnNames[0]);251column.setMaxWidth(MAX_WIDTH);252column.setResizable(false);253addColumn(column);254255// Thread name256column = new TableColumn(1, HUGE_WIDTH);257column.setHeaderValue(columnNames[1]);258column.setResizable(false);259addColumn(column);260}261} // end class JavaThreadsColumnModel262263/**264* Encapsulates the set of threads in a table model265*/266private class JavaThreadsTableModel extends AbstractTableModel {267private String[] columnNames = { "OS Thread ID", "Java Thread Name" };268269private java.util.List<CachedThread> elements;270271public JavaThreadsTableModel(java.util.List<CachedThread> threads) {272this.elements = threads;273}274275public int getColumnCount() {276return columnNames.length;277}278279public int getRowCount() {280return elements.size();281}282283public String getColumnName(int col) {284return columnNames[col];285}286287public Object getValueAt(int row, int col) {288CachedThread thread = getRow(row);289switch (col) {290case 0:291return thread.getThreadID();292case 1:293return thread.getThreadName();294default:295throw new RuntimeException("Index (" + col + ", " + row + ") out of bounds");296}297}298299/**300* Returns the selected Java Thread indexed by the row or null.301*/302public JavaThread getJavaThread(int index) {303return getRow(index).getThread();304}305306private CachedThread getRow(int row) {307return elements.get(row);308}309310private String threadIDAt(int index) {311return cachedThreads.get(index).getThreadID();312}313314private String threadNameAt(int index) {315try {316return cachedThreads.get(index).getThreadName();317} catch (AddressException e) {318return "<Error: AddressException>";319} catch (NullPointerException e) {320return "<Error: NullPointerException>";321}322}323} // end class JavaThreadsTableModel324325public void actionPerformed(ActionEvent evt) {326String command = evt.getActionCommand();327328if (command.equals(InspectAction.VALUE_COMMAND)) {329fireShowThreadOopInspector();330} else if (command.equals(MemoryAction.VALUE_COMMAND)) {331fireShowThreadStackMemory();332} else if (command.equals(ThreadInfoAction.VALUE_COMMAND)) {333fireShowThreadInfo();334} else if (command.equals(FindCrashesAction.VALUE_COMMAND)) {335if (fireShowThreadCrashes()) {336statusBar.setMessage("Some thread crashes were encountered");337} else {338statusBar.setMessage("No thread crashes encountered");339}340} else if (command.equals(JavaStackTraceAction.VALUE_COMMAND)) {341fireShowJavaStackTrace();342}343}344345// Cached data for a thread346private class CachedThread {347private JavaThread thread;348private String threadID;349private String threadName;350private boolean computed;351352public CachedThread(JavaThread thread) {353this.thread = thread;354}355356public JavaThread getThread() {357return thread;358}359360public String getThreadID() {361if (!computed) {362compute();363}364365return threadID;366}367368public String getThreadName() {369if (!computed) {370compute();371}372373return threadName;374}375376private void compute() {377ByteArrayOutputStream bos = new ByteArrayOutputStream();378thread.printThreadIDOn(new PrintStream(bos));379threadID = bos.toString();380threadName = thread.getThreadName();381382computed = true;383}384}385386//--------------------------------------------------------------------------------387// Internals only below this point388//389390protected void registerActions() {391registerAction(InspectAction.VALUE_COMMAND);392registerAction(MemoryAction.VALUE_COMMAND);393registerAction(FindCrashesAction.VALUE_COMMAND);394registerAction(JavaStackTraceAction.VALUE_COMMAND);395396// disable Inspector, Memory and Java Stack trace action until a thread is selected397ActionManager manager = ActionManager.getInstance();398manager.setActionEnabled(InspectAction.VALUE_COMMAND, false);399manager.setActionEnabled(MemoryAction.VALUE_COMMAND, false);400manager.setActionEnabled(JavaStackTraceAction.VALUE_COMMAND, false);401}402403private void registerAction(String actionName) {404ActionManager manager = ActionManager.getInstance();405DelegateAction action = manager.getDelegateAction(actionName);406action.addActionListener(this);407}408409410411private void fireShowThreadOopInspector() {412int i = threadTable.getSelectedRow();413if (i < 0) {414return;415}416417JavaThread t = dataModel.getJavaThread(i);418showThreadOopInspector(t);419}420421private void fireShowThreadStackMemory() {422int i = threadTable.getSelectedRow();423if (i < 0) {424return;425}426showThreadStackMemory(dataModel.getJavaThread(i));427}428429private void fireShowJavaStackTrace() {430int i = threadTable.getSelectedRow();431if (i < 0) {432return;433}434showJavaStackTrace(dataModel.getJavaThread(i));435}436437private void fireShowThreadInfo() {438int i = threadTable.getSelectedRow();439if (i < 0) {440return;441}442showThreadInfo(dataModel.getJavaThread(i));443}444445/**446* Shows stack memory for threads which have crashed (defined as447* having taken a signal above a Java frame)448*449* @return a flag which indicates if crashes were encountered.450*/451private boolean fireShowThreadCrashes() {452Optional<JavaThread> crashed =453cachedThreads.stream()454.map(t -> t.getThread())455.filter(t -> t.getAddress().equals(456crashThread.getValue()))457.findAny();458crashed.ifPresent(this::showThreadStackMemory);459return crashed.isPresent();460}461462private void cache() {463Threads threads = VM.getVM().getThreads();464for (int i = 0; i < threads.getNumberOfThreads(); i++) {465JavaThread t = threads.getJavaThreadAt(i);466if (t.isJavaThread()) {467cachedThreads.add(new CachedThread(t));468}469}470}471472private void decache() {473cachedThreads.clear();474}475476}477478479