1 /* 2 * Copyright Amazon.com Inc. 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 /* 26 * @test 27 * @bug 8277444 28 * 29 * @library /test/lib 30 * @compile SimpleIdentityTransformer.java 31 * @run shell MakeJAR.sh retransformAgent 32 * @run main/othervm -javaagent:retransformAgent.jar RetransformBigClassTest 33 */ 34 35 import jdk.test.lib.compiler.InMemoryJavaCompiler; 36 37 /* 38 * JvmtiClassFileReconstituter::copy_bytecodes restores bytecodes rewritten 39 * by the linking process. It is used by RetransformClasses. 40 * JDK-8277444 is a data race between copy_bytecodes and the linking process. 41 * This test puts the linking process in one thread and the retransforming process 42 * in another thread. The test uses Class.forName("BigClass", false, classLoader) 43 * which does not link the class. When the class is used, the linking process starts. 44 * In another thread retransforming of the class is happening. 45 * We generate a class with big methods. A number of methods and their size are 46 * chosen to make the linking and retransforming processes run concurrently. 47 * We delay the retransforming process to follow the linking process. 48 * If there is no synchronization between the processes, a data race will happen. 49 */ 50 public class RetransformBigClassTest extends AInstrumentationTestCase { 51 52 private static final Object LOCK = new Object(); 53 private static final int COUNTER_INC_COUNT = 2000; // A number of 'c+=1;' statements in methods of a class. 54 private static final int MIN_LINK_TIME_MS = 60; // Large enough so the linking and retransforming processes run in parallel. 55 private static final int RETRANSFORM_CLASSES_DELAY_MS = 37; // We manage to create a data race when a delay is in the range 0.52x - 0.62x of MIN_LINK_TIME_MS. 56 57 private static Class<?> bigClass; 58 private static byte[] bigClassBytecode; 59 60 private Thread retransformThread; 61 62 RetransformBigClassTest() { 63 super("RetransformBigClassTest"); 64 } 65 66 public static void main(String[] args) throws Throwable { 67 new RetransformBigClassTest().runTest(); 68 } 69 70 protected final void doRunTest() throws Throwable { 71 ClassLoader classLoader = new ClassLoader() { 72 @Override 73 protected Class<?> findClass(String name) throws ClassNotFoundException { 74 if (name.equals("BigClass")) { 75 return defineClass(name, bigClassBytecode, 0, bigClassBytecode.length); 76 } 77 78 return super.findClass(name); 79 } 80 }; 81 synchronized (LOCK) { 82 bigClass = Class.forName("BigClass", false, classLoader); 83 LOCK.notify(); 84 } 85 // Make a use of the BigClass 86 assertTrue(bigClass.getConstructor().newInstance().hashCode() != 0); 87 retransformThread.join(); 88 } 89 90 private byte[] createClassBytecode(String className, int methodCount) throws Exception { 91 String methodBody = ""; 92 for (int j = 0; j < COUNTER_INC_COUNT; j++) { 93 methodBody += "c+=1;"; 94 } 95 96 String classSrc = "public class " + className + " { int c;"; 97 98 for (int i = 0; i < methodCount; i++) { 99 classSrc += "\npublic void m" + i + "(){"; 100 classSrc += methodBody; 101 classSrc += "\n}"; 102 } 103 classSrc += "\n}"; 104 105 return InMemoryJavaCompiler.compile(className, classSrc); 106 } 107 108 // We need a number of methods such that the linking time is greater than 109 // or equal to MIN_LINK_TIME_MS. 110 // We create a class having 5 methods and trigger the linking process. 111 // We measure the time taken and use it to calculate the needed number. 112 private int findMethodCount() throws Exception { 113 int methodCount = 5; 114 final String className = "BigClass" + methodCount; 115 final byte[] bytecode = createClassBytecode(className, methodCount); 116 ClassLoader classLoader = new ClassLoader() { 117 @Override 118 protected Class<?> findClass(String name) throws ClassNotFoundException { 119 if (name.equals(className)) { 120 return defineClass(name, bytecode, 0, bytecode.length); 121 } 122 123 return super.findClass(name); 124 } 125 }; 126 var bigClass = Class.forName(className, false, classLoader); 127 long startTime = System.nanoTime(); 128 assertTrue(bigClass.getConstructor().newInstance().hashCode() != 0); 129 double linkTimeMs = (System.nanoTime() - startTime) / 1000000.0; 130 System.out.println("Link time for a class with " + methodCount + " methods each having " + COUNTER_INC_COUNT + " counter increments: " + Math.round(linkTimeMs)); 131 if (linkTimeMs < MIN_LINK_TIME_MS) { 132 methodCount = (int)Math.round((MIN_LINK_TIME_MS * methodCount) / linkTimeMs); 133 } 134 System.out.println("The number of methods to exceed " + MIN_LINK_TIME_MS + " ms linking time: " + methodCount); 135 return methodCount; 136 } 137 138 @Override 139 protected void setUp() throws Exception { 140 super.setUp(); 141 142 bigClassBytecode = createClassBytecode("BigClass", findMethodCount()); 143 fInst.addTransformer(new SimpleIdentityTransformer()); 144 retransformThread = new Thread(() -> { 145 try { 146 synchronized (LOCK) { 147 while (bigClass == null) { 148 System.out.println("[retransformThread]: Waiting for bigClass"); 149 LOCK.wait(); 150 } 151 } 152 Thread.sleep(RETRANSFORM_CLASSES_DELAY_MS); 153 fInst.retransformClasses(bigClass); 154 } catch (Exception e) { 155 e.printStackTrace(); 156 } 157 }); 158 retransformThread.start(); 159 Thread.sleep(100); 160 } 161 }