hotspot/src/jdk.hotspot.agent/test/jdi/TestScaffold.java
changeset 40100 a9b665f0879f
parent 40099 24807846ffe1
child 40101 3b8101f0fd65
equal deleted inserted replaced
40099:24807846ffe1 40100:a9b665f0879f
     1 /*
       
     2  * Copyright (c) 2002, 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 
       
    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 }