1 /* |
|
2 * Copyright (c) 2001, 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. |
|
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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
20 * or visit www.oracle.com if you need additional information or have any |
|
21 * 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 } |
|