Path: blob/master/test/hotspot/jtreg/vmTestbase/vm/mlvm/anonloader/share/StressClassLoadingTest.java
41161 views
/*1* Copyright (c) 2010, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223package vm.mlvm.anonloader.share;2425import java.lang.invoke.MethodHandles;26import java.lang.invoke.MethodHandles.Lookup;2728import java.io.File;29import java.util.Objects;30import java.util.concurrent.atomic.AtomicBoolean;31import java.nio.file.Files;32import java.nio.file.Path;33import java.nio.file.Paths;34import nsk.share.test.Stresser;35import vm.share.options.Option;36import vm.share.options.OptionSupport;37import vm.share.options.IgnoreUnknownArgumentsHandler;38import vm.mlvm.share.Env;39import vm.mlvm.share.MlvmTest;40import vm.mlvm.share.CustomClassLoaders;41import vm.share.FileUtils;42import vm.share.UnsafeAccess;4344/**45* Does stress-testing of class loading subsystem.46* This class should be subclassed by the tests47* to provide test class data.48*49* <p>StressClassLoadingTest performs a number of iterations50* (the default value is 100 000).51* Each iteration gets class bytes from the subclass52* and loads it into JVM using either:53* <ul>54* <li>a custom {@link java.lang.ClassLoader} implementation or55* <li>{@link java.lang.invoke.MethodHandles.Lookup#defineHiddenClass} call.56* </ul>57*58* <p>Loading is done in a separate thread. If this thread is stuck,59* it is killed after some timeout (default is 10 seconds, please see60* -parseTimeout option). The class file is saved as hangXX.class, where XX61* starts at 00 and is increased on every hangup.62* A prefix can be added to the file name using {@link #setFileNamePrefix}63*64* <p>The test fails, if there were hangups.65*66* <p>By default, before loading class, the bytes are67* saved to {@code _AnonkTestee01.class} file in the current directory.68* If JVM crashes, the bytecodes can be analysed.69* Class saving is controlled by -saveClassFile option.70* A prefix can be added to the file name using {@link #setFileNamePrefix}71* function.72*/73public abstract class StressClassLoadingTest extends MlvmTest {74private static final String RESCUE_FILE_NAME = "_AnonkTestee01.class";75private static final String HUNG_CLASS_FILE_NAME = "hang.class";7677@Option(name = "iterations", default_value = "100000",78description = "How many times generate a class and parse it")79private static int iterations;8081@Option(name = "saveClassFile", default_value = "true",82description = "Save generated class file before loading."83+ " Useful when VM crashes on loading")84private static boolean saveClassFile;8586@Option(name = "parseTimeout", default_value = "10000",87description = "Timeout in millisectionds to detect hung parser"88+ " thread. The parser thread is killed after the timeout")89private static int parseTimeout;9091@Option(name = "hiddenLoad", default_value = "false",92description = "An option for adhoc experiments: load class as a hidden class.")93private static boolean hiddenLoad;9495private String fileNamePrefix = "";9697private final static AtomicBoolean classFileMessagePrinted98= new AtomicBoolean(false);99100/**101* Sets prefix for names of the files, created by test:102* _AnonkTestee01.class and hangXX.class.103*104* @param p a prefix to add before file name.105* @throws java.lang.NullPointerException if p is null106*/107public void setFileNamePrefix(String p) {108Objects.requireNonNull(p);109fileNamePrefix = p;110}111112static volatile boolean optionsSetup = false;113public static void setupOptions(Object instance) {114if (!optionsSetup) {115synchronized (StressClassLoadingTest.class) {116if (!optionsSetup) {117OptionSupport.setup(instance, Env.getArgParser().getRawArguments(), new IgnoreUnknownArgumentsHandler());118optionsSetup = true;119120Env.traceNormal("StressClassLoadingTest options: iterations: " + iterations);121Env.traceNormal("StressClassLoadingTest options: hiddenLoad: " + hiddenLoad);122Env.traceNormal("StressClassLoadingTest options: parseTimeout: " + parseTimeout);123Env.traceNormal("StressClassLoadingTest options: saveClassFile: " + saveClassFile);124}125}126}127}128129public boolean run() throws Exception {130setupOptions(this);131132Stresser stresser = createStresser();133stresser.start(iterations);134135while (stresser.continueExecution()) {136stresser.iteration();137138byte[] classBytes = generateClassBytes();139Class<?> hostClass = getHostClass();140String className = hostClass.getName();141File rescueFile = new File(String.format("%s_%d_%s",142fileNamePrefix, stresser.getIteration(), RESCUE_FILE_NAME));143if (saveClassFile) {144// Write out the class file being loaded. It's useful145// to have if the JVM crashes.146FileUtils.writeBytesToFile(rescueFile, classBytes);147if (classFileMessagePrinted.compareAndSet(false, true)) {148Env.traceImportant("If the JVM crashes then "149+ "the class file causing the crash is saved as *_*_"150+ RESCUE_FILE_NAME);151}152}153154Thread parserThread = new Thread() {155public void run() {156try {157Class<?> c;158if (hiddenLoad) {159Lookup lookup = MethodHandles.lookup();160c = lookup.defineHiddenClass(classBytes, true).lookupClass();161162} else {163c = CustomClassLoaders.makeClassBytesLoader(classBytes, className)164.loadClass(className);165}166UnsafeAccess.unsafe.ensureClassInitialized(c);167} catch (Throwable e) {168Env.traceVerbose(e, "parser caught exception");169}170}171};172173parserThread.start();174parserThread.join(parseTimeout);175176if (parserThread.isAlive()) {177Env.traceImportant("parser thread may be hung!");178StackTraceElement[] stack = parserThread.getStackTrace();179Env.traceImportant("parser thread stack len: " + stack.length);180Env.traceImportant(parserThread + " stack trace:");181for (int i = 0; i < stack.length; ++i) {182Env.traceImportant(parserThread + "\tat " + stack[i]);183}184185Path savedClassPath = Paths.get(fileNamePrefix + HUNG_CLASS_FILE_NAME);186187if (saveClassFile) {188Files.move(rescueFile.toPath(), savedClassPath);189Env.traceImportant("There was a possible hangup during parsing."190+ " The class file, which produced the possible hangup, was saved as "191+ fileNamePrefix + HUNG_CLASS_FILE_NAME192+ "... in the test directory. You may want to analyse it "193+ "if this test times out.");194}195196parserThread.join(); // Wait until either thread finishes or test times out.197if (saveClassFile) {198savedClassPath.toFile().delete();199}200} else if (saveClassFile) {201rescueFile.delete();202}203}204205stresser.finish();206return true;207}208209/**210* Generated class bytes. The method is called for each iteration.211*212* @return Byte array with the generated class213*/214protected abstract byte[] generateClassBytes();215216/**217* Returns a host class for the generated class.218*219* @return A host class that for the generated class220*/221protected abstract Class<?> getHostClass();222}223224225