0
|
1 /*
|
|
2 * Copyright 2002 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 import com.sun.jdi.*;
|
|
26 import com.sun.jdi.request.*;
|
|
27 import com.sun.jdi.event.*;
|
|
28 import java.util.*;
|
|
29 import java.io.*;
|
|
30
|
|
31 /**
|
|
32 * Framework used by all JDI regression tests
|
|
33 */
|
|
34 abstract public class TestScaffold extends TargetAdapter {
|
|
35 private boolean shouldTrace = false;
|
|
36 private VMConnection connection;
|
|
37 private VirtualMachine vm;
|
|
38 private EventRequestManager requestManager;
|
|
39 private List listeners = Collections.synchronizedList(new LinkedList());
|
|
40
|
|
41 /**
|
|
42 * We create a VMDeathRequest, SUSPEND_ALL, to sync the BE and FE.
|
|
43 */
|
|
44 //private VMDeathRequest ourVMDeathRequest = null;
|
|
45 Object ourVMDeathRequest = null;
|
|
46
|
|
47 /**
|
|
48 * We create an ExceptionRequest, SUSPEND_NONE so that we can
|
|
49 * catch it and output a msg if an exception occurs in the
|
|
50 * debuggee.
|
|
51 */
|
|
52 private ExceptionRequest ourExceptionRequest = null;
|
|
53
|
|
54 /**
|
|
55 * If we do catch an uncaught exception, we set this true
|
|
56 * so the testcase can find out if it wants to.
|
|
57 */
|
|
58 private boolean exceptionCaught = false;
|
|
59 ThreadReference vmStartThread = null;
|
|
60 boolean vmDied = false;
|
|
61 boolean vmDisconnected = false;
|
|
62 final String[] args;
|
|
63 protected boolean testFailed = false;
|
|
64
|
|
65 static private class ArgInfo {
|
|
66 String targetVMArgs = "";
|
|
67 String targetAppCommandLine = "";
|
|
68 String connectorSpec = "com.sun.jdi.CommandLineLaunch:";
|
|
69 int traceFlags = 0;
|
|
70 }
|
|
71
|
|
72 /**
|
|
73 * An easy way to sleep for awhile
|
|
74 */
|
|
75 public void mySleep(int millis) {
|
|
76 try {
|
|
77 Thread.sleep(millis);
|
|
78 } catch (InterruptedException ee) {
|
|
79 }
|
|
80 }
|
|
81
|
|
82 boolean getExceptionCaught() {
|
|
83 return exceptionCaught;
|
|
84 }
|
|
85
|
|
86 void setExceptionCaught(boolean value) {
|
|
87 exceptionCaught = value;
|
|
88 }
|
|
89
|
|
90 /**
|
|
91 * Return true if eventSet contains the VMDeathEvent for the request in
|
|
92 * the ourVMDeathRequest ivar.
|
|
93 */
|
|
94 private boolean containsOurVMDeathRequest(EventSet eventSet) {
|
|
95 if (ourVMDeathRequest != null) {
|
|
96 Iterator myIter = eventSet.iterator();
|
|
97 while (myIter.hasNext()) {
|
|
98 Event myEvent = (Event)myIter.next();
|
|
99 if (!(myEvent instanceof VMDeathEvent)) {
|
|
100 // We assume that an EventSet contains only VMDeathEvents
|
|
101 // or no VMDeathEvents.
|
|
102 break;
|
|
103 }
|
|
104 if (ourVMDeathRequest.equals(myEvent.request())) {
|
|
105 return true;
|
|
106 }
|
|
107 }
|
|
108 }
|
|
109 return false;
|
|
110 }
|
|
111
|
|
112 /************************************************************************
|
|
113 * The following methods override those in our base class, TargetAdapter.
|
|
114 *************************************************************************/
|
|
115
|
|
116 /**
|
|
117 * Events handled directly by scaffold always resume (well, almost always)
|
|
118 */
|
|
119 public void eventSetComplete(EventSet set) {
|
|
120 // The listener in connect(..) resumes after receiving our
|
|
121 // special VMDeathEvent. We can't also do the resume
|
|
122 // here or we will probably get a VMDisconnectedException
|
|
123 if (!containsOurVMDeathRequest(set)) {
|
|
124 traceln("TS: set.resume() called");
|
|
125 set.resume();
|
|
126 }
|
|
127 }
|
|
128
|
|
129 /**
|
|
130 * This method sets up default requests.
|
|
131 * Testcases can override this to change default behavior.
|
|
132 */
|
|
133 protected void createDefaultEventRequests() {
|
|
134 createDefaultVMDeathRequest();
|
|
135 createDefaultExceptionRequest();
|
|
136 }
|
|
137
|
|
138 /**
|
|
139 * We want the BE to stop when it issues a VMDeathEvent in order to
|
|
140 * give the FE time to complete handling events that occured before
|
|
141 * the VMDeath. When we get the VMDeathEvent for this request in
|
|
142 * the listener in connect(), we will do a resume.
|
|
143 * If a testcase wants to do something special with VMDeathEvent's,
|
|
144 * then it should override this method with an empty method or
|
|
145 * whatever in order to suppress the automatic resume. The testcase
|
|
146 * will then be responsible for the handling of VMDeathEvents. It
|
|
147 * has to be sure that it does a resume if it gets a VMDeathEvent
|
|
148 * with SUSPEND_ALL, and it has to be sure that it doesn't do a
|
|
149 * resume after getting a VMDeath with SUSPEND_NONE (the automatically
|
|
150 * generated VMDeathEvent.)
|
|
151 */
|
|
152 protected void createDefaultVMDeathRequest() {
|
|
153 // ourVMDeathRequest = requestManager.createVMDeathRequest();
|
|
154 // ourVMDeathRequest.setSuspendPolicy(EventRequest.SUSPEND_ALL);
|
|
155 // ourVMDeathRequest.enable();
|
|
156 }
|
|
157
|
|
158 /**
|
|
159 * This will allow us to print a warning if a debuggee gets an
|
|
160 * unexpected exception. The unexpected exception will be handled in
|
|
161 * the exceptionThrown method in the listener created in the connect()
|
|
162 * method.
|
|
163 * If a testcase does not want an uncaught exception to cause a
|
|
164 * msg, it must override this method.
|
|
165 */
|
|
166 protected void createDefaultExceptionRequest() {
|
|
167 ourExceptionRequest = requestManager.createExceptionRequest(null,
|
|
168 false, true);
|
|
169
|
|
170 // We can't afford to make this be other than SUSPEND_NONE. Otherwise,
|
|
171 // it would have to be resumed. If our connect() listener resumes it,
|
|
172 // what about the case where the EventSet contains other events with
|
|
173 // SUSPEND_ALL and there are other listeners who expect the BE to still
|
|
174 // be suspended when their handlers get called?
|
|
175 ourExceptionRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE);
|
|
176 ourExceptionRequest.enable();
|
|
177 }
|
|
178
|
|
179 private class EventHandler implements Runnable {
|
|
180 EventHandler() {
|
|
181 Thread thread = new Thread(this);
|
|
182 thread.setDaemon(true);
|
|
183 thread.start();
|
|
184 }
|
|
185
|
|
186 private void notifyEvent(TargetListener listener, Event event) {
|
|
187 if (event instanceof BreakpointEvent) {
|
|
188 listener.breakpointReached((BreakpointEvent)event);
|
|
189 } else if (event instanceof ExceptionEvent) {
|
|
190 listener.exceptionThrown((ExceptionEvent)event);
|
|
191 } else if (event instanceof StepEvent) {
|
|
192 listener.stepCompleted((StepEvent)event);
|
|
193 } else if (event instanceof ClassPrepareEvent) {
|
|
194 listener.classPrepared((ClassPrepareEvent)event);
|
|
195 } else if (event instanceof ClassUnloadEvent) {
|
|
196 listener.classUnloaded((ClassUnloadEvent)event);
|
|
197 } else if (event instanceof MethodEntryEvent) {
|
|
198 listener.methodEntered((MethodEntryEvent)event);
|
|
199 } else if (event instanceof MethodExitEvent) {
|
|
200 listener.methodExited((MethodExitEvent)event);
|
|
201 } else if (event instanceof AccessWatchpointEvent) {
|
|
202 listener.fieldAccessed((AccessWatchpointEvent)event);
|
|
203 } else if (event instanceof ModificationWatchpointEvent) {
|
|
204 listener.fieldModified((ModificationWatchpointEvent)event);
|
|
205 } else if (event instanceof ThreadStartEvent) {
|
|
206 listener.threadStarted((ThreadStartEvent)event);
|
|
207 } else if (event instanceof ThreadDeathEvent) {
|
|
208 listener.threadDied((ThreadDeathEvent)event);
|
|
209 } else if (event instanceof VMStartEvent) {
|
|
210 listener.vmStarted((VMStartEvent)event);
|
|
211 } else if (event instanceof VMDeathEvent) {
|
|
212 listener.vmDied((VMDeathEvent)event);
|
|
213 } else if (event instanceof VMDisconnectEvent) {
|
|
214 listener.vmDisconnected((VMDisconnectEvent)event);
|
|
215 } else {
|
|
216 throw new InternalError("Unknown event type: " + event.getClass());
|
|
217 }
|
|
218 }
|
|
219
|
|
220 private void traceSuspendPolicy(int policy) {
|
|
221 if (shouldTrace) {
|
|
222 switch (policy) {
|
|
223 case EventRequest.SUSPEND_NONE:
|
|
224 traceln("TS: eventHandler: suspend = SUSPEND_NONE");
|
|
225 break;
|
|
226 case EventRequest.SUSPEND_ALL:
|
|
227 traceln("TS: eventHandler: suspend = SUSPEND_ALL");
|
|
228 break;
|
|
229 case EventRequest.SUSPEND_EVENT_THREAD:
|
|
230 traceln("TS: eventHandler: suspend = SUSPEND_EVENT_THREAD");
|
|
231 break;
|
|
232 }
|
|
233 }
|
|
234 }
|
|
235
|
|
236 public void run() {
|
|
237 boolean connected = true;
|
|
238 do {
|
|
239 try {
|
|
240 EventSet set = vm.eventQueue().remove();
|
|
241 traceSuspendPolicy(set.suspendPolicy());
|
|
242 synchronized (listeners) {
|
|
243 ListIterator iter = listeners.listIterator();
|
|
244 while (iter.hasNext()) {
|
|
245 TargetListener listener = (TargetListener)iter.next();
|
|
246 traceln("TS: eventHandler: listener = " + listener);
|
|
247 listener.eventSetReceived(set);
|
|
248 if (listener.shouldRemoveListener()) {
|
|
249 iter.remove();
|
|
250 } else {
|
|
251 Iterator jter = set.iterator();
|
|
252 while (jter.hasNext()) {
|
|
253 Event event = (Event)jter.next();
|
|
254 traceln("TS: eventHandler: event = " + event.getClass());
|
|
255
|
|
256 if (event instanceof VMDisconnectEvent) {
|
|
257 connected = false;
|
|
258 }
|
|
259 listener.eventReceived(event);
|
|
260 if (listener.shouldRemoveListener()) {
|
|
261 iter.remove();
|
|
262 break;
|
|
263 }
|
|
264 notifyEvent(listener, event);
|
|
265 if (listener.shouldRemoveListener()) {
|
|
266 iter.remove();
|
|
267 break;
|
|
268 }
|
|
269 }
|
|
270 traceln("TS: eventHandler: end of events loop");
|
|
271 if (!listener.shouldRemoveListener()) {
|
|
272 traceln("TS: eventHandler: calling ESC");
|
|
273 listener.eventSetComplete(set);
|
|
274 if (listener.shouldRemoveListener()) {
|
|
275 iter.remove();
|
|
276 }
|
|
277 }
|
|
278 }
|
|
279 traceln("TS: eventHandler: end of listeners loop");
|
|
280 }
|
|
281 }
|
|
282 } catch (InterruptedException e) {
|
|
283 traceln("TS: eventHandler: InterruptedException");
|
|
284 } catch (Exception e) {
|
|
285 failure("FAILED: Exception occured in eventHandler: " + e);
|
|
286 e.printStackTrace();
|
|
287 connected = false;
|
|
288 synchronized(TestScaffold.this) {
|
|
289 // This will make the waiters such as waitForVMDisconnect
|
|
290 // exit their wait loops.
|
|
291 vmDisconnected = true;
|
|
292 TestScaffold.this.notifyAll();
|
|
293 }
|
|
294 }
|
|
295 traceln("TS: eventHandler: End of outerloop");
|
|
296 } while (connected);
|
|
297 traceln("TS: eventHandler: finished");
|
|
298 }
|
|
299 }
|
|
300
|
|
301 /**
|
|
302 * Constructor
|
|
303 */
|
|
304 public TestScaffold(String[] args) {
|
|
305 this.args = args;
|
|
306 }
|
|
307
|
|
308 public void enableScaffoldTrace() {
|
|
309 this.shouldTrace = true;
|
|
310 }
|
|
311
|
|
312 public void disableScaffoldTrace() {
|
|
313 this.shouldTrace = false;
|
|
314 }
|
|
315
|
|
316
|
|
317 protected void startUp(String targetName) {
|
|
318 List argList = new ArrayList(Arrays.asList(args));
|
|
319 argList.add(targetName);
|
|
320 println("run args: " + argList);
|
|
321 connect((String[]) argList.toArray(args));
|
|
322 waitForVMStart();
|
|
323 }
|
|
324
|
|
325 protected BreakpointEvent startToMain(String targetName) {
|
|
326 startUp(targetName);
|
|
327 traceln("TS: back from startUp");
|
|
328 BreakpointEvent bpr = resumeTo(targetName, "main", "([Ljava/lang/String;)V");
|
|
329 waitForInput();
|
|
330 return bpr;
|
|
331 }
|
|
332
|
|
333 protected void waitForInput() {
|
|
334 if (System.getProperty("jpda.wait") != null) {
|
|
335 try {
|
|
336 System.err.println("Press <enter> to continue");
|
|
337 System.in.read();
|
|
338 System.err.println("running...");
|
|
339
|
|
340 } catch(Exception e) {
|
|
341 }
|
|
342 }
|
|
343 }
|
|
344
|
|
345 /*
|
|
346 * Test cases should implement tests in runTests and should
|
|
347 * initiate testing by calling run().
|
|
348 */
|
|
349 abstract protected void runTests() throws Exception;
|
|
350
|
|
351 final public void startTests() throws Exception {
|
|
352 try {
|
|
353 runTests();
|
|
354 } finally {
|
|
355 shutdown();
|
|
356 }
|
|
357 }
|
|
358
|
|
359 protected void println(String str) {
|
|
360 System.err.println(str);
|
|
361 }
|
|
362
|
|
363 protected void print(String str) {
|
|
364 System.err.print(str);
|
|
365 }
|
|
366
|
|
367 protected void traceln(String str) {
|
|
368 if (shouldTrace) {
|
|
369 println(str);
|
|
370 }
|
|
371 }
|
|
372
|
|
373 protected void failure(String str) {
|
|
374 println(str);
|
|
375 testFailed = true;
|
|
376 }
|
|
377
|
|
378 private ArgInfo parseArgs(String args[]) {
|
|
379 ArgInfo argInfo = new ArgInfo();
|
|
380 for (int i = 0; i < args.length; i++) {
|
|
381 if (args[i].equals("-connect")) {
|
|
382 i++;
|
|
383 argInfo.connectorSpec = args[i];
|
|
384 } else if (args[i].equals("-trace")) {
|
|
385 i++;
|
|
386 argInfo.traceFlags = Integer.decode(args[i]).intValue();
|
|
387 } else if (args[i].startsWith("-J")) {
|
|
388 argInfo.targetVMArgs += (args[i].substring(2) + ' ');
|
|
389
|
|
390 /*
|
|
391 * classpath can span two arguments so we need to handle
|
|
392 * it specially.
|
|
393 */
|
|
394 if (args[i].equals("-J-classpath")) {
|
|
395 i++;
|
|
396 argInfo.targetVMArgs += (args[i] + ' ');
|
|
397 }
|
|
398 } else {
|
|
399 argInfo.targetAppCommandLine += (args[i] + ' ');
|
|
400 }
|
|
401 }
|
|
402 return argInfo;
|
|
403 }
|
|
404
|
|
405 /**
|
|
406 * This is called to connect to a debuggee VM. It starts the VM and
|
|
407 * installs a listener to catch VMStartEvent, our default events, and
|
|
408 * VMDisconnectedEvent. When these events appear, that is remembered
|
|
409 * and waiters are notified.
|
|
410 * This is normally called in the main thread of the test case.
|
|
411 * It starts up an EventHandler thread that gets events coming in
|
|
412 * from the debuggee and distributes them to listeners. That thread
|
|
413 * keeps running until a VMDisconnectedEvent occurs or some exception
|
|
414 * occurs during its processing.
|
|
415 *
|
|
416 * The 'listenUntilVMDisconnect' method adds 'this' as a listener.
|
|
417 * This means that 'this's vmDied method will get called. This has a
|
|
418 * default impl in TargetAdapter.java which can be overridden in the
|
|
419 * testcase.
|
|
420 *
|
|
421 * waitForRequestedEvent also adds an adaptor listener that listens
|
|
422 * for the particular event it is supposed to wait for (and it also
|
|
423 * catches VMDisconnectEvents.) This listener is removed once
|
|
424 * its eventReceived method is called.
|
|
425 * waitForRequestedEvent is called by most of the methods to do bkpts,
|
|
426 * etc.
|
|
427 */
|
|
428 public void connect(String args[]) {
|
|
429 ArgInfo argInfo = parseArgs(args);
|
|
430
|
|
431 argInfo.targetVMArgs += VMConnection.getDebuggeeVMOptions();
|
|
432 connection = new VMConnection(argInfo.connectorSpec,
|
|
433 argInfo.traceFlags);
|
|
434
|
|
435 addListener(new TargetAdapter() {
|
|
436 public void eventSetComplete(EventSet set) {
|
|
437 if (TestScaffold.this.containsOurVMDeathRequest(set)) {
|
|
438 traceln("TS: connect: set.resume() called");
|
|
439 set.resume();
|
|
440
|
|
441 // Note that we want to do the above resume before
|
|
442 // waking up any sleepers.
|
|
443 synchronized(TestScaffold.this) {
|
|
444 TestScaffold.this.notifyAll();
|
|
445 }
|
|
446 }
|
|
447 }
|
|
448
|
|
449 public void vmStarted(VMStartEvent event) {
|
|
450 synchronized(TestScaffold.this) {
|
|
451 vmStartThread = event.thread();
|
|
452 TestScaffold.this.notifyAll();
|
|
453 }
|
|
454 }
|
|
455 /**
|
|
456 * By default, we catch uncaught exceptions and print a msg.
|
|
457 * The testcase must override the createDefaultExceptionRequest
|
|
458 * method if it doesn't want this behavior.
|
|
459 */
|
|
460 public void exceptionThrown(ExceptionEvent event) {
|
|
461 if (TestScaffold.this.ourExceptionRequest != null &&
|
|
462 TestScaffold.this.ourExceptionRequest.equals(
|
|
463 event.request())) {
|
|
464 println("Note: Unexpected Debuggee Exception: " +
|
|
465 event.exception().referenceType().name() +
|
|
466 " at line " + event.location().lineNumber());
|
|
467 TestScaffold.this.exceptionCaught = true;
|
|
468 }
|
|
469 }
|
|
470
|
|
471 public void vmDied(VMDeathEvent event) {
|
|
472 vmDied = true;
|
|
473 traceln("TS: vmDied called");
|
|
474 }
|
|
475
|
|
476 public void vmDisconnected(VMDisconnectEvent event) {
|
|
477 synchronized(TestScaffold.this) {
|
|
478 vmDisconnected = true;
|
|
479 TestScaffold.this.notifyAll();
|
|
480 }
|
|
481 }
|
|
482 });
|
|
483 if (connection.connector().name().equals("com.sun.jdi.CommandLineLaunch")) {
|
|
484 if (argInfo.targetVMArgs.length() > 0) {
|
|
485 if (connection.connectorArg("options").length() > 0) {
|
|
486 throw new IllegalArgumentException("VM options in two places");
|
|
487 }
|
|
488 connection.setConnectorArg("options", argInfo.targetVMArgs);
|
|
489 }
|
|
490 if (argInfo.targetAppCommandLine.length() > 0) {
|
|
491 if (connection.connectorArg("main").length() > 0) {
|
|
492 throw new IllegalArgumentException("Command line in two places");
|
|
493 }
|
|
494 connection.setConnectorArg("main", argInfo.targetAppCommandLine);
|
|
495 }
|
|
496 }
|
|
497
|
|
498 vm = connection.open();
|
|
499 requestManager = vm.eventRequestManager();
|
|
500 createDefaultEventRequests();
|
|
501 new EventHandler();
|
|
502 }
|
|
503
|
|
504
|
|
505 public VirtualMachine vm() {
|
|
506 return vm;
|
|
507 }
|
|
508
|
|
509 public EventRequestManager eventRequestManager() {
|
|
510 return requestManager;
|
|
511 }
|
|
512
|
|
513 public void addListener(TargetListener listener) {
|
|
514 traceln("TS: Adding listener " + listener);
|
|
515 listeners.add(listener);
|
|
516 }
|
|
517
|
|
518 public void removeListener(TargetListener listener) {
|
|
519 traceln("TS: Removing listener " + listener);
|
|
520 listeners.remove(listener);
|
|
521 }
|
|
522
|
|
523
|
|
524 protected void listenUntilVMDisconnect() {
|
|
525 try {
|
|
526 addListener (this);
|
|
527 } catch (Exception ex){
|
|
528 ex.printStackTrace();
|
|
529 testFailed = true;
|
|
530 } finally {
|
|
531 // Allow application to complete and shut down
|
|
532 resumeToVMDisconnect();
|
|
533 }
|
|
534 }
|
|
535
|
|
536 public synchronized ThreadReference waitForVMStart() {
|
|
537 while ((vmStartThread == null) && !vmDisconnected) {
|
|
538 try {
|
|
539 wait();
|
|
540 } catch (InterruptedException e) {
|
|
541 }
|
|
542 }
|
|
543
|
|
544 if (vmStartThread == null) {
|
|
545 throw new VMDisconnectedException();
|
|
546 }
|
|
547
|
|
548 return vmStartThread;
|
|
549 }
|
|
550
|
|
551 public synchronized void waitForVMDisconnect() {
|
|
552 traceln("TS: waitForVMDisconnect");
|
|
553 while (!vmDisconnected) {
|
|
554 try {
|
|
555 wait();
|
|
556 } catch (InterruptedException e) {
|
|
557 }
|
|
558 }
|
|
559 traceln("TS: waitForVMDisconnect: done");
|
|
560 }
|
|
561
|
|
562 public Event waitForRequestedEvent(final EventRequest request) {
|
|
563 class EventNotification {
|
|
564 Event event;
|
|
565 boolean disconnected = false;
|
|
566 }
|
|
567 final EventNotification en = new EventNotification();
|
|
568
|
|
569 TargetAdapter adapter = new TargetAdapter() {
|
|
570 public void eventReceived(Event event) {
|
|
571 if (request.equals(event.request())) {
|
|
572 traceln("TS:Listener2: got requested event");
|
|
573 synchronized (en) {
|
|
574 en.event = event;
|
|
575 en.notifyAll();
|
|
576 }
|
|
577 removeThisListener();
|
|
578 } else if (event instanceof VMDisconnectEvent) {
|
|
579 traceln("TS:Listener2: got VMDisconnectEvent");
|
|
580 synchronized (en) {
|
|
581 en.disconnected = true;
|
|
582 en.notifyAll();
|
|
583 }
|
|
584 removeThisListener();
|
|
585 }
|
|
586 }
|
|
587 };
|
|
588
|
|
589 addListener(adapter);
|
|
590
|
|
591 try {
|
|
592 synchronized (en) {
|
|
593 traceln("TS: waitForRequestedEvent: vm.resume called");
|
|
594 vm.resume();
|
|
595
|
|
596 while (!en.disconnected && (en.event == null)) {
|
|
597 en.wait();
|
|
598 }
|
|
599 }
|
|
600 } catch (InterruptedException e) {
|
|
601 return null;
|
|
602 }
|
|
603
|
|
604 if (en.disconnected) {
|
|
605 throw new RuntimeException("VM Disconnected before requested event occurred");
|
|
606 }
|
|
607 return en.event;
|
|
608 }
|
|
609
|
|
610 private StepEvent doStep(ThreadReference thread, int gran, int depth) {
|
|
611 final StepRequest sr =
|
|
612 requestManager.createStepRequest(thread, gran, depth);
|
|
613
|
|
614 sr.addClassExclusionFilter("java.*");
|
|
615 sr.addClassExclusionFilter("sun.*");
|
|
616 sr.addClassExclusionFilter("com.sun.*");
|
|
617 sr.addCountFilter(1);
|
|
618 sr.enable();
|
|
619 StepEvent retEvent = (StepEvent)waitForRequestedEvent(sr);
|
|
620 requestManager.deleteEventRequest(sr);
|
|
621 return retEvent;
|
|
622 }
|
|
623
|
|
624 public StepEvent stepIntoInstruction(ThreadReference thread) {
|
|
625 return doStep(thread, StepRequest.STEP_MIN, StepRequest.STEP_INTO);
|
|
626 }
|
|
627
|
|
628 public StepEvent stepIntoLine(ThreadReference thread) {
|
|
629 return doStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_INTO);
|
|
630 }
|
|
631
|
|
632 public StepEvent stepOverInstruction(ThreadReference thread) {
|
|
633 return doStep(thread, StepRequest.STEP_MIN, StepRequest.STEP_OVER);
|
|
634 }
|
|
635
|
|
636 public StepEvent stepOverLine(ThreadReference thread) {
|
|
637 return doStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_OVER);
|
|
638 }
|
|
639
|
|
640 public StepEvent stepOut(ThreadReference thread) {
|
|
641 return doStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_OUT);
|
|
642 }
|
|
643
|
|
644 public BreakpointEvent resumeTo(Location loc) {
|
|
645 final BreakpointRequest request =
|
|
646 requestManager.createBreakpointRequest(loc);
|
|
647 request.addCountFilter(1);
|
|
648 request.enable();
|
|
649 return (BreakpointEvent)waitForRequestedEvent(request);
|
|
650 }
|
|
651
|
|
652 public ReferenceType findReferenceType(String name) {
|
|
653 List rts = vm.classesByName(name);
|
|
654 Iterator iter = rts.iterator();
|
|
655 while (iter.hasNext()) {
|
|
656 ReferenceType rt = (ReferenceType)iter.next();
|
|
657 if (rt.name().equals(name)) {
|
|
658 return rt;
|
|
659 }
|
|
660 }
|
|
661 return null;
|
|
662 }
|
|
663
|
|
664 public Method findMethod(ReferenceType rt, String name, String signature) {
|
|
665 List methods = rt.methods();
|
|
666 Iterator iter = methods.iterator();
|
|
667 while (iter.hasNext()) {
|
|
668 Method method = (Method)iter.next();
|
|
669 if (method.name().equals(name) &&
|
|
670 method.signature().equals(signature)) {
|
|
671 return method;
|
|
672 }
|
|
673 }
|
|
674 return null;
|
|
675 }
|
|
676
|
|
677 public Location findLocation(ReferenceType rt, int lineNumber)
|
|
678 throws AbsentInformationException {
|
|
679 List locs = rt.locationsOfLine(lineNumber);
|
|
680 if (locs.size() == 0) {
|
|
681 throw new IllegalArgumentException("Bad line number");
|
|
682 } else if (locs.size() > 1) {
|
|
683 throw new IllegalArgumentException("Line number has multiple locations");
|
|
684 }
|
|
685
|
|
686 return (Location)locs.get(0);
|
|
687 }
|
|
688
|
|
689 public BreakpointEvent resumeTo(String clsName, String methodName,
|
|
690 String methodSignature) {
|
|
691 ReferenceType rt = findReferenceType(clsName);
|
|
692 if (rt == null) {
|
|
693 rt = resumeToPrepareOf(clsName).referenceType();
|
|
694 }
|
|
695
|
|
696 Method method = findMethod(rt, methodName, methodSignature);
|
|
697 if (method == null) {
|
|
698 throw new IllegalArgumentException("Bad method name/signature");
|
|
699 }
|
|
700
|
|
701 return resumeTo(method.location());
|
|
702 }
|
|
703
|
|
704 public BreakpointEvent resumeTo(String clsName, int lineNumber) throws AbsentInformationException {
|
|
705 ReferenceType rt = findReferenceType(clsName);
|
|
706 if (rt == null) {
|
|
707 rt = resumeToPrepareOf(clsName).referenceType();
|
|
708 }
|
|
709
|
|
710 return resumeTo(findLocation(rt, lineNumber));
|
|
711 }
|
|
712
|
|
713 public ClassPrepareEvent resumeToPrepareOf(String className) {
|
|
714 final ClassPrepareRequest request =
|
|
715 requestManager.createClassPrepareRequest();
|
|
716 request.addClassFilter(className);
|
|
717 request.addCountFilter(1);
|
|
718 request.enable();
|
|
719 return (ClassPrepareEvent)waitForRequestedEvent(request);
|
|
720 }
|
|
721
|
|
722 public void resumeToVMDisconnect() {
|
|
723 try {
|
|
724 traceln("TS: resumeToVMDisconnect: vm.resume called");
|
|
725 vm.resume();
|
|
726 } catch (VMDisconnectedException e) {
|
|
727 // clean up below
|
|
728 }
|
|
729 waitForVMDisconnect();
|
|
730 }
|
|
731
|
|
732 public void shutdown() {
|
|
733 shutdown(null);
|
|
734 }
|
|
735
|
|
736 public void shutdown(String message) {
|
|
737 traceln("TS: shutdown: vmDied= " + vmDied +
|
|
738 ", vmDisconnected= " + vmDisconnected +
|
|
739 ", connection = " + connection);
|
|
740
|
|
741 if ((connection != null)) {
|
|
742 try {
|
|
743 connection.disposeVM();
|
|
744 } catch (VMDisconnectedException e) {
|
|
745 // Shutting down after the VM has gone away. This is
|
|
746 // not an error, and we just ignore it.
|
|
747 }
|
|
748 } else {
|
|
749 traceln("TS: shutdown: disposeVM not called");
|
|
750 }
|
|
751 if (message != null) {
|
|
752 println(message);
|
|
753 }
|
|
754
|
|
755 vmDied = true;
|
|
756 vmDisconnected = true;
|
|
757 }
|
|
758 }
|