Mercurial > hg > truffle
diff agent/src/share/classes/sun/jvm/hotspot/ui/JavaThreadsPanel.java @ 0:a61af66fc99e jdk7-b24
Initial load
author | duke |
---|---|
date | Sat, 01 Dec 2007 00:00:00 +0000 |
parents | |
children | c18cbe5936b8 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/src/share/classes/sun/jvm/hotspot/ui/JavaThreadsPanel.java Sat Dec 01 00:00:00 2007 +0000 @@ -0,0 +1,470 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.BorderLayout; +import java.awt.Dimension; + +import java.awt.event.*; + +import java.io.*; +import java.util.*; + +import javax.swing.*; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.table.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; + +import sun.jvm.hotspot.ui.action.*; + +import com.sun.java.swing.ui.*; +import com.sun.java.swing.action.*; + +/** + * This panel contains a JTable which displays the list of Java + * threads as their native thread identifiers combined with their + * Java names. It allows selection and examination of any of the + * threads. + */ +public class JavaThreadsPanel extends SAPanel implements ActionListener { + private JavaThreadsTableModel dataModel; + private StatusBar statusBar; + private JTable threadTable; + private java.util.List cachedThreads = new ArrayList(); + + + /** Constructor assumes the threads panel is created while the VM is + suspended. Subsequent resume and suspend operations of the VM + will cause the threads panel to clear and fill itself back in, + respectively. */ + public JavaThreadsPanel() { + VM.getVM().registerVMResumedObserver(new Observer() { + public void update(Observable o, Object data) { + decache(); + } + }); + + VM.getVM().registerVMSuspendedObserver(new Observer() { + public void update(Observable o, Object data) { + cache(); + } + }); + + cache(); + + setLayout(new BorderLayout()); + + dataModel = new JavaThreadsTableModel(cachedThreads); + statusBar = new StatusBar(); + + threadTable = new JTable(dataModel, new JavaThreadsColumnModel()); + threadTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + threadTable.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent evt) { + if (evt.getClickCount() == 2) { + // double clicking will display the oop inspector. + fireShowThreadOopInspector(); + } + } + }); + + add(new JavaThreadsToolBar(statusBar), BorderLayout.NORTH); + add(new ThreadPanel(threadTable), BorderLayout.CENTER); + add(statusBar, BorderLayout.SOUTH); + + registerActions(); + } + + /** + * A splitpane panel which contains the thread table and the Thread Info. + * the thread info is toggleable + */ + private class ThreadPanel extends JPanel { + + private JSplitPane splitPane; + private JTable threadTable; + private ThreadInfoPanel threadInfo; + private int dividerSize; + private int dividerLocation = -1; + private boolean actionsEnabled = false; + + public ThreadPanel(JTable table) { + setLayout(new BorderLayout()); + this.threadInfo = new ThreadInfoPanel(); + this.threadTable = table; + + splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); + splitPane.setOneTouchExpandable(true); + splitPane.setTopComponent(new JScrollPane(table)); + + // Set the size of the divider to 0 but save it so it can be restored + dividerSize = splitPane.getDividerSize(); + splitPane.setDividerSize(0); + + add(splitPane, BorderLayout.CENTER); + + // Register an ItemListener on the LogViewerAction which toggles + // the apearance of the ThreadInfoPanel + ActionManager manager = HSDBActionManager.getInstance(); + StateChangeAction action = manager.getStateChangeAction(ThreadInfoAction.VALUE_COMMAND); + if (action != null) { + action.setItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent evt) { + if (evt.getStateChange() == ItemEvent.SELECTED) { + showOutputPane(); + } else { + hideOutputPane(); + } + } + }); + } + + // A listener is added to listen to changes in row selection + // and changes the contents of the ThreadInfoPanel. + ListSelectionModel selModel = table.getSelectionModel(); + selModel.addListSelectionListener(new ListSelectionListener() { + public void valueChanged(ListSelectionEvent evt) { + if (evt.getValueIsAdjusting() == false) { + setActionsEnabled(true); + if (isInfoVisible()) { + showCurrentThreadInfo(); + } + } + } + }); + } + + /** + * Returns a flag to indicate if the thread info is visible + */ + private boolean isInfoVisible() { + return (splitPane.getBottomComponent() != null); + } + + private void showOutputPane() { + if (splitPane.getBottomComponent() == null) { + splitPane.setBottomComponent(threadInfo); + + if (dividerLocation == -1) { + // Calculate the divider location from the pref size. + Dimension pSize = this.getSize(); + dividerLocation = pSize.height / 2; + } + + splitPane.setDividerSize(dividerSize); + splitPane.setDividerLocation(dividerLocation); + showCurrentThreadInfo(); + } + } + + private void hideOutputPane() { + dividerLocation = splitPane.getDividerLocation(); + splitPane.remove(threadInfo); + splitPane.setDividerSize(0); + } + + private void showCurrentThreadInfo() { + int row = threadTable.getSelectedRow(); + if (row >= 0) { + threadInfo.setJavaThread(dataModel.getJavaThread(row)); + } + } + + private void setActionsEnabled(boolean enabled) { + if (actionsEnabled != enabled) { + ActionManager manager = ActionManager.getInstance(); + manager.setActionEnabled(InspectAction.VALUE_COMMAND, enabled); + manager.setActionEnabled(MemoryAction.VALUE_COMMAND, enabled); + manager.setActionEnabled(JavaStackTraceAction.VALUE_COMMAND, enabled); + actionsEnabled = enabled; + } + } + + } // end ThreadPanel + + private class JavaThreadsToolBar extends CommonToolBar { + public JavaThreadsToolBar(StatusBar status) { + super(HSDBActionManager.getInstance(), status); + } + + protected void addComponents() { + addButton(manager.getAction(InspectAction.VALUE_COMMAND)); + addButton(manager.getAction(MemoryAction.VALUE_COMMAND)); + addButton(manager.getAction(JavaStackTraceAction.VALUE_COMMAND)); + + addToggleButton(manager.getStateChangeAction(ThreadInfoAction.VALUE_COMMAND)); + addButton(manager.getAction(FindCrashesAction.VALUE_COMMAND)); + } + } + + private class JavaThreadsColumnModel extends DefaultTableColumnModel { + private String[] columnNames = { "OS Thread ID", "Java Thread Name" }; + + public JavaThreadsColumnModel() { + // Should actually get the line metrics for + int PREF_WIDTH = 80; + int MAX_WIDTH = 100; + int HUGE_WIDTH = 140; + + TableColumn column; + + // Thread ID + column = new TableColumn(0, MAX_WIDTH); + column.setHeaderValue(columnNames[0]); + column.setMaxWidth(MAX_WIDTH); + column.setResizable(false); + addColumn(column); + + // Thread name + column = new TableColumn(1, HUGE_WIDTH); + column.setHeaderValue(columnNames[1]); + column.setResizable(false); + addColumn(column); + } + } // end class JavaThreadsColumnModel + + /** + * Encapsulates the set of threads in a table model + */ + private class JavaThreadsTableModel extends AbstractTableModel { + private String[] columnNames = { "OS Thread ID", "Java Thread Name" }; + + private java.util.List elements; + + public JavaThreadsTableModel(java.util.List threads) { + this.elements = threads; + } + + public int getColumnCount() { + return columnNames.length; + } + + public int getRowCount() { + return elements.size(); + } + + public String getColumnName(int col) { + return columnNames[col]; + } + + public Object getValueAt(int row, int col) { + CachedThread thread = getRow(row); + switch (col) { + case 0: + return thread.getThreadID(); + case 1: + return thread.getThreadName(); + default: + throw new RuntimeException("Index (" + col + ", " + row + ") out of bounds"); + } + } + + /** + * Returns the selected Java Thread indexed by the row or null. + */ + public JavaThread getJavaThread(int index) { + return getRow(index).getThread(); + } + + private CachedThread getRow(int row) { + return (CachedThread)elements.get(row); + } + + private String threadIDAt(int index) { + return ((CachedThread) cachedThreads.get(index)).getThreadID(); + } + + private String threadNameAt(int index) { + try { + return ((CachedThread) cachedThreads.get(index)).getThreadName(); + } catch (AddressException e) { + return "<Error: AddressException>"; + } catch (NullPointerException e) { + return "<Error: NullPointerException>"; + } + } + } // end class JavaThreadsTableModel + + public void actionPerformed(ActionEvent evt) { + String command = evt.getActionCommand(); + + if (command.equals(InspectAction.VALUE_COMMAND)) { + fireShowThreadOopInspector(); + } else if (command.equals(MemoryAction.VALUE_COMMAND)) { + fireShowThreadStackMemory(); + } else if (command.equals(ThreadInfoAction.VALUE_COMMAND)) { + fireShowThreadInfo(); + } else if (command.equals(FindCrashesAction.VALUE_COMMAND)) { + if (fireShowThreadCrashes()) { + statusBar.setMessage("Some thread crashes were encountered"); + } else { + statusBar.setMessage("No thread crashes encountered"); + } + } else if (command.equals(JavaStackTraceAction.VALUE_COMMAND)) { + fireShowJavaStackTrace(); + } + } + + // Cached data for a thread + private class CachedThread { + private JavaThread thread; + private String threadID; + private String threadName; + private boolean computed; + + public CachedThread(JavaThread thread) { + this.thread = thread; + } + + public JavaThread getThread() { + return thread; + } + + public String getThreadID() { + if (!computed) { + compute(); + } + + return threadID; + } + + public String getThreadName() { + if (!computed) { + compute(); + } + + return threadName; + } + + private void compute() { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + thread.printThreadIDOn(new PrintStream(bos)); + threadID = bos.toString(); + threadName = thread.getThreadName(); + + computed = true; + } + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + protected void registerActions() { + registerAction(InspectAction.VALUE_COMMAND); + registerAction(MemoryAction.VALUE_COMMAND); + registerAction(FindCrashesAction.VALUE_COMMAND); + registerAction(JavaStackTraceAction.VALUE_COMMAND); + + // disable Inspector, Memory and Java Stack trace action until a thread is selected + ActionManager manager = ActionManager.getInstance(); + manager.setActionEnabled(InspectAction.VALUE_COMMAND, false); + manager.setActionEnabled(MemoryAction.VALUE_COMMAND, false); + manager.setActionEnabled(JavaStackTraceAction.VALUE_COMMAND, false); + } + + private void registerAction(String actionName) { + ActionManager manager = ActionManager.getInstance(); + DelegateAction action = manager.getDelegateAction(actionName); + action.addActionListener(this); + } + + + + private void fireShowThreadOopInspector() { + int i = threadTable.getSelectedRow(); + if (i < 0) { + return; + } + + JavaThread t = dataModel.getJavaThread(i); + showThreadOopInspector(t); + } + + private void fireShowThreadStackMemory() { + int i = threadTable.getSelectedRow(); + if (i < 0) { + return; + } + showThreadStackMemory(dataModel.getJavaThread(i)); + } + + private void fireShowJavaStackTrace() { + int i = threadTable.getSelectedRow(); + if (i < 0) { + return; + } + showJavaStackTrace(dataModel.getJavaThread(i)); + } + + private void fireShowThreadInfo() { + int i = threadTable.getSelectedRow(); + if (i < 0) { + return; + } + showThreadInfo(dataModel.getJavaThread(i)); + } + + /** + * Shows stack memory for threads which have crashed (defined as + * having taken a signal above a Java frame) + * + * @return a flag which indicates if crashes were encountered. + */ + private boolean fireShowThreadCrashes() { + boolean crash = false; + for (Iterator iter = cachedThreads.iterator(); iter.hasNext(); ) { + JavaThread t = (JavaThread) ((CachedThread) iter.next()).getThread(); + sun.jvm.hotspot.runtime.Frame tmpFrame = t.getCurrentFrameGuess(); + RegisterMap tmpMap = t.newRegisterMap(false); + while ((tmpFrame != null) && (!tmpFrame.isFirstFrame())) { + if (tmpFrame.isSignalHandlerFrameDbg()) { + showThreadStackMemory(t); + crash = true; + break; + } + tmpFrame = tmpFrame.sender(tmpMap); + } + } + return crash; + } + + private void cache() { + Threads threads = VM.getVM().getThreads(); + for (JavaThread t = threads.first(); t != null; t = t.next()) { + if (t.isJavaThread()) { + cachedThreads.add(new CachedThread(t)); + } + } + } + + private void decache() { + cachedThreads.clear(); + } + +}