blob: cc5a4b68940430c6ece880f958cb7d65ccc52857 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2004 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any 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 */
34import java.io.*;
35import java.lang.instrument.*;
36import java.security.ProtectionDomain;
37import java.util.*;
38
39public 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 private Vector fCheckedTransformers;
83 private Instrumentation fInstrumentation;
84 private int fFinished;
85 private ExecuteTransformersThread fExec;
86
87 // Need to use this for synchronization in subclass
88 protected Vector fAddedTransformers;
89 private String fDummyClassName;
90
91 /**
92 * Constructor for TransformerManagementThreadAddTests.
93 * @param name Name for the test
94 */
95 public TransformerManagementThreadAddTests(String name)
96 {
97 super(name);
98
99 fCheckedTransformers = new Vector();
100 fAddedTransformers = new Vector();
101
102 fDummyClassName = "DummyClass";
103 String resourceName = "DummyClass.class";
104 File f = new File(System.getProperty("test.classes", "."), resourceName);
105 System.out.println("Reading test class from " + f);
106 try
107 {
108 InputStream redefineStream = new FileInputStream(f);
109 fDummyClassBytes = NamedBuffer.loadBufferFromStream(redefineStream);
110 }
111 catch (IOException e)
112 {
113 fail("Could not load the class: "+resourceName);
114 }
115 }
116
117 public void
118 testMultiThreadAdds()
119 {
120 TransformerThread[] threads = new TransformerThread[TOTAL_THREADS];
121 for (int i = MIN_TRANS; i <= MAX_TRANS; i++)
122 {
123 int index = i - MIN_TRANS;
124 threads[index] = new TransformerThread("Trans"+prettyNum(index,2), i);
125 }
126
127 ExecuteTransformersThread exec = new ExecuteTransformersThread();
128 exec.start();
129 for (int i = threads.length - 1; i >= 0; i--)
130 {
131 threads[i].start();
132 }
133
134 while (!exec.fDone)
135 {
136 Thread.currentThread().yield();
137 }
138 assertTrue(finalCheck());
139
140 if (LOG_TRANSFORMATIONS) {
141 printTransformers();
142 }
143 }
144
145 /**
146 * Returns the Instrumentation.
147 * @return Instrumentation the data type with JPLIS calls
148 */
149 public Instrumentation getInstrumentation()
150 {
151 return fInstrumentation;
152 }
153
154 /**
155 * Returns the execution thread
156 * @return ExecuteTransformersThread
157 */
158 protected ExecuteTransformersThread getExecThread()
159 {
160 return fExec;
161 }
162
163 /**
164 * Sets the execution thread
165 * @param exec The execution thread to set
166 */
167 protected void setExecThread(ExecuteTransformersThread exec)
168 {
169 this.fExec = exec;
170 }
171
172 protected synchronized void
173 threadFinished(Thread t)
174 {
175 fFinished++;
176 }
177
178 protected boolean
179 threadsDone()
180 {
181 return fFinished == TOTAL_THREADS;
182 }
183
184 /**
185 * Method testCompleted.
186 * @return boolean
187 */
188 protected boolean
189 testCompleted()
190 {
191 return getExecThread().fDone;
192 }
193
194 /**
195 *
196 */
197 protected boolean
198 finalCheck()
199 {
200 if (LOG_TRANSFORMATIONS) {
201 // log the list
202 for (int x = 0; x < fCheckedTransformers.size(); x++ ) {
203 System.out.println(x + "\t\t" + fCheckedTransformers.get(x));
204 }
205 System.out.println();
206 System.out.println();
207
208 // check for multiples
209 for (int x = 0; x < fCheckedTransformers.size(); x++ ) {
210 Object current = fCheckedTransformers.get(x);
211 for ( int y = x + 1; y < fCheckedTransformers.size(); y++) {
212 Object running = fCheckedTransformers.get(y);
213 if ( current.equals(running) ) {
214 System.out.println(x + "\t" + y + " \t" + "FOUND DUPLICATE: " + current);
215 }
216 }
217 }
218 }
219
220 for (int j = 1; j < fCheckedTransformers.size(); j++) {
221 ThreadTransformer transformer = (ThreadTransformer)fCheckedTransformers.get(j);
222 for (int i = 0; i < j; i++) {
223 ThreadTransformer currTrans = (ThreadTransformer)fCheckedTransformers.get(i);
224 assertTrue(currTrans + " incorrectly appeared before " +
225 transformer + " i=" + i + " j=" + j + " size=" +
226 fCheckedTransformers.size(),
227 !(
228 currTrans.getThread().equals(transformer.getThread()) &&
229 currTrans.getIndex() > transformer.getIndex()));
230 }
231 }
232 return true;
233 }
234
235 /**
236 *
237 */
238 protected void
239 setUp()
240 throws Exception
241 {
242 super.setUp();
243
244 fFinished = 0;
245 assertTrue(MIN_TRANS < MAX_TRANS);
246 fInstrumentation = InstrumentationHandoff.getInstrumentationOrThrow();
247 }
248
249 /**
250 *
251 */
252 protected void
253 tearDown()
254 throws Exception
255 {
256 super.tearDown();
257 }
258
259
260
261 /**
262 * Method executeTransform.
263 */
264 private void
265 executeTransform()
266 {
267 fCheckedTransformers.clear();
268
269 try
270 {
271 ClassDefinition cd = new ClassDefinition(DummyClass.class, fDummyClassBytes);
272 getInstrumentation().redefineClasses(new ClassDefinition[]{ cd });
273 }
274 catch (ClassNotFoundException e)
275 {
276 fail("Could not find the class: "+DummyClass.class.getName());
277 }
278 catch (UnmodifiableClassException e)
279 {
280 fail("Could not modify the class: "+DummyClass.class.getName());
281 }
282 }
283
284 /**
285 * Method addTransformerToManager.
286 * @param threadTransformer
287 */
288 private void
289 addTransformerToManager(ThreadTransformer threadTransformer)
290 {
291 getInstrumentation().addTransformer(threadTransformer);
292 fAddedTransformers.add(threadTransformer);
293 }
294
295 /**
296 * Method checkInTransformer.
297 * @param myClassFileTransformer
298 */
299 private void
300 checkInTransformer(ThreadTransformer transformer)
301 {
302 fCheckedTransformers.add(transformer);
303 }
304
305 /**
306 * Method createTransformer.
307 * @param transformerThread
308 * @param i
309 * @return ThreadTransformer
310 */
311 private ThreadTransformer
312 createTransformer(TransformerThread thread, int index)
313 {
314 ThreadTransformer tt = null;
315
316 tt = new ThreadTransformer(thread, index);
317
318 return tt;
319 }
320
321
322 protected class
323 ExecuteTransformersThread
324 extends Thread
325 {
326 private boolean fDone = false;
327
328 public void
329 run()
330 {
331 while(!threadsDone())
332 {
333 executeTransform();
334 }
335
336 // Do a final check for good measure
337 executeTransform();
338 fDone = true;
339 }
340 }
341
342
343 protected class
344 TransformerThread
345 extends Thread
346 {
347 private final ThreadTransformer[] fThreadTransformers;
348
349 TransformerThread(String name, int numTransformers)
350 {
351 super(name);
352
353 fThreadTransformers = makeTransformers(numTransformers);
354 }
355
356 /**
357 * Method makeTransformers.
358 * @param numTransformers
359 * @return ThreadTransformer[]
360 */
361 private ThreadTransformer[]
362 makeTransformers(int numTransformers)
363 {
364 ThreadTransformer[] trans = new ThreadTransformer[numTransformers];
365
366 for (int i = 0; i < trans.length; i++)
367 {
368 trans[i] = createTransformer(TransformerThread.this, i);
369 }
370
371 return trans;
372 }
373
374 public void
375 run()
376 {
377 for (int i = 0; i < fThreadTransformers.length; i++)
378 {
379 addTransformerToManager(fThreadTransformers[i]);
380 }
381 threadFinished(TransformerThread.this);
382 }
383 }
384
385 /**
386 * ClassFileTransformer implementation that knows its thread
387 */
388 protected class
389 ThreadTransformer extends SimpleIdentityTransformer
390 {
391 private final String fName;
392 private final int fIndex;
393 private final Thread fThread;
394
395 /**
396 * Constructor for ThreadTransformer.
397 */
398 public ThreadTransformer(Thread thread, int index) {
399 super();
400 fThread = thread;
401 fIndex = index;
402 fName = "TT["+fThread.getName()+"]["+prettyNum(fIndex,3)+"]";
403 }
404
405 public String toString()
406 {
407 return fName;
408 }
409
410 /**
411 *
412 */
413 public byte[]
414 transform(
415 ClassLoader loader,
416 String className,
417 Class<?> classBeingRedefined,
418 ProtectionDomain domain,
419 byte[] classfileBuffer)
420 {
421 if ( className.equals(TransformerManagementThreadAddTests.this.fDummyClassName) ) {
422 checkInTransformer(ThreadTransformer.this);
423 }
424 return super.transform( loader,
425 className,
426 classBeingRedefined,
427 domain,
428 classfileBuffer);
429 }
430
431 /**
432 * Returns the index.
433 * @return int
434 */
435 public int getIndex()
436 {
437 return fIndex;
438 }
439
440 /**
441 * Returns the thread.
442 * @return Thread
443 */
444 public Thread getThread()
445 {
446 return fThread;
447 }
448
449 }
450
451 /**
452 * DEBUG STUFF
453 */
454 private int NUM_SWITCHES;
455
456 /**
457 * Method printTransformers.
458 */
459 protected void printTransformers()
460 {
461 NUM_SWITCHES = 0;
462 Iterator trans = fCheckedTransformers.iterator();
463 ThreadTransformer old = null;
464 StringBuffer buf = new StringBuffer();
465
466 for (int i = 1; trans.hasNext(); i++)
467 {
468 ThreadTransformer t = (ThreadTransformer)trans.next();
469 buf.append(t.toString());
470 if (old != null)
471 {
472 if (!old.getThread().equals(t.getThread()))
473 {
474 NUM_SWITCHES++;
475 buf.append("*");
476 }
477 else
478 { buf.append(" "); }
479 }
480 else
481 { buf.append(" "); }
482
483 if (i % 5 == 0)
484 {
485 buf.append("\n");
486 }
487 else
488 { buf.append(" "); }
489
490 old = t;
491 }
492 System.out.println(buf);
493 System.out.println("\nNumber of transitions from one thread to another: "+NUM_SWITCHES);
494 }
495
496 protected String
497 prettyNum(int n, int numSize)
498 {
499 StringBuffer num = new StringBuffer(Integer.toString(n));
500 int size = num.length();
501 for (int i = 0; i < numSize - size; i++)
502 {
503 num.insert(0, "0");
504 }
505
506 return num.toString();
507 }
508 /**
509 * END DEBUG STUFF
510 */
511
512}