blob: aab7c4c4b3868b2be02ac544953265b2f04af108 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2005 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
24package ilib;
25
26import java.io.IOException;
27import java.io.File;
28import java.io.FileOutputStream;
29import java.io.DataOutputStream;
30import java.util.List;
31import java.util.Iterator;
32import java.util.ArrayList;
33
34public class Inject implements RuntimeConstants {
35
36 public static byte[] instrumentation(Options opt,
37 ClassLoader loader,
38 String className,
39 byte[] classfileBuffer) {
40 ClassReaderWriter c = new ClassReaderWriter(classfileBuffer);
41 (new Inject(className, c, loader == null, opt)).doit();
42 return c.result();
43 }
44
45 static boolean verbose = false;
46
47 final String className;
48 final ClassReaderWriter c;
49 final boolean isSystem;
50 final Options options;
51
52 int constantPoolCount;
53 int methodsCount;
54 int methodsCountPos;
55 int profiler;
56 int wrappedTrackerIndex = 0;
57 int thisClassIndex = 0;
58
59 TrackerInjector callInjector;
60 TrackerInjector allocInjector;
61 TrackerInjector defaultInjector;
62
63 static interface TrackerInjector extends Injector {
64 void reinit(int tracker);
65 int stackSize(int currentSize);
66 }
67
68 static class SimpleInjector implements TrackerInjector {
69 byte[] injection;
70
71 public int stackSize(int currentSize) {
72 return currentSize;
73 }
74
75 public void reinit(int tracker) {
76 injection = new byte[3];
77 injection[0] = (byte)opc_invokestatic;
78 injection[1] = (byte)(tracker >> 8);
79 injection[2] = (byte)tracker;
80 }
81
82 public byte[] bytecodes(String className, String methodName, int location) {
83 return injection;
84 }
85 }
86
87 static class ObjectInjector implements TrackerInjector {
88 byte[] injection;
89
90 public int stackSize(int currentSize) {
91 return currentSize + 1;
92 }
93
94 public void reinit(int tracker) {
95 injection = new byte[4];
96 injection[0] = (byte)opc_dup;
97 injection[1] = (byte)opc_invokestatic;
98 injection[2] = (byte)(tracker >> 8);
99 injection[3] = (byte)tracker;
100 }
101
102 public byte[] bytecodes(String className, String methodName, int location) {
103 return injection;
104 }
105 }
106
107 class IndexedInjector implements TrackerInjector {
108 int counter = 0;
109 int tracker;
110 List<Info> infoList = new ArrayList<Info>();
111
112 public int stackSize(int currentSize) {
113 return currentSize + 1;
114 }
115
116 public void reinit(int tracker) {
117 this.tracker = tracker;
118 }
119
120 void dump(File outDir, String filename) throws IOException {
121 FileOutputStream fileOut = new FileOutputStream(new File(outDir, filename));
122 DataOutputStream dataOut = new DataOutputStream(fileOut);
123
124 String currentClassName = null;
125
126 dataOut.writeInt(infoList.size());
127 for (Iterator<Info> it = infoList.iterator(); it.hasNext(); ) {
128 Info info = it.next();
129 if (!info.className.equals(currentClassName)) {
130 dataOut.writeInt(123456); // class name marker
131 currentClassName = info.className;
132 dataOut.writeUTF(currentClassName);
133 }
134 dataOut.writeInt(info.location);
135 dataOut.writeUTF(info.methodName);
136 }
137 dataOut.close();
138 }
139
140 public byte[] bytecodes(String className, String methodName, int location) {
141 byte[] injection = new byte[6];
142 int injectedIndex = options.fixedIndex != 0? options.fixedIndex : ++counter;
143 infoList.add(new Info(counter, className, methodName, location));
144 injection[0] = (byte)opc_sipush;
145 injection[1] = (byte)(injectedIndex >> 8);
146 injection[2] = (byte)injectedIndex;
147 injection[3] = (byte)opc_invokestatic;
148 injection[4] = (byte)(tracker >> 8);
149 injection[5] = (byte)tracker;
150 return injection;
151 }
152 }
153
154 Inject(String className, ClassReaderWriter c, boolean isSystem, Options options) {
155 this.className = className;
156 this.c = c;
157 this.isSystem = isSystem;
158 this.options = options;
159 }
160
161 void doit() {
162 int i;
163 c.copy(4 + 2 + 2); // magic min/maj version
164 int constantPoolCountPos = c.generatedPosition();
165 constantPoolCount = c.copyU2();
166 // copy old constant pool
167 c.copyConstantPool(constantPoolCount);
168
169 if (verbose) {
170 System.out.println("ConstantPool expanded from: " +
171 constantPoolCount);
172 }
173
174 profiler = addClassToConstantPool(options.trackerClassName);
175 if (options.shouldInstrumentNew || options.shouldInstrumentObjectInit) {
176 if (options.shouldInstrumentIndexed) {
177 if (allocInjector == null) {
178 // first time - create it
179 allocInjector = new IndexedInjector();
180 }
181 int allocTracker = addMethodToConstantPool(profiler,
182 options.allocTrackerMethodName,
183 "(I)V");
184 allocInjector.reinit(allocTracker);
185 } else if (options.shouldInstrumentObject) {
186 if (allocInjector == null) {
187 // first time - create it
188 allocInjector = new ObjectInjector();
189 }
190 int allocTracker = addMethodToConstantPool(profiler,
191 options.allocTrackerMethodName,
192 "(Ljava/lang/Object;)V");
193 allocInjector.reinit(allocTracker);
194 } else {
195 if (allocInjector == null) {
196 // first time - create it
197 allocInjector = new SimpleInjector();
198 }
199 int allocTracker = addMethodToConstantPool(profiler,
200 options.allocTrackerMethodName,
201 "()V");
202 allocInjector.reinit(allocTracker);
203 }
204 defaultInjector = allocInjector;
205 }
206 if (options.shouldInstrumentCall) {
207 if (options.shouldInstrumentIndexed) {
208 if (callInjector == null) {
209 // first time - create it
210 callInjector = new IndexedInjector();
211 }
212 int callTracker = addMethodToConstantPool(profiler,
213 options.callTrackerMethodName,
214 "(I)V");
215 callInjector.reinit(callTracker);
216 } else {
217 if (callInjector == null) {
218 // first time - create it
219 callInjector = new SimpleInjector();
220 }
221 int callTracker = addMethodToConstantPool(profiler,
222 options.callTrackerMethodName,
223 "()V");
224 callInjector.reinit(callTracker);
225 }
226 defaultInjector = callInjector;
227 }
228
229 if (verbose) {
230 System.out.println("To: " + constantPoolCount);
231 }
232
233 c.setSection(1);
234
235 c.copy(2 + 2 + 2); // access, this, super
236 int interfaceCount = c.copyU2();
237 if (verbose) {
238 System.out.println("interfaceCount: " + interfaceCount);
239 }
240 c.copy(interfaceCount * 2);
241 copyFields(); // fields
242 copyMethods(); // methods
243 int attrCountPos = c.generatedPosition();
244 int attrCount = c.copyU2();
245 if (verbose) {
246 System.out.println("class attrCount: " + attrCount);
247 }
248 // copy the class attributes
249 copyAttrs(attrCount);
250
251 c.randomAccessWriteU2(constantPoolCountPos, constantPoolCount);
252 }
253
254
255 void copyFields() {
256 int count = c.copyU2();
257 if (verbose) {
258 System.out.println("fields count: " + count);
259 }
260 for (int i = 0; i < count; ++i) {
261 c.copy(6); // access, name, descriptor
262 int attrCount = c.copyU2();
263 if (verbose) {
264 System.out.println("field attr count: " + attrCount);
265 }
266 copyAttrs(attrCount);
267 }
268 }
269
270 void copyMethods() {
271 methodsCountPos = c.generatedPosition();
272 methodsCount = c.copyU2();
273 int initialMethodsCount = methodsCount;
274 if (verbose) {
275 System.out.println("methods count: " + methodsCount);
276 }
277 for (int i = 0; i < initialMethodsCount; ++i) {
278 copyMethod();
279 }
280 }
281
282 void copyMethod() {
283 int accessFlags = c.copyU2();// access flags
284 if (options.shouldInstrumentNativeMethods && (accessFlags & ACC_NATIVE) != 0) {
285 wrapNativeMethod(accessFlags);
286 return;
287 }
288 int nameIndex = c.copyU2(); // name
289 String methodName = c.constantPoolString(nameIndex);
290 c.copyU2(); // descriptor
291 int attrCount = c.copyU2(); // attribute count
292 if (verbose) {
293 System.out.println("methods attr count: " + attrCount);
294 }
295 for (int i = 0; i < attrCount; ++i) {
296 copyAttrForMethod(methodName, accessFlags);
297 }
298 }
299
300 void wrapNativeMethod(int accessFlags) {
301 // first, copy the native method with the name changed
302 // accessFlags have already been copied
303 int nameIndex = c.readU2(); // name
304 String methodName = c.constantPoolString(nameIndex);
305 String wrappedMethodName = options.wrappedPrefix + methodName;
306 int wrappedNameIndex = writeCPEntryUtf8(wrappedMethodName);
307 c.writeU2(wrappedNameIndex); // change to the wrapped name
308
309 int descriptorIndex = c.copyU2(); // descriptor index
310
311 int attrCount = c.copyU2(); // attribute count
312 // need to replicate these attributes (esp Exceptions) in wrapper
313 // so mark this location so we can rewind
314 c.markLocalPositionStart();
315 for (int i = 0; i < attrCount; ++i) {
316 copyAttrForMethod(methodName, accessFlags);
317 }
318 if (true) {
319 System.err.println(" wrapped: " + methodName);
320 }
321
322 // now write the wrapper method
323 c.writeU2(accessFlags & ~ACC_NATIVE);
324 c.writeU2(nameIndex); // original unwrapped name
325 c.writeU2(descriptorIndex); // descriptor is the same
326
327 c.writeU2(attrCount + 1); // wrapped plus a code attribute
328 // rewind to wrapped attributes
329 c.rewind();
330 for (int i = 0; i < attrCount; ++i) {
331 copyAttrForMethod(methodName, accessFlags);
332 }
333
334 // generate a Code attribute for the wrapper method
335 int wrappedIndex = addMethodToConstantPool(getThisClassIndex(),
336 wrappedNameIndex,
337 descriptorIndex);
338 String descriptor = c.constantPoolString(descriptorIndex);
339 createWrapperCodeAttr(nameIndex, accessFlags, descriptor, wrappedIndex);
340
341 // increment method count
342 c.randomAccessWriteU2(methodsCountPos, ++methodsCount);
343 }
344
345 void copyAttrs(int attrCount) {
346 for (int i = 0; i < attrCount; ++i) {
347 copyAttr();
348 }
349 }
350
351 void copyAttr() {
352 c.copy(2); // name
353 int len = c.copyU4(); // attr len
354 if (verbose) {
355 System.out.println("attr len: " + len);
356 }
357 c.copy(len); // attribute info
358 }
359
360 void copyAttrForMethod(String methodName, int accessFlags) {
361 int nameIndex = c.copyU2(); // name
362 // check for Code attr
363 if (nameIndex == c.codeAttributeIndex) {
364 try {
365 copyCodeAttr(methodName);
366 } catch (IOException exc) {
367 System.err.println("Code Exception - " + exc);
368 System.exit(1);
369 }
370 } else {
371 int len = c.copyU4(); // attr len
372 if (verbose) {
373 System.out.println("method attr len: " + len);
374 }
375 c.copy(len); // attribute info
376 }
377 }
378
379 void copyAttrForCode(InjectBytecodes ib) throws IOException {
380 int nameIndex = c.copyU2(); // name
381
382 // check for Code attr
383 if (nameIndex == c.lineNumberAttributeIndex) {
384 ib.copyLineNumberAttr();
385 } else if (nameIndex == c.localVarAttributeIndex) {
386 ib.copyLocalVarAttr();
387 } else {
388 int len = c.copyU4(); // attr len
389 if (verbose) {
390 System.out.println("code attr len: " + len);
391 }
392 c.copy(len); // attribute info
393 }
394 }
395
396 void copyCodeAttr(String methodName) throws IOException {
397 if (verbose) {
398 System.out.println("Code attr found");
399 }
400 int attrLengthPos = c.generatedPosition();
401 int attrLength = c.copyU4(); // attr len
402 int maxStack = c.readU2(); // max stack
403 c.writeU2(defaultInjector == null? maxStack :
404 defaultInjector.stackSize(maxStack)); // big enough for injected code
405 c.copyU2(); // max locals
406 int codeLengthPos = c.generatedPosition();
407 int codeLength = c.copyU4(); // code length
408 if (options.targetMethod != null && !options.targetMethod.equals(methodName)) {
409 c.copy(attrLength - 8); // copy remainder minus already copied
410 return;
411 }
412 if (isSystem) {
413 if (codeLength == 1 && methodName.equals("finalize")) {
414 if (verbose) {
415 System.out.println("empty system finalizer not instrumented");
416 }
417 c.copy(attrLength - 8); // copy remainder minus already copied
418 return;
419 }
420 if (codeLength == 1 && methodName.equals("<init>")) {
421 if (verbose) {
422 System.out.println("empty system constructor not instrumented");
423 }
424 if (!options.shouldInstrumentObjectInit) {
425 c.copy(attrLength - 8); // copy remainder minus already copied
426 return;
427 }
428 }
429 if (methodName.equals("<clinit>")) {
430 if (verbose) {
431 System.out.println("system class initializer not instrumented");
432 }
433 c.copy(attrLength - 8); // copy remainder minus already copied
434 return;
435 }
436 }
437 if (options.shouldInstrumentObjectInit
438 && (!className.equals("java/lang/Object")
439 || !methodName.equals("<init>"))) {
440 c.copy(attrLength - 8); // copy remainder minus already copied
441 return;
442 }
443
444 InjectBytecodes ib = new InjectBytecodes(c, codeLength, className, methodName);
445
446 if (options.shouldInstrumentNew) {
447 ib.injectAfter(opc_new, allocInjector);
448 ib.injectAfter(opc_newarray, allocInjector);
449 ib.injectAfter(opc_anewarray, allocInjector);
450 ib.injectAfter(opc_multianewarray, allocInjector);
451 }
452 if (options.shouldInstrumentCall) {
453 ib.inject(0, callInjector.bytecodes(className, methodName, 0));
454 }
455 if (options.shouldInstrumentObjectInit) {
456 ib.inject(0, allocInjector.bytecodes(className, methodName, 0));
457 }
458
459 ib.adjustOffsets();
460
461 // fix up code length
462 int newCodeLength = c.generatedPosition() - (codeLengthPos + 4);
463 c.randomAccessWriteU4(codeLengthPos, newCodeLength);
464 if (verbose) {
465 System.out.println("code length old: " + codeLength +
466 ", new: " + newCodeLength);
467 }
468
469 ib.copyExceptionTable();
470
471 int attrCount = c.copyU2();
472 for (int i = 0; i < attrCount; ++i) {
473 copyAttrForCode(ib);
474 }
475
476 // fix up attr length
477 int newAttrLength = c.generatedPosition() - (attrLengthPos + 4);
478 c.randomAccessWriteU4(attrLengthPos, newAttrLength);
479 if (verbose) {
480 System.out.println("attr length old: " + attrLength +
481 ", new: " + newAttrLength);
482 }
483 }
484
485 int nextDescriptorIndex(String descriptor, int index) {
486 switch (descriptor.charAt(index)) {
487 case 'B': // byte
488 case 'C': // char
489 case 'I': // int
490 case 'S': // short
491 case 'Z': // boolean
492 case 'F': // float
493 case 'D': // double
494 case 'J': // long
495 return index + 1;
496 case 'L': // object
497 int i = index + 1;
498 while (descriptor.charAt(i) != ';') {
499 ++i;
500 }
501 return i + 1;
502 case '[': // array
503 return nextDescriptorIndex(descriptor, index + 1);
504 }
505 throw new InternalError("should not reach here");
506 }
507
508 int getWrappedTrackerIndex() {
509 if (wrappedTrackerIndex == 0) {
510 wrappedTrackerIndex = addMethodToConstantPool(profiler,
511 options.wrappedTrackerMethodName,
512 "(Ljava/lang/String;I)V");
513 }
514 return wrappedTrackerIndex;
515 }
516
517 int getThisClassIndex() {
518 if (thisClassIndex == 0) {
519 thisClassIndex = addClassToConstantPool(className);
520 }
521 return thisClassIndex;
522 }
523
524 int computeMaxLocals(String descriptor, int accessFlags) {
525 int index = 1;
526 int slot = 0;
527
528 if ((accessFlags & ACC_STATIC) == 0) {
529 ++slot;
530 }
531 char type;
532 while ((type = descriptor.charAt(index)) != ')') {
533 switch (type) {
534 case 'B': // byte
535 case 'C': // char
536 case 'I': // int
537 case 'S': // short
538 case 'Z': // boolean
539 case 'F': // float
540 case 'L': // object
541 case '[': // array
542 ++slot;
543 break;
544 case 'D': // double
545 case 'J': // long
546 slot += 2;
547 break;
548 }
549 index = nextDescriptorIndex(descriptor, index);
550 }
551
552 return slot;
553 }
554
555
556 void createWrapperCodeAttr(int methodNameIndex, int accessFlags,
557 String descriptor, int wrappedIndex) {
558 int maxLocals = computeMaxLocals(descriptor, accessFlags);
559
560 c.writeU2(c.codeAttributeIndex); //
561 int attrLengthPos = c.generatedPosition();
562 c.writeU4(0); // attr len -- fix up below
563 c.writeU2(maxLocals + 4); // max stack
564 c.writeU2(maxLocals); // max locals
565 int codeLengthPos = c.generatedPosition();
566 c.writeU4(0); // code length -- fix up below
567
568 int methodStringIndex = writeCPEntryString(methodNameIndex);
569
570 c.writeU1(opc_ldc_w);
571 c.writeU2(methodStringIndex); // send the method name
572 c.writeU1(opc_sipush);
573 c.writeU2(options.fixedIndex);
574 c.writeU1(opc_invokestatic);
575 c.writeU2(getWrappedTrackerIndex());
576
577 // set-up args
578 int index = 1;
579 int slot = 0;
580 if ((accessFlags & ACC_STATIC) == 0) {
581 c.writeU1(opc_aload_0); // this
582 ++slot;
583 }
584 char type;
585 while ((type = descriptor.charAt(index)) != ')') {
586 switch (type) {
587 case 'B': // byte
588 case 'C': // char
589 case 'I': // int
590 case 'S': // short
591 case 'Z': // boolean
592 c.writeU1(opc_iload);
593 c.writeU1(slot);
594 ++slot;
595 break;
596 case 'F': // float
597 c.writeU1(opc_fload);
598 c.writeU1(slot);
599 ++slot;
600 break;
601 case 'D': // double
602 c.writeU1(opc_dload);
603 c.writeU1(slot);
604 slot += 2;
605 break;
606 case 'J': // long
607 c.writeU1(opc_lload);
608 c.writeU1(slot);
609 slot += 2;
610 break;
611 case 'L': // object
612 case '[': // array
613 c.writeU1(opc_aload);
614 c.writeU1(slot);
615 ++slot;
616 break;
617 }
618 index = nextDescriptorIndex(descriptor, index);
619 }
620
621 // call the wrapped version
622 if ((accessFlags & ACC_STATIC) == 0) {
623 c.writeU1(opc_invokevirtual);
624 } else {
625 c.writeU1(opc_invokestatic);
626 }
627 c.writeU2(wrappedIndex);
628
629 // return correct type
630 switch (descriptor.charAt(index+1)) {
631 case 'B': // byte
632 case 'C': // char
633 case 'I': // int
634 case 'S': // short
635 case 'Z': // boolean
636 c.writeU1(opc_ireturn);
637 break;
638 case 'F': // float
639 c.writeU1(opc_freturn);
640 break;
641 case 'D': // double
642 c.writeU1(opc_dreturn);
643 break;
644 case 'J': // long
645 c.writeU1(opc_lreturn);
646 break;
647 case 'L': // object
648 case '[': // array
649 c.writeU1(opc_areturn);
650 break;
651 case 'V': // void
652 c.writeU1(opc_return);
653 break;
654 }
655
656 // end of code
657
658 // fix up code length
659 int newCodeLength = c.generatedPosition() - (codeLengthPos + 4);
660 c.randomAccessWriteU4(codeLengthPos, newCodeLength);
661
662 c.writeU2(0); // exception table length
663 c.writeU2(0); // attribute count
664
665 // fix up attr length
666 int newAttrLength = c.generatedPosition() - (attrLengthPos + 4);
667 c.randomAccessWriteU4(attrLengthPos, newAttrLength);
668 }
669
670
671 int addClassToConstantPool(String className) {
672 int prevSection = c.setSection(0);
673 int classNameIndex = writeCPEntryUtf8(className);
674 int classIndex = writeCPEntryClass(classNameIndex);
675 c.setSection(prevSection);
676 return classIndex;
677 }
678
679 int addMethodToConstantPool(int classIndex,
680 String methodName,
681 String descr) {
682 int prevSection = c.setSection(0);
683 int methodNameIndex = writeCPEntryUtf8(methodName);
684 int descrIndex = writeCPEntryUtf8(descr);
685 c.setSection(prevSection);
686 return addMethodToConstantPool(classIndex, methodNameIndex, descrIndex);
687 }
688
689 int addMethodToConstantPool(int classIndex,
690 int methodNameIndex,
691 int descrIndex) {
692 int prevSection = c.setSection(0);
693 int nameAndTypeIndex = writeCPEntryNameAndType(methodNameIndex,
694 descrIndex);
695 int methodIndex = writeCPEntryMethodRef(classIndex, nameAndTypeIndex);
696 c.setSection(prevSection);
697 return methodIndex;
698 }
699
700 int writeCPEntryUtf8(String str) {
701 int prevSection = c.setSection(0);
702 int len = str.length();
703 c.writeU1(CONSTANT_UTF8); // Utf8 tag
704 c.writeU2(len);
705 for (int i = 0; i < len; ++i) {
706 c.writeU1(str.charAt(i));
707 }
708 c.setSection(prevSection);
709 return constantPoolCount++;
710 }
711
712 int writeCPEntryString(int utf8Index) {
713 int prevSection = c.setSection(0);
714 c.writeU1(CONSTANT_STRING);
715 c.writeU2(utf8Index);
716 c.setSection(prevSection);
717 return constantPoolCount++;
718 }
719
720 int writeCPEntryClass(int classNameIndex) {
721 int prevSection = c.setSection(0);
722 c.writeU1(CONSTANT_CLASS);
723 c.writeU2(classNameIndex);
724 c.setSection(prevSection);
725 return constantPoolCount++;
726 }
727
728 int writeCPEntryNameAndType(int nameIndex, int descrIndex) {
729 int prevSection = c.setSection(0);
730 c.writeU1(CONSTANT_NAMEANDTYPE);
731 c.writeU2(nameIndex);
732 c.writeU2(descrIndex);
733 c.setSection(prevSection);
734 return constantPoolCount++;
735 }
736
737 int writeCPEntryMethodRef(int classIndex, int nameAndTypeIndex) {
738 int prevSection = c.setSection(0);
739 c.writeU1(CONSTANT_METHOD);
740 c.writeU2(classIndex);
741 c.writeU2(nameAndTypeIndex);
742 c.setSection(prevSection);
743 return constantPoolCount++;
744 }
745}