KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > alt > jiapi > util > HotSpotAdvisor


1 package alt.jiapi.util;
2
3 import java.lang.reflect.Modifier JavaDoc;
4
5 import alt.jiapi.InstrumentationDescriptor;
6 import alt.jiapi.InstrumentationException;
7 import alt.jiapi.MethodInstrumentor;
8 import alt.jiapi.Rule;
9 import alt.jiapi.reflect.BranchInstruction;
10 import alt.jiapi.reflect.Instruction;
11 import alt.jiapi.reflect.InstructionFactory;
12 import alt.jiapi.reflect.InstructionList;
13 import alt.jiapi.reflect.JiapiClass;
14 import alt.jiapi.reflect.JiapiMethod;
15 import alt.jiapi.reflect.Loader;
16 import alt.jiapi.reflect.Signature;
17 import alt.jiapi.reflect.instruction.Invocation;
18 import alt.jiapi.reflect.instruction.OpcodeGroups;
19 import alt.jiapi.reflect.instruction.Opcodes;
20
21 import org.apache.log4j.Category;
22
23 /**
24  * HotSpotAdvisor is used to copy instructions from
25  * HotSpotAdvice to class being instrumented. This makes
26  * it possible to instrument target classes without any
27  * knowledge about Java bytecodes or instruction set.<p>
28  *
29  * Conseptually, this class, with the aid of HotSpotAdvise,
30  * provides the same thing as
31  * <a HREF="http://eclipse.org/aspectj/">AspectJ</a>s'
32  * join-point, before advice, after advice and around advice.<p>
33  *
34  * At some point in time, we might add point-cut semantics
35  * here.
36  * At the moment, point-cut semantics can be simulated to some extent with
37  * the aid of InstrumentationDescriptor<p>
38  *
39  * At the moment there is not such thing as HotSpotContext.
40  * For example, HotSpotAdvice knows, that an invocation is being made,
41  * but it does not know what are the call parameters of the invocation,
42  * and it does not know what the invocation returned.<p>
43  *
44  * Future versions of HotSpotAdvice might have this knowledge.
45  *
46  * @see HotSpotAdvice
47  * @see HotSpot for the definition of hotspot
48  */

