< prev index next >

test/hotspot/jtreg/vmTestbase/vm/mlvm/anonloader/share/StressClassLoadingTest.java

Print this page




  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 vm.mlvm.anonloader.share;
  25 
  26 import java.io.File;
  27 import java.util.Objects;
  28 import java.util.concurrent.atomic.AtomicBoolean;
  29 import java.nio.file.Files;
  30 import java.nio.file.Path;
  31 import java.nio.file.Paths;
  32 import nsk.share.test.Stresser;
  33 import vm.share.options.Option;
  34 import vm.share.options.OptionSupport;
  35 import vm.share.options.IgnoreUnknownArgumentsHandler;
  36 import vm.mlvm.share.Env;
  37 import vm.mlvm.share.MlvmTest;
  38 import vm.mlvm.share.CustomClassLoaders;
  39 import vm.share.FileUtils;
  40 import vm.share.UnsafeAccess;
  41 
  42 /**
  43  * Does stress-testing of class loading subsystem.
  44  * This class should be subclassed by the tests
  45  * to provide test class data.
  46  *
  47  * <p>StressClassLoadingTest performs a number of iterations
  48  * (the default value is 100 000).
  49  * Each iteration gets class bytes from the subclass
  50  * and loads it into JVM using either:


  59  * starts at 00 and is increased on every hangup.
  60  * A prefix can be added to the file name using {@link #setFileNamePrefix}
  61  *
  62  * <p>The test fails, if there were hangups.
  63  *
  64  * <p>By default, before loading class, the bytes are
  65  * saved to {@code _AnonkTestee01.class} file in the current directory.
  66  * If JVM crashes, the bytecodes can be analysed.
  67  * Class saving is controlled by -saveClassFile option.
  68  * A prefix can be added to the file name using {@link #setFileNamePrefix}
  69  * function.
  70  *
  71  * <p>There is a tool to load the saved .class file.
  72  * The tool tries to load class using a number of class loaders. For more
  73  * information, please see tool documentation: {@link vm.mlvm.tools.LoadClass}.
  74  *
  75  * @see vm.mlvm.tools.LoadClass
  76  */
  77 public abstract class StressClassLoadingTest extends MlvmTest {
  78     private static final String RESCUE_FILE_NAME = "_AnonkTestee01.class";
  79     private static final String HUNG_CLASS_FILE_NAME = "hang.class";
  80 
  81     @Option(name = "iterations", default_value = "100000",
  82             description = "How many times generate a class and parse it")
  83     private static int iterations;
  84 
  85     @Option(name = "saveClassFile", default_value = "true",
  86             description = "Save generated class file before loading."
  87                     + " Useful when VM crashes on loading")
  88     private static boolean saveClassFile;
  89 
  90     @Option(name = "parseTimeout", default_value = "10000",
  91             description = "Timeout in millisectionds to detect hung parser"
  92                     + " thread. The parser thread is killed after the timeout")
  93     private static int parseTimeout;
  94 
  95     @Option(name = "unsafeLoad", default_value = "false",
  96             description = "An option for adhoc experiments: load class via "
  97                     + "Unsafe.defineAnonymousClass(). Since in this way the "
  98                     + "loading process skips several security checks, if the "
  99                     + "class is not valid, crashes and assertions are normal.")


 119     static volatile boolean optionsSetup = false;
 120     public static void setupOptions(Object instance) {
 121         if (!optionsSetup) {
 122             synchronized (StressClassLoadingTest.class) {
 123                 if (!optionsSetup) {
 124                     OptionSupport.setup(instance, Env.getArgParser().getRawArguments(), new IgnoreUnknownArgumentsHandler());
 125                     optionsSetup = true;
 126 
 127                     Env.traceNormal("StressClassLoadingTest options: iterations: " + iterations);
 128                     Env.traceNormal("StressClassLoadingTest options: unsafeLoad: " + unsafeLoad);
 129                     Env.traceNormal("StressClassLoadingTest options: parseTimeout: " + parseTimeout);
 130                     Env.traceNormal("StressClassLoadingTest options: saveClassFile: " + saveClassFile);
 131                 }
 132             }
 133         }
 134     }
 135 
 136     public boolean run() throws Exception {
 137         setupOptions(this);
 138 


 139         Stresser stresser = createStresser();
 140         stresser.start(iterations);
 141 
 142         while (stresser.continueExecution()) {
 143             stresser.iteration();
 144 
 145             byte[] classBytes = generateClassBytes();
 146             Class<?> hostClass = getHostClass();
 147             String className = hostClass.getName();
 148             File rescueFile = new File(String.format("%s_%d_%s",
 149                     fileNamePrefix, stresser.getIteration(), RESCUE_FILE_NAME));
 150             if (saveClassFile) {
 151                 // Write out the class file being loaded.  It's useful
 152                 // to have if the JVM crashes.
 153                 FileUtils.writeBytesToFile(rescueFile, classBytes);
 154                 if (classFileMessagePrinted.compareAndSet(false, true)) {
 155                     Env.traceImportant("If the JVM crashes then "
 156                             + "the class file causing the crash is saved as *_*_"
 157                             + RESCUE_FILE_NAME);
 158                 }
 159             }
 160 
 161             Thread parserThread  = new Thread() {
 162                 public void run() {
 163                     try {
 164                         Class<?> c;
 165                         if (unsafeLoad) {
 166                             c = UnsafeAccess.unsafe.defineAnonymousClass(hostClass, classBytes, null);
 167                         } else {
 168                             c = CustomClassLoaders.makeClassBytesLoader(classBytes, className)
 169                                     .loadClass(className);
 170                         }
 171                         UnsafeAccess.unsafe.ensureClassInitialized(c);
 172                     } catch (Throwable e) {
 173                         Env.traceVerbose(e, "parser caught exception");
 174                     }
 175                 }
 176             };
 177 

 178             parserThread.start();
 179             parserThread.join(parseTimeout);
 180 
 181             if (parserThread.isAlive()) {
 182                 Env.traceImportant("parser thread may be hung!");
 183                 StackTraceElement[] stack = parserThread.getStackTrace();
 184                 Env.traceImportant("parser thread stack len: " + stack.length);
 185                 Env.traceImportant(parserThread + " stack trace:");
 186                 for (int i = 0; i < stack.length; ++i) {
 187                     Env.traceImportant(parserThread + "\tat " + stack[i]);
 188                 }
 189 
 190                 Path savedClassPath = Paths.get(fileNamePrefix + HUNG_CLASS_FILE_NAME);
 191 
 192                 if (saveClassFile) {
 193                     Files.move(rescueFile.toPath(), savedClassPath);
 194                     Env.traceImportant("There was a possible hangup during parsing."
 195                         + " The class file, which produced the possible hangup, was saved as "
 196                         + fileNamePrefix + HUNG_CLASS_FILE_NAME
 197                         + "... in the test directory. You may want to analyse it "
 198                         + "if this test times out.");
 199                 }
 200 
 201                 parserThread.join(); // Wait until either thread finishes or test times out.
 202                 if (saveClassFile) {
 203                     savedClassPath.toFile().delete();

 204                 }

 205             } else if (saveClassFile) {
 206                 rescueFile.delete();
 207             }
 208         }
 209 
 210         stresser.finish();










 211         return true;
 212     }
 213 
 214     /**
 215      * Generated class bytes. The method is called for each iteration.
 216      *
 217      * @return Byte array with the generated class
 218      */
 219     protected abstract byte[] generateClassBytes();
 220 
 221     /**
 222      * Returns a host class for the generated class.
 223      *
 224      * @return A host class that for the generated class
 225      */
 226     protected abstract Class<?> getHostClass();
 227 }


  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 vm.mlvm.anonloader.share;
  25 
  26 import java.io.File;
  27 import java.util.Objects;
  28 import java.util.concurrent.atomic.AtomicBoolean;
  29 import java.nio.file.Files;

  30 import java.nio.file.Paths;
  31 import nsk.share.test.Stresser;
  32 import vm.share.options.Option;
  33 import vm.share.options.OptionSupport;
  34 import vm.share.options.IgnoreUnknownArgumentsHandler;
  35 import vm.mlvm.share.Env;
  36 import vm.mlvm.share.MlvmTest;
  37 import vm.mlvm.share.CustomClassLoaders;
  38 import vm.share.FileUtils;
  39 import vm.share.UnsafeAccess;
  40 
  41 /**
  42  * Does stress-testing of class loading subsystem.
  43  * This class should be subclassed by the tests
  44  * to provide test class data.
  45  *
  46  * <p>StressClassLoadingTest performs a number of iterations
  47  * (the default value is 100 000).
  48  * Each iteration gets class bytes from the subclass
  49  * and loads it into JVM using either:


  58  * starts at 00 and is increased on every hangup.
  59  * A prefix can be added to the file name using {@link #setFileNamePrefix}
  60  *
  61  * <p>The test fails, if there were hangups.
  62  *
  63  * <p>By default, before loading class, the bytes are
  64  * saved to {@code _AnonkTestee01.class} file in the current directory.
  65  * If JVM crashes, the bytecodes can be analysed.
  66  * Class saving is controlled by -saveClassFile option.
  67  * A prefix can be added to the file name using {@link #setFileNamePrefix}
  68  * function.
  69  *
  70  * <p>There is a tool to load the saved .class file.
  71  * The tool tries to load class using a number of class loaders. For more
  72  * information, please see tool documentation: {@link vm.mlvm.tools.LoadClass}.
  73  *
  74  * @see vm.mlvm.tools.LoadClass
  75  */
  76 public abstract class StressClassLoadingTest extends MlvmTest {
  77     private static final String RESCUE_FILE_NAME = "_AnonkTestee01.class";
  78     private static final String HUNG_CLASS_FILE_NAME = "hang%02d.class";
  79 
  80     @Option(name = "iterations", default_value = "100000",
  81             description = "How many times generate a class and parse it")
  82     private static int iterations;
  83 
  84     @Option(name = "saveClassFile", default_value = "true",
  85             description = "Save generated class file before loading."
  86                     + " Useful when VM crashes on loading")
  87     private static boolean saveClassFile;
  88 
  89     @Option(name = "parseTimeout", default_value = "10000",
  90             description = "Timeout in millisectionds to detect hung parser"
  91                     + " thread. The parser thread is killed after the timeout")
  92     private static int parseTimeout;
  93 
  94     @Option(name = "unsafeLoad", default_value = "false",
  95             description = "An option for adhoc experiments: load class via "
  96                     + "Unsafe.defineAnonymousClass(). Since in this way the "
  97                     + "loading process skips several security checks, if the "
  98                     + "class is not valid, crashes and assertions are normal.")


 118     static volatile boolean optionsSetup = false;
 119     public static void setupOptions(Object instance) {
 120         if (!optionsSetup) {
 121             synchronized (StressClassLoadingTest.class) {
 122                 if (!optionsSetup) {
 123                     OptionSupport.setup(instance, Env.getArgParser().getRawArguments(), new IgnoreUnknownArgumentsHandler());
 124                     optionsSetup = true;
 125 
 126                     Env.traceNormal("StressClassLoadingTest options: iterations: " + iterations);
 127                     Env.traceNormal("StressClassLoadingTest options: unsafeLoad: " + unsafeLoad);
 128                     Env.traceNormal("StressClassLoadingTest options: parseTimeout: " + parseTimeout);
 129                     Env.traceNormal("StressClassLoadingTest options: saveClassFile: " + saveClassFile);
 130                 }
 131             }
 132         }
 133     }
 134 
 135     public boolean run() throws Exception {
 136         setupOptions(this);
 137 
 138         int hangNum = 0;
 139 
 140         Stresser stresser = createStresser();
 141         stresser.start(iterations);
 142 
 143         while (stresser.continueExecution()) {
 144             stresser.iteration();
 145 
 146             byte[] classBytes = generateClassBytes();
 147             Class<?> hostClass = getHostClass();
 148             String className = hostClass.getName();
 149             File rescueFile = new File(String.format("%s_%d_%s",
 150                     fileNamePrefix, stresser.getIteration(), RESCUE_FILE_NAME));
 151             if (saveClassFile) {
 152                 // Write out the class file being loaded.  It's useful
 153                 // to have if the JVM crashes.
 154                 FileUtils.writeBytesToFile(rescueFile, classBytes);
 155                 if (classFileMessagePrinted.compareAndSet(false, true)) {
 156                     Env.traceImportant("If the JVM crashes then "
 157                             + "the class file causing the crash is saved as *_*_"
 158                             + RESCUE_FILE_NAME);
 159                 }
 160             }
 161 
 162             Thread parserThread  = new Thread() {
 163                 public void run() {
 164                     try {
 165                         Class<?> c;
 166                         if (unsafeLoad) {
 167                             c = UnsafeAccess.unsafe.defineAnonymousClass(hostClass, classBytes, null);
 168                         } else {
 169                             c = CustomClassLoaders.makeClassBytesLoader(classBytes, className)
 170                                     .loadClass(className);
 171                         }
 172                         UnsafeAccess.unsafe.ensureClassInitialized(c);
 173                     } catch (Throwable e) {
 174                         Env.traceVerbose(e, "parser caught exception");
 175                     }
 176                 }
 177             };
 178 
 179             parserThread.setDaemon(true);
 180             parserThread.start();
 181             parserThread.join(parseTimeout);
 182 
 183             if (parserThread.isAlive()) {
 184                 Env.complain("Killing parser thread");
 185                 StackTraceElement[] stack = parserThread.getStackTrace();

 186                 Env.traceImportant(parserThread + " stack trace:");
 187                 for (int i = 0; i < stack.length; ++i) {
 188                     Env.traceImportant(parserThread + "\tat " + stack[i]);
 189                 }
 190 












 191                 if (saveClassFile) {
 192                     Files.move(rescueFile.toPath(), Paths.get(fileNamePrefix
 193                             + String.format(HUNG_CLASS_FILE_NAME, hangNum)));
 194                 }
 195                 ++hangNum;
 196             } else if (saveClassFile) {
 197                 rescueFile.delete();
 198             }
 199         }
 200 
 201         stresser.finish();
 202 
 203         if (hangNum > 0) {
 204             Env.complain("There were " + hangNum + " hangups during parsing."
 205                     + " The class files, which produced hangup were saved as "
 206                     + fileNamePrefix + String.format(HUNG_CLASS_FILE_NAME, 0)
 207                     + "... in the test directory. You may want to analyse them."
 208                     + " Failing this test because of hangups.");
 209             return false;
 210         }
 211 
 212         return true;
 213     }
 214 
 215     /**
 216      * Generated class bytes. The method is called for each iteration.
 217      *
 218      * @return Byte array with the generated class
 219      */
 220     protected abstract byte[] generateClassBytes();
 221 
 222     /**
 223      * Returns a host class for the generated class.
 224      *
 225      * @return A host class that for the generated class
 226      */
 227     protected abstract Class<?> getHostClass();
 228 }
< prev index next >