DebugInfoEncoder.java revision 446b11cd80658706d77d58996670b8eead3683a4
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.dx.dex.file; 18 19import com.android.dx.dex.code.LocalList; 20import com.android.dx.dex.code.PositionList; 21import com.android.dx.rop.code.RegisterSpec; 22import com.android.dx.rop.code.SourcePosition; 23import com.android.dx.rop.cst.CstMethodRef; 24import com.android.dx.rop.cst.CstType; 25import com.android.dx.rop.cst.CstUtf8; 26import com.android.dx.rop.type.Prototype; 27import com.android.dx.rop.type.StdTypeList; 28import com.android.dx.rop.type.Type; 29import com.android.dx.util.ByteArrayAnnotatedOutput; 30import com.android.dx.util.AnnotatedOutput; 31import com.android.dx.util.ExceptionWithContext; 32 33import java.io.IOException; 34import java.io.PrintWriter; 35import java.util.ArrayList; 36import java.util.Collections; 37import java.util.Comparator; 38import java.util.BitSet; 39 40import static com.android.dx.dex.file.DebugInfoConstants.*; 41 42/** 43 * An encoder for the dex debug info state machine format. The format 44 * for each method enrty is as follows: 45 * <ol> 46 * <li> signed LEB128: initial value for line register. 47 * <li> n instances of signed LEB128: string indicies (offset by 1) 48 * for each method argument in left-to-right order 49 * with <code>this</code> excluded. A value of '0' indicates "no name" 50 * <li> A sequence of special or normal opcodes as defined in 51 * <code>DebugInfoConstants</code>. 52 * <li> A single terminating <code>OP_END_SEQUENCE</code> 53 * </ol> 54 */ 55public final class DebugInfoEncoder { 56 private static final boolean DEBUG = false; 57 58 /** null-ok; positions (line numbers) to encode */ 59 private final PositionList positionlist; 60 61 /** null-ok; local variables to encode */ 62 private final LocalList locallist; 63 64 private final ByteArrayAnnotatedOutput output; 65 private final DexFile file; 66 private final int codeSize; 67 private final int regSize; 68 69 private final Prototype desc; 70 private final boolean isStatic; 71 72 /** current encoding state: bytecode address */ 73 private int address = 0; 74 75 /** current encoding state: line number */ 76 private int line = 1; 77 78 /** 79 * if non-null: the output to write annotations to. No normal 80 * output is written to this. 81 */ 82 private AnnotatedOutput annotateTo; 83 84 /** if non-null: another possible output for annotations */ 85 private PrintWriter debugPrint; 86 87 /** if non-null: the prefix for each annotation or debugPrint line */ 88 private String prefix; 89 90 /** true if output should be consumed during annotation */ 91 private boolean shouldConsume; 92 93 /** indexed by register; last local alive in register */ 94 private final LocalList.Entry[] lastEntryForReg; 95 96 /** 97 * Creates an instance. 98 * 99 * @param pl null-ok; positions (line numbers) to encode 100 * @param ll null-ok; local variables to encode 101 * @param file null-ok; may only be <code>null</code> if simply using 102 * this class to do a debug print 103 * @param codeSize 104 * @param regSize 105 * @param isStatic 106 * @param ref 107 */ 108 public DebugInfoEncoder(PositionList pl, LocalList ll, 109 DexFile file, int codeSize, int regSize, 110 boolean isStatic, CstMethodRef ref) { 111 this.positionlist = pl; 112 this.locallist = ll; 113 this.file = file; 114 output = new ByteArrayAnnotatedOutput(); 115 this.desc = ref.getPrototype(); 116 this.isStatic = isStatic; 117 118 this.codeSize = codeSize; 119 this.regSize = regSize; 120 121 lastEntryForReg = new LocalList.Entry[regSize]; 122 } 123 124 /** 125 * Annotates or writes a message to the <code>debugPrint</code> writer 126 * if applicable. 127 * 128 * @param length the number of bytes associated with this message 129 * @param message the message itself 130 */ 131 private void annotate(int length, String message) { 132 if (prefix != null) { 133 message = prefix + message; 134 } 135 136 if (annotateTo != null) { 137 annotateTo.annotate(shouldConsume ? length : 0, message); 138 } 139 140 if (debugPrint != null) { 141 debugPrint.println(message); 142 } 143 } 144 145 /** 146 * Converts this (PositionList, LocalList) pair into a state machine 147 * sequence. 148 * 149 * @return encoded byte sequence without padding and 150 * terminated with a <code>'\00'</code> 151 */ 152 public byte[] convert() { 153 try { 154 byte[] ret; 155 ret = convert0(); 156 157 if (DEBUG) { 158 for (int i = 0 ; i < ret.length; i++) { 159 System.err.printf("byte %02x\n", (0xff & ret[i])); 160 } 161 } 162 163 return ret; 164 } catch (IOException ex) { 165 throw ExceptionWithContext 166 .withContext(ex, "...while encoding debug info"); 167 } 168 } 169 170 /** 171 * Converts and produces annotations on a stream. Does not write 172 * actual bits to the <code>AnnotatedOutput</code>. 173 * 174 * @param prefix null-ok; prefix to attach to each line of output 175 * @param debugPrint null-ok; if specified, an alternate output for 176 * annotations 177 * @param out null-ok; if specified, where annotations should go 178 * @param consume whether to claim to have consumed output for 179 * <code>out</code> 180 * @return output sequence 181 */ 182 public byte[] convertAndAnnotate(String prefix, PrintWriter debugPrint, 183 AnnotatedOutput out, boolean consume) { 184 this.prefix = prefix; 185 this.debugPrint = debugPrint; 186 annotateTo = out; 187 shouldConsume = consume; 188 189 byte[] result = convert(); 190 191 return result; 192 } 193 194 private byte[] convert0() throws IOException { 195 ArrayList<PositionList.Entry> sortedPositions = buildSortedPositions(); 196 ArrayList<LocalList.Entry> sortedLocalsStart = buildLocalsStart(); 197 198 // Parameter locals are removed from sortedLocalsStart here. 199 ArrayList<LocalList.Entry> methodArgs 200 = extractMethodArguments(sortedLocalsStart); 201 202 ArrayList<LocalList.Entry> sortedLocalsEnd 203 = buildLocalsEnd(sortedLocalsStart); 204 205 emitHeader(sortedPositions, methodArgs); 206 207 // TODO: Make this mark the actual prologue end. 208 output.writeByte(DBG_SET_PROLOGUE_END); 209 210 if (annotateTo != null || debugPrint != null) { 211 annotate(1, String.format("%04x: prologue end",address)); 212 } 213 214 int szp = sortedPositions.size(); 215 int szl = sortedLocalsStart.size(); 216 217 // Current index in sortedPositions 218 int curp = 0; 219 // Current index in sortedLocalsStart 220 int curls = 0; 221 // Current index in sortedLocalsEnd 222 int curle = 0; 223 224 for (;;) { 225 /* 226 * Emit any information for the current address. 227 */ 228 229 curle = emitLocalEndsAtAddress(curle, sortedLocalsEnd, curls, 230 sortedLocalsStart); 231 232 /* 233 * Our locals-sorted-by-range-end has reached the end 234 * of the code block. Ignore everything else. 235 */ 236 if (address == codeSize) { 237 curle = szl; 238 } 239 240 curls = emitLocalStartsAtAddress(curls, sortedLocalsStart); 241 242 curp = emitPositionsAtAddress(curp, sortedPositions); 243 244 /* 245 * Figure out what the next important address is. 246 */ 247 248 int nextAddrLS = Integer.MAX_VALUE; // local start 249 int nextAddrLE = Integer.MAX_VALUE; // local end 250 int nextAddrP = Integer.MAX_VALUE; // position (line number) 251 252 if (curls < szl) { 253 nextAddrLS = sortedLocalsStart.get(curls).getStart(); 254 } 255 256 if (curle < szl) { 257 nextAddrLE = sortedLocalsEnd.get(curle).getEnd(); 258 } 259 260 if (curp < szp) { 261 nextAddrP = sortedPositions.get(curp).getAddress(); 262 } 263 264 int next = Math.min(nextAddrP, Math.min(nextAddrLS, nextAddrLE)); 265 266 // No next important address == done. 267 if (next == Integer.MAX_VALUE) { 268 break; 269 } 270 271 /* 272 * If the only work remaining are local ends at the end of the 273 * block, stop here. Those are implied anyway. 274 */ 275 if (next == codeSize 276 && nextAddrLS == Integer.MAX_VALUE 277 && nextAddrP == Integer.MAX_VALUE) { 278 break; 279 } 280 281 if (next == nextAddrP) { 282 // Combined advance PC + position entry 283 emitPosition(sortedPositions.get(curp++)); 284 } else { 285 emitAdvancePc(next - address); 286 } 287 } 288 289 emitEndSequence(); 290 291 return output.toByteArray(); 292 } 293 294 /** 295 * Emits all local ends that occur at the current <code>address</code> 296 * 297 * @param curle Current index in sortedLocalsEnd 298 * @param sortedLocalsEnd Locals, sorted by ascending end address 299 * @param curls Current index in sortedLocalsStart 300 * @param sortedLocalsStart Locals, sorted by ascending start address 301 * @return new value for <code>curle</code> 302 * @throws IOException 303 */ 304 private int emitLocalEndsAtAddress(int curle, 305 ArrayList<LocalList.Entry> sortedLocalsEnd, int curls, 306 ArrayList<LocalList.Entry> sortedLocalsStart) 307 throws IOException { 308 309 int szl = sortedLocalsEnd.size(); 310 311 // Ignore "local ends" at end of code. 312 while (curle < szl 313 && sortedLocalsEnd.get(curle).getEnd() == address 314 && address != codeSize) { 315 316 boolean skipLocalEnd = false; 317 318 /* 319 * Check to see if there's a range-start that appears at 320 * the same address for the same register. If so, the 321 * end-range is implicit so skip it. 322 */ 323 for (int j = curls; j < szl 324 && sortedLocalsStart.get(j).getStart() == address 325 ; j++) { 326 327 if (sortedLocalsStart.get(j).getRegister() 328 == sortedLocalsEnd.get(curle).getRegister()) { 329 skipLocalEnd = true; 330 331 if (DEBUG) { 332 System.err.printf("skip local end v%d\n", 333 sortedLocalsEnd.get(curle).getRegister()); 334 } 335 break; 336 } 337 } 338 339 if (!skipLocalEnd) { 340 emitLocalEnd(sortedLocalsEnd.get(curle)); 341 } 342 343 curle++; 344 } 345 return curle; 346 } 347 348 /** 349 * Emits all local starts that occur at the current <code>address</code> 350 * 351 * @param curls Current index in sortedLocalsStart 352 * @param sortedLocalsStart Locals, sorted by ascending start address 353 * @return new value for <code>curls</code> 354 * @throws IOException 355 */ 356 private int emitLocalStartsAtAddress(int curls, 357 ArrayList<LocalList.Entry> sortedLocalsStart) 358 throws IOException { 359 360 int szl = sortedLocalsStart.size(); 361 362 while (curls < szl 363 && sortedLocalsStart.get(curls).getStart() == address) { 364 LocalList.Entry lle = sortedLocalsStart.get(curls++); 365 int reg = lle.getRegister(); 366 LocalList.Entry prevlle = lastEntryForReg[reg]; 367 368 if (lle == prevlle) { 369 /* 370 * Here we ignore locals entries for parameters, 371 * which have already been represented and placed in the 372 * lastEntryForReg array. 373 */ 374 continue; 375 } 376 377 // At this point we have a new live entry one way or another. 378 lastEntryForReg[reg] = lle; 379 380 if ((prevlle != null) && lle.matches(prevlle)) { 381 if (prevlle.getEnd() == lle.getStart()) { 382 /* 383 * There is nothing more to do in this case: It's 384 * an adjacent range with the same register. The 385 * previous emitLocalEndsAtAddress() call skipped 386 * this local end, so we'll skip this local start 387 * as well. 388 */ 389 } else { 390 emitLocalRestart(lle); 391 } 392 } else { 393 emitLocalStart(lle); 394 } 395 } 396 return curls; 397 } 398 399 /** 400 * Emits all positions that occur at the current <code>address</code> 401 * 402 * @param curp Current index in sortedPositions 403 * @param sortedPositions positions, sorted by ascending address 404 * @return new value for <code>curp</code> 405 * @throws IOException 406 */ 407 private int emitPositionsAtAddress(int curp, 408 ArrayList<PositionList.Entry> sortedPositions) 409 throws IOException { 410 411 int szp = sortedPositions.size(); 412 while (curp < szp 413 && sortedPositions.get(curp).getAddress() == address) { 414 emitPosition(sortedPositions.get(curp++)); 415 } 416 return curp; 417 } 418 419 /** 420 * Emits the header sequence, which consists of LEB128-encoded initial 421 * line number and string indicies for names of all non-"this" arguments. 422 * 423 * @param sortedPositions positions, sorted by ascending address 424 * @param methodArgs local list entries for method argumens arguments, 425 * in left-to-right order omitting "this" 426 * @throws IOException 427 */ 428 private void emitHeader(ArrayList<PositionList.Entry> sortedPositions, 429 ArrayList<LocalList.Entry> methodArgs) throws IOException { 430 boolean annotate = (annotateTo != null) || (debugPrint != null); 431 int mark = output.getCursor(); 432 433 // Start by initializing the line number register. 434 if (sortedPositions.size() > 0) { 435 PositionList.Entry entry = sortedPositions.get(0); 436 line = entry.getPosition().getLine(); 437 } 438 output.writeUnsignedLeb128(line); 439 440 if (annotate) { 441 annotate(output.getCursor() - mark, "line_start: " + line); 442 } 443 444 int curParam = getParamBase(); 445 // paramTypes will not include 'this' 446 StdTypeList paramTypes = desc.getParameterTypes(); 447 int szParamTypes = paramTypes.size(); 448 449 /* 450 * Initialize lastEntryForReg to have an initial 451 * entry for the 'this' pointer. 452 */ 453 if (!isStatic) { 454 for (LocalList.Entry arg: methodArgs) { 455 if (curParam == arg.getRegister()) { 456 lastEntryForReg[curParam] = arg; 457 break; 458 } 459 } 460 curParam++; 461 } 462 463 // Write out the number of parameter entries that will follow. 464 mark = output.getCursor(); 465 output.writeUnsignedLeb128(szParamTypes); 466 467 if (annotate) { 468 annotate(output.getCursor() - mark, 469 String.format("parameters_size: %04x", szParamTypes)); 470 } 471 472 /* 473 * Then emit the string indicies of all the method parameters. 474 * Note that 'this', if applicable, is excluded. 475 */ 476 for (int i = 0; i < szParamTypes; i++) { 477 Type pt = paramTypes.get(i); 478 LocalList.Entry found = null; 479 480 mark = output.getCursor(); 481 482 for (LocalList.Entry arg: methodArgs) { 483 if (curParam == arg.getRegister()) { 484 found = arg; 485 486 if (arg.getSignature() != null) { 487 /* 488 * Parameters with signatures will be re-emitted 489 * in complete as LOCAL_START_EXTENDED's below. 490 */ 491 emitStringIndex(null); 492 } else { 493 emitStringIndex(arg.getName()); 494 } 495 lastEntryForReg[curParam] = arg; 496 497 break; 498 } 499 } 500 501 if (found == null) { 502 /* 503 * Emit a null symbol for "unnamed." This is common 504 * for, e.g., synthesized methods and inner-class 505 * this$0 arguments. 506 */ 507 emitStringIndex(null); 508 } 509 510 if (annotate) { 511 String parameterName 512 = (found == null || found.getSignature() != null) 513 ? "<unnamed>" : found.getName().toHuman(); 514 annotate(output.getCursor() - mark, 515 "parameter " + parameterName + " " 516 + RegisterSpec.PREFIX + curParam); 517 } 518 519 curParam += pt.getCategory(); 520 } 521 522 /* 523 * If anything emitted above has a type signature, emit it again as 524 * a LOCAL_RESTART_EXTENDED 525 */ 526 527 for (LocalList.Entry arg: lastEntryForReg) { 528 if (arg == null) { 529 continue; 530 } 531 532 CstUtf8 signature = arg.getSignature(); 533 534 if (signature != null) { 535 emitLocalStartExtended(arg); 536 } 537 } 538 } 539 540 /** 541 * Builds a list of position entries, sorted by ascending address. 542 * 543 * @return A sorted positions list 544 */ 545 private ArrayList<PositionList.Entry> buildSortedPositions() { 546 int sz = (positionlist == null) ? 0 : positionlist.size(); 547 ArrayList<PositionList.Entry> result = new ArrayList(sz); 548 549 for (int i = 0; i < sz; i++) { 550 result.add(positionlist.get(i)); 551 } 552 553 // Sort ascending by address. 554 Collections.sort (result, new Comparator<PositionList.Entry>() { 555 public int compare (PositionList.Entry a, PositionList.Entry b) { 556 return a.getAddress() - b.getAddress(); 557 } 558 559 public boolean equals (Object obj) { 560 return obj == this; 561 } 562 }); 563 return result; 564 } 565 566 /** 567 * Builds a list of locals entries sorted by ascending start address. 568 * 569 * @return A sorted locals list list 570 */ 571 private ArrayList<LocalList.Entry> buildLocalsStart() { 572 int sz = (locallist == null) ? 0 : locallist.size(); 573 ArrayList<LocalList.Entry> result = new ArrayList(sz); 574 575 // Add all the entries 576 for (int i = 0; i < sz; i++) { 577 LocalList.Entry e = locallist.get(i); 578 result.add(locallist.get(i)); 579 } 580 581 // Sort ascending by start address. 582 Collections.sort (result, new Comparator<LocalList.Entry>() { 583 public int compare (LocalList.Entry a, LocalList.Entry b) { 584 return a.getStart() - b.getStart(); 585 } 586 587 public boolean equals (Object obj) { 588 return obj == this; 589 } 590 }); 591 return result; 592 } 593 594 /** 595 * Builds a list of locals entries sorted by ascending end address. 596 * 597 * @param list locals list in any order 598 * @return a sorted locals list 599 */ 600 private ArrayList<LocalList.Entry> buildLocalsEnd( 601 ArrayList<LocalList.Entry> list) { 602 603 ArrayList<LocalList.Entry> sortedLocalsEnd = new ArrayList(list); 604 605 // Sort ascending by end address. 606 Collections.sort (sortedLocalsEnd, new Comparator<LocalList.Entry>() { 607 public int compare (LocalList.Entry a, LocalList.Entry b) { 608 return a.getEnd() - b.getEnd(); 609 } 610 611 public boolean equals (Object obj) { 612 return obj == this; 613 } 614 }); 615 return sortedLocalsEnd; 616 } 617 618 /** 619 * Gets the register that begins the method's parameter range (including 620 * the 'this' parameter for non-static methods). The range continues until 621 * <code>regSize</code> 622 * 623 * @return register as noted above 624 */ 625 private int getParamBase() { 626 return regSize 627 - desc.getParameterTypes().getWordCount() - (isStatic? 0 : 1); 628 } 629 630 /** 631 * Extracts method arguments from a locals list. These will be collected 632 * from the input list and sorted by ascending register in the 633 * returned list. 634 * 635 * @param sortedLocals locals list, sorted by ascending start address, 636 * to process; left unmodified 637 * @return list of non-<code>this</code> method argument locals, 638 * sorted by ascending register 639 */ 640 private ArrayList<LocalList.Entry> extractMethodArguments ( 641 ArrayList<LocalList.Entry> sortedLocals) { 642 643 ArrayList<LocalList.Entry> result 644 = new ArrayList(desc.getParameterTypes().size()); 645 646 int argBase = getParamBase(); 647 648 BitSet seen = new BitSet(regSize - argBase); 649 650 int sz = sortedLocals.size(); 651 for (int i = 0; i < sz; i++) { 652 LocalList.Entry e = sortedLocals.get(i); 653 int reg = e.getRegister(); 654 655 if (reg < argBase) { 656 continue; 657 } 658 659 // only the lowest-start-address entry is included. 660 if (seen.get(reg - argBase)) { 661 continue; 662 } 663 664 seen.set(reg - argBase); 665 result.add(e); 666 } 667 668 // Sort by ascending register. 669 Collections.sort (result, new Comparator<LocalList.Entry>() { 670 public int compare (LocalList.Entry a, LocalList.Entry b) { 671 return a.getRegister() - b.getRegister(); 672 } 673 674 public boolean equals (Object obj) { 675 return obj == this; 676 } 677 }); 678 679 return result; 680 } 681 682 /** 683 * Returns a string representation of this LocalList entry that is 684 * appropriate for emitting as an annotation. 685 * 686 * @param e non-null; entry 687 * @return non-null; annotation string 688 */ 689 private String entryAnnotationString(LocalList.Entry e) { 690 StringBuilder sb = new StringBuilder(); 691 692 sb.append(RegisterSpec.PREFIX); 693 sb.append(e.getRegister()); 694 sb.append(' '); 695 696 CstUtf8 name = e.getName(); 697 if (name == null) { 698 sb.append("null"); 699 } else { 700 sb.append(name.toHuman()); 701 } 702 sb.append(' '); 703 704 CstType type = e.getType(); 705 if (type == null) { 706 sb.append("null"); 707 } else { 708 sb.append(type.toHuman()); 709 } 710 711 CstUtf8 signature = e.getSignature(); 712 713 if (signature != null) { 714 sb.append(' '); 715 sb.append(signature.toHuman()); 716 } 717 718 return sb.toString(); 719 } 720 721 /** 722 * Emits a {@link DebugInfoConstants#DBG_RESTART_LOCAL DBG_RESTART_LOCAL} 723 * sequence. 724 * 725 * @param entry entry associated with this restart 726 * @throws IOException 727 */ 728 private void emitLocalRestart(LocalList.Entry entry) 729 throws IOException { 730 731 int mark = output.getCursor(); 732 733 output.writeByte(DBG_RESTART_LOCAL); 734 emitUnsignedLeb128(entry.getRegister()); 735 736 if (annotateTo != null || debugPrint != null) { 737 annotate(output.getCursor() - mark, 738 String.format("%04x: +local restart %s", 739 address, entryAnnotationString(entry))); 740 } 741 742 if (DEBUG) { 743 System.err.println("emit local restart"); 744 } 745 } 746 747 /** 748 * Emits a string index as an unsigned LEB128. The actual value written 749 * is shifted by 1, so that the '0' value is reserved for "null". The 750 * null symbol is used in some cases by the parameter name list 751 * at the beginning of the sequence. 752 * 753 * @param string null-ok; string to emit 754 * @throws IOException 755 */ 756 private void emitStringIndex(CstUtf8 string) throws IOException { 757 if ((string == null) || (file == null)) { 758 output.writeUnsignedLeb128(0); 759 } else { 760 output.writeUnsignedLeb128( 761 1 + file.getStringIds().indexOf(string)); 762 } 763 764 if (DEBUG) { 765 System.err.printf("Emit string %s\n", 766 string == null ? "<null>" : string.toQuoted()); 767 } 768 } 769 770 /** 771 * Emits a type index as an unsigned LEB128. The actual value written 772 * is shifted by 1, so that the '0' value is reserved for "null". 773 * 774 * @param type null-ok; type to emit 775 * @throws IOException 776 */ 777 private void emitTypeIndex(CstType type) throws IOException { 778 if ((type == null) || (file == null)) { 779 output.writeUnsignedLeb128(0); 780 } else { 781 output.writeUnsignedLeb128( 782 1 + file.getTypeIds().indexOf(type)); 783 } 784 785 if (DEBUG) { 786 System.err.printf("Emit type %s\n", 787 type == null ? "<null>" : type.toHuman()); 788 } 789 } 790 791 /** 792 * Emits a {@link DebugInfoConstants#DBG_START_LOCAL DBG_START_LOCAL} or 793 * {@link DebugInfoConstants#DBG_START_LOCAL_EXTENDED 794 * DBG_START_LOCAL_EXTENDED} sequence. 795 * 796 * @param entry entry to emit 797 * @throws IOException 798 */ 799 private void emitLocalStart(LocalList.Entry entry) 800 throws IOException { 801 802 if (entry.getSignature() != null) { 803 emitLocalStartExtended(entry); 804 return; 805 } 806 807 int mark = output.getCursor(); 808 809 output.writeByte(DBG_START_LOCAL); 810 811 emitUnsignedLeb128(entry.getRegister()); 812 emitStringIndex(entry.getName()); 813 emitTypeIndex(entry.getType()); 814 815 if (annotateTo != null || debugPrint != null) { 816 annotate(output.getCursor() - mark, 817 String.format("%04x: +local %s", address, 818 entryAnnotationString(entry))); 819 } 820 821 if (DEBUG) { 822 System.err.println("emit local start"); 823 } 824 } 825 826 /** 827 * Emits a {@link DebugInfoConstants#DBG_START_LOCAL_EXTENDED 828 * DBG_START_LOCAL_EXTENDED} sequence. 829 * 830 * @param entry entry to emit 831 * @throws IOException 832 */ 833 private void emitLocalStartExtended(LocalList.Entry entry) 834 throws IOException { 835 836 int mark = output.getCursor(); 837 838 output.writeByte(DBG_START_LOCAL_EXTENDED); 839 840 emitUnsignedLeb128(entry.getRegister()); 841 emitStringIndex(entry.getName()); 842 emitTypeIndex(entry.getType()); 843 emitStringIndex(entry.getSignature()); 844 845 if (annotateTo != null || debugPrint != null) { 846 annotate(output.getCursor() - mark, 847 String.format("%04x: +localx %s", address, 848 entryAnnotationString(entry))); 849 } 850 851 if (DEBUG) { 852 System.err.println("emit local start"); 853 } 854 } 855 856 /** 857 * Emits a {@link DebugInfoConstants#DBG_END_LOCAL DBG_END_LOCAL} sequence. 858 * 859 * @param entry entry non-null; entry associated with end. 860 * @throws IOException 861 */ 862 private void emitLocalEnd(LocalList.Entry entry) 863 throws IOException { 864 865 int mark = output.getCursor(); 866 867 output.writeByte(DBG_END_LOCAL); 868 output.writeUnsignedLeb128(entry.getRegister()); 869 870 if (annotateTo != null || debugPrint != null) { 871 annotate(output.getCursor() - mark, 872 String.format("%04x: -local %s", address, 873 entryAnnotationString(entry))); 874 } 875 876 if (DEBUG) { 877 System.err.println("emit local end"); 878 } 879 } 880 881 /** 882 * Emits the necessary byte sequences to emit the given position table 883 * entry. This will typically be a single special opcode, although 884 * it may also require DBG_ADVANCE_PC or DBG_ADVANCE_LINE. 885 * 886 * @param entry position entry to emit. 887 * @throws IOException 888 */ 889 private void emitPosition(PositionList.Entry entry) 890 throws IOException { 891 892 SourcePosition pos = entry.getPosition(); 893 int newLine = pos.getLine(); 894 int newAddress = entry.getAddress(); 895 896 int opcode; 897 898 int deltaLines = newLine - line; 899 int deltaAddress = newAddress - address; 900 901 if (deltaAddress < 0) { 902 throw new RuntimeException( 903 "Position entries must be in ascending address order"); 904 } 905 906 if ((deltaLines < DBG_LINE_BASE) 907 || (deltaLines > (DBG_LINE_BASE + DBG_LINE_RANGE -1))) { 908 emitAdvanceLine(deltaLines); 909 deltaLines = 0; 910 } 911 912 opcode = computeOpcode (deltaLines, deltaAddress); 913 914 if ((opcode & ~0xff) > 0) { 915 emitAdvancePc(deltaAddress); 916 deltaAddress = 0; 917 opcode = computeOpcode (deltaLines, deltaAddress); 918 919 if ((opcode & ~0xff) > 0) { 920 emitAdvanceLine(deltaLines); 921 deltaLines = 0; 922 opcode = computeOpcode (deltaLines, deltaAddress); 923 } 924 } 925 926 output.writeByte(opcode); 927 928 line += deltaLines; 929 address += deltaAddress; 930 931 if (annotateTo != null || debugPrint != null) { 932 annotate(1, 933 String.format("%04x: line %d", address, line)); 934 } 935 } 936 937 /** 938 * Computes a special opcode that will encode the given position change. 939 * If the return value is > 0xff, then the request cannot be fulfilled. 940 * Essentially the same as described in "DWARF Debugging Format Version 3" 941 * section 6.2.5.1. 942 * 943 * @param deltaLines >= DBG_LINE_BASE and <= DBG_LINE_BASE + 944 * DBG_LINE_RANGE, the line change to encode 945 * @param deltaAddress >= 0; the address change to encode 946 * @return <= 0xff if in range, otherwise parameters are out of range 947 */ 948 private static int computeOpcode(int deltaLines, int deltaAddress) { 949 if (deltaLines < DBG_LINE_BASE 950 || deltaLines > (DBG_LINE_BASE + DBG_LINE_RANGE -1)) { 951 952 throw new RuntimeException("Parameter out of range"); 953 } 954 955 return (deltaLines - DBG_LINE_BASE) 956 + (DBG_LINE_RANGE * deltaAddress) + DBG_FIRST_SPECIAL; 957 } 958 959 /** 960 * Emits an {@link DebugInfoConstants#DBG_ADVANCE_LINE DBG_ADVANCE_LINE} 961 * sequence. 962 * 963 * @param deltaLines amount to change line number register by 964 * @throws IOException 965 */ 966 private void emitAdvanceLine(int deltaLines) throws IOException { 967 int mark = output.getCursor(); 968 969 output.writeByte(DBG_ADVANCE_LINE); 970 output.writeSignedLeb128(deltaLines); 971 line += deltaLines; 972 973 if (annotateTo != null || debugPrint != null) { 974 annotate(output.getCursor() - mark, 975 String.format("line = %d", line)); 976 } 977 978 if (DEBUG) { 979 System.err.printf("Emitting advance_line for %d\n", deltaLines); 980 } 981 } 982 983 /** 984 * Emits an {@link DebugInfoConstants#DBG_ADVANCE_PC DBG_ADVANCE_PC} 985 * sequence. 986 * 987 * @param deltaAddress >= 0 amount to change program counter by 988 * @throws IOException 989 */ 990 private void emitAdvancePc(int deltaAddress) throws IOException { 991 int mark = output.getCursor(); 992 993 output.writeByte(DBG_ADVANCE_PC); 994 output.writeUnsignedLeb128(deltaAddress); 995 address += deltaAddress; 996 997 if (annotateTo != null || debugPrint != null) { 998 annotate(output.getCursor() - mark, 999 String.format("%04x: advance pc", address)); 1000 } 1001 1002 if (DEBUG) { 1003 System.err.printf("Emitting advance_pc for %d\n", deltaAddress); 1004 } 1005 } 1006 1007 /** 1008 * Emits an unsigned LEB128 value. 1009 * 1010 * @param n >= 0 vallue to emit. Note that, although this can represent 1011 * integers larger than Integer.MAX_VALUE, we currently don't allow that. 1012 * @throws IOException 1013 */ 1014 private void emitUnsignedLeb128(int n) throws IOException { 1015 // We'll never need the top end of the unsigned range anyway. 1016 if (n < 0) { 1017 throw new RuntimeException( 1018 "Signed value where unsigned required: " + n); 1019 } 1020 1021 output.writeSignedLeb128(n); 1022 } 1023 1024 /** 1025 * Emits the {@link DebugInfoConstants#DBG_END_SEQUENCE DBG_END_SEQUENCE} 1026 * bytecode. 1027 */ 1028 private void emitEndSequence() { 1029 output.writeByte(DBG_END_SEQUENCE); 1030 1031 if (annotateTo != null || debugPrint != null) { 1032 annotate(1, "end sequence"); 1033 } 1034 } 1035} 1036