Added initial support for method level debug info (currently line info only)

git-svn-id: https://smali.googlecode.com/svn/trunk@44 55b6fa8a-2a1e-11de-a435-ffa8d773f76a
This commit is contained in:
JesusFreke@JesusFreke.com 2009-05-18 06:45:09 +00:00
parent 51b326d5a6
commit aa7e507bac
9 changed files with 270 additions and 17 deletions

View File

@ -464,6 +464,11 @@ CATCH_PHRASE
WS 'using' WS
(LABEL_EMIT | OFFSET_EMIT);
LINE_PHRASE
: LINE_DIRECTIVE_EMIT
WS
INTEGRAL_LITERAL_EMITCHILD;
//TODO: add support for both relative and absolute offsets?
fragment OFFSET_EMIT
@ -551,6 +556,11 @@ fragment CATCH_DIRECTIVE_EMIT
fragment CATCH_DIRECTIVE
: '.catch';
fragment LINE_DIRECTIVE_EMIT
: LINE_DIRECTIVE {emit($LINE_DIRECTIVE, LINE_DIRECTIVE);};
fragment LINE_DIRECTIVE
: '.line';
fragment REGISTER_EMIT
: REGISTER {emit($REGISTER, REGISTER);};
fragment REGISTER

View File

@ -65,6 +65,8 @@ tokens {
I_CATCH;
I_CATCHES;
I_CATCH_ADDRESS;
I_LINE;
I_LINES;
I_STATEMENTS;
I_STATEMENT_FORMAT10t;
I_STATEMENT_FORMAT10x;
@ -213,13 +215,18 @@ statements
: {$statements::currentAddress = 0;}
( instruction {$statements::currentAddress += $instruction.size/2;}
| catch_directive
| line_directive
| label)*
-> ^(I_LABELS label*) ^(I_STATEMENTS instruction*) ^(I_CATCHES catch_directive*);
-> ^(I_LABELS label*) ^(I_STATEMENTS instruction*) ^(I_CATCHES catch_directive*) ^(I_LINES line_directive*);
catch_directive
: CATCH_DIRECTIVE field_type_descriptor from=offset_or_label to=offset_or_label using=offset_or_label
-> ^(I_CATCH[$start, "I_CATCH"] I_CATCH_ADDRESS[$start, Integer.toString($statements::currentAddress)] field_type_descriptor $from $to $using)
;
line_directive
: LINE_DIRECTIVE integral_literal -> ^(I_LINE integral_literal {new CommonTree(new CommonToken(INTEGER_LITERAL,Integer.toString($statements::currentAddress)))});
label
: LABEL -> ^(I_LABEL LABEL {new CommonTree(new CommonToken(INTEGER_LITERAL,Integer.toString($statements::currentAddress)))});

View File

@ -286,13 +286,15 @@ method returns[ClassDataItem.EncodedMethod encodedMethod]
HashMap<String, Integer> labels;
TryListBuilder tryList;
int currentAddress;
DebugInfoBuilder debugInfo;
}
: {
$method::labels = new HashMap<String, Integer>();
$method::tryList = new TryListBuilder();
$method::currentAddress = 0;
$method::debugInfo = new DebugInfoBuilder();
}
^(I_METHOD method_name_and_prototype access_list registers_directive labels statements catches)
^(I_METHOD method_name_and_prototype access_list registers_directive labels statements catches lines)
{
MethodIdItem methodIdItem = $method_name_and_prototype.methodIdItem;
int registers = $registers_directive.registers;
@ -304,7 +306,15 @@ method returns[ClassDataItem.EncodedMethod encodedMethod]
List<CodeItem.TryItem> tries = temp.first;
List<CodeItem.EncodedCatchHandler> handlers = temp.second;
CodeItem codeItem = new CodeItem(dexFile, registers, methodIdItem.getParameterWordCount(isStatic), instructions, tries, handlers);
DebugInfoItem debugInfoItem = $method::debugInfo.encodeDebugInfo(dexFile);
CodeItem codeItem = new CodeItem(dexFile,
registers,
methodIdItem.getParameterWordCount(isStatic),
instructions,
debugInfoItem,
tries,
handlers);
$encodedMethod = new ClassDataItem.EncodedMethod(dexFile, methodIdItem, access, codeItem);
};
@ -376,6 +386,15 @@ catch_directive
$method::tryList.addHandler(type, startAddress, endAddress, handlerAddress);
};
lines
: ^(I_LINES line*);
line
: ^(I_LINE integral_literal integer_literal)
{
$method::debugInfo.addLine($integer_literal.value, $integral_literal.value);
};
labels
: ^(I_LABELS label_def*);
@ -844,6 +863,16 @@ short_integral_literal returns[short value]
}
| short_literal {$value = $short_literal.value;}
| byte_literal {$value = $byte_literal.value;};
integral_literal returns[int value]
: long_literal
{
literalTools.checkInt($long_literal.value);
$value = (short)$long_literal.value;
}
| integer_literal {$value = (short)$integer_literal.value;}
| short_literal {$value = $short_literal.value;}
| byte_literal {$value = $byte_literal.value;};
integer_literal returns[int value]