49 public class HotSpotAdvisor {
50     private static Category log = Category.getInstance(HotSpotAdvisor.class);
51     /**
52      * This field can be used in constructor to search for any
53      * invocations being made. Note, that a call to super classes'
54      * constructor, is technically just an invocation. If you like
55      * to exclude calls to super class constructor, define your own
56      * byte array like this:
57      * <pre>
58      * byte[] invocations = new byte[] {
59      * Opcodes.INVOKESTATIC,
60      * Opcodes.INVOKEVIRTUAL,
61      * Opcodes.INVOKEINTERFACE
62      * };
63      * </pre>
64      */

65     public static final byte[] INVOCATIONS = OpcodeGroups.INVOKE_INSTRUCTIONS;
66     /**
67      * This field can be used in constructor to search for hotspots,
68      * that makes a method return normally.
69      */

70     public static final byte[] RETURNS = OpcodeGroups.RETURN_INSTRUCTIONS;
71     /**
72      * This field can be used in constructor to search for hotspots,
73      * that do some sort of field access. Like get or set field.
74      */

75     public static final byte[] FIELD_ACCESSES = OpcodeGroups.FIELD_ACCESS_INSTRUCTIONS;
76
77     /**
78      * Creates new HotSpotAdvisor.
79      *
80      * @param id InstrumentationDescriptor to use. A Special
81      * instruction copy Instrumentor is added to this
82      * descriptor, so users should only add inclusion/exclusion
83      * rules to descriptor.
84      * @param advice A HotSpotAdvice where instructions to be copied are
85      * taken.
86      * @param hotSpot this byte represents an opcode
87      * of the hotspot.
88      * @see alt.jiapi.reflect.instruction.Opcodes
89      */

90     public HotSpotAdvisor(InstrumentationDescriptor id,
91                           HotSpotAdvice advice,
92                           byte hotSpot) {
93         this(id, advice, new byte[] {hotSpot});
94     }
95
96     /**
97      * Creates new HotSpotAdvisor.
98      *
99      * @param id InstrumentationDescriptor to use. A Special
100      * instruction copy Instrumentor is added to this
101      * descriptor, so users should only add inclusion/exclusion
102      * rules to descriptor.
103      * @param advice A HotSpotAdvice where instructions to be copied are
104      * taken.
105      * @param hotSpots an array of bytes. Each byte represents an opcode
106      * of the hotspot. So, multiple opcodes may be used.
107      *
108      * @see #INVOCATIONS
109      * @see #RETURNS
110      * @see #FIELD_ACCESSES
111      * @see alt.jiapi.reflect.instruction.Opcodes
112      * @see alt.jiapi.reflect.instruction.OpcodeGroups
113      */

114     public HotSpotAdvisor(InstrumentationDescriptor id,
115                           HotSpotAdvice advice,
116                           byte[] hotSpots) {
117         this(id, advice, hotSpots, "*");
118     }
119
120
121     /**
122      * Creates new HotSpotAdvisor.
123      *
124      * @param id InstrumentationDescriptor to use. A Special
125      * instruction copy Instrumentor is added to this
126      * descriptor, so users should only add inclusion/exclusion
127      * rules to descriptor.
128      * @param advice A HotSpotAdvice where instructions to be copied are
129      * taken.
130      * @param hotSpots an array of bytes. Each byte represents an opcode
131      * of the hotspot. So, multiple opcodes may be used.
132      *
133      * @see #INVOCATIONS
134      * @see #RETURNS
135      * @see #FIELD_ACCESSES
136      * @see alt.jiapi.reflect.instruction.Opcodes
137      * @see alt.jiapi.reflect.instruction.OpcodeGroups
138      */

139     public HotSpotAdvisor(InstrumentationDescriptor id,
140                           HotSpotAdvice advice,
141                           byte[] hotSpots, String JavaDoc resolution) {
142         try {
143             // Do we need this. In my testing, advice got instrumented
144
// by accident by include=samples* rule.
145
// System.out.println("Adding exclusion rule '" +
146
// advice.getClass().getName() + "'");
147
id.addExclusionRule(advice.getClass().getName());
148         }
149         catch(Exception JavaDoc e) {
150         }
151
152         id.addInstrumentor(new HSInstrumentor(advice, hotSpots, resolution));
153     }
154     
155
156     /**
157      * This private Instrucmentor makes the actual bytecode
158      * copy procedure.
159      */

160     private class HSInstrumentor extends MethodInstrumentor {
161         private byte[] hotSpots;
162         private JiapiMethod adviceMethod;
163         private Rule rule;
164         
165         HSInstrumentor(HotSpotAdvice advice, byte[] hotSpots, String JavaDoc resolution) {
166             try {
167                 this.rule = new Rule(resolution);
168                 Loader l = new Loader();
169                 log.debug("Loading advice " + advice.getClass().getName());
170                 JiapiClass clazz = l.loadClass(advice.getClass().getName());
171
172                 log.debug("Getting advice() method ");
173                 this.adviceMethod =
174                     clazz.getDeclaredMethod("advice", new String JavaDoc[0]);
175                 this.hotSpots = hotSpots;
176             }
177             catch(NoSuchMethodException JavaDoc nsme) {
178                 // Should not happen, since it is an abstract method and
179
// there has to be an implementation for it.
180
log.error("Internal error: Could not find HotSpotAdvice.advice() method", nsme);
181                 throw new InstrumentationException("Could not find method advice() from " + advice.getClass() + ": " + nsme);
182             }
183             catch(ClassNotFoundException JavaDoc cnfe) {
184                 // Should not happen, can happen if Advice was loaded
185
// from a place other than classpath
186
log.error("Internal error: Could load class HotSpotAdvice", cnfe);
187
188                 throw new InstrumentationException("Could not load class " + advice.getClass() + ": " + cnfe);
189             }
190             catch(java.io.IOException JavaDoc ioe) {
191                 // Should not happen
192
log.error("Internal error: Could load class HotSpotAdvice", ioe);
193
194                 throw new InstrumentationException("Could not load class " + advice.getClass() + ": " + ioe);
195             }
196             catch(Exception JavaDoc e) {
197                 log.error("Failed to initialize HotSpotAdvisor", e);
198
199                 throw new InstrumentationException("Failed to initialize HotSpotAdvisor: " + e);
200             }
201         }
202
203         /**
204          * Instrument given method. Find hotspots, and copy instructions
205          * from advice before/after/around hotspot
206          */

207         public void instrument(JiapiMethod m) throws InstrumentationException {
208             log.debug("Instrumenting " + m.getDeclaringClass().getName() +
209                       "#" + m);
210
211             HotSpotLocator hsl =
212                 new HotSpotLocator(m.getInstructionList(), hotSpots);
213
214             HotSpot[] hsa = hsl.getHotSpots();
215             for (int i = 0; i < hsa.length; i++) {
216                 HotSpot hs = hsa[i];
217                 if (rule.match(hs.getName())) {
218                     copyInstructions(hs);
219                 }
220             }
221         }
222
223         /**
224          * Actual copying of instructions to hotspot.
225          */

226         private void copyInstructions(HotSpot hs) {
227             InstructionList am_il = adviceMethod.getInstructionList();
228             InstructionList il = hs.getInstructionList();
229         Instruction firstHotSpotInstruction = il.get(0);
230
231             //
232
// NOTE: We must handle local variables correctly
233
// NOTE: should be done in InstructionList.add/insert(Instruction)
234
am_il = changeLocalVars(am_il, il.getDeclaringMethod().getMaxLocals()-1);
235
236             int idx = indexOfDoHotSpot();
237                         
238             if (idx == -1) {
239                 log.debug("Replacing hotspot with advice-method");
240                 il.clear();
241                 il.add(am_il);
242                 return;
243             }
244             else {
245                 InstructionList before = am_il.createView(0, idx - 1);
246
247         // If first hs instruction is branch target, we
248
// will have to change this to first instruction of
249
// before-list, so that we do not introduce dead-code.
250
if (before.size() > 0) {
251             checkForBranchTarget(firstHotSpotInstruction, il.getDeclaringMethod().getInstructionList(), before.get(0));
252         }
253
254                 InstructionList after = null;
255                 if (idx+1 < am_il.size()) {
256                     // Strip 'return' statement
257
after = am_il.createView(idx + 1, am_il.size() - 1);
258                 }
259                 
260
261                 //System.out.println("il before change " + il);
262
il.insert(0, before);
263                 if (after != null) {
264                     il.add(after);
265                 }
266             }
267
268
269             // process advice list
270
processAdviceTriggers(il, hs);
271         }
272
273
274         /**
275          * Finds <b>first</b> occurense of a call to doHotSpot();
276          * If not found, return -1.
277          *
278          * Is it a bug, if there are more occurences of doHotSpot()
279          * calls in Advice. And if it is a bug, is it a bug in
280          * this class(not taking this into account), or Advice
281          * (being rogue Advice). Should we enable this???
282          *
283          * Anyway, at the moment, instrumented class will not pass bytecode
284          * verifier(I think), since there will be a non-existent call to
285          * super.doHotSpot().
286          */

287         private int indexOfDoHotSpot() {
288             InstructionList il = adviceMethod.getInstructionList();
289             HotSpotLocator hsl = new HotSpotLocator(il, Opcodes.INVOKEVIRTUAL);
290
291             HotSpot[] dohsCandidates = hsl.getHotSpots();
292             for (int i = 0; i < dohsCandidates.length; i++) {
293                 Invocation inv =
294                     (Invocation)dohsCandidates[i].getHotSpotInstruction();
295
296                 if (!inv.getClassName().equals(adviceMethod.getDeclaringClass().getName())) {
297                     // Process only calls to HotSpotAdvice class
298
continue;
299                 }
300
301                 if ("doHotSpot".equals(inv.getMethodName())) {
302 // if (inv.getAttribute("synthetic") != null) {
303
// // Found it
304
// return il.indexOf(inv);
305
// }
306
// continue;
307
return il.indexOf(inv);
308                 }
309             }
310             
311             return -1;
312         }
313
314
315         /**
316          * Change usage of local variables in target InstructionList so,
317          * that it will not overlap with 'other' list.
318          *
319          * If Advice declares local variables, they start from index 0,
320          * but so do local vars in target class. So this method changes
321          * local variable accesses in advice to start from
322          * <target.getMaxLocals()>
323          *
324          * @param advice InstructionList to change
325          * @param maxLocalsInOtherList Max-Locals in other list
326          */

327         private InstructionList changeLocalVars(InstructionList advice,
328                                                 int maxLocalsInOtherList) {
329 // if (maxLocalsInOtherList == 0) {
330
// // Nothing to do, other list does not contain
331
// // local variables
332
// return advice;
333
// }
334

335             // BUG: These should not be needed. For some reason,
336
// JiapiMethod.getMaxLocals() returns wrong values?
337
// Commenting these out has no harm other than
338
// missing unused local variable slots
339
maxLocalsInOtherList--;
340             // following could be added, if no longs/doubles are in target
341
// list; they reserve two slots for local vars. Another
342
// silly stuff for longs/doubles in JVMs
343
// maxLocalsInOtherList--;
344

345             InstructionList newList = new InstructionList();
346             
347             InstructionFactory factory = new InstructionFactory();
348             for(int i = 0; i < advice.size(); i++) {
349                 Instruction ins = advice.get(i);
350                 newList.add(ins);
351                 switch(ins.getOpcode()) {
352                     // -- ALOAD family --------------------------------------
353
case Opcodes.ALOAD_0:
354                         newList.replace(i, factory.aload(maxLocalsInOtherList));
355                         break;
356                     case Opcodes.ALOAD_1:
357                         newList.replace(i, factory.aload(maxLocalsInOtherList + 1));
358                         break;
359                     case Opcodes.ALOAD_2:
360                         newList.replace(i, factory.aload(maxLocalsInOtherList + 2));
361                         break;
362                     case Opcodes.ALOAD_3:
363                         newList.replace(i, factory.aload(maxLocalsInOtherList + 3));
364                         break;
365                     case Opcodes.ALOAD:
366                         // Handle this differently. This
367
// form of handling does not break exception table
368
// So, we minimize damage by handling this differently
369
ins.getBytes()[1] += (byte)maxLocalsInOtherList;
370                         break;
371                     case Opcodes.ASTORE_0:
372                         newList.replace(i, factory.astore(maxLocalsInOtherList));
373                         break;
374                     case Opcodes.ASTORE_1:
375                         newList.replace(i, factory.astore(maxLocalsInOtherList + 1));
376                         break;
377                     case Opcodes.ASTORE_2:
378                         newList.replace(i, factory.astore(maxLocalsInOtherList + 2));
379                         break;
380                     case Opcodes.ASTORE_3:
381                         newList.replace(i, factory.astore(maxLocalsInOtherList + 3));
382                         break;
383                     case Opcodes.ASTORE:
384                         // Handle this differently. This
385
// form of handling does not break exception table
386
// So, we minimize damage by handling this differently
387
ins.getBytes()[1] += (byte)maxLocalsInOtherList;
388                         break;
389
390                     // -- ILOAD family --------------------------------------
391
case Opcodes.ILOAD_0:
392                         newList.replace(i, factory.iload(maxLocalsInOtherList));
393                         break;
394                     case Opcodes.ILOAD_1:
395                         newList.replace(i, factory.iload(maxLocalsInOtherList + 1));
396                         break;
397                     case Opcodes.ILOAD_2:
398                         newList.replace(i, factory.iload(maxLocalsInOtherList + 2));
399                         break;
400                     case Opcodes.ILOAD_3:
401                         newList.replace(i, factory.iload(maxLocalsInOtherList + 3));
402                         break;
403                     case Opcodes.ILOAD:
404                         // Handle this differently. This
405
// form of handling does not break exception table
406
// So, we minimize damage by handling this differently
407
ins.getBytes()[1] += (byte)maxLocalsInOtherList;
408                         break;
409                     case Opcodes.ISTORE_0:
410                         newList.replace(i, factory.istore(maxLocalsInOtherList));
411                         break;
412                     case Opcodes.ISTORE_1:
413                         newList.replace(i, factory.istore(maxLocalsInOtherList + 1));
414                         break;
415                     case Opcodes.ISTORE_2:
416                         newList.replace(i, factory.istore(maxLocalsInOtherList + 2));
417                         break;
418                     case Opcodes.ISTORE_3:
419                         newList.replace(i, factory.istore(maxLocalsInOtherList + 3));
420                         break;
421                     case Opcodes.ISTORE:
422                         // Handle this differently. This
423
// form of handling does not break exception table
424
// So, we minimize damage by handling this differently
425
ins.getBytes()[1] += (byte)maxLocalsInOtherList;
426                         break;
427
428                     // -- DLOAD family --------------------------------------
429
case Opcodes.DLOAD_0:
430                         newList.replace(i, factory.dload(maxLocalsInOtherList));
431                         break;
432                     case Opcodes.DLOAD_1:
433                         newList.replace(i, factory.dload(maxLocalsInOtherList + 1));
434                         break;
435                     case Opcodes.DLOAD_2:
436                         newList.replace(i, factory.dload(maxLocalsInOtherList + 2));
437                         break;
438                     case Opcodes.DLOAD_3:
439                         newList.replace(i, factory.dload(maxLocalsInOtherList + 3));
440                         break;
441                     case Opcodes.DLOAD:
442                         // Handle this differently. This
443
// form of handling does not break exception table
444
// So, we minimize damage by handling this differently
445
ins.getBytes()[1] += (byte)maxLocalsInOtherList;
446                         break;
447                     case Opcodes.DSTORE_0:
448                         newList.replace(i, factory.dstore(maxLocalsInOtherList));
449                         break;
450                     case Opcodes.DSTORE_1:
451                         newList.replace(i, factory.dstore(maxLocalsInOtherList + 1));
452                         break;
453                     case Opcodes.DSTORE_2:
454                         newList.replace(i, factory.dstore(maxLocalsInOtherList + 2));
455                         break;
456                     case Opcodes.DSTORE_3:
457                         newList.replace(i, factory.dstore(maxLocalsInOtherList + 3));
458                         break;
459                     case Opcodes.DSTORE:
460                         // Handle this differently. This
461
// form of handling does not break exception table
462
// So, we minimize damage by handling this differently
463
ins.getBytes()[1] += (byte)maxLocalsInOtherList;
464                         break;
465
466                     // -- LLOAD family --------------------------------------
467
case Opcodes.LLOAD_0:
468                         newList.replace(i, factory.lload(maxLocalsInOtherList));
469                         break;
470                     case Opcodes.LLOAD_1:
471                         newList.replace(i, factory.lload(maxLocalsInOtherList + 1));
472                         break;
473                     case Opcodes.LLOAD_2:
474                         newList.replace(i, factory.lload(maxLocalsInOtherList + 2));
475                         break;
476                     case Opcodes.LLOAD_3:
477                         newList.replace(i, factory.lload(maxLocalsInOtherList + 3));
478                         break;
479                     case Opcodes.LLOAD:
480                         // Handle this differently. This
481
// form of handling does not break exception table
482
// So, we minimize damage by handling this differently
483
ins.getBytes()[1] += (byte)maxLocalsInOtherList;
484                         break;
485                     case Opcodes.LSTORE_0:
486                         newList.replace(i, factory.lstore(maxLocalsInOtherList));
487                         break;
488                     case Opcodes.LSTORE_1:
489                         newList.replace(i, factory.lstore(maxLocalsInOtherList + 1));
490                         break;
491                     case Opcodes.LSTORE_2:
492                         newList.replace(i, factory.lstore(maxLocalsInOtherList + 2));
493                         break;
494                     case Opcodes.LSTORE_3:
495                         newList.replace(i, factory.lstore(maxLocalsInOtherList + 3));
496                         break;
497                     case Opcodes.LSTORE:
498                         // Handle this differently. This
499
// form of handling does not break exception table
500
// So, we minimize damage by handling this differently
501
ins.getBytes()[1] += (byte)maxLocalsInOtherList;
502                         break;
503                         
504                     // -- FLOAD family --------------------------------------
505
case Opcodes.FLOAD_0:
506                         newList.replace(i, factory.fload(maxLocalsInOtherList));
507                         break;
508                     case Opcodes.FLOAD_1:
509                         newList.replace(i, factory.fload(maxLocalsInOtherList + 1));
510                         break;
511                     case Opcodes.FLOAD_2:
512                         newList.replace(i, factory.fload(maxLocalsInOtherList + 2));
513                         break;
514                     case Opcodes.FLOAD_3:
515                         newList.replace(i, factory.fload(maxLocalsInOtherList + 3));
516                         break;
517                     case Opcodes.FLOAD:
518                         // Handle this differently. This
519
// form of handling does not break exception table
520
// So, we minimize damage by handling this differently
521
ins.getBytes()[1] += (byte)maxLocalsInOtherList;
522                         break;
523                     case Opcodes.FSTORE_0:
524                         newList.replace(i, factory.fstore(maxLocalsInOtherList));
525                         break;
526                     case Opcodes.FSTORE_1:
527                         newList.replace(i, factory.fstore(maxLocalsInOtherList + 1));
528                         break;
529                     case Opcodes.FSTORE_2:
530                         newList.replace(i, factory.fstore(maxLocalsInOtherList + 2));
531                         break;
532                     case Opcodes.FSTORE_3:
533                         newList.replace(i, factory.fstore(maxLocalsInOtherList + 3));
534                         break;
535                     case Opcodes.FSTORE:
536                         // Handle this differently. This
537
// form of handling does not break exception table
538
// So, we minimize damage by handling this differently
539
ins.getBytes()[1] += (byte)maxLocalsInOtherList;
540                         break;
541                         
542                 }
543             }
544
545             return newList;
546         }
547
548
549         /**
550          * Process all the advice triggers found from advice() method,
551          * such as a call to getHotSpotName()
552          */

553         private void processAdviceTriggers(InstructionList il, HotSpot hs) {
554             
555             InstructionFactory factory = il.getInstructionFactory();
556             // Search for calls to possible trigger methods in
557
// advice method.
558
HotSpotLocator hsl = new HotSpotLocator(il, Opcodes.INVOKEVIRTUAL);
559
560             HotSpot[] triggerCandidates = hsl.getHotSpots();
561             for (int i = 0; i < triggerCandidates.length; i++) {
562                 Invocation inv =
563                     (Invocation)triggerCandidates[i].getHotSpotInstruction();
564
565                 if (!inv.getClassName().equals(adviceMethod.getDeclaringClass().getName())) {
566                     // Process only calls to HotSpotAdvice class
567
continue;
568                 }
569
570                 // Index of the trigger instruction
571
int idx = il.indexOf(inv);
572
573                 // Following assumes, that trigger call is
574
// void-void method.
575
// First, we remove a call to Advice class...
576
InstructionList view = il.createView(idx-1, idx + 1);
577         view.clear();
578
579                 // ...then we replace, that call with trigger action
580
// instructions.
581
if ("getHotSpotName".equals(inv.getMethodName())) {
582                     view.add(factory.pushConstant(hs.getName()));
583                 }
584                 else if ("getArguments".equals(inv.getMethodName())) {
585                     // NOT IMPLEMENTED
586
// This needs some further thinking before implementing
587
}
588                 else if ("getInstrumentedObject".equals(inv.getMethodName())) {
589             JiapiMethod jm = il.getDeclaringMethod();
590             int modifiers = jm.getModifiers();
591             if (Modifier.isStatic(modifiers)) {
592             // Static methods do not have reference to 'this'
593
//view.add(factory.pushNull());
594
view.add(factory.pushConstant(jm.getDeclaringClass().getName()));
595             view.add(factory.invoke(Modifier.STATIC, "java.lang.Class", "forName", new Signature("java.lang.Class", new String JavaDoc[] {"java.lang.String"})));
596             }
597             else {
598             view.add(factory.aload(0));
599             }
600                     // NOT IMPLEMENTED
601
// This needs some further thinking before implementing
602
}
603             }
604         }
605     }
606
607
608     public void checkForBranchTarget(Instruction firstHotSpotInstruction,
609                      InstructionList methodList,
610                      Instruction firstBeforeInstruction) {
611     for (int idx = 0; idx < methodList.size(); idx++) {
612         Instruction i = (Instruction)methodList.get(idx);
613
614         if (i instanceof BranchInstruction) {
615         BranchInstruction bi = (BranchInstruction)i;
616         if (bi.getTarget() == firstHotSpotInstruction) {
617             bi.setTarget(firstBeforeInstruction);
618         }
619         }
620     }
621     }
622
623 }
624
Popular Tags