blob: 02157c626cb989e6173ea4999db5abc3f7031890 [file] [log] [blame]
/*
* Copyright (c) 2004, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package nsk.share.jdi;
import com.sun.jdi.*;
import com.sun.jdi.event.*;
import com.sun.jdi.request.*;
import java.io.*;
import nsk.share.*;
/**
* This class is a template for test debugger.
* It bases on some existed tests for testing of
* JDI requests and events.
*
* Requirements for a test debugger which extends
* this template are as folllows:
* - the subclass must have 'main' and 'run' methods
* defined like:
* public static void main (String argv[]) {
* System.exit(run(argv, System.out) + Consts.JCK_STATUS_BASE);
* }
*
* public static int run (String argv[], PrintStream out) {
* debuggeeName = <DEBUGGEE_NAME_AS_STRING>;
* return new filter001().runThis(argv, out);
* }
*
* - the subclass must implement 'testRun' method
* which must contain checkings of all test's assertions.
*/
public abstract class TestDebuggerType1 {
protected static int waitTime;
protected static int testExitCode = Consts.TEST_PASSED;
protected static Binder binder;
protected static Debugee debuggee;
protected static VirtualMachine vm;
protected static ArgumentHandler argsHandler;
protected static Log log;
protected static String debuggeeName; /* must be assigned in a subclass */
protected static ReferenceType debuggeeClass;
protected static EventRequestManager eventRManager;
protected static EventHandler eventHandler;
protected static BreakpointRequest bpRequest /* communication breakpoint */;
protected static volatile int bpCount;
protected static void display(String message) {
log.display("debugger> " + message);
}
protected static void complain(String message) {
log.complain("debugger> " + message);
}
/**
* Should be used in test debugger when assertion failed.
*/
protected static void setFailedStatus(String message) {
complain(message);
testExitCode = Consts.TEST_FAILED;
}
protected int runThis (String argv[], PrintStream out) {
argsHandler = new ArgumentHandler(argv);
log = new Log(out, argsHandler);
binder = new Binder(argsHandler, log);
waitTime = argsHandler.getWaitTime() * 60000;
try {
debuggee = binder.bindToDebugee(debuggeeName);
debuggee.redirectStdout(log, Debugee.DEBUGEE_STDERR_LOG_PREFIX);
vm = debuggee.VM();
eventRManager = vm.eventRequestManager();
eventHandler = new EventHandler(debuggee, log);
eventHandler.startListening();
debuggeeClass = waitForClassPrepared(debuggeeName);
/* A debuggee class must define 'methodForCommunication'
* method and invoke it in points of synchronization
* with a debugger.
*/
setCommunicationBreakpoint(debuggeeClass,"methodForCommunication");
display("TESTING BEGINS");
// after waitForClassPrepared() main debuggee thread is suspended, resume it before test start
display("RESUME DEBUGGEE VM");
vm.resume();
testRun();
display("TESTING ENDS");
display("Waiting for debuggee's exit...");
eventHandler.waitForVMDisconnect();
int status = EventHandler.getStatus();
if (status != 0) {
setFailedStatus("Event handler returned unexpected exit status: " + status);
} else {
display("Event handler thread exited.");
}
debuggee.endDebugee();
status = debuggee.getStatus();
if (status != (Consts.TEST_PASSED + Consts.JCK_STATUS_BASE)) {
setFailedStatus("Debuggee returned unexpected exit status: " + status);
} else {
display("Debuggee PASSED.");
}
} catch (VMDisconnectedException e) {
setFailedStatus("Unexpected VMDisconnectedException");
e.printStackTrace(log.getOutStream());
throw new Failure(e.getMessage());
} catch (Exception e) {
setFailedStatus("Unexpected exception : " + e.getMessage());
e.printStackTrace(log.getOutStream());
display("Forcing debuggee to exit...");
vm.exit(Consts.TEST_PASSED + Consts.JCK_STATUS_BASE);
}
return testExitCode;
}
/**
* This method which be implemented in all test debuggers
* and must contain checking of test assertions.
*/
protected abstract void testRun();
/**
* Waits until debuggee class is prepared.
*
*/
private ReferenceType waitForClassPrepared(String className) {
ClassPrepareRequest cpRequest = eventRManager.createClassPrepareRequest();
cpRequest.setSuspendPolicy( EventRequest.SUSPEND_EVENT_THREAD);
cpRequest.addClassFilter(className);
ClassPrepareEvent event = (ClassPrepareEvent)eventHandler.waitForRequestedEvent(
new EventRequest[]{cpRequest}, waitTime, true);
ReferenceType refType = event.referenceType();
if (!refType.name().equals(className))
throw new Failure("Unexpected class name for received ClassPrepareEvent: " + refType.name() +
"\n\texpected name: " + debuggeeName);
display("Received ClassPrepareEvent for debuggee class: " + refType.name());
return refType;
};
/**
* Sets up breakpoint request which serves for synchronization
* between debugger and debuggee.
*
*/
private void setCommunicationBreakpoint(ReferenceType refType, String methodName) {
Method method = debuggee.methodByName(refType, methodName);
Location location = null;
try {
location = method.allLineLocations().get(0);
} catch (AbsentInformationException e) {
throw new Failure(e);
}
bpRequest = debuggee.makeBreakpoint(location);
bpRequest.setSuspendPolicy(EventRequest.SUSPEND_ALL);
bpRequest.putProperty("number", "zero");
bpRequest.enable();
eventHandler.addListener(
new EventHandler.EventListener() {
public boolean eventReceived(Event event) {
if (event instanceof BreakpointEvent && bpRequest.equals(event.request())) {
synchronized(eventHandler) {
display("Received communication breakpoint event.");
bpCount++;
eventHandler.notifyAll();
}
return true;
}
return false;
}
}
);
}
/**
* Waits for synchronization breakpoint event and checks
* up if there are more test case to check.
*
* Note: debuggee VM shouldn't be suspended when this method
* is called
*
* @return true if there are more test case to check,
* false otherwise or debuggee is disconnected.
*/
protected boolean shouldRunAfterBreakpoint() {
display("shouldRunAfterBreakpoint: entered");
boolean shouldRun = true;
long timeToFinish = System.currentTimeMillis() + waitTime;
long timeLeft = waitTime;
synchronized(eventHandler) {
while (!EventHandler.isDisconnected() && bpCount <= 0 && timeLeft > 0) {
display("shouldRunAfterBreakpoint: waiting for breakpoint event during 1 sec.");
try {
eventHandler.wait(1000);
} catch (InterruptedException e) {
throw new Failure(e);
}
timeLeft = timeToFinish - System.currentTimeMillis();
}
}
if (timeLeft <= 0 && bpCount <= 0) {
setFailedStatus("shouldRunAfterBreakpoint: had not received breakpoint event during waitTime.");
shouldRun = false;
} else if (bpCount > 0) {
display("shouldRunAfterBreakpoint: received breakpoint event.");
bpCount--;
}
if (!EventHandler.isDisconnected()) {
try {
int instruction = ((IntegerValue)
(debuggeeClass.getValue(debuggeeClass.fieldByName("instruction")))).value();
if (instruction == 0) {
display("shouldRunAfterBreakpoint: received instruction from debuggee to finish.");
shouldRun = false;
}
} catch (VMDisconnectedException e) {
shouldRun = false;
}
} else {
shouldRun = false;
}
if (shouldRun) {
display("shouldRunAfterBreakpoint: exited with true.");
} else {
display("shouldRunAfterBreakpoint: exited with false.");
}
return shouldRun;
}
}