1/*
2 * Copyright (c) 2001, 2013, 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
24import com.sun.jdi.*;
25import com.sun.jdi.request.*;
26import com.sun.jdi.event.*;
27import java.util.*;
28import java.io.*;
29
30/**
31 * Framework used by all JDI regression tests
32 */
33abstract 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                            redefine(rt);
546                        }
547                    }
548                }
549
550                public void vmStarted(VMStartEvent event) {
551                    synchronized(TestScaffold.this) {
552                        vmStartThread = event.thread();
553                        TestScaffold.this.notifyAll();
554                    }
555                }
556                /**
557                 * By default, we catch uncaught exceptions and print a msg.
558                 * The testcase must override the createDefaultExceptionRequest
559                 * method if it doesn't want this behavior.
560                 */
561                public void exceptionThrown(ExceptionEvent event) {
562                    if (TestScaffold.this.ourExceptionRequest != null &&
563                        TestScaffold.this.ourExceptionRequest.equals(
564                                                        event.request())) {
565                        /*
566                         * See
567                         *    5038723: com/sun/jdi/sde/TemperatureTableTest.java:
568                         *             intermittent ObjectCollectedException
569                         * Since this request was SUSPEND_NONE, the debuggee
570                         * could keep running and the calls below back into
571                         * the debuggee might not work.  That is why we
572                         * have this try/catch.
573                         */
574                        try {
575                            println("Note: Unexpected Debuggee Exception: " +
576                                    event.exception().referenceType().name() +
577                                    " at line " + event.location().lineNumber());
578                            TestScaffold.this.exceptionCaught = true;
579
580                            ObjectReference obj = event.exception();
581                            ReferenceType rtt = obj.referenceType();
582                            Field detail = rtt.fieldByName("detailMessage");
583                            Value val = obj.getValue(detail);
584                            println("detailMessage = " + val);
585
586                            /*
587                             * This code is commented out because it needs a thread
588                             * in which to do the invokeMethod and we don't have
589                             * one.  To enable this code change the request
590                             * to be SUSPEND_ALL in createDefaultExceptionRequest,
591                             * and then put this line
592                             *    mainThread = bpe.thread();
593                             * in the testcase after the line
594                             *    BreakpointEvent bpe = startToMain("....");
595                             */
596                            if (false) {
597                                List lll = rtt.methodsByName("printStackTrace");
598                                Method mm = (Method)lll.get(0);
599                                obj.invokeMethod(mainThread, mm, new ArrayList(0), 0);
600                            }
601                        } catch (Exception ee) {
602                            println("TestScaffold Exception while handling debuggee Exception: "
603                                    + ee);
604                        }
605                    }
606                }
607
608                public void vmDied(VMDeathEvent event) {
609                    vmDied = true;
610                    traceln("TS: vmDied called");
611                }
612
613                public void vmDisconnected(VMDisconnectEvent event) {
614                    synchronized(TestScaffold.this) {
615                        vmDisconnected = true;
616                        TestScaffold.this.notifyAll();
617                    }
618                }
619            });
620        if (connection.connector().name().equals("com.sun.jdi.CommandLineLaunch")) {
621            if (argInfo.targetVMArgs.length() > 0) {
622                if (connection.connectorArg("options").length() > 0) {
623                    throw new IllegalArgumentException("VM options in two places");
624                }
625                connection.setConnectorArg("options", argInfo.targetVMArgs);
626            }
627            if (argInfo.targetAppCommandLine.length() > 0) {
628                if (connection.connectorArg("main").length() > 0) {
629                    throw new IllegalArgumentException("Command line in two places");
630                }
631                connection.setConnectorArg("main", argInfo.targetAppCommandLine);
632            }
633        }
634
635        vm = connection.open();
636        requestManager = vm.eventRequestManager();
637        createDefaultEventRequests();
638        new EventHandler();
639    }
640
641
642    public VirtualMachine vm() {
643        return vm;
644    }
645
646    public EventRequestManager eventRequestManager() {
647        return requestManager;
648    }
649
650    public void addListener(TargetListener listener) {
651        traceln("TS: Adding listener " + listener);
652        listeners.add(listener);
653    }
654
655    public void removeListener(TargetListener listener) {
656        traceln("TS: Removing listener " + listener);
657        listeners.remove(listener);
658    }
659
660
661    protected void listenUntilVMDisconnect() {
662        try {
663            addListener (this);
664        } catch (Exception ex){
665            ex.printStackTrace();
666            testFailed = true;
667        } finally {
668            // Allow application to complete and shut down
669            resumeToVMDisconnect();
670        }
671    }
672
673    public synchronized ThreadReference waitForVMStart() {
674        while ((vmStartThread == null) && !vmDisconnected) {
675            try {
676                wait();
677            } catch (InterruptedException e) {
678            }
679        }
680
681        if (vmStartThread == null) {
682            throw new VMDisconnectedException();
683        }
684
685        return vmStartThread;
686    }
687
688    public synchronized void waitForVMDisconnect() {
689        traceln("TS: waitForVMDisconnect");
690        while (!vmDisconnected) {
691            try {
692                wait();
693            } catch (InterruptedException e) {
694            }
695        }
696        traceln("TS: waitForVMDisconnect: done");
697    }
698
699    public Event waitForRequestedEvent(final EventRequest request) {
700        class EventNotification {
701            Event event;
702            boolean disconnected = false;
703        }
704        final EventNotification en = new EventNotification();
705
706        TargetAdapter adapter = new TargetAdapter() {
707            public void eventReceived(Event event) {
708                if (request.equals(event.request())) {
709                    traceln("TS:Listener2: got requested event");
710                    synchronized (en) {
711                        en.event = event;
712                        en.notifyAll();
713                    }
714                    removeThisListener();
715                } else if (event instanceof VMDisconnectEvent) {
716                    traceln("TS:Listener2: got VMDisconnectEvent");
717                    synchronized (en) {
718                        en.disconnected = true;
719                        en.notifyAll();
720                    }
721                    removeThisListener();
722                }
723            }
724        };
725
726        addListener(adapter);
727
728        try {
729            synchronized (en) {
730                traceln("TS: waitForRequestedEvent: vm.resume called");
731                vm.resume();
732
733                while (!en.disconnected && (en.event == null)) {
734                    en.wait();
735                }
736            }
737        } catch (InterruptedException e) {
738            return null;
739        }
740
741        if (en.disconnected) {
742            throw new RuntimeException("VM Disconnected before requested event occurred");
743        }
744        return en.event;
745    }
746
747    private StepEvent doStep(ThreadReference thread, int gran, int depth) {
748        final StepRequest sr =
749                  requestManager.createStepRequest(thread, gran, depth);
750
751        sr.addClassExclusionFilter("java.*");
752        sr.addClassExclusionFilter("javax.*");
753        sr.addClassExclusionFilter("sun.*");
754        sr.addClassExclusionFilter("com.sun.*");
755        sr.addClassExclusionFilter("com.oracle.*");
756        sr.addClassExclusionFilter("oracle.*");
757        sr.addClassExclusionFilter("jdk.internal.*");
758        sr.addClassExclusionFilter("jdk.jfr.*");
759        sr.addCountFilter(1);
760        sr.enable();
761        StepEvent retEvent = (StepEvent)waitForRequestedEvent(sr);
762        requestManager.deleteEventRequest(sr);
763        return retEvent;
764    }
765
766    public StepEvent stepIntoInstruction(ThreadReference thread) {
767        return doStep(thread, StepRequest.STEP_MIN, StepRequest.STEP_INTO);
768    }
769
770    public StepEvent stepIntoLine(ThreadReference thread) {
771        return doStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_INTO);
772    }
773
774    public StepEvent stepOverInstruction(ThreadReference thread) {
775        return doStep(thread, StepRequest.STEP_MIN, StepRequest.STEP_OVER);
776    }
777
778    public StepEvent stepOverLine(ThreadReference thread) {
779        return doStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_OVER);
780    }
781
782    public StepEvent stepOut(ThreadReference thread) {
783        return doStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_OUT);
784    }
785
786    public BreakpointEvent resumeTo(Location loc) {
787        final BreakpointRequest request =
788            requestManager.createBreakpointRequest(loc);
789        request.addCountFilter(1);
790        request.enable();
791        return (BreakpointEvent)waitForRequestedEvent(request);
792    }
793
794    public ReferenceType findReferenceType(String name) {
795        List rts = vm.classesByName(name);
796        Iterator iter = rts.iterator();
797        while (iter.hasNext()) {
798            ReferenceType rt = (ReferenceType)iter.next();
799            if (rt.name().equals(name)) {
800                return rt;
801            }
802        }
803        return null;
804    }
805
806    public Method findMethod(ReferenceType rt, String name, String signature) {
807        List methods = rt.methods();
808        Iterator iter = methods.iterator();
809        while (iter.hasNext()) {
810            Method method = (Method)iter.next();
811            if (method.name().equals(name) &&
812                method.signature().equals(signature)) {
813                return method;
814            }
815        }
816        return null;
817    }
818
819    public Location findLocation(ReferenceType rt, int lineNumber)
820                         throws AbsentInformationException {
821        List locs = rt.locationsOfLine(lineNumber);
822        if (locs.size() == 0) {
823            throw new IllegalArgumentException("Bad line number");
824        } else if (locs.size() > 1) {
825            throw new IllegalArgumentException("Line number has multiple locations");
826        }
827
828        return (Location)locs.get(0);
829    }
830
831    public BreakpointEvent resumeTo(String clsName, String methodName,
832                                         String methodSignature) {
833        ReferenceType rt = findReferenceType(clsName);
834        if (rt == null) {
835            rt = resumeToPrepareOf(clsName).referenceType();
836        }
837
838        Method method = findMethod(rt, methodName, methodSignature);
839        if (method == null) {
840            throw new IllegalArgumentException("Bad method name/signature: "
841                    + clsName + "." + methodName + ":" + methodSignature);
842        }
843
844        return resumeTo(method.location());
845    }
846
847    public BreakpointEvent resumeTo(String clsName, int lineNumber) throws AbsentInformationException {
848        ReferenceType rt = findReferenceType(clsName);
849        if (rt == null) {
850            rt = resumeToPrepareOf(clsName).referenceType();
851        }
852
853        return resumeTo(findLocation(rt, lineNumber));
854    }
855
856    public ClassPrepareEvent resumeToPrepareOf(String className) {
857        final ClassPrepareRequest request =
858            requestManager.createClassPrepareRequest();
859        request.addClassFilter(className);
860        request.addCountFilter(1);
861        request.enable();
862        return (ClassPrepareEvent)waitForRequestedEvent(request);
863    }
864
865    public void resumeForMsecs(long msecs) {
866        try {
867            addListener (this);
868        } catch (Exception ex){
869            ex.printStackTrace();
870            testFailed = true;
871            return;
872        }
873
874        try {
875            vm().resume();
876        } catch (VMDisconnectedException e) {
877        }
878
879        if (!vmDisconnected) {
880            try {
881                System.out.println("Sleeping for " + msecs + " milleseconds");
882                Thread.sleep(msecs);
883                vm().suspend();
884            } catch (InterruptedException e) {
885            }
886        }
887    }
888
889    public void resumeToVMDisconnect() {
890        try {
891            traceln("TS: resumeToVMDisconnect: vm.resume called");
892            vm.resume();
893        } catch (VMDisconnectedException e) {
894            // clean up below
895        }
896        waitForVMDisconnect();
897    }
898
899    public void shutdown() {
900        shutdown(null);
901    }
902
903    public void shutdown(String message) {
904        traceln("TS: shutdown: vmDied= " + vmDied +
905                 ", vmDisconnected= " + vmDisconnected +
906                 ", connection = " + connection);
907
908        if ((connection != null)) {
909            try {
910                connection.disposeVM();
911             } catch (VMDisconnectedException e) {
912                // Shutting down after the VM has gone away. This is
913                // not an error, and we just ignore it.
914            }
915        } else {
916            traceln("TS: shutdown: disposeVM not called");
917        }
918        if (message != null) {
919            println(message);
920        }
921
922        vmDied = true;
923        vmDisconnected = true;
924    }
925}
926