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