1 /* 2 * Copyright (c) 2002, 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 package nsk.share; 25 26 import java.io.*; 27 import java.lang.reflect.Method; 28 import java.util.*; 29 30 /** 31 * Class used as an agent for Java serviceability reliability testing (RAS). 32 * It sets different RAS options and/or modes for a special agent which 33 * actually performs the specified RAS testing.<br> 34 * The agent recognizes arguments, started with ''<code>-ras.</code>''. They 35 * may be as follows:<p> 36 * <li><code>-ras.help</code> - print usage message and exit 37 * <li><code>-ras.verbose</code> - verbose mode 38 * <li><code>-ras.invoke_run</code> - invoke the method <i>run(String[],PrintStream)</i> 39 * of the test instead of <i>main(String[])</i> which is invoked by default. 40 * <li><code>-ras.hotswap=<stress_level></code> - enable JVMTI hotswap of 41 * the currently running test classes. Here are the possible HotSwap stress 42 * levels:<br> 43 * 0 - HotSwap off<br> 44 * 2 - HotSwap tested class in every JVMTI method entry event of running test 45 * (default mode)<br> 46 * 20 - HotSwap tested class in every JVMTI method entry event of every class<br> 47 * 3 - HotSwap tested class in every JVMTI single step event of running test<br> 48 * 4 - HotSwap tested class in every JVMTI exception event of running test<br> 49 * 40 - HotSwap tested class in every JVMTI exception event of every class<p> 50 */ 51 public class RASagent { 52 static final int HOTSWAP_OFF = 0; 53 static final int HOTSWAP_EVERY_METHOD_ENTRY = 2; 54 static final int HOTSWAP_EVERY_METHOD_ENTRY_FOR_EVERY_CLASS = 20; 55 static final int HOTSWAP_EVERY_SINGLE_STEP = 3; 56 static final int HOTSWAP_EVERY_EXCEPTION = 4; 57 static final int HOTSWAP_EVERY_EXCEPTION_FOR_EVERY_CLASS = 40; 58 59 // path to the directory with class files of the invoked test 60 static String clfBasePath = null; 61 62 private static boolean verbose = false; 63 64 private static PrintStream out; 65 setHotSwapMode(boolean vrb, int stress_lev, String shortName)66 native static int setHotSwapMode(boolean vrb, int stress_lev, 67 String shortName); 68 main(String argv[])69 public static void main(String argv[]) { 70 System.exit(run(argv, System.out) + Consts.JCK_STATUS_BASE); 71 } 72 run(String argv[], PrintStream out)73 public static int run(String argv[], PrintStream out) { 74 return new RASagent().runThis(argv, out); 75 } 76 runThis(String argv[], PrintStream out)77 private int runThis(String argv[], PrintStream out) { 78 int skipArgs = 1; // number of arguments which must be skipped 79 // for the invoked test 80 boolean invokeRun = false; // invoke the method "main" by default 81 int hotSwapMode = HOTSWAP_EVERY_METHOD_ENTRY; // HotSwap default stress level 82 int res; 83 String hotSwapModeName = "HOTSWAP_EVERY_METHOD_ENTRY"; 84 85 RASagent.out = out; 86 87 if (argv.length != 0) { 88 // parse arguments for the RASagent and then skip them 89 while(argv[skipArgs-1].startsWith("-ras.")) { 90 if (argv[skipArgs-1].equals("-ras.verbose")) { 91 verbose = true; 92 } else if (argv[skipArgs-1].equals("-ras.help")) { 93 printHelp(); 94 return Consts.TEST_FAILED; 95 } else if (argv[skipArgs-1].equals("-ras.invoke_run")) { 96 invokeRun = true; 97 } else if (argv[skipArgs-1].startsWith("-ras.hotswap=")) { 98 try { 99 hotSwapMode = Integer.parseInt( 100 argv[skipArgs-1].substring(argv[skipArgs-1].lastIndexOf("=")+1)); 101 } catch (NumberFormatException e) { 102 e.printStackTrace(); 103 out.println("\nERROR: RASagent: specified HotSwap mode \"" 104 + hotSwapMode + "\" is not an integer"); 105 printHelp(); 106 return Consts.TEST_FAILED; 107 } 108 switch(hotSwapMode) { 109 case HOTSWAP_EVERY_METHOD_ENTRY: 110 hotSwapModeName = "HOTSWAP_EVERY_METHOD_ENTRY"; 111 break; 112 case HOTSWAP_EVERY_METHOD_ENTRY_FOR_EVERY_CLASS: 113 hotSwapModeName = "HOTSWAP_EVERY_METHOD_ENTRY_FOR_EVERY_CLASS"; 114 break; 115 case HOTSWAP_EVERY_SINGLE_STEP: 116 hotSwapModeName = "HOTSWAP_EVERY_SINGLE_STEP"; 117 break; 118 case HOTSWAP_EVERY_EXCEPTION: 119 hotSwapModeName = "HOTSWAP_EVERY_EXCEPTION"; 120 break; 121 case HOTSWAP_EVERY_EXCEPTION_FOR_EVERY_CLASS: 122 hotSwapModeName = "HOTSWAP_EVERY_EXCEPTION_FOR_EVERY_CLASS"; 123 break; 124 default: 125 out.println("\nERROR: RASagent: specified HotSwap mode \"" 126 + hotSwapMode + "\" is unrecognized"); 127 printHelp(); 128 return Consts.TEST_FAILED; 129 } 130 } 131 skipArgs++; 132 } 133 134 String shortTestName = getTestNameAndPath(argv[skipArgs-1]); 135 136 display("\n#### RASagent: setting hotswap mode \"" 137 + hotSwapModeName + "\" for class \"" 138 + shortTestName + "\" ..."); 139 if ((res = setHotSwapMode(verbose, hotSwapMode, shortTestName)) != 0) { 140 out.println("\nERROR: RASagent: unable to set HotSwap stress level for \"" 141 + shortTestName + "\", exiting"); 142 return Consts.TEST_FAILED; 143 } 144 display("\n#### RASagent: ... setting hotswap mode done"); 145 146 try { 147 Class testCls = Class.forName(argv[skipArgs-1]); 148 display("\n#### RASagent: main class \"" 149 + testCls.toString() + "\" loaded"); 150 151 // copy arguments for the invoked test 152 String args[] = new String[argv.length-skipArgs]; 153 System.arraycopy(argv, skipArgs, args, 0, args.length); 154 155 // invoke the test 156 if (invokeRun) 157 return invokeRunMethod(testCls, args); 158 else 159 return invokeMainMethod(testCls, args); 160 } catch(ClassNotFoundException e) { 161 // just pass: the invoked test is already a RAS specific one 162 out.println("\nWARNING: the test was not really run due to the following error:" 163 + "\n\tunable to get the Class object for \"" 164 + argv[skipArgs-1] + "\"\n\tcaught: " + e); 165 return Consts.TEST_PASSED; 166 } 167 168 } else { 169 out.println("\nERROR: RASagent: required test name is absent in parameters list"); 170 return Consts.TEST_FAILED; 171 } 172 } 173 174 /** 175 * Verify that test's class file exists with a path given as a parameter 176 * and, if so, store that path in the static field "clfBasePath". 177 */ pathValid(String pathToCheck, String testName)178 private boolean pathValid(String pathToCheck, String testName) { 179 String fullPath = pathToCheck + File.separator 180 + testName.replace('.', File.separatorChar) + ".class"; 181 File classFile = null; 182 183 display("\n#### RASagent: verifying class path\n<RASagent>\t" 184 + pathToCheck + " ..."); 185 try { 186 classFile = new File(fullPath); 187 } catch (NullPointerException e) { 188 e.printStackTrace(); 189 out.println("\nERROR: RASagent: verification of class file " 190 + fullPath + " failed: caught " + e); 191 System.exit(Consts.JCK_STATUS_BASE + Consts.TEST_FAILED); 192 } 193 194 if (classFile.exists()) { 195 clfBasePath = pathToCheck; 196 display("<RASagent>\tthe class file exists:\n<RASagent>\t\t" 197 + fullPath + "\n<RASagent>\tclass file base directory found:\n" 198 + "<RASagent>\t\t" + clfBasePath 199 + "\n#### RASagent: ... class path verification done\n"); 200 return true; 201 } 202 else { 203 display("<RASagent>\tno class file at location :\n\t\t" 204 + fullPath 205 + "\n#### RASagent: ... class path verification done\n"); 206 return false; 207 } 208 } 209 210 /** 211 * Get short name of an invoked test (i.e. without package name) and 212 * store path to the directory with the test's class files. 213 */ getTestNameAndPath(String testName)214 private String getTestNameAndPath(String testName) { 215 String shortTestName = testName; 216 String packageName = ""; 217 218 // if '.' occurs, it means that current test is inside a package 219 if (testName.lastIndexOf(".") != -1) { 220 shortTestName = testName.substring(testName.lastIndexOf(".")+1); 221 packageName = testName.substring(0, testName.lastIndexOf(".")); 222 } 223 224 StringTokenizer clPathes = new StringTokenizer( 225 System.getProperty("java.class.path"), File.pathSeparator); 226 227 while(clPathes.hasMoreTokens()) { 228 String clPath = clPathes.nextToken(); 229 230 // trying to load a class file defining the current test from 231 // this entry of "java.class.path": the class file may locate 232 // at the test's work directory or if it's already compiled, 233 // at any directory in classpath 234 if (pathValid(clPath, testName)) 235 return shortTestName; 236 } 237 238 // directory with the test's class files was not found. 239 // Actually, it means that the invoked test has own Java 240 // options such as, for example, "-verify" 241 out.println("\nWARNING: the test was not really run due to the following reason:" 242 + "\n\tthe invoked test has the own Java option: " 243 + testName); 244 System.exit(Consts.JCK_STATUS_BASE + Consts.TEST_PASSED); 245 246 return null; // fake return for too smart javac 247 } 248 249 /** 250 * Invoke the method <i>main(String[])</i> of the test. 251 */ invokeMainMethod(Class testCls, String args[])252 private int invokeMainMethod(Class testCls, String args[]) { 253 Class[] methType = { String[].class }; 254 Object[] methArgs = { args }; 255 256 return invokeMethod(testCls, "main", methType, methArgs); 257 } 258 259 /** 260 * Invoke the method <i>run(String[], PrintStream)</i> of the test. 261 */ invokeRunMethod(Class testCls, String args[])262 private int invokeRunMethod(Class testCls, String args[]) { 263 Class[] methType = { String[].class, PrintStream.class }; 264 Object[] methArgs = { args, out }; 265 266 return invokeMethod(testCls, "run", methType, methArgs); 267 } 268 269 /** 270 * Low level invocation of the test. 271 */ invokeMethod(Class<?> testCls, String methodName, Class methType[], Object methArgs[])272 private int invokeMethod(Class<?> testCls, String methodName, 273 Class methType[], Object methArgs[]) { 274 275 try { 276 Method testMeth = testCls.getMethod(methodName, methType); 277 display("\n#### RASagent: invoking method \"" 278 + testMeth.toString() + "\" ..."); 279 280 Object result = testMeth.invoke(null, methArgs); 281 282 display("\n#### RASagent: ... invocation of \"" 283 + testMeth.toString() + "\" done"); 284 if (result instanceof Integer) { 285 Integer retCode = (Integer) result; 286 return retCode.intValue(); 287 } 288 } catch(NoSuchMethodException e) { 289 e.printStackTrace(); 290 out.println("\nFAILURE: RASagent: unable to get method \"" 291 + methodName + "\" in class " 292 + testCls + "\n\tcaught " + e); 293 return Consts.TEST_FAILED; 294 } catch(Exception e) { 295 e.printStackTrace(); 296 out.println("\nFAILURE: RASagent: caught during invokation of the test class " 297 + testCls + " " + e); 298 return Consts.TEST_FAILED; 299 } 300 301 return -1; 302 } 303 304 /** 305 * Load class bytes for HotSwap. 306 */ loadFromClassFile(String signature)307 static byte[] loadFromClassFile(String signature) { 308 String testPath = clfBasePath + File.separator + signature.substring( 309 1, signature.length()-1).replace('/', File.separatorChar) + ".class"; 310 File classFile = null; 311 312 display("\n#### RASagent: looking for class file\n<RASagent>\t" 313 + testPath + " ..."); 314 315 try { 316 classFile = new File(testPath); 317 } catch (NullPointerException e) { 318 out.println("\nFAILURE: RASagent: path name to the redefining class file is null"); 319 } 320 321 display("\n#### RASagent: loading " + classFile.length() 322 + " bytes from class file "+ testPath + " ..."); 323 byte[] buf = new byte[(int) classFile.length()]; 324 try { 325 InputStream in = new FileInputStream(classFile); 326 in.read(buf); 327 in.close(); 328 } catch(FileNotFoundException e) { 329 e.printStackTrace(); 330 out.println("\nFAILURE: RASagent: loadFromClassFile: file " + 331 classFile.getName() + " not found"); 332 System.exit(Consts.JCK_STATUS_BASE + Consts.TEST_FAILED); 333 } catch (Exception e) { 334 e.printStackTrace(); 335 out.println("\nFAILURE: RASagent: unable to load bytes from the file:\n"); 336 out.println("\t" + testPath + ": caught " + e); 337 System.exit(Consts.JCK_STATUS_BASE + Consts.TEST_FAILED); 338 } 339 340 display("\n#### RASagent: ... " + classFile.length() + " bytes loaded"); 341 342 return buf; 343 } 344 345 /** 346 * This method is used in verbose mode. It prints paramter string only 347 * in case of verbose mode. 348 */ display(String msg)349 private static void display(String msg) { 350 if (verbose) 351 out.println(msg); 352 } 353 354 /** 355 * This method prints out RASagent usage message. 356 */ printHelp()357 private static void printHelp() { 358 out.println("\nRASagent usage: RASagent [option, ...] test" + 359 "\n\t-ras.help print this message and exit" + 360 "\n\t-ras.verbose verbose mode (off by default)" + 361 "\n\t-ras.hotswap=mode enable HotSwap of the running test classes" + 362 "\n\t\twhere mode is:" + 363 "\n\t\t\t" + HOTSWAP_EVERY_METHOD_ENTRY 364 + " - hotswap tested class in its every method entry event" + 365 "\n\t\t\t" + HOTSWAP_EVERY_METHOD_ENTRY_FOR_EVERY_CLASS 366 + " - hotswap tested class in every method entry event for every class" + 367 "\n\t\t\t" + HOTSWAP_EVERY_SINGLE_STEP 368 + " - hotswap tested class in its every single step event" + 369 "\n\t\t\t" + HOTSWAP_EVERY_EXCEPTION 370 + " - hotswap tested class in its every exception event" + 371 "\n\t\t\t" + HOTSWAP_EVERY_EXCEPTION_FOR_EVERY_CLASS 372 + " - hotswap tested class in every exception event for every class\n" + 373 "\n\t-ras.invoke_run invoke the method run() of the test" + 374 "\n\t\tinstead of main() by default"); 375 } 376 } 377