1 /* 2 * Copyright (c) 2004, 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 nsk.jvmti.RedefineClasses; 25 26 import java.io.*; 27 import java.util.*; 28 import nsk.share.*; 29 import nsk.share.jvmti.*; 30 31 /** 32 * The test exercises that the JVMTI function RedefineClasses() 33 * enable to redefine a static inner (nested) class properly. Redefiniton 34 * is performed in asynchronous manner from a separate 35 * thread when the VM is provoked to switch into compiled mode.<br> 36 * The test works as follows. Two threads are started: java thread 37 * executing a nested class to be redefined, and native one executing 38 * an agent code. Then the nested class method <code>redefclass029HotMethod()</code> 39 * is provoked to be compiled (optimized), and thus the JVMTI event 40 * <code>CompiledMethodLoad</code> should be sent. After that, the 41 * agent redefines the nested class. Different kinds of outer fields 42 * are accessed from executing methods in both versions of the 43 * nested class. Upon the redefinition, the main test thread verifies 44 * via the outer fields values that the nested method <code>run()</code> 45 * having an active stack frame stays obsolete but the other nested 46 * methods have been redefined. It also verifies that the outer class 47 * is still can access the nested class fields after the redefinition. 48 */ 49 public class redefclass029 extends DebugeeClass { 50 final static String REDEF_CLS_SIGNATURE = 51 "Lnsk/jvmti/RedefineClasses/redefclass029$RedefClass;"; 52 53 static int status = Consts.TEST_PASSED; 54 55 static Log log = null; 56 57 // path to the directory with redefining class files 58 static String clfBasePath = null; 59 60 // dummy outer fields to be changed by the nested class 61 private static int prStOuterFl[] = {0,0,0}; 62 static int packStOuterFl[] = {0,0,0}; 63 public static int pubStOuterFl[] = {0,0,0}; 64 65 /* a dummy outer private field to be accessed or not in 66 the nested class and thus provoking compiler to add or not 67 a synthetic access method into the outer class. */ 68 private static char prOuterFieldToBeAccessed = 'a'; 69 storeClassBytes(byte[] classBytes)70 native static void storeClassBytes(byte[] classBytes); notifyNativeAgent()71 native static void notifyNativeAgent(); /** notify native agent that "hot" method is entered */ isRedefinitionOccurred()72 native static boolean isRedefinitionOccurred(); /** check whether class redefinition was already occurred */ 73 main(String args[])74 public static void main(String args[]) { 75 args = nsk.share.jvmti.JVMTITest.commonInit(args); 76 77 // produce JCK-like exit status. 78 System.exit(run(args, System.out) + Consts.JCK_STATUS_BASE); 79 } 80 run(String args[], PrintStream out)81 public static int run(String args[], PrintStream out) { 82 return new redefclass029().runIt(args, out); 83 } 84 runIt(String args[], PrintStream out)85 private int runIt(String args[], PrintStream out) { 86 int iter; 87 88 ArgumentHandler argHandler = new ArgumentHandler(args); 89 log = new Log(out, argHandler); 90 91 try { 92 // number of iterations for a method to become 'hotspot' one 93 iter = Integer.parseInt(args[0]); 94 95 // directory to search a redefining class 96 clfBasePath = args[1]; 97 } catch(Exception e) { 98 throw new Failure("TEST BUG: Wrong test parameters, caught: " 99 + e); 100 } 101 102 storeClassBytes(loadFromClassFile(REDEF_CLS_SIGNATURE)); 103 104 // testing sync 105 log.display("waiting for the agent start ...\n"); 106 status = checkStatus(status); 107 108 log.display("starting an auxiliary thread ...\n"); 109 RedefClass redefCls = new RedefClass(iter); 110 redefCls.setDaemon(true); 111 synchronized(redefCls) { 112 redefCls.start(); 113 log.display("waiting for auxiliary thread readiness...\n"); 114 try { 115 redefCls.wait(); // wait for the thread's readiness 116 } catch (InterruptedException e) { 117 redefCls.interrupt(); 118 throw new Failure("TEST FAILURE: waiting for auxiliary thread start, caught: " 119 + e); 120 } 121 } 122 123 // testing sync 124 log.display("auxiliary thread started\n" 125 + "waiting for the agent finish ...\n"); 126 status = checkStatus(status); 127 128 boolean isRedefinitionStarted = waitForRedefinitionStarted(); 129 boolean isRedefinitionCompleted = false; 130 if (isRedefinitionStarted) { 131 isRedefinitionCompleted = waitForRedefinitionCompleted(redefCls); 132 } 133 134 log.display("waiting for auxiliary thread ...\n"); 135 redefCls.stopMe = true; 136 try { 137 redefCls.join(); 138 } catch (InterruptedException e) { 139 redefCls.interrupt(); 140 throw new Failure("TEST FAILURE: waiting for auxiliary thread death, caught: " 141 + e); 142 } 143 144 // CR 6604375: check whether class redefinition occurred 145 if (isRedefinitionCompleted) { 146 // verify results 147 checkOuterFields(0, 1); 148 checkOuterFields(1, 2); 149 checkOuterFields(2, 2); 150 checkInnerFields(redefCls, 1); 151 } 152 153 return status; 154 } 155 waitForRedefinitionStarted()156 private boolean waitForRedefinitionStarted() { 157 final int SLEEP_MS = 20; 158 int iterationsLeft = 2000 / SLEEP_MS; 159 while (iterationsLeft >= 0) { 160 if (isRedefinitionOccurred()) { 161 log.display("Redefinition started."); 162 return true; 163 } 164 --iterationsLeft; 165 safeSleep(SLEEP_MS); 166 } 167 log.complain("Redefinition not started. May need more time for -Xcomp."); 168 status = Consts.TEST_FAILED; 169 return false; 170 } 171 waitForRedefinitionCompleted(RedefClass redefCls)172 private boolean waitForRedefinitionCompleted(RedefClass redefCls) { 173 final int SLEEP_MS = 20; 174 int iterationsLeft = 10000 / SLEEP_MS; 175 while (iterationsLeft >= 0) { 176 // Check if new code has changed fields. 177 if (prStOuterFl[1] == 2 && prStOuterFl[2] == 2 && redefCls.prInnerFl == 1) { 178 log.display("Redefinition completed."); 179 return true; 180 } 181 --iterationsLeft; 182 safeSleep(SLEEP_MS); 183 } 184 log.complain("Redefinition not completed. May need more time for -Xcomp."); 185 status = Consts.TEST_FAILED; 186 return false; 187 } 188 189 checkOuterFields(int index, int expValue)190 private void checkOuterFields(int index, int expValue) { 191 if (prStOuterFl[index] != expValue 192 || packStOuterFl[index] != expValue 193 || pubStOuterFl[index] != expValue) { 194 status = Consts.TEST_FAILED; 195 log.complain("TEST FAILED: unexpected values of outer fields:" 196 + "\n\tprStOuterFl["+ index +"]: got: " + prStOuterFl[index] 197 + ", expected: " + expValue 198 + "\n\tpackStOuterFl["+ index +"]: got: " + packStOuterFl[index] 199 + ", expected: " + expValue 200 + "\n\tpubStOuterFl["+ index +"]: got: " + pubStOuterFl[index] 201 + ", expected: " + expValue); 202 } 203 } 204 checkInnerFields(RedefClass redefCls, int expValue)205 private void checkInnerFields(RedefClass redefCls, int expValue) { 206 if (redefCls.prInnerFl != expValue 207 || redefCls.packInnerFl != expValue 208 || redefCls.pubInnerFl != expValue) { 209 status = Consts.TEST_FAILED; 210 log.complain("TEST FAILED: unexpected values of inner fields:" 211 + "\n\tprInnerFl: got: " + redefCls.prInnerFl 212 + ", expected: " + expValue 213 + "\n\tpackInnerFl: got: " + redefCls.packInnerFl 214 + ", expected: " + expValue 215 + "\n\tpubInnerFl: got: " + redefCls.pubInnerFl 216 + ", expected: " + expValue); 217 } 218 } 219 220 /** 221 * Load bytes of a redefining class. 222 */ loadFromClassFile(String signature)223 private static byte[] loadFromClassFile(String signature) { 224 String testPath = clfBasePath + File.separator 225 + "newclass" + File.separator 226 + signature.substring(1, signature.length()-1).replace('/', File.separatorChar) 227 + ".class"; 228 File classFile = null; 229 230 log.display("looking for class file at\n\t" 231 + testPath + " ...\n"); 232 233 try { 234 classFile = new File(testPath); 235 } catch (NullPointerException e) { 236 throw new Failure("FAILURE: failed to open class file, caught: " 237 + e); 238 } 239 240 log.display("loading " + classFile.length() 241 + " bytes from class file "+ testPath + " ...\n"); 242 byte[] buf = new byte[(int) classFile.length()]; 243 try { 244 InputStream in = new FileInputStream(classFile); 245 in.read(buf); 246 in.close(); 247 } catch (Exception e) { 248 throw new Failure("FAILURE: failed to load bytes from class file, caught: " 249 + e); 250 } 251 252 log.display(classFile.length() 253 + " bytes of a redefining class loaded\n"); 254 255 return buf; 256 } 257 258 /** 259 * Static inner (nested) class to be redefined in the agent. 260 */ 261 static class RedefClass extends Thread { 262 boolean stopMe = false; 263 int iter; 264 265 // dummy inner fields to be accessed by the outer class 266 private int prInnerFl = 1; 267 int packInnerFl = 1; 268 public int pubInnerFl = 1; 269 RedefClass(int iter)270 RedefClass(int iter) { 271 super("RedefClass"); 272 this.iter = iter; 273 } 274 275 /** 276 * This method will have an active stack frame during 277 * nested class redinition, so this version should continue 278 * execution and thus outer fields should have values equal 1. 279 */ run()280 public void run() { 281 log.display(this.getName() + ": started"); 282 synchronized(this) { 283 this.notify(); // notify the main thread 284 285 prStOuterFl[0] = 1; 286 packStOuterFl[0] = 1; 287 pubStOuterFl[0] = 1; 288 289 while(!stopMe) { 290 warmUpMethod(); 291 292 // get the main thread chance to obtain CPU 293 try { 294 this.wait(100); 295 } catch(Exception e) {} 296 } 297 log.display(this.getName() + ": exiting"); 298 } 299 } 300 warmUpMethod()301 void warmUpMethod() { 302 prStOuterFl[1] = 1; 303 packStOuterFl[1] = 1; 304 pubStOuterFl[1] = 1; 305 306 for (int i=0; i<iter; i++) 307 redefclass029HotMethod(i); 308 309 redefclass029HotMethod(10); 310 } 311 312 /** 313 * Hotspot method to be compiled. 314 */ redefclass029HotMethod(int i)315 void redefclass029HotMethod(int i) { 316 prStOuterFl[2] = 1; 317 packStOuterFl[2] = 1; 318 pubStOuterFl[2] = 1; 319 320 int j=0; 321 322 j +=i; 323 j--; 324 if (j >10) 325 j = 0; 326 327 notifyNativeAgent(); 328 } 329 330 /** 331 * A dummy method accessing a private field of the outer class. 332 * It provokes compiler to add a synthetic access method 333 * into the outer class. 334 */ methAccessingOuterPrivateField()335 void methAccessingOuterPrivateField() { 336 prOuterFieldToBeAccessed = 'b'; 337 } 338 } 339 340 } 341