Fields Summary |
---|
private static final boolean | DEBUG |
private final com.android.dx.dex.code.PositionList | positions{@code null-ok;} positions (line numbers) to encode |
private final com.android.dx.dex.code.LocalList | locals{@code null-ok;} local variables to encode |
private final com.android.dx.util.ByteArrayAnnotatedOutput | output |
private final DexFile | file |
private final int | codeSize |
private final int | regSize |
private final com.android.dx.rop.type.Prototype | desc |
private final boolean | isStatic |
private int | addresscurrent encoding state: bytecode address |
private int | linecurrent encoding state: line number |
private com.android.dx.util.AnnotatedOutput | annotateToif non-null: the output to write annotations to. No normal
output is written to this. |
private PrintWriter | debugPrintif non-null: another possible output for annotations |
private String | prefixif non-null: the prefix for each annotation or debugPrint line |
private boolean | shouldConsumetrue if output should be consumed during annotation |
private final LocalList.Entry[] | lastEntryForRegindexed by register; last local alive in register |
Methods Summary |
---|
private void | annotate(int length, java.lang.String message)Annotates or writes a message to the {@code debugPrint} writer
if applicable.
if (prefix != null) {
message = prefix + message;
}
if (annotateTo != null) {
annotateTo.annotate(shouldConsume ? length : 0, message);
}
if (debugPrint != null) {
debugPrint.println(message);
}
|
private java.util.ArrayList | buildSortedPositions()Builds a list of position entries, sorted by ascending address.
int sz = (positions == null) ? 0 : positions.size();
ArrayList<PositionList.Entry> result = new ArrayList(sz);
for (int i = 0; i < sz; i++) {
result.add(positions.get(i));
}
// Sort ascending by address.
Collections.sort (result, new Comparator<PositionList.Entry>() {
public int compare (PositionList.Entry a, PositionList.Entry b) {
return a.getAddress() - b.getAddress();
}
public boolean equals (Object obj) {
return obj == this;
}
});
return result;
|
private static int | computeOpcode(int deltaLines, int deltaAddress)Computes a special opcode that will encode the given position change.
If the return value is > 0xff, then the request cannot be fulfilled.
Essentially the same as described in "DWARF Debugging Format Version 3"
section 6.2.5.1.
if (deltaLines < DBG_LINE_BASE
|| deltaLines > (DBG_LINE_BASE + DBG_LINE_RANGE -1)) {
throw new RuntimeException("Parameter out of range");
}
return (deltaLines - DBG_LINE_BASE)
+ (DBG_LINE_RANGE * deltaAddress) + DBG_FIRST_SPECIAL;
|
public byte[] | convert()Converts this (PositionList, LocalList) pair into a state machine
sequence.
try {
byte[] ret;
ret = convert0();
if (DEBUG) {
for (int i = 0 ; i < ret.length; i++) {
System.err.printf("byte %02x\n", (0xff & ret[i]));
}
}
return ret;
} catch (IOException ex) {
throw ExceptionWithContext
.withContext(ex, "...while encoding debug info");
}
|
private byte[] | convert0()
ArrayList<PositionList.Entry> sortedPositions = buildSortedPositions();
ArrayList<LocalList.Entry> methodArgs = extractMethodArguments();
emitHeader(sortedPositions, methodArgs);
// TODO: Make this mark be the actual prologue end.
output.writeByte(DBG_SET_PROLOGUE_END);
if (annotateTo != null || debugPrint != null) {
annotate(1, String.format("%04x: prologue end",address));
}
int positionsSz = sortedPositions.size();
int localsSz = locals.size();
// Current index in sortedPositions
int curPositionIdx = 0;
// Current index in locals
int curLocalIdx = 0;
for (;;) {
/*
* Emit any information for the current address.
*/
curLocalIdx = emitLocalsAtAddress(curLocalIdx);
curPositionIdx =
emitPositionsAtAddress(curPositionIdx, sortedPositions);
/*
* Figure out what the next important address is.
*/
int nextAddrL = Integer.MAX_VALUE; // local variable
int nextAddrP = Integer.MAX_VALUE; // position (line number)
if (curLocalIdx < localsSz) {
nextAddrL = locals.get(curLocalIdx).getAddress();
}
if (curPositionIdx < positionsSz) {
nextAddrP = sortedPositions.get(curPositionIdx).getAddress();
}
int next = Math.min(nextAddrP, nextAddrL);
// No next important address == done.
if (next == Integer.MAX_VALUE) {
break;
}
/*
* If the only work remaining are local ends at the end of the
* block, stop here. Those are implied anyway.
*/
if (next == codeSize
&& nextAddrL == Integer.MAX_VALUE
&& nextAddrP == Integer.MAX_VALUE) {
break;
}
if (next == nextAddrP) {
// Combined advance PC + position entry
emitPosition(sortedPositions.get(curPositionIdx++));
} else {
emitAdvancePc(next - address);
}
}
emitEndSequence();
return output.toByteArray();
|
public byte[] | convertAndAnnotate(java.lang.String prefix, java.io.PrintWriter debugPrint, com.android.dx.util.AnnotatedOutput out, boolean consume)Converts and produces annotations on a stream. Does not write
actual bits to the {@code AnnotatedOutput}.
this.prefix = prefix;
this.debugPrint = debugPrint;
annotateTo = out;
shouldConsume = consume;
byte[] result = convert();
return result;
|
private void | emitAdvanceLine(int deltaLines)Emits an {@link DebugInfoConstants#DBG_ADVANCE_LINE DBG_ADVANCE_LINE}
sequence.
int mark = output.getCursor();
output.writeByte(DBG_ADVANCE_LINE);
output.writeSleb128(deltaLines);
line += deltaLines;
if (annotateTo != null || debugPrint != null) {
annotate(output.getCursor() - mark,
String.format("line = %d", line));
}
if (DEBUG) {
System.err.printf("Emitting advance_line for %d\n", deltaLines);
}
|
private void | emitAdvancePc(int deltaAddress)Emits an {@link DebugInfoConstants#DBG_ADVANCE_PC DBG_ADVANCE_PC}
sequence.
int mark = output.getCursor();
output.writeByte(DBG_ADVANCE_PC);
output.writeUleb128(deltaAddress);
address += deltaAddress;
if (annotateTo != null || debugPrint != null) {
annotate(output.getCursor() - mark,
String.format("%04x: advance pc", address));
}
if (DEBUG) {
System.err.printf("Emitting advance_pc for %d\n", deltaAddress);
}
|
private void | emitEndSequence()Emits the {@link DebugInfoConstants#DBG_END_SEQUENCE DBG_END_SEQUENCE}
bytecode.
output.writeByte(DBG_END_SEQUENCE);
if (annotateTo != null || debugPrint != null) {
annotate(1, "end sequence");
}
|
private void | emitHeader(java.util.ArrayList sortedPositions, java.util.ArrayList methodArgs)Emits the header sequence, which consists of LEB128-encoded initial
line number and string indicies for names of all non-"this" arguments.
boolean annotate = (annotateTo != null) || (debugPrint != null);
int mark = output.getCursor();
// Start by initializing the line number register.
if (sortedPositions.size() > 0) {
PositionList.Entry entry = sortedPositions.get(0);
line = entry.getPosition().getLine();
}
output.writeUleb128(line);
if (annotate) {
annotate(output.getCursor() - mark, "line_start: " + line);
}
int curParam = getParamBase();
// paramTypes will not include 'this'
StdTypeList paramTypes = desc.getParameterTypes();
int szParamTypes = paramTypes.size();
/*
* Initialize lastEntryForReg to have an initial
* entry for the 'this' pointer.
*/
if (!isStatic) {
for (LocalList.Entry arg : methodArgs) {
if (curParam == arg.getRegister()) {
lastEntryForReg[curParam] = arg;
break;
}
}
curParam++;
}
// Write out the number of parameter entries that will follow.
mark = output.getCursor();
output.writeUleb128(szParamTypes);
if (annotate) {
annotate(output.getCursor() - mark,
String.format("parameters_size: %04x", szParamTypes));
}
/*
* Then emit the string indicies of all the method parameters.
* Note that 'this', if applicable, is excluded.
*/
for (int i = 0; i < szParamTypes; i++) {
Type pt = paramTypes.get(i);
LocalList.Entry found = null;
mark = output.getCursor();
for (LocalList.Entry arg : methodArgs) {
if (curParam == arg.getRegister()) {
found = arg;
if (arg.getSignature() != null) {
/*
* Parameters with signatures will be re-emitted
* in complete as LOCAL_START_EXTENDED's below.
*/
emitStringIndex(null);
} else {
emitStringIndex(arg.getName());
}
lastEntryForReg[curParam] = arg;
break;
}
}
if (found == null) {
/*
* Emit a null symbol for "unnamed." This is common
* for, e.g., synthesized methods and inner-class
* this$0 arguments.
*/
emitStringIndex(null);
}
if (annotate) {
String parameterName
= (found == null || found.getSignature() != null)
? "<unnamed>" : found.getName().toHuman();
annotate(output.getCursor() - mark,
"parameter " + parameterName + " "
+ RegisterSpec.PREFIX + curParam);
}
curParam += pt.getCategory();
}
/*
* If anything emitted above has a type signature, emit it again as
* a LOCAL_RESTART_EXTENDED
*/
for (LocalList.Entry arg : lastEntryForReg) {
if (arg == null) {
continue;
}
CstString signature = arg.getSignature();
if (signature != null) {
emitLocalStartExtended(arg);
}
}
|
private void | emitLocalEnd(LocalList.Entry entry)Emits a {@link DebugInfoConstants#DBG_END_LOCAL DBG_END_LOCAL} sequence.
int mark = output.getCursor();
output.writeByte(DBG_END_LOCAL);
output.writeUleb128(entry.getRegister());
if (annotateTo != null || debugPrint != null) {
annotate(output.getCursor() - mark,
String.format("%04x: -local %s", address,
entryAnnotationString(entry)));
}
if (DEBUG) {
System.err.println("emit local end");
}
|
private void | emitLocalRestart(LocalList.Entry entry)Emits a {@link DebugInfoConstants#DBG_RESTART_LOCAL DBG_RESTART_LOCAL}
sequence.
int mark = output.getCursor();
output.writeByte(DBG_RESTART_LOCAL);
emitUnsignedLeb128(entry.getRegister());
if (annotateTo != null || debugPrint != null) {
annotate(output.getCursor() - mark,
String.format("%04x: +local restart %s",
address, entryAnnotationString(entry)));
}
if (DEBUG) {
System.err.println("emit local restart");
}
|
private void | emitLocalStart(LocalList.Entry entry)Emits a {@link DebugInfoConstants#DBG_START_LOCAL DBG_START_LOCAL} or
{@link DebugInfoConstants#DBG_START_LOCAL_EXTENDED
DBG_START_LOCAL_EXTENDED} sequence.
if (entry.getSignature() != null) {
emitLocalStartExtended(entry);
return;
}
int mark = output.getCursor();
output.writeByte(DBG_START_LOCAL);
emitUnsignedLeb128(entry.getRegister());
emitStringIndex(entry.getName());
emitTypeIndex(entry.getType());
if (annotateTo != null || debugPrint != null) {
annotate(output.getCursor() - mark,
String.format("%04x: +local %s", address,
entryAnnotationString(entry)));
}
if (DEBUG) {
System.err.println("emit local start");
}
|
private void | emitLocalStartExtended(LocalList.Entry entry)Emits a {@link DebugInfoConstants#DBG_START_LOCAL_EXTENDED
DBG_START_LOCAL_EXTENDED} sequence.
int mark = output.getCursor();
output.writeByte(DBG_START_LOCAL_EXTENDED);
emitUnsignedLeb128(entry.getRegister());
emitStringIndex(entry.getName());
emitTypeIndex(entry.getType());
emitStringIndex(entry.getSignature());
if (annotateTo != null || debugPrint != null) {
annotate(output.getCursor() - mark,
String.format("%04x: +localx %s", address,
entryAnnotationString(entry)));
}
if (DEBUG) {
System.err.println("emit local start");
}
|
private int | emitLocalsAtAddress(int curLocalIdx)Emits all local variable activity that occurs at the current
{@link #address} starting at the given index into {@code
locals} and including all subsequent activity at the same
address.
int sz = locals.size();
// TODO: Don't emit ends implied by starts.
while ((curLocalIdx < sz)
&& (locals.get(curLocalIdx).getAddress() == address)) {
LocalList.Entry entry = locals.get(curLocalIdx++);
int reg = entry.getRegister();
LocalList.Entry prevEntry = lastEntryForReg[reg];
if (entry == prevEntry) {
/*
* Here we ignore locals entries for parameters,
* which have already been represented and placed in the
* lastEntryForReg array.
*/
continue;
}
// At this point we have a new entry one way or another.
lastEntryForReg[reg] = entry;
if (entry.isStart()) {
if ((prevEntry != null) && entry.matches(prevEntry)) {
/*
* The previous local in this register has the same
* name and type as the one being introduced now, so
* use the more efficient "restart" form.
*/
if (prevEntry.isStart()) {
/*
* We should never be handed a start when a
* a matching local is already active.
*/
throw new RuntimeException("shouldn't happen");
}
emitLocalRestart(entry);
} else {
emitLocalStart(entry);
}
} else {
/*
* Only emit a local end if it is *not* due to a direct
* replacement. Direct replacements imply an end of the
* previous local in the same register.
*
* TODO: Make sure the runtime can deal with implied
* local ends from category-2 interactions, and when so,
* also stop emitting local ends for those cases.
*/
if (entry.getDisposition()
!= LocalList.Disposition.END_REPLACED) {
emitLocalEnd(entry);
}
}
}
return curLocalIdx;
|
private void | emitPosition(PositionList.Entry entry)Emits the necessary byte sequences to emit the given position table
entry. This will typically be a single special opcode, although
it may also require DBG_ADVANCE_PC or DBG_ADVANCE_LINE.
SourcePosition pos = entry.getPosition();
int newLine = pos.getLine();
int newAddress = entry.getAddress();
int opcode;
int deltaLines = newLine - line;
int deltaAddress = newAddress - address;
if (deltaAddress < 0) {
throw new RuntimeException(
"Position entries must be in ascending address order");
}
if ((deltaLines < DBG_LINE_BASE)
|| (deltaLines > (DBG_LINE_BASE + DBG_LINE_RANGE -1))) {
emitAdvanceLine(deltaLines);
deltaLines = 0;
}
opcode = computeOpcode (deltaLines, deltaAddress);
if ((opcode & ~0xff) > 0) {
emitAdvancePc(deltaAddress);
deltaAddress = 0;
opcode = computeOpcode (deltaLines, deltaAddress);
if ((opcode & ~0xff) > 0) {
emitAdvanceLine(deltaLines);
deltaLines = 0;
opcode = computeOpcode (deltaLines, deltaAddress);
}
}
output.writeByte(opcode);
line += deltaLines;
address += deltaAddress;
if (annotateTo != null || debugPrint != null) {
annotate(1,
String.format("%04x: line %d", address, line));
}
|
private int | emitPositionsAtAddress(int curPositionIdx, java.util.ArrayList sortedPositions)Emits all positions that occur at the current {@code address}
int positionsSz = sortedPositions.size();
while ((curPositionIdx < positionsSz)
&& (sortedPositions.get(curPositionIdx).getAddress()
== address)) {
emitPosition(sortedPositions.get(curPositionIdx++));
}
return curPositionIdx;
|
private void | emitStringIndex(com.android.dx.rop.cst.CstString string)Emits a string index as an unsigned LEB128. The actual value written
is shifted by 1, so that the '0' value is reserved for "null". The
null symbol is used in some cases by the parameter name list
at the beginning of the sequence.
if ((string == null) || (file == null)) {
output.writeUleb128(0);
} else {
output.writeUleb128(
1 + file.getStringIds().indexOf(string));
}
if (DEBUG) {
System.err.printf("Emit string %s\n",
string == null ? "<null>" : string.toQuoted());
}
|
private void | emitTypeIndex(com.android.dx.rop.cst.CstType type)Emits a type index as an unsigned LEB128. The actual value written
is shifted by 1, so that the '0' value is reserved for "null".
if ((type == null) || (file == null)) {
output.writeUleb128(0);
} else {
output.writeUleb128(
1 + file.getTypeIds().indexOf(type));
}
if (DEBUG) {
System.err.printf("Emit type %s\n",
type == null ? "<null>" : type.toHuman());
}
|
private void | emitUnsignedLeb128(int n)Emits an unsigned LEB128 value.
// We'll never need the top end of the unsigned range anyway.
if (n < 0) {
throw new RuntimeException(
"Signed value where unsigned required: " + n);
}
output.writeUleb128(n);
|
private java.lang.String | entryAnnotationString(LocalList.Entry e)Returns a string representation of this LocalList entry that is
appropriate for emitting as an annotation.
StringBuilder sb = new StringBuilder();
sb.append(RegisterSpec.PREFIX);
sb.append(e.getRegister());
sb.append(' ");
CstString name = e.getName();
if (name == null) {
sb.append("null");
} else {
sb.append(name.toHuman());
}
sb.append(' ");
CstType type = e.getType();
if (type == null) {
sb.append("null");
} else {
sb.append(type.toHuman());
}
CstString signature = e.getSignature();
if (signature != null) {
sb.append(' ");
sb.append(signature.toHuman());
}
return sb.toString();
|
private java.util.ArrayList | extractMethodArguments()Extracts method arguments from a locals list. These will be collected
from the input list and sorted by ascending register in the
returned list.
ArrayList<LocalList.Entry> result
= new ArrayList(desc.getParameterTypes().size());
int argBase = getParamBase();
BitSet seen = new BitSet(regSize - argBase);
int sz = locals.size();
for (int i = 0; i < sz; i++) {
LocalList.Entry e = locals.get(i);
int reg = e.getRegister();
if (reg < argBase) {
continue;
}
// only the lowest-start-address entry is included.
if (seen.get(reg - argBase)) {
continue;
}
seen.set(reg - argBase);
result.add(e);
}
// Sort by ascending register.
Collections.sort(result, new Comparator<LocalList.Entry>() {
public int compare(LocalList.Entry a, LocalList.Entry b) {
return a.getRegister() - b.getRegister();
}
public boolean equals(Object obj) {
return obj == this;
}
});
return result;
|
private int | getParamBase()Gets the register that begins the method's parameter range (including
the 'this' parameter for non-static methods). The range continues until
{@code regSize}
return regSize
- desc.getParameterTypes().getWordCount() - (isStatic? 0 : 1);
|