blob: 0668297d211fa1038658767f7ada192a0aee98bf [file] [log] [blame]
/*
* Copyright (c) 2006, 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.
*/
// THIS TEST IS LINE NUMBER SENSITIVE
package nsk.share.jpda;
import java.io.*;
import java.util.*;
import nsk.share.*;
import nsk.share.test.*;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
/*
* Class can be used as base debuggee class in jdi and jdwp tests.
* Class contains common method for initializing log, pipe, vm, and several common auxiliary methods. Subclass should implement parseCommand() and, if needed, doInit(parse command line parameters)
* !!! Edit carefully, value of 'DEFAULT_BREAKPOINT_LINE' is hardcoded !!!
*/
public class AbstractDebuggeeTest {
protected DebugeeArgumentHandler argHandler;
protected IOPipe pipe;
protected Log log;
protected boolean callExit = true;
private boolean success = true;
protected void setSuccess(boolean value) {
success = value;
}
public boolean getSuccess() {
return success;
}
public final static int DEFAULT_BREAKPOINT_LINE = 63;
public final static String DEFAULT_BREAKPOINT_METHOD_NAME = "breakpointMethod";
public void breakpointMethod() {
log.display("In breakpoint method: 'AbstractDebuggeeTest.breakpointMethod()'"); // DEFAULT_BREAKPOINT_LINE
}
protected Map<String, ClassUnloader> loadedClasses = new TreeMap<String, ClassUnloader>();
public final static String COMMAND_FORCE_BREAKPOINT = "forceBreakpoint";
//load class with given name with possibility to unload it
static public final String COMMAND_LOAD_CLASS = "loadClass";
// unload class with given name(it is possible for classes loaded via loadTestClass method)
// command:className[:<unloadResult>], <unloadResult> - one of UNLOAD_RESULT_TRUE or UNLOAD_RESULT_FALSE
static public final String COMMAND_UNLOAD_CLASS = "unloadClass";
// Optional arguments of COMMAND_UNLOAD_CLASS
// (is after unloading class should be really unloaded, default value is UNLOAD_RESULT_TRUE)
static public final String UNLOAD_RESULT_TRUE = "unloadResultTrue";
static public final String UNLOAD_RESULT_FALSE = "unloadResultFalse";
static public final String COMMAND_CREATE_STATETESTTHREAD = "createStateTestThread";
static public final String COMMAND_NEXTSTATE_STATETESTTHREAD = "stateTestThreadNextState";
//force GC using AbstractDebuggeeTest.eatMemory()
static public final String COMMAND_FORCE_GC = "forceGC";
// GCcount is used to get information about GC activity during test
static public final String COMMAND_GC_COUNT = "GCcount";
private int lastGCCount;
static public final String stateTestThreadName = "stateTestThread";
static public final String stateTestThreadClassName = StateTestThread.class.getName();
// path to classes intended for loading/unloading
protected String classpath;
// classloader loads only test classes from nsk.*
public static class TestClassLoader extends CustomClassLoader {
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name.startsWith("nsk."))
return findClass(name);
else
return super.loadClass(name);
}
}
protected StressOptions stressOptions;
protected Stresser stresser;
// initialize test and remove unsupported by nsk.share.jdi.ArgumentHandler arguments
// (ArgumentHandler constructor throws BadOption exception if command line contains unrecognized by ArgumentHandler options)
// support -testClassPath parameter: path to find classes for custom classloader
protected String[] doInit(String[] args) {
stressOptions = new StressOptions(args);
stresser = new Stresser(stressOptions);
ArrayList<String> standardArgs = new ArrayList<String>();
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-testClassPath") && (i < args.length - 1)) {
classpath = args[i + 1];
i++;
} else
standardArgs.add(args[i]);
}
return standardArgs.toArray(new String[] {});
}
public void loadTestClass(String className) {
if (classpath == null) {
throw new TestBug("Debuggee requires 'testClassPath' parameter");
}
try {
ClassUnloader classUnloader = new ClassUnloader();
classUnloader.setClassLoader(new TestClassLoader());
classUnloader.loadClass(className, classpath);
loadedClasses.put(className, classUnloader);
} catch (ClassNotFoundException e) {
log.complain("Unexpected 'ClassNotFoundException' on loading of the requested class(" + className + ")");
e.printStackTrace(log.getOutStream());
throw new TestBug("Unexpected 'ClassNotFoundException' on loading of the requested class(" + className + ")");
}
}
public static final int MAX_UNLOAD_ATTEMPS = 5;
public void unloadTestClass(String className, boolean expectedUnloadingResult) {
ClassUnloader classUnloader = loadedClasses.get(className);
int unloadAttemps = 0;
if (classUnloader != null) {
boolean wasUnloaded = false;
while (!wasUnloaded && (unloadAttemps++ < MAX_UNLOAD_ATTEMPS)) {
wasUnloaded = classUnloader.unloadClass();
}
if (wasUnloaded)
loadedClasses.remove(className);
else {
log.display("Class " + className + " was not unloaded");
}
if (wasUnloaded != expectedUnloadingResult) {
setSuccess(false);
if (wasUnloaded)
log.complain("Class " + className + " was unloaded!");
else
log.complain("Class " + className + " wasn't unloaded!");
}
} else {
log.complain("Invalid command 'unloadClass' is requested: class " + className + " was not loaded via ClassUnloader");
throw new TestBug("Invalid command 'unloadClass' is requested: class " + className + " was not loaded via ClassUnloader");
}
}
static public void sleep1sec() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
private StateTestThread stateTestThread;
public static final String COMMAND_QUIT = "quit";
public static final String COMMAND_READY = "ready";
private void createStateTestThread() {
if (stateTestThread != null)
throw new TestBug("StateTestThread already created");
stateTestThread = new StateTestThread(stateTestThreadName);
}
private void stateTestThreadNextState() {
if (stateTestThread == null)
throw new TestBug("StateTestThread not created");
stateTestThread.nextState();
}
public boolean parseCommand(String command) {
try {
StreamTokenizer tokenizer = new StreamTokenizer(new StringReader(command));
tokenizer.whitespaceChars(':', ':');
tokenizer.wordChars('_', '_');
tokenizer.wordChars('$', '$');
tokenizer.wordChars('[', ']');
if (command.equals(COMMAND_FORCE_GC)) {
forceGC();
lastGCCount = getCurrentGCCount();
return true;
} else if (command.equals(COMMAND_GC_COUNT)) {
pipe.println(COMMAND_GC_COUNT + ":" + (getCurrentGCCount() - lastGCCount));
return true;
} else if (command.equals(COMMAND_FORCE_BREAKPOINT)) {
breakpointMethod();
return true;
} else if (command.equals(COMMAND_CREATE_STATETESTTHREAD)) {
createStateTestThread();
return true;
} else if (command.equals(COMMAND_NEXTSTATE_STATETESTTHREAD)) {
stateTestThreadNextState();
return true;
} else if (command.startsWith(COMMAND_LOAD_CLASS)) {
tokenizer.nextToken();
if (tokenizer.nextToken() != StreamTokenizer.TT_WORD)
throw new TestBug("Invalid command format: " + command);
String className = tokenizer.sval;
loadTestClass(className);
return true;
} else if (command.startsWith(COMMAND_UNLOAD_CLASS)) {
tokenizer.nextToken();
if (tokenizer.nextToken() != StreamTokenizer.TT_WORD)
throw new TestBug("Invalid command format: " + command);
String className = tokenizer.sval;
boolean expectedUnloadingResult = true;
if (tokenizer.nextToken() == StreamTokenizer.TT_WORD) {
if (tokenizer.sval.equals(UNLOAD_RESULT_TRUE))
expectedUnloadingResult = true;
else if (tokenizer.sval.equals(UNLOAD_RESULT_FALSE))
expectedUnloadingResult = false;
else
throw new TestBug("Invalid command format: " + command);
}
unloadTestClass(className, expectedUnloadingResult);
return true;
}
} catch (IOException e) {
throw new TestBug("Invalid command format: " + command);
}
return false;
}
protected DebugeeArgumentHandler createArgumentHandler(String args[]) {
return new DebugeeArgumentHandler(args);
}
protected void init(String args[]) {
argHandler = createArgumentHandler(doInit(args));
pipe = argHandler.createDebugeeIOPipe();
log = argHandler.createDebugeeLog();
lastGCCount = getCurrentGCCount();
}
public void initDebuggee(DebugeeArgumentHandler argHandler, Log log, IOPipe pipe, String[] args, boolean callExit) {
this.argHandler = argHandler;
this.log = log;
this.pipe = pipe;
this.callExit = callExit;
doInit(args);
}
public void doTest(String args[]) {
init(args);
doTest();
}
public void doTest() {
do {
log.display("Debuggee " + getClass().getName() + " : sending the command: " + AbstractDebuggeeTest.COMMAND_READY);
pipe.println(AbstractDebuggeeTest.COMMAND_READY);
String command = pipe.readln();
log.display("Debuggee: received the command: " + command);
if (command.equals(AbstractDebuggeeTest.COMMAND_QUIT)) {
break;
} else {
try {
if (!parseCommand(command)) {
log.complain("TEST BUG: unknown debugger command: " + command);
System.exit(Consts.JCK_STATUS_BASE + Consts.TEST_FAILED);
}
} catch (Throwable t) {
log.complain("Unexpected exception in debuggee: " + t);
t.printStackTrace(log.getOutStream());
System.exit(Consts.JCK_STATUS_BASE + Consts.TEST_FAILED);
}
}
} while (true);
log.display("Debuggee: exiting");
if (callExit) {
if (success)
System.exit(Consts.JCK_STATUS_BASE + Consts.TEST_PASSED);
else
System.exit(Consts.JCK_STATUS_BASE + Consts.TEST_FAILED);
}
}
public static void eatMemory() {
Runtime runtime = Runtime.getRuntime();
long maxMemory = runtime.maxMemory();
int memoryChunk = (int) (maxMemory / 50);
try {
List<Object> list = new ArrayList<Object>();
while (true) {
list.add(new byte[memoryChunk]);
}
} catch (OutOfMemoryError e) {
// expected exception
}
}
public static int getCurrentGCCount() {
int result = 0;
List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
for (GarbageCollectorMXBean bean : gcBeans) {
result += bean.getCollectionCount();
}
return result;
}
public void forceGC() {
eatMemory();
}
public void voidValueMethod() {
}
public void unexpectedException(Throwable t) {
setSuccess(false);
t.printStackTrace(log.getOutStream());
log.complain("Unexpected exception: " + t);
}
}