blob: 2e8a53af4dc54ede7df97fd13d22318c4aed030f [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package com.sun.tools.example.trace;
27
28import com.sun.jdi.*;
29import com.sun.jdi.request.*;
30import com.sun.jdi.event.*;
31
32import java.util.*;
33import java.io.PrintWriter;
34
35/**
36 * This class processes incoming JDI events and displays them
37 *
38 * @author Robert Field
39 */
40public class EventThread extends Thread {
41
42 private final VirtualMachine vm; // Running VM
43 private final String[] excludes; // Packages to exclude
44 private final PrintWriter writer; // Where output goes
45
46 static String nextBaseIndent = ""; // Starting indent for next thread
47
48 private boolean connected = true; // Connected to VM
49 private boolean vmDied = true; // VMDeath occurred
50
51 // Maps ThreadReference to ThreadTrace instances
52 private Map traceMap = new HashMap();
53
54 EventThread(VirtualMachine vm, String[] excludes, PrintWriter writer) {
55 super("event-handler");
56 this.vm = vm;
57 this.excludes = excludes;
58 this.writer = writer;
59 }
60
61 /**
62 * Run the event handling thread.
63 * As long as we are connected, get event sets off
64 * the queue and dispatch the events within them.
65 */
66 public void run() {
67 EventQueue queue = vm.eventQueue();
68 while (connected) {
69 try {
70 EventSet eventSet = queue.remove();
71 EventIterator it = eventSet.eventIterator();
72 while (it.hasNext()) {
73 handleEvent(it.nextEvent());
74 }
75 eventSet.resume();
76 } catch (InterruptedException exc) {
77 // Ignore
78 } catch (VMDisconnectedException discExc) {
79 handleDisconnectedException();
80 break;
81 }
82 }
83 }
84
85 /**
86 * Create the desired event requests, and enable
87 * them so that we will get events.
88 * @param excludes Class patterns for which we don't want events
89 * @param watchFields Do we want to watch assignments to fields
90 */
91 void setEventRequests(boolean watchFields) {
92 EventRequestManager mgr = vm.eventRequestManager();
93
94 // want all exceptions
95 ExceptionRequest excReq = mgr.createExceptionRequest(null,
96 true, true);
97 // suspend so we can step
98 excReq.setSuspendPolicy(EventRequest.SUSPEND_ALL);
99 excReq.enable();
100
101 MethodEntryRequest menr = mgr.createMethodEntryRequest();
102 for (int i=0; i<excludes.length; ++i) {
103 menr.addClassExclusionFilter(excludes[i]);
104 }
105 menr.setSuspendPolicy(EventRequest.SUSPEND_NONE);
106 menr.enable();
107
108 MethodExitRequest mexr = mgr.createMethodExitRequest();
109 for (int i=0; i<excludes.length; ++i) {
110 mexr.addClassExclusionFilter(excludes[i]);
111 }
112 mexr.setSuspendPolicy(EventRequest.SUSPEND_NONE);
113 mexr.enable();
114
115 ThreadDeathRequest tdr = mgr.createThreadDeathRequest();
116 // Make sure we sync on thread death
117 tdr.setSuspendPolicy(EventRequest.SUSPEND_ALL);
118 tdr.enable();
119
120 if (watchFields) {
121 ClassPrepareRequest cpr = mgr.createClassPrepareRequest();
122 for (int i=0; i<excludes.length; ++i) {
123 cpr.addClassExclusionFilter(excludes[i]);
124 }
125 cpr.setSuspendPolicy(EventRequest.SUSPEND_ALL);
126 cpr.enable();
127 }
128 }
129
130 /**
131 * This class keeps context on events in one thread.
132 * In this implementation, context is the indentation prefix.
133 */
134 class ThreadTrace {
135 final ThreadReference thread;
136 final String baseIndent;
137 static final String threadDelta = " ";
138 StringBuffer indent;
139
140 ThreadTrace(ThreadReference thread) {
141 this.thread = thread;
142 this.baseIndent = nextBaseIndent;
143 indent = new StringBuffer(baseIndent);
144 nextBaseIndent += threadDelta;
145 println("====== " + thread.name() + " ======");
146 }
147
148 private void println(String str) {
149 writer.print(indent);
150 writer.println(str);
151 }
152
153 void methodEntryEvent(MethodEntryEvent event) {
154 println(event.method().name() + " -- "
155 + event.method().declaringType().name());
156 indent.append("| ");
157 }
158
159 void methodExitEvent(MethodExitEvent event) {
160 indent.setLength(indent.length()-2);
161 }
162
163 void fieldWatchEvent(ModificationWatchpointEvent event) {
164 Field field = event.field();
165 Value value = event.valueToBe();
166 println(" " + field.name() + " = " + value);
167 }
168
169 void exceptionEvent(ExceptionEvent event) {
170 println("Exception: " + event.exception() +
171 " catch: " + event.catchLocation());
172
173 // Step to the catch
174 EventRequestManager mgr = vm.eventRequestManager();
175 StepRequest req = mgr.createStepRequest(thread,
176 StepRequest.STEP_MIN,
177 StepRequest.STEP_INTO);
178 req.addCountFilter(1); // next step only
179 req.setSuspendPolicy(EventRequest.SUSPEND_ALL);
180 req.enable();
181 }
182
183 // Step to exception catch
184 void stepEvent(StepEvent event) {
185 // Adjust call depth
186 int cnt = 0;
187 indent = new StringBuffer(baseIndent);
188 try {
189 cnt = thread.frameCount();
190 } catch (IncompatibleThreadStateException exc) {
191 }
192 while (cnt-- > 0) {
193 indent.append("| ");
194 }
195
196 EventRequestManager mgr = vm.eventRequestManager();
197 mgr.deleteEventRequest(event.request());
198 }
199
200 void threadDeathEvent(ThreadDeathEvent event) {
201 indent = new StringBuffer(baseIndent);
202 println("====== " + thread.name() + " end ======");
203 }
204 }
205
206 /**
207 * Returns the ThreadTrace instance for the specified thread,
208 * creating one if needed.
209 */
210 ThreadTrace threadTrace(ThreadReference thread) {
211 ThreadTrace trace = (ThreadTrace)traceMap.get(thread);
212 if (trace == null) {
213 trace = new ThreadTrace(thread);
214 traceMap.put(thread, trace);
215 }
216 return trace;
217 }
218
219 /**
220 * Dispatch incoming events
221 */
222 private void handleEvent(Event event) {
223 if (event instanceof ExceptionEvent) {
224 exceptionEvent((ExceptionEvent)event);
225 } else if (event instanceof ModificationWatchpointEvent) {
226 fieldWatchEvent((ModificationWatchpointEvent)event);
227 } else if (event instanceof MethodEntryEvent) {
228 methodEntryEvent((MethodEntryEvent)event);
229 } else if (event instanceof MethodExitEvent) {
230 methodExitEvent((MethodExitEvent)event);
231 } else if (event instanceof StepEvent) {
232 stepEvent((StepEvent)event);
233 } else if (event instanceof ThreadDeathEvent) {
234 threadDeathEvent((ThreadDeathEvent)event);
235 } else if (event instanceof ClassPrepareEvent) {
236 classPrepareEvent((ClassPrepareEvent)event);
237 } else if (event instanceof VMStartEvent) {
238 vmStartEvent((VMStartEvent)event);
239 } else if (event instanceof VMDeathEvent) {
240 vmDeathEvent((VMDeathEvent)event);
241 } else if (event instanceof VMDisconnectEvent) {
242 vmDisconnectEvent((VMDisconnectEvent)event);
243 } else {
244 throw new Error("Unexpected event type");
245 }
246 }
247
248 /***
249 * A VMDisconnectedException has happened while dealing with
250 * another event. We need to flush the event queue, dealing only
251 * with exit events (VMDeath, VMDisconnect) so that we terminate
252 * correctly.
253 */
254 synchronized void handleDisconnectedException() {
255 EventQueue queue = vm.eventQueue();
256 while (connected) {
257 try {
258 EventSet eventSet = queue.remove();
259 EventIterator iter = eventSet.eventIterator();
260 while (iter.hasNext()) {
261 Event event = iter.nextEvent();
262 if (event instanceof VMDeathEvent) {
263 vmDeathEvent((VMDeathEvent)event);
264 } else if (event instanceof VMDisconnectEvent) {
265 vmDisconnectEvent((VMDisconnectEvent)event);
266 }
267 }
268 eventSet.resume(); // Resume the VM
269 } catch (InterruptedException exc) {
270 // ignore
271 }
272 }
273 }
274
275 private void vmStartEvent(VMStartEvent event) {
276 writer.println("-- VM Started --");
277 }
278
279 // Forward event for thread specific processing
280 private void methodEntryEvent(MethodEntryEvent event) {
281 threadTrace(event.thread()).methodEntryEvent(event);
282 }
283
284 // Forward event for thread specific processing
285 private void methodExitEvent(MethodExitEvent event) {
286 threadTrace(event.thread()).methodExitEvent(event);
287 }
288
289 // Forward event for thread specific processing
290 private void stepEvent(StepEvent event) {
291 threadTrace(event.thread()).stepEvent(event);
292 }
293
294 // Forward event for thread specific processing
295 private void fieldWatchEvent(ModificationWatchpointEvent event) {
296 threadTrace(event.thread()).fieldWatchEvent(event);
297 }
298
299 void threadDeathEvent(ThreadDeathEvent event) {
300 ThreadTrace trace = (ThreadTrace)traceMap.get(event.thread());
301 if (trace != null) { // only want threads we care about
302 trace.threadDeathEvent(event); // Forward event
303 }
304 }
305
306 /**
307 * A new class has been loaded.
308 * Set watchpoints on each of its fields
309 */
310 private void classPrepareEvent(ClassPrepareEvent event) {
311 EventRequestManager mgr = vm.eventRequestManager();
312 List fields = event.referenceType().visibleFields();
313 for (Iterator it = fields.iterator(); it.hasNext(); ) {
314 Field field = (Field)it.next();
315 ModificationWatchpointRequest req =
316 mgr.createModificationWatchpointRequest(field);
317 for (int i=0; i<excludes.length; ++i) {
318 req.addClassExclusionFilter(excludes[i]);
319 }
320 req.setSuspendPolicy(EventRequest.SUSPEND_NONE);
321 req.enable();
322 }
323 }
324
325 private void exceptionEvent(ExceptionEvent event) {
326 ThreadTrace trace = (ThreadTrace)traceMap.get(event.thread());
327 if (trace != null) { // only want threads we care about
328 trace.exceptionEvent(event); // Forward event
329 }
330 }
331
332 public void vmDeathEvent(VMDeathEvent event) {
333 vmDied = true;
334 writer.println("-- The application exited --");
335 }
336
337 public void vmDisconnectEvent(VMDisconnectEvent event) {
338 connected = false;
339 if (!vmDied) {
340 writer.println("-- The application has been disconnected --");
341 }
342 }
343}