|
1 /* |
|
2 * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. |
|
8 * |
|
9 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
12 * version 2 for more details (a copy is included in the LICENSE file that |
|
13 * accompanied this code). |
|
14 * |
|
15 * You should have received a copy of the GNU General Public License version |
|
16 * 2 along with this work; if not, write to the Free Software Foundation, |
|
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
18 * |
|
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
20 * or visit www.oracle.com if you need additional information or have any |
|
21 * questions. |
|
22 */ |
|
23 |
|
24 // THIS TEST IS LINE NUMBER SENSITIVE |
|
25 |
|
26 package nsk.share.jpda; |
|
27 |
|
28 import java.io.*; |
|
29 import java.util.*; |
|
30 import nsk.share.*; |
|
31 import nsk.share.test.*; |
|
32 import java.lang.management.GarbageCollectorMXBean; |
|
33 import java.lang.management.ManagementFactory; |
|
34 /* |
|
35 * Class can be used as base debuggee class in jdi and jdwp tests. |
|
36 * 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) |
|
37 * !!! Edit carefully, value of 'DEFAULT_BREAKPOINT_LINE' is hardcoded !!! |
|
38 */ |
|
39 public class AbstractDebuggeeTest { |
|
40 protected DebugeeArgumentHandler argHandler; |
|
41 |
|
42 protected IOPipe pipe; |
|
43 |
|
44 protected Log log; |
|
45 |
|
46 protected boolean callExit = true; |
|
47 |
|
48 private boolean success = true; |
|
49 |
|
50 protected void setSuccess(boolean value) { |
|
51 success = value; |
|
52 } |
|
53 |
|
54 public boolean getSuccess() { |
|
55 return success; |
|
56 } |
|
57 |
|
58 public final static int DEFAULT_BREAKPOINT_LINE = 63; |
|
59 |
|
60 public final static String DEFAULT_BREAKPOINT_METHOD_NAME = "breakpointMethod"; |
|
61 |
|
62 public void breakpointMethod() { |
|
63 log.display("In breakpoint method: 'AbstractDebuggeeTest.breakpointMethod()'"); // DEFAULT_BREAKPOINT_LINE |
|
64 } |
|
65 |
|
66 protected Map<String, ClassUnloader> loadedClasses = new TreeMap<String, ClassUnloader>(); |
|
67 |
|
68 public final static String COMMAND_FORCE_BREAKPOINT = "forceBreakpoint"; |
|
69 |
|
70 //load class with given name with possibility to unload it |
|
71 static public final String COMMAND_LOAD_CLASS = "loadClass"; |
|
72 |
|
73 // unload class with given name(it is possible for classes loaded via loadTestClass method) |
|
74 // command:className[:<unloadResult>], <unloadResult> - one of UNLOAD_RESULT_TRUE or UNLOAD_RESULT_FALSE |
|
75 static public final String COMMAND_UNLOAD_CLASS = "unloadClass"; |
|
76 |
|
77 // Optional arguments of COMMAND_UNLOAD_CLASS |
|
78 // (is after unloading class should be really unloaded, default value is UNLOAD_RESULT_TRUE) |
|
79 static public final String UNLOAD_RESULT_TRUE = "unloadResultTrue"; |
|
80 |
|
81 static public final String UNLOAD_RESULT_FALSE = "unloadResultFalse"; |
|
82 |
|
83 static public final String COMMAND_CREATE_STATETESTTHREAD = "createStateTestThread"; |
|
84 |
|
85 static public final String COMMAND_NEXTSTATE_STATETESTTHREAD = "stateTestThreadNextState"; |
|
86 |
|
87 //force GC using AbstractDebuggeeTest.eatMemory() |
|
88 static public final String COMMAND_FORCE_GC = "forceGC"; |
|
89 // GCcount is used to get information about GC activity during test |
|
90 static public final String COMMAND_GC_COUNT = "GCcount"; |
|
91 private int lastGCCount; |
|
92 |
|
93 |
|
94 static public final String stateTestThreadName = "stateTestThread"; |
|
95 |
|
96 static public final String stateTestThreadClassName = StateTestThread.class.getName(); |
|
97 |
|
98 // path to classes intended for loading/unloading |
|
99 protected String classpath; |
|
100 |
|
101 // classloader loads only test classes from nsk.* |
|
102 public static class TestClassLoader extends CustomClassLoader { |
|
103 public Class<?> loadClass(String name) throws ClassNotFoundException { |
|
104 if (name.startsWith("nsk.")) |
|
105 return findClass(name); |
|
106 else |
|
107 return super.loadClass(name); |
|
108 } |
|
109 } |
|
110 |
|
111 protected StressOptions stressOptions; |
|
112 protected Stresser stresser; |
|
113 |
|
114 // initialize test and remove unsupported by nsk.share.jdi.ArgumentHandler arguments |
|
115 // (ArgumentHandler constructor throws BadOption exception if command line contains unrecognized by ArgumentHandler options) |
|
116 // support -testClassPath parameter: path to find classes for custom classloader |
|
117 protected String[] doInit(String[] args) { |
|
118 stressOptions = new StressOptions(args); |
|
119 stresser = new Stresser(stressOptions); |
|
120 |
|
121 ArrayList<String> standardArgs = new ArrayList<String>(); |
|
122 |
|
123 for (int i = 0; i < args.length; i++) { |
|
124 if (args[i].equals("-testClassPath") && (i < args.length - 1)) { |
|
125 classpath = args[i + 1]; |
|
126 i++; |
|
127 } else |
|
128 standardArgs.add(args[i]); |
|
129 } |
|
130 |
|
131 return standardArgs.toArray(new String[] {}); |
|
132 } |
|
133 |
|
134 public void loadTestClass(String className) { |
|
135 if (classpath == null) { |
|
136 throw new TestBug("Debuggee requires 'testClassPath' parameter"); |
|
137 } |
|
138 |
|
139 try { |
|
140 ClassUnloader classUnloader = new ClassUnloader(); |
|
141 |
|
142 classUnloader.setClassLoader(new TestClassLoader()); |
|
143 classUnloader.loadClass(className, classpath); |
|
144 loadedClasses.put(className, classUnloader); |
|
145 } catch (ClassNotFoundException e) { |
|
146 log.complain("Unexpected 'ClassNotFoundException' on loading of the requested class(" + className + ")"); |
|
147 e.printStackTrace(log.getOutStream()); |
|
148 throw new TestBug("Unexpected 'ClassNotFoundException' on loading of the requested class(" + className + ")"); |
|
149 } |
|
150 } |
|
151 |
|
152 public static final int MAX_UNLOAD_ATTEMPS = 5; |
|
153 |
|
154 public void unloadTestClass(String className, boolean expectedUnloadingResult) { |
|
155 ClassUnloader classUnloader = loadedClasses.get(className); |
|
156 |
|
157 int unloadAttemps = 0; |
|
158 |
|
159 if (classUnloader != null) { |
|
160 boolean wasUnloaded = false; |
|
161 |
|
162 while (!wasUnloaded && (unloadAttemps++ < MAX_UNLOAD_ATTEMPS)) { |
|
163 wasUnloaded = classUnloader.unloadClass(); |
|
164 } |
|
165 |
|
166 if (wasUnloaded) |
|
167 loadedClasses.remove(className); |
|
168 else { |
|
169 log.display("Class " + className + " was not unloaded"); |
|
170 } |
|
171 |
|
172 if (wasUnloaded != expectedUnloadingResult) { |
|
173 setSuccess(false); |
|
174 |
|
175 if (wasUnloaded) |
|
176 log.complain("Class " + className + " was unloaded!"); |
|
177 else |
|
178 log.complain("Class " + className + " wasn't unloaded!"); |
|
179 } |
|
180 } else { |
|
181 log.complain("Invalid command 'unloadClass' is requested: class " + className + " was not loaded via ClassUnloader"); |
|
182 throw new TestBug("Invalid command 'unloadClass' is requested: class " + className + " was not loaded via ClassUnloader"); |
|
183 } |
|
184 } |
|
185 |
|
186 static public void sleep1sec() { |
|
187 try { |
|
188 Thread.sleep(1000); |
|
189 } catch (InterruptedException e) { |
|
190 } |
|
191 } |
|
192 |
|
193 private StateTestThread stateTestThread; |
|
194 |
|
195 public static final String COMMAND_QUIT = "quit"; |
|
196 |
|
197 public static final String COMMAND_READY = "ready"; |
|
198 |
|
199 private void createStateTestThread() { |
|
200 if (stateTestThread != null) |
|
201 throw new TestBug("StateTestThread already created"); |
|
202 |
|
203 stateTestThread = new StateTestThread(stateTestThreadName); |
|
204 } |
|
205 |
|
206 private void stateTestThreadNextState() { |
|
207 if (stateTestThread == null) |
|
208 throw new TestBug("StateTestThread not created"); |
|
209 |
|
210 stateTestThread.nextState(); |
|
211 } |
|
212 |
|
213 public boolean parseCommand(String command) { |
|
214 try { |
|
215 StreamTokenizer tokenizer = new StreamTokenizer(new StringReader(command)); |
|
216 tokenizer.whitespaceChars(':', ':'); |
|
217 tokenizer.wordChars('_', '_'); |
|
218 tokenizer.wordChars('$', '$'); |
|
219 tokenizer.wordChars('[', ']'); |
|
220 |
|
221 if (command.equals(COMMAND_FORCE_GC)) { |
|
222 forceGC(); |
|
223 lastGCCount = getCurrentGCCount(); |
|
224 return true; |
|
225 } else if (command.equals(COMMAND_GC_COUNT)) { |
|
226 pipe.println(COMMAND_GC_COUNT + ":" + (getCurrentGCCount() - lastGCCount)); |
|
227 return true; |
|
228 } else if (command.equals(COMMAND_FORCE_BREAKPOINT)) { |
|
229 breakpointMethod(); |
|
230 return true; |
|
231 } else if (command.equals(COMMAND_CREATE_STATETESTTHREAD)) { |
|
232 createStateTestThread(); |
|
233 |
|
234 return true; |
|
235 } else if (command.equals(COMMAND_NEXTSTATE_STATETESTTHREAD)) { |
|
236 stateTestThreadNextState(); |
|
237 |
|
238 return true; |
|
239 } else if (command.startsWith(COMMAND_LOAD_CLASS)) { |
|
240 tokenizer.nextToken(); |
|
241 |
|
242 if (tokenizer.nextToken() != StreamTokenizer.TT_WORD) |
|
243 throw new TestBug("Invalid command format: " + command); |
|
244 |
|
245 String className = tokenizer.sval; |
|
246 |
|
247 loadTestClass(className); |
|
248 |
|
249 return true; |
|
250 } else if (command.startsWith(COMMAND_UNLOAD_CLASS)) { |
|
251 tokenizer.nextToken(); |
|
252 |
|
253 if (tokenizer.nextToken() != StreamTokenizer.TT_WORD) |
|
254 throw new TestBug("Invalid command format: " + command); |
|
255 |
|
256 String className = tokenizer.sval; |
|
257 |
|
258 boolean expectedUnloadingResult = true; |
|
259 |
|
260 if (tokenizer.nextToken() == StreamTokenizer.TT_WORD) { |
|
261 if (tokenizer.sval.equals(UNLOAD_RESULT_TRUE)) |
|
262 expectedUnloadingResult = true; |
|
263 else if (tokenizer.sval.equals(UNLOAD_RESULT_FALSE)) |
|
264 expectedUnloadingResult = false; |
|
265 else |
|
266 throw new TestBug("Invalid command format: " + command); |
|
267 } |
|
268 |
|
269 unloadTestClass(className, expectedUnloadingResult); |
|
270 |
|
271 return true; |
|
272 } |
|
273 } catch (IOException e) { |
|
274 throw new TestBug("Invalid command format: " + command); |
|
275 } |
|
276 |
|
277 return false; |
|
278 } |
|
279 |
|
280 protected DebugeeArgumentHandler createArgumentHandler(String args[]) { |
|
281 return new DebugeeArgumentHandler(args); |
|
282 } |
|
283 |
|
284 protected void init(String args[]) { |
|
285 argHandler = createArgumentHandler(doInit(args)); |
|
286 pipe = argHandler.createDebugeeIOPipe(); |
|
287 log = argHandler.createDebugeeLog(); |
|
288 lastGCCount = getCurrentGCCount(); |
|
289 } |
|
290 |
|
291 public void initDebuggee(DebugeeArgumentHandler argHandler, Log log, IOPipe pipe, String[] args, boolean callExit) { |
|
292 this.argHandler = argHandler; |
|
293 this.log = log; |
|
294 this.pipe = pipe; |
|
295 this.callExit = callExit; |
|
296 doInit(args); |
|
297 } |
|
298 |
|
299 public void doTest(String args[]) { |
|
300 init(args); |
|
301 doTest(); |
|
302 } |
|
303 |
|
304 public void doTest() { |
|
305 do { |
|
306 log.display("Debuggee " + getClass().getName() + " : sending the command: " + AbstractDebuggeeTest.COMMAND_READY); |
|
307 pipe.println(AbstractDebuggeeTest.COMMAND_READY); |
|
308 |
|
309 String command = pipe.readln(); |
|
310 log.display("Debuggee: received the command: " + command); |
|
311 |
|
312 if (command.equals(AbstractDebuggeeTest.COMMAND_QUIT)) { |
|
313 break; |
|
314 } else { |
|
315 try { |
|
316 if (!parseCommand(command)) { |
|
317 log.complain("TEST BUG: unknown debugger command: " + command); |
|
318 System.exit(Consts.JCK_STATUS_BASE + Consts.TEST_FAILED); |
|
319 } |
|
320 } catch (Throwable t) { |
|
321 log.complain("Unexpected exception in debuggee: " + t); |
|
322 t.printStackTrace(log.getOutStream()); |
|
323 System.exit(Consts.JCK_STATUS_BASE + Consts.TEST_FAILED); |
|
324 } |
|
325 } |
|
326 } while (true); |
|
327 |
|
328 log.display("Debuggee: exiting"); |
|
329 |
|
330 if (callExit) { |
|
331 if (success) |
|
332 System.exit(Consts.JCK_STATUS_BASE + Consts.TEST_PASSED); |
|
333 else |
|
334 System.exit(Consts.JCK_STATUS_BASE + Consts.TEST_FAILED); |
|
335 } |
|
336 } |
|
337 |
|
338 public static void eatMemory() { |
|
339 Runtime runtime = Runtime.getRuntime(); |
|
340 long maxMemory = runtime.maxMemory(); |
|
341 int memoryChunk = (int) (maxMemory / 50); |
|
342 try { |
|
343 List<Object> list = new ArrayList<Object>(); |
|
344 while (true) { |
|
345 list.add(new byte[memoryChunk]); |
|
346 } |
|
347 } catch (OutOfMemoryError e) { |
|
348 // expected exception |
|
349 } |
|
350 } |
|
351 |
|
352 public static int getCurrentGCCount() { |
|
353 int result = 0; |
|
354 List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans(); |
|
355 for (GarbageCollectorMXBean bean : gcBeans) { |
|
356 result += bean.getCollectionCount(); |
|
357 } |
|
358 return result; |
|
359 } |
|
360 |
|
361 public void forceGC() { |
|
362 eatMemory(); |
|
363 } |
|
364 |
|
365 public void voidValueMethod() { |
|
366 } |
|
367 |
|
368 public void unexpectedException(Throwable t) { |
|
369 setSuccess(false); |
|
370 t.printStackTrace(log.getOutStream()); |
|
371 log.complain("Unexpected exception: " + t); |
|
372 } |
|
373 |
|
374 } |