1 /* 2 * Copyright (c) 2003, 2015, 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 * @bug 4882798 27 * @summary multi-thread test to exercise sync and contention for adds to transformer registry 28 * @author Gabriel Adauto, Wily Technology 29 * 30 * @run build TransformerManagementThreadAddTests 31 * @run shell MakeJAR.sh redefineAgent 32 * @run main/othervm -javaagent:redefineAgent.jar TransformerManagementThreadAddTests TransformerManagementThreadAddTests 33 */ 34 import java.io.*; 35 import java.lang.instrument.*; 36 import java.security.ProtectionDomain; 37 import java.util.*; 38 39 public class TransformerManagementThreadAddTests extends ATestCaseScaffold 40 { 41 public static void 42 main (String[] args) 43 throws Throwable { 44 ATestCaseScaffold test = new TransformerManagementThreadAddTests(args[0]); 45 test.runTest(); 46 } 47 48 protected void 49 doRunTest() 50 throws Throwable { 51 testMultiThreadAdds(); 52 } 53 54 55 /** 56 * CONFIGURATION FOR TEST 57 * ---------------------- 58 * Set these variables to different values to test the object that 59 * manages the transformers. 60 * 61 * MIN_TRANS: the minimum number of transformers to add by a thread 62 * MAX_TRANS: the maximum number of transformers to add by a thread 63 * There will be a total of MAX_TRANS-MIN_TRANS+1 threads created. 64 * Each thread will add between MIN_TRANS and MAX_TRANS transformers 65 * to the manager. 66 * 67 * REMOVE_THREADS: the number of threads to run that spend their time 68 * removing transformers 69 */ 70 protected static final int MIN_TRANS = 33; 71 protected static final int MAX_TRANS = 45; 72 protected static final int REMOVE_THREADS = 5; 73 74 protected static final boolean LOG_TRANSFORMATIONS = false; 75 76 /** 77 * Field variables 78 */ 79 protected static final int TOTAL_THREADS = MAX_TRANS - MIN_TRANS + 1; 80 81 private byte[] fDummyClassBytes; 82 // fCheckedTransformers is a Vector that is used to verify 83 // that the transform() function is called in the same 84 // order in which the transformers were added to the 85 // TransformerManager. The test currently verifies that all 86 // transformers for a specific worker thread are in 87 // increasing order by index value. 88 private Vector fCheckedTransformers; 89 private Instrumentation fInstrumentation; 90 private int fFinished; 91 private ExecuteTransformersThread fExec; 92 93 // Need to use this for synchronization in subclass 94 protected Vector fAddedTransformers; 95 private String fDummyClassName; 96 97 /** 98 * Constructor for TransformerManagementThreadAddTests. 99 * @param name Name for the test 100 */ 101 public TransformerManagementThreadAddTests(String name) 102 { 103 super(name); 104 105 fCheckedTransformers = new Vector(); 106 fAddedTransformers = new Vector(); 107 108 fDummyClassName = "DummyClass"; 109 String resourceName = "DummyClass.class"; 110 File f = new File(System.getProperty("test.classes", "."), resourceName); 111 System.out.println("Reading test class from " + f); 112 try 113 { 114 InputStream redefineStream = new FileInputStream(f); 115 fDummyClassBytes = NamedBuffer.loadBufferFromStream(redefineStream); 116 } 117 catch (IOException e) 118 { 119 fail("Could not load the class: "+resourceName); 120 } 121 } 122 123 public void 124 testMultiThreadAdds() 125 { 126 TransformerThread[] threads = new TransformerThread[TOTAL_THREADS]; 127 for (int i = MIN_TRANS; i <= MAX_TRANS; i++) 128 { 129 int index = i - MIN_TRANS; 130 threads[index] = new TransformerThread("Trans"+prettyNum(index,2), i); 131 } 132 133 ExecuteTransformersThread exec = new ExecuteTransformersThread(); 134 exec.start(); 135 for (int i = threads.length - 1; i >= 0; i--) 136 { 137 threads[i].start(); 138 } 139 140 // Effective Java - Item 48: Synchronize access to shared mutable data 141 // Don't use a direct field getter. 142 while (!exec.isDone()) 143 { 144 // Effective Java - Item 51: Don't depend on the thread scheduler 145 // Use sleep() instead of yield(). 146 try { 147 Thread.sleep(500); 148 } catch (InterruptedException ie) { 149 } 150 } 151 assertTrue(finalCheck()); 152 153 if (LOG_TRANSFORMATIONS) { 154 printTransformers(); 155 } 156 } 157 158 /** 159 * Returns the Instrumentation. 160 * @return Instrumentation the data type with JPLIS calls 161 */ 162 public Instrumentation getInstrumentation() 163 { 164 return fInstrumentation; 165 } 166 167 /** 168 * Returns the execution thread 169 * @return ExecuteTransformersThread 170 */ 171 protected ExecuteTransformersThread getExecThread() 172 { 173 return fExec; 174 } 175 176 /** 177 * Sets the execution thread 178 * @param exec The execution thread to set 179 */ 180 protected void setExecThread(ExecuteTransformersThread exec) 181 { 182 this.fExec = exec; 183 } 184 185 // Effective Java - Item 48: Synchronize access to shared mutable data 186 // Document a synchronized setter. 187 protected synchronized void 188 threadFinished(Thread t) 189 { 190 fFinished++; 191 } 192 193 // Effective Java - Item 48: Synchronize access to shared mutable data 194 // Provide synchronized getter. 195 protected synchronized boolean 196 threadsDone() 197 { 198 return fFinished == TOTAL_THREADS; 199 } 200 201 /** 202 * Method testCompleted. 203 * @return boolean 204 */ 205 protected boolean 206 testCompleted() 207 { 208 // Effective Java - Item 48: Synchronize access to shared mutable data 209 // Don't use direct field getter. 210 return getExecThread().isDone(); 211 } 212 213 /** 214 * 215 */ 216 protected boolean 217 finalCheck() 218 { 219 if (LOG_TRANSFORMATIONS) { 220 // log the list 221 for (int x = 0; x < fCheckedTransformers.size(); x++ ) { 222 System.out.println(x + "\t\t" + fCheckedTransformers.get(x)); 223 } 224 System.out.println(); 225 System.out.println(); 226 227 // check for multiples 228 for (int x = 0; x < fCheckedTransformers.size(); x++ ) { 229 Object current = fCheckedTransformers.get(x); 230 for ( int y = x + 1; y < fCheckedTransformers.size(); y++) { 231 Object running = fCheckedTransformers.get(y); 232 if ( current.equals(running) ) { 233 System.out.println(x + "\t" + y + " \t" + "FOUND DUPLICATE: " + current); 234 } 235 } 236 } 237 } 238 239 for (int j = 1; j < fCheckedTransformers.size(); j++) { 240 ThreadTransformer transformer = (ThreadTransformer)fCheckedTransformers.get(j); 241 for (int i = 0; i < j; i++) { 242 ThreadTransformer currTrans = (ThreadTransformer)fCheckedTransformers.get(i); 243 assertTrue(currTrans + " incorrectly appeared before " + 244 transformer + " i=" + i + " j=" + j + " size=" + 245 fCheckedTransformers.size(), 246 !( 247 currTrans.getThread().equals(transformer.getThread()) && 248 currTrans.getIndex() > transformer.getIndex())); 249 } 250 } 251 return true; 252 } 253 254 /** 255 * 256 */ 257 protected void 258 setUp() 259 throws Exception 260 { 261 super.setUp(); 262 263 fFinished = 0; 264 assertTrue(MIN_TRANS < MAX_TRANS); 265 fInstrumentation = InstrumentationHandoff.getInstrumentationOrThrow(); 266 } 267 268 /** 269 * 270 */ 271 protected void 272 tearDown() 273 throws Exception 274 { 275 super.tearDown(); 276 } 277 278 279 280 /** 281 * Method executeTransform. 282 */ 283 private void 284 executeTransform() 285 { 286 try 287 { 288 ClassDefinition cd = new ClassDefinition(DummyClass.class, fDummyClassBytes); 289 290 // When the ClassDefinition above is created for the first 291 // time and every time redefineClasses() below is called, 292 // the transform() function is called for each registered 293 // transformer. We only want one complete set of calls to 294 // be logged in the fCheckedTransformers Vector so we clear 295 // any calls logged for ClassDefinition above and just use 296 // the ones logged for redefineClasses() below. 297 fCheckedTransformers.clear(); 298 299 getInstrumentation().redefineClasses(new ClassDefinition[]{ cd }); 300 } 301 catch (ClassNotFoundException e) 302 { 303 fail("Could not find the class: "+DummyClass.class.getName()); 304 } 305 catch (UnmodifiableClassException e) 306 { 307 fail("Could not modify the class: "+DummyClass.class.getName()); 308 } 309 } 310 311 /** 312 * Method addTransformerToManager. 313 * @param threadTransformer 314 */ 315 private void 316 addTransformerToManager(ThreadTransformer threadTransformer) 317 { 318 getInstrumentation().addTransformer(threadTransformer); 319 fAddedTransformers.add(threadTransformer); 320 } 321 322 /** 323 * Method checkInTransformer. 324 * @param myClassFileTransformer 325 */ 326 private void 327 checkInTransformer(ThreadTransformer transformer) 328 { 329 fCheckedTransformers.add(transformer); 330 } 331 332 /** 333 * Method createTransformer. 334 * @param transformerThread 335 * @param i 336 * @return ThreadTransformer 337 */ 338 private ThreadTransformer 339 createTransformer(TransformerThread thread, int index) 340 { 341 ThreadTransformer tt = null; 342 343 tt = new ThreadTransformer(thread, index); 344 345 return tt; 346 } 347 348 349 protected class 350 ExecuteTransformersThread 351 extends Thread 352 { 353 private boolean fDone = false; 354 355 // Effective Java - Item 48: Synchronize access to shared mutable data 356 // Provide a synchronized getter. 357 private synchronized boolean isDone() { 358 return fDone; 359 } 360 361 // Effective Java - Item 48: Synchronize access to shared mutable data 362 // Provide a synchronized setter. 363 private synchronized void setIsDone() { 364 fDone = true; 365 } 366 367 public void 368 run() 369 { 370 while(!threadsDone()) 371 { 372 executeTransform(); 373 } 374 375 // Do a final check for good measure 376 executeTransform(); 377 // Effective Java - Item 48: Synchronize access to shared mutable data 378 // Don't use direct field setter. 379 setIsDone(); 380 } 381 } 382 383 384 protected class 385 TransformerThread 386 extends Thread 387 { 388 private final ThreadTransformer[] fThreadTransformers; 389 390 TransformerThread(String name, int numTransformers) 391 { 392 super(name); 393 394 fThreadTransformers = makeTransformers(numTransformers); 395 } 396 397 /** 398 * Method makeTransformers. 399 * @param numTransformers 400 * @return ThreadTransformer[] 401 */ 402 private ThreadTransformer[] 403 makeTransformers(int numTransformers) 404 { 405 ThreadTransformer[] trans = new ThreadTransformer[numTransformers]; 406 407 for (int i = 0; i < trans.length; i++) 408 { 409 trans[i] = createTransformer(TransformerThread.this, i); 410 } 411 412 return trans; 413 } 414 415 public void 416 run() 417 { 418 for (int i = 0; i < fThreadTransformers.length; i++) 419 { 420 addTransformerToManager(fThreadTransformers[i]); 421 } 422 threadFinished(TransformerThread.this); 423 } 424 } 425 426 /** 427 * ClassFileTransformer implementation that knows its thread 428 */ 429 protected class 430 ThreadTransformer extends SimpleIdentityTransformer 431 { 432 private final String fName; 433 private final int fIndex; 434 private final Thread fThread; 435 436 /** 437 * Constructor for ThreadTransformer. 438 */ 439 public ThreadTransformer(Thread thread, int index) { 440 super(); 441 fThread = thread; 442 fIndex = index; 443 fName = "TT["+fThread.getName()+"]["+prettyNum(fIndex,3)+"]"; 444 } 445 446 public String toString() 447 { 448 return fName; 449 } 450 451 /** 452 * 453 */ 454 public byte[] 455 transform( 456 ClassLoader loader, 457 String className, 458 Class<?> classBeingRedefined, 459 ProtectionDomain domain, 460 byte[] classfileBuffer) 461 { 462 if ( className.equals(TransformerManagementThreadAddTests.this.fDummyClassName) ) { 463 checkInTransformer(ThreadTransformer.this); 464 } 465 return super.transform( loader, 466 className, 467 classBeingRedefined, 468 domain, 469 classfileBuffer); 470 } 471 472 /** 473 * Returns the index. 474 * @return int 475 */ 476 public int getIndex() 477 { 478 return fIndex; 479 } 480 481 /** 482 * Returns the thread. 483 * @return Thread 484 */ 485 public Thread getThread() 486 { 487 return fThread; 488 } 489 490 } 491 492 /** 493 * DEBUG STUFF 494 */ 495 private int NUM_SWITCHES; 496 497 /** 498 * Method printTransformers. 499 */ 500 protected void printTransformers() 501 { 502 NUM_SWITCHES = 0; 503 Iterator trans = fCheckedTransformers.iterator(); 504 ThreadTransformer old = null; 505 StringBuffer buf = new StringBuffer(); 506 507 for (int i = 1; trans.hasNext(); i++) 508 { 509 ThreadTransformer t = (ThreadTransformer)trans.next(); 510 buf.append(t.toString()); 511 if (old != null) 512 { 513 if (!old.getThread().equals(t.getThread())) 514 { 515 NUM_SWITCHES++; 516 buf.append("*"); 517 } 518 else 519 { buf.append(" "); } 520 } 521 else 522 { buf.append(" "); } 523 524 if (i % 5 == 0) 525 { 526 buf.append("\n"); 527 } 528 else 529 { buf.append(" "); } 530 531 old = t; 532 } 533 System.out.println(buf); 534 System.out.println("\nNumber of transitions from one thread to another: "+NUM_SWITCHES); 535 } 536 537 protected String 538 prettyNum(int n, int numSize) 539 { 540 StringBuffer num = new StringBuffer(Integer.toString(n)); 541 int size = num.length(); 542 for (int i = 0; i < numSize - size; i++) 543 { 544 num.insert(0, "0"); 545 } 546 547 return num.toString(); 548 } 549 /** 550 * END DEBUG STUFF 551 */ 552 553 }