duke@435: /* duke@435: * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. duke@435: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@435: * duke@435: * This code is free software; you can redistribute it and/or modify it duke@435: * under the terms of the GNU General Public License version 2 only, as duke@435: * published by the Free Software Foundation. duke@435: * duke@435: * This code is distributed in the hope that it will be useful, but WITHOUT duke@435: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@435: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@435: * version 2 for more details (a copy is included in the LICENSE file that duke@435: * accompanied this code). duke@435: * duke@435: * You should have received a copy of the GNU General Public License version duke@435: * 2 along with this work; if not, write to the Free Software Foundation, duke@435: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@435: * duke@435: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, duke@435: * CA 95054 USA or visit www.sun.com if you need additional information or duke@435: * have any questions. duke@435: * duke@435: */ duke@435: duke@435: import com.sun.jdi.*; duke@435: import com.sun.jdi.request.*; duke@435: import com.sun.jdi.event.*; duke@435: import java.util.*; duke@435: import java.io.*; duke@435: duke@435: /** duke@435: * Framework used by all JDI regression tests duke@435: */ duke@435: abstract public class TestScaffold extends TargetAdapter { duke@435: private boolean shouldTrace = false; duke@435: private VMConnection connection; duke@435: private VirtualMachine vm; duke@435: private EventRequestManager requestManager; duke@435: private List listeners = Collections.synchronizedList(new LinkedList()); duke@435: duke@435: /** duke@435: * We create a VMDeathRequest, SUSPEND_ALL, to sync the BE and FE. duke@435: */ duke@435: //private VMDeathRequest ourVMDeathRequest = null; duke@435: Object ourVMDeathRequest = null; duke@435: duke@435: /** duke@435: * We create an ExceptionRequest, SUSPEND_NONE so that we can duke@435: * catch it and output a msg if an exception occurs in the duke@435: * debuggee. duke@435: */ duke@435: private ExceptionRequest ourExceptionRequest = null; duke@435: duke@435: /** duke@435: * If we do catch an uncaught exception, we set this true duke@435: * so the testcase can find out if it wants to. duke@435: */ duke@435: private boolean exceptionCaught = false; duke@435: ThreadReference vmStartThread = null; duke@435: boolean vmDied = false; duke@435: boolean vmDisconnected = false; duke@435: final String[] args; duke@435: protected boolean testFailed = false; duke@435: duke@435: static private class ArgInfo { duke@435: String targetVMArgs = ""; duke@435: String targetAppCommandLine = ""; duke@435: String connectorSpec = "com.sun.jdi.CommandLineLaunch:"; duke@435: int traceFlags = 0; duke@435: } duke@435: duke@435: /** duke@435: * An easy way to sleep for awhile duke@435: */ duke@435: public void mySleep(int millis) { duke@435: try { duke@435: Thread.sleep(millis); duke@435: } catch (InterruptedException ee) { duke@435: } duke@435: } duke@435: duke@435: boolean getExceptionCaught() { duke@435: return exceptionCaught; duke@435: } duke@435: duke@435: void setExceptionCaught(boolean value) { duke@435: exceptionCaught = value; duke@435: } duke@435: duke@435: /** duke@435: * Return true if eventSet contains the VMDeathEvent for the request in duke@435: * the ourVMDeathRequest ivar. duke@435: */ duke@435: private boolean containsOurVMDeathRequest(EventSet eventSet) { duke@435: if (ourVMDeathRequest != null) { duke@435: Iterator myIter = eventSet.iterator(); duke@435: while (myIter.hasNext()) { duke@435: Event myEvent = (Event)myIter.next(); duke@435: if (!(myEvent instanceof VMDeathEvent)) { duke@435: // We assume that an EventSet contains only VMDeathEvents duke@435: // or no VMDeathEvents. duke@435: break; duke@435: } duke@435: if (ourVMDeathRequest.equals(myEvent.request())) { duke@435: return true; duke@435: } duke@435: } duke@435: } duke@435: return false; duke@435: } duke@435: duke@435: /************************************************************************ duke@435: * The following methods override those in our base class, TargetAdapter. duke@435: *************************************************************************/ duke@435: duke@435: /** duke@435: * Events handled directly by scaffold always resume (well, almost always) duke@435: */ duke@435: public void eventSetComplete(EventSet set) { duke@435: // The listener in connect(..) resumes after receiving our duke@435: // special VMDeathEvent. We can't also do the resume duke@435: // here or we will probably get a VMDisconnectedException duke@435: if (!containsOurVMDeathRequest(set)) { duke@435: traceln("TS: set.resume() called"); duke@435: set.resume(); duke@435: } duke@435: } duke@435: duke@435: /** duke@435: * This method sets up default requests. duke@435: * Testcases can override this to change default behavior. duke@435: */ duke@435: protected void createDefaultEventRequests() { duke@435: createDefaultVMDeathRequest(); duke@435: createDefaultExceptionRequest(); duke@435: } duke@435: duke@435: /** duke@435: * We want the BE to stop when it issues a VMDeathEvent in order to duke@435: * give the FE time to complete handling events that occured before duke@435: * the VMDeath. When we get the VMDeathEvent for this request in duke@435: * the listener in connect(), we will do a resume. duke@435: * If a testcase wants to do something special with VMDeathEvent's, duke@435: * then it should override this method with an empty method or duke@435: * whatever in order to suppress the automatic resume. The testcase duke@435: * will then be responsible for the handling of VMDeathEvents. It duke@435: * has to be sure that it does a resume if it gets a VMDeathEvent duke@435: * with SUSPEND_ALL, and it has to be sure that it doesn't do a duke@435: * resume after getting a VMDeath with SUSPEND_NONE (the automatically duke@435: * generated VMDeathEvent.) duke@435: */ duke@435: protected void createDefaultVMDeathRequest() { duke@435: // ourVMDeathRequest = requestManager.createVMDeathRequest(); duke@435: // ourVMDeathRequest.setSuspendPolicy(EventRequest.SUSPEND_ALL); duke@435: // ourVMDeathRequest.enable(); duke@435: } duke@435: duke@435: /** duke@435: * This will allow us to print a warning if a debuggee gets an duke@435: * unexpected exception. The unexpected exception will be handled in duke@435: * the exceptionThrown method in the listener created in the connect() duke@435: * method. duke@435: * If a testcase does not want an uncaught exception to cause a duke@435: * msg, it must override this method. duke@435: */ duke@435: protected void createDefaultExceptionRequest() { duke@435: ourExceptionRequest = requestManager.createExceptionRequest(null, duke@435: false, true); duke@435: duke@435: // We can't afford to make this be other than SUSPEND_NONE. Otherwise, duke@435: // it would have to be resumed. If our connect() listener resumes it, duke@435: // what about the case where the EventSet contains other events with duke@435: // SUSPEND_ALL and there are other listeners who expect the BE to still duke@435: // be suspended when their handlers get called? duke@435: ourExceptionRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE); duke@435: ourExceptionRequest.enable(); duke@435: } duke@435: duke@435: private class EventHandler implements Runnable { duke@435: EventHandler() { duke@435: Thread thread = new Thread(this); duke@435: thread.setDaemon(true); duke@435: thread.start(); duke@435: } duke@435: duke@435: private void notifyEvent(TargetListener listener, Event event) { duke@435: if (event instanceof BreakpointEvent) { duke@435: listener.breakpointReached((BreakpointEvent)event); duke@435: } else if (event instanceof ExceptionEvent) { duke@435: listener.exceptionThrown((ExceptionEvent)event); duke@435: } else if (event instanceof StepEvent) { duke@435: listener.stepCompleted((StepEvent)event); duke@435: } else if (event instanceof ClassPrepareEvent) { duke@435: listener.classPrepared((ClassPrepareEvent)event); duke@435: } else if (event instanceof ClassUnloadEvent) { duke@435: listener.classUnloaded((ClassUnloadEvent)event); duke@435: } else if (event instanceof MethodEntryEvent) { duke@435: listener.methodEntered((MethodEntryEvent)event); duke@435: } else if (event instanceof MethodExitEvent) { duke@435: listener.methodExited((MethodExitEvent)event); duke@435: } else if (event instanceof AccessWatchpointEvent) { duke@435: listener.fieldAccessed((AccessWatchpointEvent)event); duke@435: } else if (event instanceof ModificationWatchpointEvent) { duke@435: listener.fieldModified((ModificationWatchpointEvent)event); duke@435: } else if (event instanceof ThreadStartEvent) { duke@435: listener.threadStarted((ThreadStartEvent)event); duke@435: } else if (event instanceof ThreadDeathEvent) { duke@435: listener.threadDied((ThreadDeathEvent)event); duke@435: } else if (event instanceof VMStartEvent) { duke@435: listener.vmStarted((VMStartEvent)event); duke@435: } else if (event instanceof VMDeathEvent) { duke@435: listener.vmDied((VMDeathEvent)event); duke@435: } else if (event instanceof VMDisconnectEvent) { duke@435: listener.vmDisconnected((VMDisconnectEvent)event); duke@435: } else { duke@435: throw new InternalError("Unknown event type: " + event.getClass()); duke@435: } duke@435: } duke@435: duke@435: private void traceSuspendPolicy(int policy) { duke@435: if (shouldTrace) { duke@435: switch (policy) { duke@435: case EventRequest.SUSPEND_NONE: duke@435: traceln("TS: eventHandler: suspend = SUSPEND_NONE"); duke@435: break; duke@435: case EventRequest.SUSPEND_ALL: duke@435: traceln("TS: eventHandler: suspend = SUSPEND_ALL"); duke@435: break; duke@435: case EventRequest.SUSPEND_EVENT_THREAD: duke@435: traceln("TS: eventHandler: suspend = SUSPEND_EVENT_THREAD"); duke@435: break; duke@435: } duke@435: } duke@435: } duke@435: duke@435: public void run() { duke@435: boolean connected = true; duke@435: do { duke@435: try { duke@435: EventSet set = vm.eventQueue().remove(); duke@435: traceSuspendPolicy(set.suspendPolicy()); duke@435: synchronized (listeners) { duke@435: ListIterator iter = listeners.listIterator(); duke@435: while (iter.hasNext()) { duke@435: TargetListener listener = (TargetListener)iter.next(); duke@435: traceln("TS: eventHandler: listener = " + listener); duke@435: listener.eventSetReceived(set); duke@435: if (listener.shouldRemoveListener()) { duke@435: iter.remove(); duke@435: } else { duke@435: Iterator jter = set.iterator(); duke@435: while (jter.hasNext()) { duke@435: Event event = (Event)jter.next(); duke@435: traceln("TS: eventHandler: event = " + event.getClass()); duke@435: duke@435: if (event instanceof VMDisconnectEvent) { duke@435: connected = false; duke@435: } duke@435: listener.eventReceived(event); duke@435: if (listener.shouldRemoveListener()) { duke@435: iter.remove(); duke@435: break; duke@435: } duke@435: notifyEvent(listener, event); duke@435: if (listener.shouldRemoveListener()) { duke@435: iter.remove(); duke@435: break; duke@435: } duke@435: } duke@435: traceln("TS: eventHandler: end of events loop"); duke@435: if (!listener.shouldRemoveListener()) { duke@435: traceln("TS: eventHandler: calling ESC"); duke@435: listener.eventSetComplete(set); duke@435: if (listener.shouldRemoveListener()) { duke@435: iter.remove(); duke@435: } duke@435: } duke@435: } duke@435: traceln("TS: eventHandler: end of listeners loop"); duke@435: } duke@435: } duke@435: } catch (InterruptedException e) { duke@435: traceln("TS: eventHandler: InterruptedException"); duke@435: } catch (Exception e) { duke@435: failure("FAILED: Exception occured in eventHandler: " + e); duke@435: e.printStackTrace(); duke@435: connected = false; duke@435: synchronized(TestScaffold.this) { duke@435: // This will make the waiters such as waitForVMDisconnect duke@435: // exit their wait loops. duke@435: vmDisconnected = true; duke@435: TestScaffold.this.notifyAll(); duke@435: } duke@435: } duke@435: traceln("TS: eventHandler: End of outerloop"); duke@435: } while (connected); duke@435: traceln("TS: eventHandler: finished"); duke@435: } duke@435: } duke@435: duke@435: /** duke@435: * Constructor duke@435: */ duke@435: public TestScaffold(String[] args) { duke@435: this.args = args; duke@435: } duke@435: duke@435: public void enableScaffoldTrace() { duke@435: this.shouldTrace = true; duke@435: } duke@435: duke@435: public void disableScaffoldTrace() { duke@435: this.shouldTrace = false; duke@435: } duke@435: duke@435: duke@435: protected void startUp(String targetName) { duke@435: List argList = new ArrayList(Arrays.asList(args)); duke@435: argList.add(targetName); duke@435: println("run args: " + argList); duke@435: connect((String[]) argList.toArray(args)); duke@435: waitForVMStart(); duke@435: } duke@435: duke@435: protected BreakpointEvent startToMain(String targetName) { duke@435: startUp(targetName); duke@435: traceln("TS: back from startUp"); duke@435: BreakpointEvent bpr = resumeTo(targetName, "main", "([Ljava/lang/String;)V"); duke@435: waitForInput(); duke@435: return bpr; duke@435: } duke@435: duke@435: protected void waitForInput() { duke@435: if (System.getProperty("jpda.wait") != null) { duke@435: try { duke@435: System.err.println("Press to continue"); duke@435: System.in.read(); duke@435: System.err.println("running..."); duke@435: duke@435: } catch(Exception e) { duke@435: } duke@435: } duke@435: } duke@435: duke@435: /* duke@435: * Test cases should implement tests in runTests and should duke@435: * initiate testing by calling run(). duke@435: */ duke@435: abstract protected void runTests() throws Exception; duke@435: duke@435: final public void startTests() throws Exception { duke@435: try { duke@435: runTests(); duke@435: } finally { duke@435: shutdown(); duke@435: } duke@435: } duke@435: duke@435: protected void println(String str) { duke@435: System.err.println(str); duke@435: } duke@435: duke@435: protected void print(String str) { duke@435: System.err.print(str); duke@435: } duke@435: duke@435: protected void traceln(String str) { duke@435: if (shouldTrace) { duke@435: println(str); duke@435: } duke@435: } duke@435: duke@435: protected void failure(String str) { duke@435: println(str); duke@435: testFailed = true; duke@435: } duke@435: duke@435: private ArgInfo parseArgs(String args[]) { duke@435: ArgInfo argInfo = new ArgInfo(); duke@435: for (int i = 0; i < args.length; i++) { duke@435: if (args[i].equals("-connect")) { duke@435: i++; duke@435: argInfo.connectorSpec = args[i]; duke@435: } else if (args[i].equals("-trace")) { duke@435: i++; duke@435: argInfo.traceFlags = Integer.decode(args[i]).intValue(); duke@435: } else if (args[i].startsWith("-J")) { duke@435: argInfo.targetVMArgs += (args[i].substring(2) + ' '); duke@435: duke@435: /* duke@435: * classpath can span two arguments so we need to handle duke@435: * it specially. duke@435: */ duke@435: if (args[i].equals("-J-classpath")) { duke@435: i++; duke@435: argInfo.targetVMArgs += (args[i] + ' '); duke@435: } duke@435: } else { duke@435: argInfo.targetAppCommandLine += (args[i] + ' '); duke@435: } duke@435: } duke@435: return argInfo; duke@435: } duke@435: duke@435: /** duke@435: * This is called to connect to a debuggee VM. It starts the VM and duke@435: * installs a listener to catch VMStartEvent, our default events, and duke@435: * VMDisconnectedEvent. When these events appear, that is remembered duke@435: * and waiters are notified. duke@435: * This is normally called in the main thread of the test case. duke@435: * It starts up an EventHandler thread that gets events coming in duke@435: * from the debuggee and distributes them to listeners. That thread duke@435: * keeps running until a VMDisconnectedEvent occurs or some exception duke@435: * occurs during its processing. duke@435: * duke@435: * The 'listenUntilVMDisconnect' method adds 'this' as a listener. duke@435: * This means that 'this's vmDied method will get called. This has a duke@435: * default impl in TargetAdapter.java which can be overridden in the duke@435: * testcase. duke@435: * duke@435: * waitForRequestedEvent also adds an adaptor listener that listens duke@435: * for the particular event it is supposed to wait for (and it also duke@435: * catches VMDisconnectEvents.) This listener is removed once duke@435: * its eventReceived method is called. duke@435: * waitForRequestedEvent is called by most of the methods to do bkpts, duke@435: * etc. duke@435: */ duke@435: public void connect(String args[]) { duke@435: ArgInfo argInfo = parseArgs(args); duke@435: duke@435: argInfo.targetVMArgs += VMConnection.getDebuggeeVMOptions(); duke@435: connection = new VMConnection(argInfo.connectorSpec, duke@435: argInfo.traceFlags); duke@435: duke@435: addListener(new TargetAdapter() { duke@435: public void eventSetComplete(EventSet set) { duke@435: if (TestScaffold.this.containsOurVMDeathRequest(set)) { duke@435: traceln("TS: connect: set.resume() called"); duke@435: set.resume(); duke@435: duke@435: // Note that we want to do the above resume before duke@435: // waking up any sleepers. duke@435: synchronized(TestScaffold.this) { duke@435: TestScaffold.this.notifyAll(); duke@435: } duke@435: } duke@435: } duke@435: duke@435: public void vmStarted(VMStartEvent event) { duke@435: synchronized(TestScaffold.this) { duke@435: vmStartThread = event.thread(); duke@435: TestScaffold.this.notifyAll(); duke@435: } duke@435: } duke@435: /** duke@435: * By default, we catch uncaught exceptions and print a msg. duke@435: * The testcase must override the createDefaultExceptionRequest duke@435: * method if it doesn't want this behavior. duke@435: */ duke@435: public void exceptionThrown(ExceptionEvent event) { duke@435: if (TestScaffold.this.ourExceptionRequest != null && duke@435: TestScaffold.this.ourExceptionRequest.equals( duke@435: event.request())) { duke@435: println("Note: Unexpected Debuggee Exception: " + duke@435: event.exception().referenceType().name() + duke@435: " at line " + event.location().lineNumber()); duke@435: TestScaffold.this.exceptionCaught = true; duke@435: } duke@435: } duke@435: duke@435: public void vmDied(VMDeathEvent event) { duke@435: vmDied = true; duke@435: traceln("TS: vmDied called"); duke@435: } duke@435: duke@435: public void vmDisconnected(VMDisconnectEvent event) { duke@435: synchronized(TestScaffold.this) { duke@435: vmDisconnected = true; duke@435: TestScaffold.this.notifyAll(); duke@435: } duke@435: } duke@435: }); duke@435: if (connection.connector().name().equals("com.sun.jdi.CommandLineLaunch")) { duke@435: if (argInfo.targetVMArgs.length() > 0) { duke@435: if (connection.connectorArg("options").length() > 0) { duke@435: throw new IllegalArgumentException("VM options in two places"); duke@435: } duke@435: connection.setConnectorArg("options", argInfo.targetVMArgs); duke@435: } duke@435: if (argInfo.targetAppCommandLine.length() > 0) { duke@435: if (connection.connectorArg("main").length() > 0) { duke@435: throw new IllegalArgumentException("Command line in two places"); duke@435: } duke@435: connection.setConnectorArg("main", argInfo.targetAppCommandLine); duke@435: } duke@435: } duke@435: duke@435: vm = connection.open(); duke@435: requestManager = vm.eventRequestManager(); duke@435: createDefaultEventRequests(); duke@435: new EventHandler(); duke@435: } duke@435: duke@435: duke@435: public VirtualMachine vm() { duke@435: return vm; duke@435: } duke@435: duke@435: public EventRequestManager eventRequestManager() { duke@435: return requestManager; duke@435: } duke@435: duke@435: public void addListener(TargetListener listener) { duke@435: traceln("TS: Adding listener " + listener); duke@435: listeners.add(listener); duke@435: } duke@435: duke@435: public void removeListener(TargetListener listener) { duke@435: traceln("TS: Removing listener " + listener); duke@435: listeners.remove(listener); duke@435: } duke@435: duke@435: duke@435: protected void listenUntilVMDisconnect() { duke@435: try { duke@435: addListener (this); duke@435: } catch (Exception ex){ duke@435: ex.printStackTrace(); duke@435: testFailed = true; duke@435: } finally { duke@435: // Allow application to complete and shut down duke@435: resumeToVMDisconnect(); duke@435: } duke@435: } duke@435: duke@435: public synchronized ThreadReference waitForVMStart() { duke@435: while ((vmStartThread == null) && !vmDisconnected) { duke@435: try { duke@435: wait(); duke@435: } catch (InterruptedException e) { duke@435: } duke@435: } duke@435: duke@435: if (vmStartThread == null) { duke@435: throw new VMDisconnectedException(); duke@435: } duke@435: duke@435: return vmStartThread; duke@435: } duke@435: duke@435: public synchronized void waitForVMDisconnect() { duke@435: traceln("TS: waitForVMDisconnect"); duke@435: while (!vmDisconnected) { duke@435: try { duke@435: wait(); duke@435: } catch (InterruptedException e) { duke@435: } duke@435: } duke@435: traceln("TS: waitForVMDisconnect: done"); duke@435: } duke@435: duke@435: public Event waitForRequestedEvent(final EventRequest request) { duke@435: class EventNotification { duke@435: Event event; duke@435: boolean disconnected = false; duke@435: } duke@435: final EventNotification en = new EventNotification(); duke@435: duke@435: TargetAdapter adapter = new TargetAdapter() { duke@435: public void eventReceived(Event event) { duke@435: if (request.equals(event.request())) { duke@435: traceln("TS:Listener2: got requested event"); duke@435: synchronized (en) { duke@435: en.event = event; duke@435: en.notifyAll(); duke@435: } duke@435: removeThisListener(); duke@435: } else if (event instanceof VMDisconnectEvent) { duke@435: traceln("TS:Listener2: got VMDisconnectEvent"); duke@435: synchronized (en) { duke@435: en.disconnected = true; duke@435: en.notifyAll(); duke@435: } duke@435: removeThisListener(); duke@435: } duke@435: } duke@435: }; duke@435: duke@435: addListener(adapter); duke@435: duke@435: try { duke@435: synchronized (en) { duke@435: traceln("TS: waitForRequestedEvent: vm.resume called"); duke@435: vm.resume(); duke@435: duke@435: while (!en.disconnected && (en.event == null)) { duke@435: en.wait(); duke@435: } duke@435: } duke@435: } catch (InterruptedException e) { duke@435: return null; duke@435: } duke@435: duke@435: if (en.disconnected) { duke@435: throw new RuntimeException("VM Disconnected before requested event occurred"); duke@435: } duke@435: return en.event; duke@435: } duke@435: duke@435: private StepEvent doStep(ThreadReference thread, int gran, int depth) { duke@435: final StepRequest sr = duke@435: requestManager.createStepRequest(thread, gran, depth); duke@435: duke@435: sr.addClassExclusionFilter("java.*"); duke@435: sr.addClassExclusionFilter("sun.*"); duke@435: sr.addClassExclusionFilter("com.sun.*"); duke@435: sr.addCountFilter(1); duke@435: sr.enable(); duke@435: StepEvent retEvent = (StepEvent)waitForRequestedEvent(sr); duke@435: requestManager.deleteEventRequest(sr); duke@435: return retEvent; duke@435: } duke@435: duke@435: public StepEvent stepIntoInstruction(ThreadReference thread) { duke@435: return doStep(thread, StepRequest.STEP_MIN, StepRequest.STEP_INTO); duke@435: } duke@435: duke@435: public StepEvent stepIntoLine(ThreadReference thread) { duke@435: return doStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_INTO); duke@435: } duke@435: duke@435: public StepEvent stepOverInstruction(ThreadReference thread) { duke@435: return doStep(thread, StepRequest.STEP_MIN, StepRequest.STEP_OVER); duke@435: } duke@435: duke@435: public StepEvent stepOverLine(ThreadReference thread) { duke@435: return doStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_OVER); duke@435: } duke@435: duke@435: public StepEvent stepOut(ThreadReference thread) { duke@435: return doStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_OUT); duke@435: } duke@435: duke@435: public BreakpointEvent resumeTo(Location loc) { duke@435: final BreakpointRequest request = duke@435: requestManager.createBreakpointRequest(loc); duke@435: request.addCountFilter(1); duke@435: request.enable(); duke@435: return (BreakpointEvent)waitForRequestedEvent(request); duke@435: } duke@435: duke@435: public ReferenceType findReferenceType(String name) { duke@435: List rts = vm.classesByName(name); duke@435: Iterator iter = rts.iterator(); duke@435: while (iter.hasNext()) { duke@435: ReferenceType rt = (ReferenceType)iter.next(); duke@435: if (rt.name().equals(name)) { duke@435: return rt; duke@435: } duke@435: } duke@435: return null; duke@435: } duke@435: duke@435: public Method findMethod(ReferenceType rt, String name, String signature) { duke@435: List methods = rt.methods(); duke@435: Iterator iter = methods.iterator(); duke@435: while (iter.hasNext()) { duke@435: Method method = (Method)iter.next(); duke@435: if (method.name().equals(name) && duke@435: method.signature().equals(signature)) { duke@435: return method; duke@435: } duke@435: } duke@435: return null; duke@435: } duke@435: duke@435: public Location findLocation(ReferenceType rt, int lineNumber) duke@435: throws AbsentInformationException { duke@435: List locs = rt.locationsOfLine(lineNumber); duke@435: if (locs.size() == 0) { duke@435: throw new IllegalArgumentException("Bad line number"); duke@435: } else if (locs.size() > 1) { duke@435: throw new IllegalArgumentException("Line number has multiple locations"); duke@435: } duke@435: duke@435: return (Location)locs.get(0); duke@435: } duke@435: duke@435: public BreakpointEvent resumeTo(String clsName, String methodName, duke@435: String methodSignature) { duke@435: ReferenceType rt = findReferenceType(clsName); duke@435: if (rt == null) { duke@435: rt = resumeToPrepareOf(clsName).referenceType(); duke@435: } duke@435: duke@435: Method method = findMethod(rt, methodName, methodSignature); duke@435: if (method == null) { duke@435: throw new IllegalArgumentException("Bad method name/signature"); duke@435: } duke@435: duke@435: return resumeTo(method.location()); duke@435: } duke@435: duke@435: public BreakpointEvent resumeTo(String clsName, int lineNumber) throws AbsentInformationException { duke@435: ReferenceType rt = findReferenceType(clsName); duke@435: if (rt == null) { duke@435: rt = resumeToPrepareOf(clsName).referenceType(); duke@435: } duke@435: duke@435: return resumeTo(findLocation(rt, lineNumber)); duke@435: } duke@435: duke@435: public ClassPrepareEvent resumeToPrepareOf(String className) { duke@435: final ClassPrepareRequest request = duke@435: requestManager.createClassPrepareRequest(); duke@435: request.addClassFilter(className); duke@435: request.addCountFilter(1); duke@435: request.enable(); duke@435: return (ClassPrepareEvent)waitForRequestedEvent(request); duke@435: } duke@435: duke@435: public void resumeToVMDisconnect() { duke@435: try { duke@435: traceln("TS: resumeToVMDisconnect: vm.resume called"); duke@435: vm.resume(); duke@435: } catch (VMDisconnectedException e) { duke@435: // clean up below duke@435: } duke@435: waitForVMDisconnect(); duke@435: } duke@435: duke@435: public void shutdown() { duke@435: shutdown(null); duke@435: } duke@435: duke@435: public void shutdown(String message) { duke@435: traceln("TS: shutdown: vmDied= " + vmDied + duke@435: ", vmDisconnected= " + vmDisconnected + duke@435: ", connection = " + connection); duke@435: duke@435: if ((connection != null)) { duke@435: try { duke@435: connection.disposeVM(); duke@435: } catch (VMDisconnectedException e) { duke@435: // Shutting down after the VM has gone away. This is duke@435: // not an error, and we just ignore it. duke@435: } duke@435: } else { duke@435: traceln("TS: shutdown: disposeVM not called"); duke@435: } duke@435: if (message != null) { duke@435: println(message); duke@435: } duke@435: duke@435: vmDied = true; duke@435: vmDisconnected = true; duke@435: } duke@435: }