/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.dx.command.dump;
import com.android.dx.cf.code.ConcreteMethod;
import com.android.dx.cf.code.Ropper;
import com.android.dx.cf.direct.DirectClassFile;
import com.android.dx.cf.direct.StdAttributeFactory;
import com.android.dx.cf.iface.Member;
import com.android.dx.cf.iface.Method;
import com.android.dx.cf.iface.ParseObserver;
import com.android.dx.rop.code.AccessFlags;
import com.android.dx.rop.code.BasicBlock;
import com.android.dx.rop.code.BasicBlockList;
import com.android.dx.rop.code.DexTranslationAdvice;
import com.android.dx.rop.code.RopMethod;
import com.android.dx.rop.code.TranslationAdvice;
import com.android.dx.ssa.Optimizer;
import com.android.dx.util.ByteArray;
import com.android.dx.util.Hex;
import com.android.dx.util.IntList;
/**
* Dumps the pred/succ graph of methods into a format compatible
* with the popular graph utility "dot".
*/
public class DotDumper implements ParseObserver {
private DirectClassFile classFile;
private final byte[] bytes;
private final String filePath;
private final boolean strictParse;
private final boolean optimize;
private final Args args;
static void dump(byte[] bytes, String filePath, Args args) {
new DotDumper(bytes, filePath, args).run();
}
DotDumper(byte[] bytes, String filePath, Args args) {
this.bytes = bytes;
this.filePath = filePath;
this.strictParse = args.strictParse;
this.optimize = args.optimize;
this.args = args;
}
private void run() {
ByteArray ba = new ByteArray(bytes);
/*
* First, parse the file completely, so we can safely refer to
* attributes, etc.
*/
classFile = new DirectClassFile(ba, filePath, strictParse);
classFile.setAttributeFactory(StdAttributeFactory.THE_ONE);
classFile.getMagic(); // Force parsing to happen.
// Next, reparse it and observe the process.
DirectClassFile liveCf =
new DirectClassFile(ba, filePath, strictParse);
liveCf.setAttributeFactory(StdAttributeFactory.THE_ONE);
liveCf.setObserver(this);
liveCf.getMagic(); // Force parsing to happen.
}
/**
* @param name method name
* @return true if this method should be dumped
*/
protected boolean shouldDumpMethod(String name) {
return args.method == null || args.method.equals(name);
}
public void changeIndent(int indentDelta) {
// This space intentionally left blank.
}
public void parsed(ByteArray bytes, int offset, int len, String human) {
// This space intentionally left blank.
}
/** {@inheritDoc} */
public void startParsingMember(ByteArray bytes, int offset, String name,
String descriptor) {
// This space intentionally left blank.
}
public void endParsingMember(ByteArray bytes, int offset, String name,
String descriptor, Member member) {
if (!(member instanceof Method)) {
return;
}
if (!shouldDumpMethod(name)) {
return;
}
ConcreteMethod meth = new ConcreteMethod((Method) member, classFile,
true, true);
TranslationAdvice advice = DexTranslationAdvice.THE_ONE;
RopMethod rmeth =
Ropper.convert(meth, advice, classFile.getMethods());
if (optimize) {
boolean isStatic = AccessFlags.isStatic(meth.getAccessFlags());
rmeth = Optimizer.optimize(rmeth,
BaseDumper.computeParamWidth(meth, isStatic), isStatic,
true, advice);
}
System.out.println("digraph " + name + "{");
System.out.println("\tfirst -> n"
+ Hex.u2(rmeth.getFirstLabel()) + ";");
BasicBlockList blocks = rmeth.getBlocks();
int sz = blocks.size();
for (int i = 0; i < sz; i++) {
BasicBlock bb = blocks.get(i);
int label = bb.getLabel();
IntList successors = bb.getSuccessors();
if (successors.size() == 0) {
System.out.println("\tn" + Hex.u2(label) + " -> returns;");
} else if (successors.size() == 1) {
System.out.println("\tn" + Hex.u2(label) + " -> n"
+ Hex.u2(successors.get(0)) + ";");
} else {
System.out.print("\tn" + Hex.u2(label) + " -> {");
for (int j = 0; j < successors.size(); j++ ) {
int successor = successors.get(j);
if (successor != bb.getPrimarySuccessor()) {
System.out.print(" n" + Hex.u2(successor) + " ");
}
}
System.out.println("};");
System.out.println("\tn" + Hex.u2(label) + " -> n"
+ Hex.u2(bb.getPrimarySuccessor())
+ " [label=\"primary\"];");
}
}
System.out.println("}");
}
}
|