| /* |
| * 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); |
| } |
| |
| } |