/* * 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("}"); } }