summaryrefslogtreecommitdiff
path: root/dx/src/com/android/dx/command/dump/DotDumper.java
diff options
context:
space:
mode:
Diffstat (limited to 'dx/src/com/android/dx/command/dump/DotDumper.java')
-rw-r--r--dx/src/com/android/dx/command/dump/DotDumper.java167
1 files changed, 167 insertions, 0 deletions
diff --git a/dx/src/com/android/dx/command/dump/DotDumper.java b/dx/src/com/android/dx/command/dump/DotDumper.java
new file mode 100644
index 0000000..9de48fc
--- /dev/null
+++ b/dx/src/com/android/dx/command/dump/DotDumper.java
@@ -0,0 +1,167 @@
+/*
+ * 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.BasicBlock;
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.DexTranslationAdvice;
+import com.android.dx.rop.code.TranslationAdvice;
+import com.android.dx.rop.code.AccessFlags;
+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);
+
+ 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("}");
+ }
+}