1 /* 2 * Copyright (c) 2013, 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 /* 25 * @test 26 * 27 * @summary converted from VM Testbase metaspace/stressDictionary. 28 * VM Testbase keywords: [nonconcurrent, javac] 29 * 30 * @library /vmTestbase /test/lib 31 * @run driver jdk.test.lib.FileInstaller . . 32 * @run main/othervm/timeout=420 -XX:+IgnoreUnrecognizedVMOptions -XX:-CMSPrecleaningEnabled metaspace.stressDictionary.StressDictionary -stressTime 30 33 */ 34 35 package metaspace.stressDictionary; 36 37 import java.util.*; 38 import java.lang.management.ManagementFactory; 39 import java.lang.reflect.InvocationTargetException; 40 import java.lang.reflect.Method; 41 import java.util.concurrent.*; 42 import java.util.concurrent.atomic.AtomicLong; 43 44 import nsk.share.gc.GCTestBase; 45 import nsk.share.test.*; 46 import vm.share.InMemoryJavaCompiler; 47 48 /** 49 * There is a data structure named "dictionary" in class BlockFreelist. It stores 50 * information about free memory blocks for further reusing. Allocation of new block goes 51 * from dictionary only if dictionary is fat enough. (At the moment of test creation this limit is 64K.) 52 * So to stress dictionary we should fill it permanently. The easiest way to fill the dictionary 53 * is to fail class loading. This failed action will return allocated blocks to dictionary. 54 * 55 * There are two type of threads in this test: threads, failing classloading and threads, 56 * loading regular classes and checking they work properly. 57 */ 58 public class StressDictionary extends GCTestBase { 59 60 private static byte[] bytecode; 61 62 private class FillingDictionaryWorker implements Callable<Object> { 63 @Override 64 public Object call() throws Exception { 65 while (stresser.continueExecution()) { 66 try { 67 byte[] badBytecode = bytecode.clone(); 68 badBytecode[random.nextInt(badBytecode.length)] = (byte) 42; 69 classloader.define(badBytecode); 70 } catch (Throwable e) { 71 // We can get ClassFormatError, ClassNotFoundException or anything else here 72 } 73 } 74 return null; 75 } 76 } 77 78 private class RegularWorker implements Callable<Object> { 79 @Override 80 public Object call() throws Exception { 81 while (stresser.continueExecution()) { 82 Class<?> c = classloader.define(bytecode); 83 testClass(c); 84 } 85 return null; 86 } 87 } 88 89 private static String[] args; 90 91 private static final String methodName = "myMethod"; 92 93 private static final int NUMBER_OF_CORRUPTING_THREADS = 10; 94 95 private static final int NUMBER_OF_METHOD_CALLS = 50; 96 97 private static final int NUMBER_OF_NOT_CORRUPTING_THREADS = 10; 98 99 private AtomicLong classesCounter = new AtomicLong(0); 100 101 private volatile ClassloaderUnderTest classloader = new ClassloaderUnderTest(); 102 103 private Random random; 104 105 private ExecutionController stresser; 106 107 public static void main(String[] args) { 108 StressDictionary.args = args; 109 Tests.runTest(new StressDictionary(), args); 110 } 111 112 public void run() { 113 random = new Random(runParams.getSeed()); 114 stresser = new Stresser(args); 115 stresser.start(1); 116 // Generate some bytecodes. 117 bytecode = generateAndCompile(); 118 List<Callable<Object>> tasks = new LinkedList<Callable<Object>>(); 119 for (int i = 0; i < NUMBER_OF_CORRUPTING_THREADS; i++) { 120 tasks.add(this.new FillingDictionaryWorker()); 121 } 122 for (int i = 0; i < NUMBER_OF_NOT_CORRUPTING_THREADS; i++) { 123 tasks.add(this.new RegularWorker()); 124 } 125 ExecutorService executorService = Executors.newCachedThreadPool(); 126 List<Future<Object>> results = null; 127 try { 128 results = executorService.invokeAll(tasks); 129 } catch (InterruptedException e) { 130 e.printStackTrace(); 131 } 132 133 int act_results = results.size(); 134 int exp_results = NUMBER_OF_CORRUPTING_THREADS + 135 NUMBER_OF_NOT_CORRUPTING_THREADS; 136 if (act_results == exp_results) { 137 System.err.println("INFO: There are " + act_results + " results."); 138 } else { 139 throw new RuntimeException("Wrong # of results from invokeAll(); " 140 + "exp_results=" + exp_results + "; " 141 + "act_results=" + act_results + "."); 142 } 143 144 int cancelled_cnt = 0; 145 int not_done_cnt = 0; 146 for (int i = 0; i < act_results; i++) { 147 if (!results.get(i).isDone()) { 148 not_done_cnt++; 149 System.err.println("ERROR: task #" + i + " is not done."); 150 } 151 if (results.get(i).isCancelled()) { 152 cancelled_cnt++; 153 System.err.println("ERROR: task #" + i + " was canceled."); 154 } 155 } 156 157 if (cancelled_cnt == 0) { 158 System.err.println("INFO: no tasks were cancelled."); 159 } 160 if (not_done_cnt == 0) { 161 System.err.println("INFO: all tasks are done."); 162 } 163 if (cancelled_cnt != 0 && not_done_cnt != 0) { 164 throw new RuntimeException(cancelled_cnt 165 + " tasks were cancelled and " 166 + not_done_cnt 167 + " tasks are not done."); 168 } else if (cancelled_cnt != 0) { 169 throw new RuntimeException(cancelled_cnt 170 + " tasks were cancelled."); 171 } else if (not_done_cnt != 0) { 172 throw new RuntimeException(not_done_cnt + " tasks are not done."); 173 } 174 } 175 176 private byte[] generateAndCompile() { 177 Map<String, CharSequence> sources = new HashMap<String, CharSequence>(); 178 String className = "MyClass" + classesCounter.incrementAndGet(); 179 sources.put(className, generateSource(className)); 180 return InMemoryJavaCompiler.compile(sources).values().iterator().next(); 181 } 182 183 private CharSequence generateSource(String className) { 184 return "public class " + className + " { " + 185 "public static String s1 = \"s1" + random.nextInt() + "\"; " + 186 "public String s2 = \"s2" + random.nextInt() + "\"; " + 187 "public String " + methodName + "() {return s1 + s2; } " + 188 "}"; 189 } 190 191 private void testClass(Class<?> clazz) { 192 try { 193 for (Method m : clazz.getMethods()) { 194 if (m.getName().equals(methodName)) { 195 for (int j = 0; j < NUMBER_OF_METHOD_CALLS; j++) { 196 m.invoke(clazz.newInstance()); 197 } 198 } 199 } 200 } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { 201 log.error("Class check failed: " + e.getMessage()); 202 e.printStackTrace(); 203 setFailed(true); 204 } 205 206 } 207 208 }