1 /* 2 * Copyright (c) 2005, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package ilib; 25 26 import java.io.IOException; 27 import java.io.File; 28 import java.io.FileOutputStream; 29 import java.io.DataOutputStream; 30 import java.util.List; 31 import java.util.Iterator; 32 import java.util.ArrayList; 33 34 public 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<>(); 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 }