agent/test/jdi/TestScaffold.java

Mon, 28 Jul 2014 15:06:38 -0700

author
fzhinkin
date
Mon, 28 Jul 2014 15:06:38 -0700
changeset 6997
dbb05f6d93c4
parent 1907
c18cbe5936b8
child 6876
710a3c8b516e
permissions
-rw-r--r--

8051344: JVM crashed in Compile::start() during method parsing w/ UseRTMDeopt turned on
Summary: call rtm_deopt() only if there were no compilation bailouts before.
Reviewed-by: kvn

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

mercurial