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