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