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 }