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 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         try {
 127             executorService.invokeAll(tasks);
 128         } catch (InterruptedException e) {
 129             e.printStackTrace();
 130         }
 131     }
 132 
 133     private byte[] generateAndCompile() {
 134         Map<String, CharSequence> sources = new HashMap<String, CharSequence>();
 135         String className = "MyClass" + classesCounter.incrementAndGet();
 136         sources.put(className, generateSource(className));
 137         return InMemoryJavaCompiler.compile(sources).values().iterator().next();
 138     }
 139 
 140     private CharSequence generateSource(String className) {
 141         return "public class " + className + " { " +
 142                         "public static String s1 = \"s1" + random.nextInt() + "\"; " +
 143                                         "public String s2 = \"s2" + random.nextInt() + "\"; " +
 144                                         "public String " + methodName + "() {return s1 + s2; } " +
 145                         "}";
 146     }
 147 
 148     private void testClass(Class<?> clazz) {
 149         try {
 150             for (Method m : clazz.getMethods()) {
 151                 if (m.getName().equals(methodName)) {
 152                     for (int j = 0; j < NUMBER_OF_METHOD_CALLS; j++) {
 153                         m.invoke(clazz.newInstance());
 154                     }
 155                 }
 156             }
 157         } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
 158             log.error("Class check failed: " + e.getMessage());
 159             e.printStackTrace();
 160             setFailed(true);
 161         }
 162 
 163     }
 164 
 165 }