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