| /* |
| * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| /** |
| * @test |
| * @bug 4882798 |
| * @summary multi-thread test to exercise sync and contention for adds to transformer registry |
| * @author Gabriel Adauto, Wily Technology |
| * |
| * @run build TransformerManagementThreadAddTests |
| * @run shell MakeJAR.sh redefineAgent |
| * @run main/othervm -javaagent:redefineAgent.jar TransformerManagementThreadAddTests TransformerManagementThreadAddTests |
| */ |
| import java.io.*; |
| import java.lang.instrument.*; |
| import java.security.ProtectionDomain; |
| import java.util.*; |
| |
| public class TransformerManagementThreadAddTests extends ATestCaseScaffold |
| { |
| public static void |
| main (String[] args) |
| throws Throwable { |
| ATestCaseScaffold test = new TransformerManagementThreadAddTests(args[0]); |
| test.runTest(); |
| } |
| |
| protected void |
| doRunTest() |
| throws Throwable { |
| testMultiThreadAdds(); |
| } |
| |
| |
| /** |
| * CONFIGURATION FOR TEST |
| * ---------------------- |
| * Set these variables to different values to test the object that |
| * manages the transformers. |
| * |
| * MIN_TRANS: the minimum number of transformers to add by a thread |
| * MAX_TRANS: the maximum number of transformers to add by a thread |
| * There will be a total of MAX_TRANS-MIN_TRANS+1 threads created. |
| * Each thread will add between MIN_TRANS and MAX_TRANS transformers |
| * to the manager. |
| * |
| * REMOVE_THREADS: the number of threads to run that spend their time |
| * removing transformers |
| */ |
| protected static final int MIN_TRANS = 33; |
| protected static final int MAX_TRANS = 45; |
| protected static final int REMOVE_THREADS = 5; |
| |
| protected static final boolean LOG_TRANSFORMATIONS = false; |
| |
| /** |
| * Field variables |
| */ |
| protected static final int TOTAL_THREADS = MAX_TRANS - MIN_TRANS + 1; |
| |
| private byte[] fDummyClassBytes; |
| // fCheckedTransformers is a Vector that is used to verify |
| // that the transform() function is called in the same |
| // order in which the transformers were added to the |
| // TransformerManager. The test currently verifies that all |
| // transformers for a specific worker thread are in |
| // increasing order by index value. |
| private Vector fCheckedTransformers; |
| private Instrumentation fInstrumentation; |
| private int fFinished; |
| private ExecuteTransformersThread fExec; |
| |
| // Need to use this for synchronization in subclass |
| protected Vector fAddedTransformers; |
| private String fDummyClassName; |
| |
| /** |
| * Constructor for TransformerManagementThreadAddTests. |
| * @param name Name for the test |
| */ |
| public TransformerManagementThreadAddTests(String name) |
| { |
| super(name); |
| |
| fCheckedTransformers = new Vector(); |
| fAddedTransformers = new Vector(); |
| |
| fDummyClassName = "DummyClass"; |
| String resourceName = "DummyClass.class"; |
| File f = new File(System.getProperty("test.classes", "."), resourceName); |
| System.out.println("Reading test class from " + f); |
| try |
| { |
| InputStream redefineStream = new FileInputStream(f); |
| fDummyClassBytes = NamedBuffer.loadBufferFromStream(redefineStream); |
| } |
| catch (IOException e) |
| { |
| fail("Could not load the class: "+resourceName); |
| } |
| } |
| |
| public void |
| testMultiThreadAdds() |
| { |
| TransformerThread[] threads = new TransformerThread[TOTAL_THREADS]; |
| for (int i = MIN_TRANS; i <= MAX_TRANS; i++) |
| { |
| int index = i - MIN_TRANS; |
| threads[index] = new TransformerThread("Trans"+prettyNum(index,2), i); |
| } |
| |
| ExecuteTransformersThread exec = new ExecuteTransformersThread(); |
| exec.start(); |
| for (int i = threads.length - 1; i >= 0; i--) |
| { |
| threads[i].start(); |
| } |
| |
| // Effective Java - Item 48: Synchronize access to shared mutable data |
| // Don't use a direct field getter. |
| while (!exec.isDone()) |
| { |
| // Effective Java - Item 51: Don't depend on the thread scheduler |
| // Use sleep() instead of yield(). |
| try { |
| Thread.sleep(500); |
| } catch (InterruptedException ie) { |
| } |
| } |
| assertTrue(finalCheck()); |
| |
| if (LOG_TRANSFORMATIONS) { |
| printTransformers(); |
| } |
| } |
| |
| /** |
| * Returns the Instrumentation. |
| * @return Instrumentation the data type with JPLIS calls |
| */ |
| public Instrumentation getInstrumentation() |
| { |
| return fInstrumentation; |
| } |
| |
| /** |
| * Returns the execution thread |
| * @return ExecuteTransformersThread |
| */ |
| protected ExecuteTransformersThread getExecThread() |
| { |
| return fExec; |
| } |
| |
| /** |
| * Sets the execution thread |
| * @param exec The execution thread to set |
| */ |
| protected void setExecThread(ExecuteTransformersThread exec) |
| { |
| this.fExec = exec; |
| } |
| |
| // Effective Java - Item 48: Synchronize access to shared mutable data |
| // Document a synchronized setter. |
| protected synchronized void |
| threadFinished(Thread t) |
| { |
| fFinished++; |
| } |
| |
| // Effective Java - Item 48: Synchronize access to shared mutable data |
| // Provide synchronized getter. |
| protected synchronized boolean |
| threadsDone() |
| { |
| return fFinished == TOTAL_THREADS; |
| } |
| |
| /** |
| * Method testCompleted. |
| * @return boolean |
| */ |
| protected boolean |
| testCompleted() |
| { |
| // Effective Java - Item 48: Synchronize access to shared mutable data |
| // Don't use direct field getter. |
| return getExecThread().isDone(); |
| } |
| |
| /** |
| * |
| */ |
| protected boolean |
| finalCheck() |
| { |
| if (LOG_TRANSFORMATIONS) { |
| // log the list |
| for (int x = 0; x < fCheckedTransformers.size(); x++ ) { |
| System.out.println(x + "\t\t" + fCheckedTransformers.get(x)); |
| } |
| System.out.println(); |
| System.out.println(); |
| |
| // check for multiples |
| for (int x = 0; x < fCheckedTransformers.size(); x++ ) { |
| Object current = fCheckedTransformers.get(x); |
| for ( int y = x + 1; y < fCheckedTransformers.size(); y++) { |
| Object running = fCheckedTransformers.get(y); |
| if ( current.equals(running) ) { |
| System.out.println(x + "\t" + y + " \t" + "FOUND DUPLICATE: " + current); |
| } |
| } |
| } |
| } |
| |
| for (int j = 1; j < fCheckedTransformers.size(); j++) { |
| ThreadTransformer transformer = (ThreadTransformer)fCheckedTransformers.get(j); |
| for (int i = 0; i < j; i++) { |
| ThreadTransformer currTrans = (ThreadTransformer)fCheckedTransformers.get(i); |
| assertTrue(currTrans + " incorrectly appeared before " + |
| transformer + " i=" + i + " j=" + j + " size=" + |
| fCheckedTransformers.size(), |
| !( |
| currTrans.getThread().equals(transformer.getThread()) && |
| currTrans.getIndex() > transformer.getIndex())); |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * |
| */ |
| protected void |
| setUp() |
| throws Exception |
| { |
| super.setUp(); |
| |
| fFinished = 0; |
| assertTrue(MIN_TRANS < MAX_TRANS); |
| fInstrumentation = InstrumentationHandoff.getInstrumentationOrThrow(); |
| } |
| |
| /** |
| * |
| */ |
| protected void |
| tearDown() |
| throws Exception |
| { |
| super.tearDown(); |
| } |
| |
| |
| |
| /** |
| * Method executeTransform. |
| */ |
| private void |
| executeTransform() |
| { |
| try |
| { |
| ClassDefinition cd = new ClassDefinition(DummyClass.class, fDummyClassBytes); |
| |
| // When the ClassDefinition above is created for the first |
| // time and every time redefineClasses() below is called, |
| // the transform() function is called for each registered |
| // transformer. We only want one complete set of calls to |
| // be logged in the fCheckedTransformers Vector so we clear |
| // any calls logged for ClassDefinition above and just use |
| // the ones logged for redefineClasses() below. |
| fCheckedTransformers.clear(); |
| |
| getInstrumentation().redefineClasses(new ClassDefinition[]{ cd }); |
| } |
| catch (ClassNotFoundException e) |
| { |
| fail("Could not find the class: "+DummyClass.class.getName()); |
| } |
| catch (UnmodifiableClassException e) |
| { |
| fail("Could not modify the class: "+DummyClass.class.getName()); |
| } |
| } |
| |
| /** |
| * Method addTransformerToManager. |
| * @param threadTransformer |
| */ |
| private void |
| addTransformerToManager(ThreadTransformer threadTransformer) |
| { |
| getInstrumentation().addTransformer(threadTransformer); |
| fAddedTransformers.add(threadTransformer); |
| } |
| |
| /** |
| * Method checkInTransformer. |
| * @param myClassFileTransformer |
| */ |
| private void |
| checkInTransformer(ThreadTransformer transformer) |
| { |
| fCheckedTransformers.add(transformer); |
| } |
| |
| /** |
| * Method createTransformer. |
| * @param transformerThread |
| * @param i |
| * @return ThreadTransformer |
| */ |
| private ThreadTransformer |
| createTransformer(TransformerThread thread, int index) |
| { |
| ThreadTransformer tt = null; |
| |
| tt = new ThreadTransformer(thread, index); |
| |
| return tt; |
| } |
| |
| |
| protected class |
| ExecuteTransformersThread |
| extends Thread |
| { |
| private boolean fDone = false; |
| |
| // Effective Java - Item 48: Synchronize access to shared mutable data |
| // Provide a synchronized getter. |
| private synchronized boolean isDone() { |
| return fDone; |
| } |
| |
| // Effective Java - Item 48: Synchronize access to shared mutable data |
| // Provide a synchronized setter. |
| private synchronized void setIsDone() { |
| fDone = true; |
| } |
| |
| public void |
| run() |
| { |
| while(!threadsDone()) |
| { |
| executeTransform(); |
| } |
| |
| // Do a final check for good measure |
| executeTransform(); |
| // Effective Java - Item 48: Synchronize access to shared mutable data |
| // Don't use direct field setter. |
| setIsDone(); |
| } |
| } |
| |
| |
| protected class |
| TransformerThread |
| extends Thread |
| { |
| private final ThreadTransformer[] fThreadTransformers; |
| |
| TransformerThread(String name, int numTransformers) |
| { |
| super(name); |
| |
| fThreadTransformers = makeTransformers(numTransformers); |
| } |
| |
| /** |
| * Method makeTransformers. |
| * @param numTransformers |
| * @return ThreadTransformer[] |
| */ |
| private ThreadTransformer[] |
| makeTransformers(int numTransformers) |
| { |
| ThreadTransformer[] trans = new ThreadTransformer[numTransformers]; |
| |
| for (int i = 0; i < trans.length; i++) |
| { |
| trans[i] = createTransformer(TransformerThread.this, i); |
| } |
| |
| return trans; |
| } |
| |
| public void |
| run() |
| { |
| for (int i = 0; i < fThreadTransformers.length; i++) |
| { |
| addTransformerToManager(fThreadTransformers[i]); |
| } |
| threadFinished(TransformerThread.this); |
| } |
| } |
| |
| /** |
| * ClassFileTransformer implementation that knows its thread |
| */ |
| protected class |
| ThreadTransformer extends SimpleIdentityTransformer |
| { |
| private final String fName; |
| private final int fIndex; |
| private final Thread fThread; |
| |
| /** |
| * Constructor for ThreadTransformer. |
| */ |
| public ThreadTransformer(Thread thread, int index) { |
| super(); |
| fThread = thread; |
| fIndex = index; |
| fName = "TT["+fThread.getName()+"]["+prettyNum(fIndex,3)+"]"; |
| } |
| |
| public String toString() |
| { |
| return fName; |
| } |
| |
| /** |
| * |
| */ |
| public byte[] |
| transform( |
| ClassLoader loader, |
| String className, |
| Class<?> classBeingRedefined, |
| ProtectionDomain domain, |
| byte[] classfileBuffer) |
| { |
| if ( className.equals(TransformerManagementThreadAddTests.this.fDummyClassName) ) { |
| checkInTransformer(ThreadTransformer.this); |
| } |
| return super.transform( loader, |
| className, |
| classBeingRedefined, |
| domain, |
| classfileBuffer); |
| } |
| |
| /** |
| * Returns the index. |
| * @return int |
| */ |
| public int getIndex() |
| { |
| return fIndex; |
| } |
| |
| /** |
| * Returns the thread. |
| * @return Thread |
| */ |
| public Thread getThread() |
| { |
| return fThread; |
| } |
| |
| } |
| |
| /** |
| * DEBUG STUFF |
| */ |
| private int NUM_SWITCHES; |
| |
| /** |
| * Method printTransformers. |
| */ |
| protected void printTransformers() |
| { |
| NUM_SWITCHES = 0; |
| Iterator trans = fCheckedTransformers.iterator(); |
| ThreadTransformer old = null; |
| StringBuffer buf = new StringBuffer(); |
| |
| for (int i = 1; trans.hasNext(); i++) |
| { |
| ThreadTransformer t = (ThreadTransformer)trans.next(); |
| buf.append(t.toString()); |
| if (old != null) |
| { |
| if (!old.getThread().equals(t.getThread())) |
| { |
| NUM_SWITCHES++; |
| buf.append("*"); |
| } |
| else |
| { buf.append(" "); } |
| } |
| else |
| { buf.append(" "); } |
| |
| if (i % 5 == 0) |
| { |
| buf.append("\n"); |
| } |
| else |
| { buf.append(" "); } |
| |
| old = t; |
| } |
| System.out.println(buf); |
| System.out.println("\nNumber of transitions from one thread to another: "+NUM_SWITCHES); |
| } |
| |
| protected String |
| prettyNum(int n, int numSize) |
| { |
| StringBuffer num = new StringBuffer(Integer.toString(n)); |
| int size = num.length(); |
| for (int i = 0; i < numSize - size; i++) |
| { |
| num.insert(0, "0"); |
| } |
| |
| return num.toString(); |
| } |
| /** |
| * END DEBUG STUFF |
| */ |
| |
| } |