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