Path: blob/aarch64-shenandoah-jdk8u272-b10/hotspot/agent/test/jdi/TestScaffold.java
38764 views
/*1* Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*22*/2324import com.sun.jdi.*;25import com.sun.jdi.request.*;26import com.sun.jdi.event.*;27import java.util.*;28import java.io.*;2930/**31* Framework used by all JDI regression tests32*/33abstract public class TestScaffold extends TargetAdapter {34private boolean shouldTrace = false;35private VMConnection connection;36private VirtualMachine vm;37private EventRequestManager requestManager;38private List listeners = Collections.synchronizedList(new LinkedList());3940/**41* We create a VMDeathRequest, SUSPEND_ALL, to sync the BE and FE.42*/43//private VMDeathRequest ourVMDeathRequest = null;44Object ourVMDeathRequest = null;4546/**47* We create an ExceptionRequest, SUSPEND_NONE so that we can48* catch it and output a msg if an exception occurs in the49* debuggee.50*/51private ExceptionRequest ourExceptionRequest = null;5253/**54* If we do catch an uncaught exception, we set this true55* so the testcase can find out if it wants to.56*/57private boolean exceptionCaught = false;58ThreadReference vmStartThread = null;59boolean vmDied = false;60boolean vmDisconnected = false;61final String[] args;62protected boolean testFailed = false;6364static private class ArgInfo {65String targetVMArgs = "";66String targetAppCommandLine = "";67String connectorSpec = "com.sun.jdi.CommandLineLaunch:";68int traceFlags = 0;69}7071/**72* An easy way to sleep for awhile73*/74public void mySleep(int millis) {75try {76Thread.sleep(millis);77} catch (InterruptedException ee) {78}79}8081boolean getExceptionCaught() {82return exceptionCaught;83}8485void setExceptionCaught(boolean value) {86exceptionCaught = value;87}8889/**90* Return true if eventSet contains the VMDeathEvent for the request in91* the ourVMDeathRequest ivar.92*/93private boolean containsOurVMDeathRequest(EventSet eventSet) {94if (ourVMDeathRequest != null) {95Iterator myIter = eventSet.iterator();96while (myIter.hasNext()) {97Event myEvent = (Event)myIter.next();98if (!(myEvent instanceof VMDeathEvent)) {99// We assume that an EventSet contains only VMDeathEvents100// or no VMDeathEvents.101break;102}103if (ourVMDeathRequest.equals(myEvent.request())) {104return true;105}106}107}108return false;109}110111/************************************************************************112* The following methods override those in our base class, TargetAdapter.113*************************************************************************/114115/**116* Events handled directly by scaffold always resume (well, almost always)117*/118public void eventSetComplete(EventSet set) {119// The listener in connect(..) resumes after receiving our120// special VMDeathEvent. We can't also do the resume121// here or we will probably get a VMDisconnectedException122if (!containsOurVMDeathRequest(set)) {123traceln("TS: set.resume() called");124set.resume();125}126}127128/**129* This method sets up default requests.130* Testcases can override this to change default behavior.131*/132protected void createDefaultEventRequests() {133createDefaultVMDeathRequest();134createDefaultExceptionRequest();135}136137/**138* We want the BE to stop when it issues a VMDeathEvent in order to139* give the FE time to complete handling events that occured before140* the VMDeath. When we get the VMDeathEvent for this request in141* the listener in connect(), we will do a resume.142* If a testcase wants to do something special with VMDeathEvent's,143* then it should override this method with an empty method or144* whatever in order to suppress the automatic resume. The testcase145* will then be responsible for the handling of VMDeathEvents. It146* has to be sure that it does a resume if it gets a VMDeathEvent147* with SUSPEND_ALL, and it has to be sure that it doesn't do a148* resume after getting a VMDeath with SUSPEND_NONE (the automatically149* generated VMDeathEvent.)150*/151protected void createDefaultVMDeathRequest() {152// ourVMDeathRequest = requestManager.createVMDeathRequest();153// ourVMDeathRequest.setSuspendPolicy(EventRequest.SUSPEND_ALL);154// ourVMDeathRequest.enable();155}156157/**158* This will allow us to print a warning if a debuggee gets an159* unexpected exception. The unexpected exception will be handled in160* the exceptionThrown method in the listener created in the connect()161* method.162* If a testcase does not want an uncaught exception to cause a163* msg, it must override this method.164*/165protected void createDefaultExceptionRequest() {166ourExceptionRequest = requestManager.createExceptionRequest(null,167false, true);168169// We can't afford to make this be other than SUSPEND_NONE. Otherwise,170// it would have to be resumed. If our connect() listener resumes it,171// what about the case where the EventSet contains other events with172// SUSPEND_ALL and there are other listeners who expect the BE to still173// be suspended when their handlers get called?174ourExceptionRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE);175ourExceptionRequest.enable();176}177178private class EventHandler implements Runnable {179EventHandler() {180Thread thread = new Thread(this);181thread.setDaemon(true);182thread.start();183}184185private void notifyEvent(TargetListener listener, Event event) {186if (event instanceof BreakpointEvent) {187listener.breakpointReached((BreakpointEvent)event);188} else if (event instanceof ExceptionEvent) {189listener.exceptionThrown((ExceptionEvent)event);190} else if (event instanceof StepEvent) {191listener.stepCompleted((StepEvent)event);192} else if (event instanceof ClassPrepareEvent) {193listener.classPrepared((ClassPrepareEvent)event);194} else if (event instanceof ClassUnloadEvent) {195listener.classUnloaded((ClassUnloadEvent)event);196} else if (event instanceof MethodEntryEvent) {197listener.methodEntered((MethodEntryEvent)event);198} else if (event instanceof MethodExitEvent) {199listener.methodExited((MethodExitEvent)event);200} else if (event instanceof AccessWatchpointEvent) {201listener.fieldAccessed((AccessWatchpointEvent)event);202} else if (event instanceof ModificationWatchpointEvent) {203listener.fieldModified((ModificationWatchpointEvent)event);204} else if (event instanceof ThreadStartEvent) {205listener.threadStarted((ThreadStartEvent)event);206} else if (event instanceof ThreadDeathEvent) {207listener.threadDied((ThreadDeathEvent)event);208} else if (event instanceof VMStartEvent) {209listener.vmStarted((VMStartEvent)event);210} else if (event instanceof VMDeathEvent) {211listener.vmDied((VMDeathEvent)event);212} else if (event instanceof VMDisconnectEvent) {213listener.vmDisconnected((VMDisconnectEvent)event);214} else {215throw new InternalError("Unknown event type: " + event.getClass());216}217}218219private void traceSuspendPolicy(int policy) {220if (shouldTrace) {221switch (policy) {222case EventRequest.SUSPEND_NONE:223traceln("TS: eventHandler: suspend = SUSPEND_NONE");224break;225case EventRequest.SUSPEND_ALL:226traceln("TS: eventHandler: suspend = SUSPEND_ALL");227break;228case EventRequest.SUSPEND_EVENT_THREAD:229traceln("TS: eventHandler: suspend = SUSPEND_EVENT_THREAD");230break;231}232}233}234235public void run() {236boolean connected = true;237do {238try {239EventSet set = vm.eventQueue().remove();240traceSuspendPolicy(set.suspendPolicy());241synchronized (listeners) {242ListIterator iter = listeners.listIterator();243while (iter.hasNext()) {244TargetListener listener = (TargetListener)iter.next();245traceln("TS: eventHandler: listener = " + listener);246listener.eventSetReceived(set);247if (listener.shouldRemoveListener()) {248iter.remove();249} else {250Iterator jter = set.iterator();251while (jter.hasNext()) {252Event event = (Event)jter.next();253traceln("TS: eventHandler: event = " + event.getClass());254255if (event instanceof VMDisconnectEvent) {256connected = false;257}258listener.eventReceived(event);259if (listener.shouldRemoveListener()) {260iter.remove();261break;262}263notifyEvent(listener, event);264if (listener.shouldRemoveListener()) {265iter.remove();266break;267}268}269traceln("TS: eventHandler: end of events loop");270if (!listener.shouldRemoveListener()) {271traceln("TS: eventHandler: calling ESC");272listener.eventSetComplete(set);273if (listener.shouldRemoveListener()) {274iter.remove();275}276}277}278traceln("TS: eventHandler: end of listeners loop");279}280}281} catch (InterruptedException e) {282traceln("TS: eventHandler: InterruptedException");283} catch (Exception e) {284failure("FAILED: Exception occured in eventHandler: " + e);285e.printStackTrace();286connected = false;287synchronized(TestScaffold.this) {288// This will make the waiters such as waitForVMDisconnect289// exit their wait loops.290vmDisconnected = true;291TestScaffold.this.notifyAll();292}293}294traceln("TS: eventHandler: End of outerloop");295} while (connected);296traceln("TS: eventHandler: finished");297}298}299300/**301* Constructor302*/303public TestScaffold(String[] args) {304this.args = args;305}306307public void enableScaffoldTrace() {308this.shouldTrace = true;309}310311public void disableScaffoldTrace() {312this.shouldTrace = false;313}314315316protected void startUp(String targetName) {317List argList = new ArrayList(Arrays.asList(args));318argList.add(targetName);319println("run args: " + argList);320connect((String[]) argList.toArray(args));321waitForVMStart();322}323324protected BreakpointEvent startToMain(String targetName) {325startUp(targetName);326traceln("TS: back from startUp");327BreakpointEvent bpr = resumeTo(targetName, "main", "([Ljava/lang/String;)V");328waitForInput();329return bpr;330}331332protected void waitForInput() {333if (System.getProperty("jpda.wait") != null) {334try {335System.err.println("Press <enter> to continue");336System.in.read();337System.err.println("running...");338339} catch(Exception e) {340}341}342}343344/*345* Test cases should implement tests in runTests and should346* initiate testing by calling run().347*/348abstract protected void runTests() throws Exception;349350final public void startTests() throws Exception {351try {352runTests();353} finally {354shutdown();355}356}357358protected void println(String str) {359System.err.println(str);360}361362protected void print(String str) {363System.err.print(str);364}365366protected void traceln(String str) {367if (shouldTrace) {368println(str);369}370}371372protected void failure(String str) {373println(str);374testFailed = true;375}376377private ArgInfo parseArgs(String args[]) {378ArgInfo argInfo = new ArgInfo();379for (int i = 0; i < args.length; i++) {380if (args[i].equals("-connect")) {381i++;382argInfo.connectorSpec = args[i];383} else if (args[i].equals("-trace")) {384i++;385argInfo.traceFlags = Integer.decode(args[i]).intValue();386} else if (args[i].startsWith("-J")) {387argInfo.targetVMArgs += (args[i].substring(2) + ' ');388389/*390* classpath can span two arguments so we need to handle391* it specially.392*/393if (args[i].equals("-J-classpath")) {394i++;395argInfo.targetVMArgs += (args[i] + ' ');396}397} else {398argInfo.targetAppCommandLine += (args[i] + ' ');399}400}401return argInfo;402}403404/**405* This is called to connect to a debuggee VM. It starts the VM and406* installs a listener to catch VMStartEvent, our default events, and407* VMDisconnectedEvent. When these events appear, that is remembered408* and waiters are notified.409* This is normally called in the main thread of the test case.410* It starts up an EventHandler thread that gets events coming in411* from the debuggee and distributes them to listeners. That thread412* keeps running until a VMDisconnectedEvent occurs or some exception413* occurs during its processing.414*415* The 'listenUntilVMDisconnect' method adds 'this' as a listener.416* This means that 'this's vmDied method will get called. This has a417* default impl in TargetAdapter.java which can be overridden in the418* testcase.419*420* waitForRequestedEvent also adds an adaptor listener that listens421* for the particular event it is supposed to wait for (and it also422* catches VMDisconnectEvents.) This listener is removed once423* its eventReceived method is called.424* waitForRequestedEvent is called by most of the methods to do bkpts,425* etc.426*/427public void connect(String args[]) {428ArgInfo argInfo = parseArgs(args);429430argInfo.targetVMArgs += VMConnection.getDebuggeeVMOptions();431connection = new VMConnection(argInfo.connectorSpec,432argInfo.traceFlags);433434addListener(new TargetAdapter() {435public void eventSetComplete(EventSet set) {436if (TestScaffold.this.containsOurVMDeathRequest(set)) {437traceln("TS: connect: set.resume() called");438set.resume();439440// Note that we want to do the above resume before441// waking up any sleepers.442synchronized(TestScaffold.this) {443TestScaffold.this.notifyAll();444}445}446}447448public void vmStarted(VMStartEvent event) {449synchronized(TestScaffold.this) {450vmStartThread = event.thread();451TestScaffold.this.notifyAll();452}453}454/**455* By default, we catch uncaught exceptions and print a msg.456* The testcase must override the createDefaultExceptionRequest457* method if it doesn't want this behavior.458*/459public void exceptionThrown(ExceptionEvent event) {460if (TestScaffold.this.ourExceptionRequest != null &&461TestScaffold.this.ourExceptionRequest.equals(462event.request())) {463println("Note: Unexpected Debuggee Exception: " +464event.exception().referenceType().name() +465" at line " + event.location().lineNumber());466TestScaffold.this.exceptionCaught = true;467}468}469470public void vmDied(VMDeathEvent event) {471vmDied = true;472traceln("TS: vmDied called");473}474475public void vmDisconnected(VMDisconnectEvent event) {476synchronized(TestScaffold.this) {477vmDisconnected = true;478TestScaffold.this.notifyAll();479}480}481});482if (connection.connector().name().equals("com.sun.jdi.CommandLineLaunch")) {483if (argInfo.targetVMArgs.length() > 0) {484if (connection.connectorArg("options").length() > 0) {485throw new IllegalArgumentException("VM options in two places");486}487connection.setConnectorArg("options", argInfo.targetVMArgs);488}489if (argInfo.targetAppCommandLine.length() > 0) {490if (connection.connectorArg("main").length() > 0) {491throw new IllegalArgumentException("Command line in two places");492}493connection.setConnectorArg("main", argInfo.targetAppCommandLine);494}495}496497vm = connection.open();498requestManager = vm.eventRequestManager();499createDefaultEventRequests();500new EventHandler();501}502503504public VirtualMachine vm() {505return vm;506}507508public EventRequestManager eventRequestManager() {509return requestManager;510}511512public void addListener(TargetListener listener) {513traceln("TS: Adding listener " + listener);514listeners.add(listener);515}516517public void removeListener(TargetListener listener) {518traceln("TS: Removing listener " + listener);519listeners.remove(listener);520}521522523protected void listenUntilVMDisconnect() {524try {525addListener (this);526} catch (Exception ex){527ex.printStackTrace();528testFailed = true;529} finally {530// Allow application to complete and shut down531resumeToVMDisconnect();532}533}534535public synchronized ThreadReference waitForVMStart() {536while ((vmStartThread == null) && !vmDisconnected) {537try {538wait();539} catch (InterruptedException e) {540}541}542543if (vmStartThread == null) {544throw new VMDisconnectedException();545}546547return vmStartThread;548}549550public synchronized void waitForVMDisconnect() {551traceln("TS: waitForVMDisconnect");552while (!vmDisconnected) {553try {554wait();555} catch (InterruptedException e) {556}557}558traceln("TS: waitForVMDisconnect: done");559}560561public Event waitForRequestedEvent(final EventRequest request) {562class EventNotification {563Event event;564boolean disconnected = false;565}566final EventNotification en = new EventNotification();567568TargetAdapter adapter = new TargetAdapter() {569public void eventReceived(Event event) {570if (request.equals(event.request())) {571traceln("TS:Listener2: got requested event");572synchronized (en) {573en.event = event;574en.notifyAll();575}576removeThisListener();577} else if (event instanceof VMDisconnectEvent) {578traceln("TS:Listener2: got VMDisconnectEvent");579synchronized (en) {580en.disconnected = true;581en.notifyAll();582}583removeThisListener();584}585}586};587588addListener(adapter);589590try {591synchronized (en) {592traceln("TS: waitForRequestedEvent: vm.resume called");593vm.resume();594595while (!en.disconnected && (en.event == null)) {596en.wait();597}598}599} catch (InterruptedException e) {600return null;601}602603if (en.disconnected) {604throw new RuntimeException("VM Disconnected before requested event occurred");605}606return en.event;607}608609private StepEvent doStep(ThreadReference thread, int gran, int depth) {610final StepRequest sr =611requestManager.createStepRequest(thread, gran, depth);612613sr.addClassExclusionFilter("java.*");614sr.addClassExclusionFilter("sun.*");615sr.addClassExclusionFilter("com.sun.*");616sr.addCountFilter(1);617sr.enable();618StepEvent retEvent = (StepEvent)waitForRequestedEvent(sr);619requestManager.deleteEventRequest(sr);620return retEvent;621}622623public StepEvent stepIntoInstruction(ThreadReference thread) {624return doStep(thread, StepRequest.STEP_MIN, StepRequest.STEP_INTO);625}626627public StepEvent stepIntoLine(ThreadReference thread) {628return doStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_INTO);629}630631public StepEvent stepOverInstruction(ThreadReference thread) {632return doStep(thread, StepRequest.STEP_MIN, StepRequest.STEP_OVER);633}634635public StepEvent stepOverLine(ThreadReference thread) {636return doStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_OVER);637}638639public StepEvent stepOut(ThreadReference thread) {640return doStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_OUT);641}642643public BreakpointEvent resumeTo(Location loc) {644final BreakpointRequest request =645requestManager.createBreakpointRequest(loc);646request.addCountFilter(1);647request.enable();648return (BreakpointEvent)waitForRequestedEvent(request);649}650651public ReferenceType findReferenceType(String name) {652List rts = vm.classesByName(name);653Iterator iter = rts.iterator();654while (iter.hasNext()) {655ReferenceType rt = (ReferenceType)iter.next();656if (rt.name().equals(name)) {657return rt;658}659}660return null;661}662663public Method findMethod(ReferenceType rt, String name, String signature) {664List methods = rt.methods();665Iterator iter = methods.iterator();666while (iter.hasNext()) {667Method method = (Method)iter.next();668if (method.name().equals(name) &&669method.signature().equals(signature)) {670return method;671}672}673return null;674}675676public Location findLocation(ReferenceType rt, int lineNumber)677throws AbsentInformationException {678List locs = rt.locationsOfLine(lineNumber);679if (locs.size() == 0) {680throw new IllegalArgumentException("Bad line number");681} else if (locs.size() > 1) {682throw new IllegalArgumentException("Line number has multiple locations");683}684685return (Location)locs.get(0);686}687688public BreakpointEvent resumeTo(String clsName, String methodName,689String methodSignature) {690ReferenceType rt = findReferenceType(clsName);691if (rt == null) {692rt = resumeToPrepareOf(clsName).referenceType();693}694695Method method = findMethod(rt, methodName, methodSignature);696if (method == null) {697throw new IllegalArgumentException("Bad method name/signature");698}699700return resumeTo(method.location());701}702703public BreakpointEvent resumeTo(String clsName, int lineNumber) throws AbsentInformationException {704ReferenceType rt = findReferenceType(clsName);705if (rt == null) {706rt = resumeToPrepareOf(clsName).referenceType();707}708709return resumeTo(findLocation(rt, lineNumber));710}711712public ClassPrepareEvent resumeToPrepareOf(String className) {713final ClassPrepareRequest request =714requestManager.createClassPrepareRequest();715request.addClassFilter(className);716request.addCountFilter(1);717request.enable();718return (ClassPrepareEvent)waitForRequestedEvent(request);719}720721public void resumeToVMDisconnect() {722try {723traceln("TS: resumeToVMDisconnect: vm.resume called");724vm.resume();725} catch (VMDisconnectedException e) {726// clean up below727}728waitForVMDisconnect();729}730731public void shutdown() {732shutdown(null);733}734735public void shutdown(String message) {736traceln("TS: shutdown: vmDied= " + vmDied +737", vmDisconnected= " + vmDisconnected +738", connection = " + connection);739740if ((connection != null)) {741try {742connection.disposeVM();743} catch (VMDisconnectedException e) {744// Shutting down after the VM has gone away. This is745// not an error, and we just ignore it.746}747} else {748traceln("TS: shutdown: disposeVM not called");749}750if (message != null) {751println(message);752}753754vmDied = true;755vmDisconnected = true;756}757}758759760