1 /*
   2  * Copyright 2001 Sun Microsystems, Inc.  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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  20  * CA 95054 USA or visit www.sun.com if you need additional information or
  21  * have any questions.
  22  *
  23  */
  24 
  25 package sun.jvm.hotspot.bugspot;
  26 
  27 import java.awt.*;
  28 import java.awt.event.*;
  29 import java.util.*;
  30 import javax.swing.*;
  31 import javax.swing.table.*;
  32 
  33 import sun.jvm.hotspot.debugger.*;
  34 import sun.jvm.hotspot.debugger.cdbg.*;
  35 import sun.jvm.hotspot.runtime.*;
  36 import sun.jvm.hotspot.ui.*;
  37 
  38 // NOTE: this class was not placed in sun.jvm.hotspot.ui to prevent
  39 // mixing components designed for C and C++ debugging with the ones
  40 // that work with the core serviceability agent functionality (which
  41 // does not require that the CDebugger interface be implemented).
  42 
  43 /** The ThreadListPanel is used for C and C++ debugging and can
  44     visualize all threads in the target process. The caller passes in
  45     a CDebugger attached to the target process and can request that
  46     JavaThreads' associations with these underlying threads be
  47     displayed; this option is only valid when attached to a HotSpot
  48     JVM and when the {@link sun.jvm.hotspot.runtime.VM} has been
  49     initialized. */
  50 
  51 public class ThreadListPanel extends JPanel {
  52   /** Listener which can be added to receive "Set Focus" events */
  53   public static interface Listener {
  54     /** ThreadProxy will always be provided; JavaThread will only be
  55         present if displayJavaThreads was specified in the constructor
  56         for the panel and the thread was a JavaThread. */
  57     public void setFocus(ThreadProxy thread, JavaThread jthread);
  58   }
  59 
  60   static class ThreadInfo {
  61     private ThreadProxy thread;
  62     // Distinguish between PC == null and no top frame
  63     private boolean     gotPC;
  64     private Address     pc;
  65     private String      location;
  66     private JavaThread  javaThread;
  67     private String      javaThreadName;
  68 
  69     public ThreadInfo(ThreadProxy thread, CDebugger dbg, JavaThread jthread) {
  70       this.thread = thread;
  71       this.location = "<unknown>";
  72       CFrame fr = dbg.topFrameForThread(thread);
  73       if (fr != null) {
  74         gotPC = true;
  75         pc = fr.pc();
  76         PCFinder.Info info = PCFinder.findPC(pc, fr.loadObjectForPC(), dbg);
  77         if (info.getName() != null) {
  78           location = info.getName();
  79           if (info.getConfidence() == PCFinder.LOW_CONFIDENCE) {
  80             location = location + " (?)";
  81           }
  82           if (info.getOffset() < 0) {
  83             location = location + " + 0x" + Long.toHexString(info.getOffset());
  84           }
  85         }
  86       }
  87       if (jthread != null) {
  88         javaThread = jthread;
  89         javaThreadName = jthread.getThreadName();
  90       }
  91     }
  92 
  93     public ThreadProxy getThread()    { return thread;       }
  94     public boolean     hasPC()        { return gotPC;        }
  95     public Address     getPC()        { return pc;           }
  96     public String      getLocation()  { return location;     }
  97     public boolean     isJavaThread() { return (javaThread != null); }
  98     public JavaThread  getJavaThread() { return javaThread; }
  99     public String      getJavaThreadName() { return javaThreadName; }
 100   }
 101 
 102   // List<ThreadInfo>
 103   private java.util.List threadList;
 104   private JTable table;
 105   private AbstractTableModel dataModel;
 106   // List<Listener>
 107   private java.util.List listeners;
 108 
 109   /** Takes a CDebugger from which the thread list is queried.
 110       displayJavaThreads must only be set to true if the debugger is
 111       attached to a HotSpot JVM and if the VM has already been
 112       initialized. */
 113   public ThreadListPanel(CDebugger dbg, final boolean displayJavaThreads) {
 114     super();
 115 
 116     Map threadToJavaThreadMap = null;
 117     if (displayJavaThreads) {
 118       // Collect Java threads from virtual machine and insert them in
 119       // table for later querying
 120       threadToJavaThreadMap = new HashMap();
 121       Threads threads = VM.getVM().getThreads();
 122       for (JavaThread thr = threads.first(); thr != null; thr = thr.next()) {
 123         threadToJavaThreadMap.put(thr.getThreadProxy(), thr);
 124       }
 125     }
 126 
 127     java.util.List/*<ThreadProxy>*/ threads = dbg.getThreadList();
 128     threadList = new ArrayList(threads.size());
 129     for (Iterator iter = threads.iterator(); iter.hasNext(); ) {
 130       ThreadProxy thr = (ThreadProxy) iter.next();
 131       JavaThread jthr = null;
 132       if (displayJavaThreads) {
 133         jthr = (JavaThread) threadToJavaThreadMap.get(thr);
 134       }
 135       threadList.add(new ThreadInfo(thr, dbg, jthr));
 136     }
 137 
 138     // Thread ID, current PC, current symbol, Java Thread, [Java thread name]
 139     dataModel = new AbstractTableModel() {
 140         public int getColumnCount() { return (displayJavaThreads ? 5 : 3); }
 141         public int getRowCount()    { return threadList.size(); }
 142         public String getColumnName(int col) {
 143           switch (col) {
 144           case 0:
 145             return "Thread ID";
 146           case 1:
 147             return "PC";
 148           case 2:
 149             return "Location";
 150           case 3:
 151             return "Java?";
 152           case 4:
 153             return "Java Thread Name";
 154           default:
 155             throw new RuntimeException("Index " + col + " out of bounds");
 156           }
 157         }
 158         public Object getValueAt(int row, int col) {
 159           ThreadInfo info = (ThreadInfo) threadList.get(row);
 160 
 161           switch (col) {
 162           case 0:
 163             return info.getThread();
 164           case 1:
 165             {
 166               if (info.hasPC()) {
 167                 return info.getPC();
 168               }
 169               return "<no frames on stack>";
 170             }
 171           case 2:
 172             return info.getLocation();
 173           case 3:
 174             if (info.isJavaThread()) {
 175               return "Yes";
 176             } else {
 177               return "";
 178             }
 179           case 4:
 180             if (info.isJavaThread()) {
 181               return info.getJavaThreadName();
 182             } else {
 183               return "";
 184             }
 185           default:
 186             throw new RuntimeException("Index (" + col + ", " + row + ") out of bounds");
 187           }
 188         }
 189       };
 190 
 191     // Build user interface
 192     setLayout(new BorderLayout());
 193     table = new JTable(dataModel);
 194     table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
 195     JTableHeader header = table.getTableHeader();
 196     header.setReorderingAllowed(false);
 197     table.setRowSelectionAllowed(true);
 198     table.setColumnSelectionAllowed(false);
 199     JScrollPane scrollPane = new JScrollPane(table);
 200     add(scrollPane, BorderLayout.CENTER);
 201     if (threadList.size() > 0) {
 202       table.setRowSelectionInterval(0, 0);
 203     }
 204 
 205     JButton button = new JButton("Set Focus");
 206     button.addActionListener(new ActionListener() {
 207         public void actionPerformed(ActionEvent e) {
 208           int i = table.getSelectedRow();
 209           if (i < 0) {
 210             return;
 211           }
 212           ThreadInfo info = (ThreadInfo) threadList.get(i);
 213           for (Iterator iter = listeners.iterator(); iter.hasNext(); ) {
 214             ((Listener) iter.next()).setFocus(info.getThread(), info.getJavaThread());
 215           }
 216         }
 217       });
 218     JPanel focusPanel = new JPanel();
 219     focusPanel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0));
 220     focusPanel.setLayout(new BoxLayout(focusPanel, BoxLayout.Y_AXIS));
 221     focusPanel.add(Box.createGlue());
 222     focusPanel.add(button);
 223     focusPanel.add(Box.createGlue());
 224     add(focusPanel, BorderLayout.EAST);
 225 
 226     // FIXME: make listener model for the debugger so if the user
 227     // specifies a mapfile for or path to a given DSO later we can
 228     // update our state
 229   }
 230 
 231   public void addListener(Listener l) {
 232     if (listeners == null) {
 233       listeners = new ArrayList();
 234     }
 235     listeners.add(l);
 236   }
 237 }