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=&lt;stress_level&gt;</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