1 /* 2 * Copyright (c) 2010, 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 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: 50 * <ul> 51 * <li>a custom {@link java.lang.ClassLoader} implementation or 52 * <li>{@link sun.misc.Unsafe#defineAnonymousClass} call. 53 * </ul> 54 * 55 * <p>Loading is done in a separate thread. If this thread is stuck, 56 * it is killed after some timeout (default is 10 seconds, please see 57 * -parseTimeout option). The class file is saved as hangXX.class, where XX 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.") 99 private static boolean unsafeLoad; 100 101 private String fileNamePrefix = ""; 102 103 private final static AtomicBoolean classFileMessagePrinted 104 = new AtomicBoolean(false); 105 106 /** 107 * Sets prefix for names of the files, created by test: 108 * _AnonkTestee01.class and hangXX.class. 109 * 110 * @param p a prefix to add before file name. 111 * @throws java.lang.NullPointerException if p is null 112 */ 113 public void setFileNamePrefix(String p) { 114 Objects.requireNonNull(p); 115 fileNamePrefix = p; 116 } 117 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 c.newInstance(); 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 }