08855df46a
Reviewed-by: vlivanov, erikj, mseledtsov, gthornbr
375 lines
13 KiB
Java
375 lines
13 KiB
Java
/*
|
|
* 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);
|
|
}
|
|
|
|
}
|