BlockDumperpublic class BlockDumper extends BaseDumper Utility to dump basic block info from methods in a human-friendly form. |
Fields Summary |
---|
private boolean | ropwhether or not to registerize (make rop blocks) | protected com.android.dx.cf.direct.DirectClassFile | classFile{@code null-ok;} the class file object being constructed;
becomes non-null during {@link #dump} | protected boolean | suppressDumpwhether or not to suppress dumping | private boolean | firstwhether this is the first method being dumped | private boolean | optimizewhether or not to run the ssa optimziations |
Constructors Summary |
---|
BlockDumper(byte[] bytes, PrintStream out, String filePath, boolean rop, Args args)Constructs an instance. This class is not publicly instantiable.
Use {@link #dump}.
super(bytes, out, filePath, args);
this.rop = rop;
this.classFile = null;
this.suppressDump = true;
this.first = true;
this.optimize = args.optimize;
|
Methods Summary |
---|
public void | changeIndent(int indentDelta){@inheritDoc}
if (!suppressDump) {
super.changeIndent(indentDelta);
}
| public static void | dump(byte[] bytes, java.io.PrintStream out, java.lang.String filePath, boolean rop, Args args)Dumps the given array, interpreting it as a class file and dumping
methods with indications of block-level stuff.
BlockDumper bd = new BlockDumper(bytes, out, filePath,
rop, args);
bd.dump();
| public void | dump()Does the dumping.
byte[] bytes = getBytes();
ByteArray ba = new ByteArray(bytes);
/*
* First, parse the file completely, so we can safely refer to
* attributes, etc.
*/
classFile = new DirectClassFile(ba, getFilePath(), getStrictParse());
classFile.setAttributeFactory(StdAttributeFactory.THE_ONE);
classFile.getMagic(); // Force parsing to happen.
// Next, reparse it and observe the process.
DirectClassFile liveCf =
new DirectClassFile(ba, getFilePath(), getStrictParse());
liveCf.setAttributeFactory(StdAttributeFactory.THE_ONE);
liveCf.setObserver(this);
liveCf.getMagic(); // Force parsing to happen.
| public void | endParsingMember(com.android.dx.util.ByteArray bytes, int offset, java.lang.String name, java.lang.String descriptor, com.android.dx.cf.iface.Member member){@inheritDoc}
if (!(member instanceof Method)) {
return;
}
if (!shouldDumpMethod(name)) {
return;
}
if ((member.getAccessFlags() & (AccessFlags.ACC_ABSTRACT |
AccessFlags.ACC_NATIVE)) != 0) {
return;
}
ConcreteMethod meth =
new ConcreteMethod((Method) member, classFile, true, true);
if (rop) {
ropDump(meth);
} else {
regularDump(meth);
}
| public void | parsed(com.android.dx.util.ByteArray bytes, int offset, int len, java.lang.String human){@inheritDoc}
if (!suppressDump) {
super.parsed(bytes, offset, len, human);
}
| private void | regularDump(com.android.dx.cf.code.ConcreteMethod meth)Does a regular basic block dump.
BytecodeArray code = meth.getCode();
ByteArray bytes = code.getBytes();
ByteBlockList list = BasicBlocker.identifyBlocks(meth);
int sz = list.size();
CodeObserver codeObserver = new CodeObserver(bytes, BlockDumper.this);
// Reset the dump cursor to the start of the bytecode.
setAt(bytes, 0);
suppressDump = false;
int byteAt = 0;
for (int i = 0; i < sz; i++) {
ByteBlock bb = list.get(i);
int start = bb.getStart();
int end = bb.getEnd();
if (byteAt < start) {
parsed(bytes, byteAt, start - byteAt,
"dead code " + Hex.u2(byteAt) + ".." + Hex.u2(start));
}
parsed(bytes, start, 0,
"block " + Hex.u2(bb.getLabel()) + ": " +
Hex.u2(start) + ".." + Hex.u2(end));
changeIndent(1);
int len;
for (int j = start; j < end; j += len) {
len = code.parseInstruction(j, codeObserver);
codeObserver.setPreviousOffset(j);
}
IntList successors = bb.getSuccessors();
int ssz = successors.size();
if (ssz == 0) {
parsed(bytes, end, 0, "returns");
} else {
for (int j = 0; j < ssz; j++) {
int succ = successors.get(j);
parsed(bytes, end, 0, "next " + Hex.u2(succ));
}
}
ByteCatchList catches = bb.getCatches();
int csz = catches.size();
for (int j = 0; j < csz; j++) {
ByteCatchList.Item one = catches.get(j);
CstType exceptionClass = one.getExceptionClass();
parsed(bytes, end, 0,
"catch " +
((exceptionClass == CstType.OBJECT) ? "<any>" :
exceptionClass.toHuman()) + " -> " +
Hex.u2(one.getHandlerPc()));
}
changeIndent(-1);
byteAt = end;
}
int end = bytes.size();
if (byteAt < end) {
parsed(bytes, byteAt, end - byteAt,
"dead code " + Hex.u2(byteAt) + ".." + Hex.u2(end));
}
suppressDump = true;
| private void | ropDump(com.android.dx.cf.code.ConcreteMethod meth)Does a registerizing dump.
TranslationAdvice advice = DexTranslationAdvice.THE_ONE;
BytecodeArray code = meth.getCode();
ByteArray bytes = code.getBytes();
RopMethod rmeth = Ropper.convert(meth, advice, classFile.getMethods());
StringBuffer sb = new StringBuffer(2000);
if (optimize) {
boolean isStatic = AccessFlags.isStatic(meth.getAccessFlags());
int paramWidth = computeParamWidth(meth, isStatic);
rmeth =
Optimizer.optimize(rmeth, paramWidth, isStatic, true, advice);
}
BasicBlockList blocks = rmeth.getBlocks();
int[] order = blocks.getLabelsInOrder();
sb.append("first " + Hex.u2(rmeth.getFirstLabel()) + "\n");
for (int label : order) {
BasicBlock bb = blocks.get(blocks.indexOfLabel(label));
sb.append("block ");
sb.append(Hex.u2(label));
sb.append("\n");
IntList preds = rmeth.labelToPredecessors(label);
int psz = preds.size();
for (int i = 0; i < psz; i++) {
sb.append(" pred ");
sb.append(Hex.u2(preds.get(i)));
sb.append("\n");
}
InsnList il = bb.getInsns();
int ilsz = il.size();
for (int i = 0; i < ilsz; i++) {
Insn one = il.get(i);
sb.append(" ");
sb.append(il.get(i).toHuman());
sb.append("\n");
}
IntList successors = bb.getSuccessors();
int ssz = successors.size();
if (ssz == 0) {
sb.append(" returns\n");
} else {
int primary = bb.getPrimarySuccessor();
for (int i = 0; i < ssz; i++) {
int succ = successors.get(i);
sb.append(" next ");
sb.append(Hex.u2(succ));
if ((ssz != 1) && (succ == primary)) {
sb.append(" *");
}
sb.append("\n");
}
}
}
suppressDump = false;
setAt(bytes, 0);
parsed(bytes, 0, bytes.size(), sb.toString());
suppressDump = true;
| protected boolean | shouldDumpMethod(java.lang.String name)
return args.method == null || args.method.equals(name);
| public void | startParsingMember(com.android.dx.util.ByteArray bytes, int offset, java.lang.String name, java.lang.String descriptor){@inheritDoc}
if (descriptor.indexOf('(") < 0) {
// It's a field, not a method
return;
}
if (!shouldDumpMethod(name)) {
return;
}
// Reset the dump cursor to the start of the method.
setAt(bytes, offset);
suppressDump = false;
if (first) {
first = false;
} else {
parsed(bytes, offset, 0, "\n");
}
parsed(bytes, offset, 0, "method " + name + " " + descriptor);
suppressDump = true;
|
|