View File

@ -81,7 +81,13 @@ public class CodeItem extends OffsettedItem<CodeItem> {
public CodeItem(final DexFile dexFile, int registersCount, int inArguments, ArrayList<Instruction> instructions, List<TryItem> tries, List<EncodedCatchHandler> handlers) {
public CodeItem(final DexFile dexFile,
int registersCount,
int inArguments,
ArrayList<Instruction> instructions,
DebugInfoItem debugInfo,
List<TryItem> tries,
List<EncodedCatchHandler> handlers) {
super(-1);
this.instructionList = new ArrayList<Instruction>(instructions);
@ -102,7 +108,7 @@ public class CodeItem extends OffsettedItem<CodeItem> {
this.inArgumentCount = new ShortIntegerField(inArguments),
this.outArgumentCount = new ShortIntegerField(instructionListField.getOutArguments()),
this.triesCount = new ListSizeField(tryItems, new ShortIntegerField(0)),
this.debugInfo = new OffsettedItemReference<DebugInfoItem>(dexFile, null, new IntegerField()),
this.debugInfo = new OffsettedItemReference<DebugInfoItem>(dexFile, debugInfo, new IntegerField()),
this.instructionsSize = new IntegerField(instructionListField.getInstructionWordCount()),
instructionListField,
this.padding = new PaddingField(),

View File

@ -36,6 +36,7 @@ import org.JesusFreke.dexlib.util.Output;
import org.JesusFreke.dexlib.util.Input;
import java.util.ArrayList;
import java.util.List;
public class DebugInfoItem extends OffsettedItem<DebugInfoItem> {
private final Field[] fields;
@ -45,21 +46,42 @@ public class DebugInfoItem extends OffsettedItem<DebugInfoItem> {
private ArrayList<DebugInstruction> instructionFields = new ArrayList<DebugInstruction>();
private final Leb128Field lineStartField;
private final ListSizeField parameterNamesSizeField;
private final FieldListField<IndexedItemReference<StringIdItem>> parameterNamesField;
private final DebugInstructionList debugInstructionListField;
public DebugInfoItem(final DexFile dexFile, int offset) {
super(offset);
fields = new Field[] {
new Leb128Field(),
new ListSizeField(parameterNames, new Leb128Field()),
new FieldListField<IndexedItemReference<StringIdItem>>(parameterNames) {
lineStartField = new Leb128Field(),
parameterNamesSizeField = new ListSizeField(parameterNames, new Leb128Field()),
parameterNamesField = new FieldListField<IndexedItemReference<StringIdItem>>(parameterNames) {
protected IndexedItemReference<StringIdItem> make() {
return new IndexedItemReference<StringIdItem>(dexFile.StringIdsSection, new Leb128p1Field());
}
},
new DebugInstructionList(dexFile)
debugInstructionListField = new DebugInstructionList(dexFile)
};
}
public DebugInfoItem(final DexFile dexFile,
int lineStart,
List<StringIdItem> parameterNames,
List<DebugInstruction> debugInstructions) {
this(dexFile, 0);
this.lineStartField.cacheValue(lineStart);
for (StringIdItem parameterName: parameterNames) {
this.parameterNames.add(new IndexedItemReference<StringIdItem>(dexFile, parameterName,
new Leb128p1Field()));
}
this.instructionFields.addAll(debugInstructions);
}
protected int getAlignment() {
return 1;
}

View File

@ -36,13 +36,21 @@ import org.JesusFreke.dexlib.SignedLeb128Field;
public class AdvanceLine extends CompositeField<AdvanceLine> implements DebugInstruction<AdvanceLine> {
private final Field[] fields;
private final ByteField opcodeField;
private final SignedLeb128Field lineDeltaField;
public AdvanceLine() {
fields = new Field[] {
new ByteField((byte)0x02),
new SignedLeb128Field()
opcodeField = new ByteField((byte)0x02),
lineDeltaField = new SignedLeb128Field()
};
}
public AdvanceLine(int lineDelta) {
this();
lineDeltaField.cacheValue(lineDelta);
}
protected Field[] getFields() {
return fields;
}

View File

@ -33,13 +33,21 @@ import org.JesusFreke.dexlib.*;
public class AdvancePC extends CompositeField<AdvancePC> implements DebugInstruction<AdvancePC> {
private final Field[] fields;
private final ByteField opcodeField;
private final Leb128Field addressDeltaField;
public AdvancePC() {
fields = new Field[] {
new ByteField((byte)0x01),
new Leb128Field()
opcodeField = new ByteField((byte)0x01),
addressDeltaField = new Leb128Field()
};
}
public AdvancePC(int addressDelta) {
this();
addressDeltaField.cacheValue(addressDelta);
}
protected Field[] getFields() {
return fields;
}

View File

@ -0,0 +1,151 @@
/*
* [The "BSD licence"]
* Copyright (c) 2009 Ben Gruver
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.JesusFreke.dexlib.util;
import org.JesusFreke.dexlib.debug.*;
import org.JesusFreke.dexlib.DexFile;
import org.JesusFreke.dexlib.DebugInfoItem;
import org.JesusFreke.dexlib.StringIdItem;
import java.util.ArrayList;
import java.util.List;
/**
* This class is intended to provide an easy to use container to build up a method's debug info. You can easily add
* an "event" at a specific address, where an event is something like a line number, start/end local, etc.
* The events must be added such that the code addresses increase monotonically. This matches how a parser would
* generally behave, and is intended to increase performance.
*/
public class DebugInfoBuilder
{
//TODO: take a look at the debug bytecode generation logic in dx, and make sure that this does the same thing
//(in the interest of being able to exactly reproduce a given dx-generated dex file)
private static final int LINE_BASE = -4;
private static final int LINE_RANGE = 15;
private static final int FIRST_SPECIAL = 0x0a;
private int lineStart = 0;
private ArrayList<String> parameterNames = new ArrayList<String>();
private ArrayList<Event> events = new ArrayList<Event>();
private int lastAddress = 0;
private boolean hasData;
private int currentAddress;
private int currentLine;
public DebugInfoBuilder() {
}
public void addLine(int address, int line) {
hasData = true;
if (lastAddress > address) {
throw new RuntimeException("Cannot add an event with an address before the address of the prior event");
}
if (lineStart == 0) {
lineStart = line;
}
events.add(new LineEvent(address, line));
}
public DebugInfoItem encodeDebugInfo(DexFile dexFile) {
if (!hasData) {
return null;
}
ArrayList<DebugInstruction> debugInstructions = new ArrayList<DebugInstruction>();
ArrayList<StringIdItem> parameterNameReferences = new ArrayList<StringIdItem>();
if (lineStart == 0) {
lineStart = 1;
}
currentLine = lineStart;
for (Event event: events) {
event.emit(debugInstructions);
}
debugInstructions.add(new EndSequence());
for (String parameterName: parameterNames) {
parameterNameReferences.add(new StringIdItem(dexFile, parameterName));
}
return new DebugInfoItem(dexFile, lineStart, parameterNameReferences, debugInstructions);
}
private interface Event
{
int getAddress();
void emit(List<DebugInstruction> debugInstructions);
}
private class LineEvent implements Event
{
private final int address;
private final int line;
public LineEvent(int address, int line) {
this.address = address;
this.line = line;
}
public int getAddress() {
return address;
}
public void emit(List<DebugInstruction> debugInstructions) {
int lineDelta = line - currentLine;
int addressDelta = address - currentAddress;
if (lineDelta < -4 || lineDelta > 10) {
debugInstructions.add(new AdvanceLine(lineDelta));
lineDelta = 0;
}
if (lineDelta < 2 && addressDelta > 16 || lineDelta > 1 && addressDelta > 15) {
debugInstructions.add(new AdvancePC(addressDelta));
addressDelta = 0;
}
debugInstructions.add(new SpecialOpcode(calculateSpecialOpcode(lineDelta, addressDelta)));
currentAddress = address;
currentLine = line;
}
private byte calculateSpecialOpcode(int lineDelta, int addressDelta) {
return (byte)(FIRST_SPECIAL + (addressDelta * LINE_RANGE) + (lineDelta - LINE_BASE));
}
}
}

View File

@ -11,7 +11,7 @@
.method public println(Ljava/lang/String;)V
.registers 1
.registers 2
return-void
.end method
@ -20,7 +20,7 @@
.method public onAccountsUpdated([Ljava/lang/String;)V
.registers 1
.registers 2
return-void
.end method
@ -360,6 +360,8 @@ SparseSwitch:
.method public testTry()Ljava/lang/String;
.registers 2
.line 4
;0
const-string v0, "This shouldn't be displayed!"
@ -368,6 +370,8 @@ SparseSwitch:
tryStart:
new-instance v1, Ljava/lang/Exception;
.line 2
;4
invoke-direct {v1}, java/lang/Exception/<init>()V
@ -379,12 +383,16 @@ SparseSwitch:
nop
nop
;8
.line 5
;10
tryEnd:
return-object v0
;9
.line 90
;11
.catch Ljava/lang/Exception; from tryStart: to tryEnd: using handler:
@ -399,6 +407,8 @@ SparseSwitch:
.method public onCreate(Landroid/os/Bundle;)V
.registers 6
.line 1
invoke-super {v4,v5}, android/app/Activity/onCreate(Landroid/os/Bundle;)V
const-string v3, "\n"
@ -406,6 +416,8 @@ SparseSwitch:
new-instance v0, Landroid/widget/TextView;
invoke-direct {v0,v4}, android/widget/TextView/<init>(Landroid/content/Context;)V
.line 3
iget-object v1, v4, org/JesusFreke/HelloWorld2/HelloWorld2/helloWorld Ljava/lang/String;
invoke-virtual {v1, v3}, java/lang/String/concat(Ljava/lang/String;)Ljava/lang/String;