Mercurial > hg > truffle
annotate agent/test/jdi/TestScaffold.java @ 3563:8780fa370aab
Support runtime calls to targets that don't fit in a 32-bit immediate: allow to query the maximum offset of a CiRuntimeCall target to determine the required immediate size and patch call sites with a mov/call instruction pair for indirect calls
author | Peter Hofer <peter.hofer@jku.at> |
---|---|
date | Wed, 31 Aug 2011 14:55:40 +0200 |
parents | c18cbe5936b8 |
children |
rev | line source |
---|---|
0 | 1 /* |
1552
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
0
diff
changeset
|
2 * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved. |
0 | 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 * | |
1552
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
0
diff
changeset
|
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
0
diff
changeset
|
20 * or visit www.oracle.com if you need additional information or have any |
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
0
diff
changeset
|
21 * questions. |
0 | 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 } |