3789983e89
Reviewed-by: darcy, ihse
554 lines
16 KiB
Java
554 lines
16 KiB
Java
/*
|
|
* 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
|
|
*/
|
|
|
|
}
|