1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.dx.command.dump; 18 19 import com.android.dx.cf.code.ConcreteMethod; 20 import com.android.dx.cf.code.Ropper; 21 import com.android.dx.cf.direct.DirectClassFile; 22 import com.android.dx.cf.direct.StdAttributeFactory; 23 import com.android.dx.cf.iface.Member; 24 import com.android.dx.cf.iface.Method; 25 import com.android.dx.cf.iface.ParseObserver; 26 import com.android.dx.dex.DexOptions; 27 import com.android.dx.rop.code.AccessFlags; 28 import com.android.dx.rop.code.BasicBlock; 29 import com.android.dx.rop.code.BasicBlockList; 30 import com.android.dx.rop.code.DexTranslationAdvice; 31 import com.android.dx.rop.code.RopMethod; 32 import com.android.dx.rop.code.TranslationAdvice; 33 import com.android.dx.ssa.Optimizer; 34 import com.android.dx.util.ByteArray; 35 import com.android.dx.util.Hex; 36 import com.android.dx.util.IntList; 37 38 /** 39 * Dumps the pred/succ graph of methods into a format compatible 40 * with the popular graph utility "dot". 41 */ 42 public class DotDumper implements ParseObserver { 43 private DirectClassFile classFile; 44 45 private final byte[] bytes; 46 private final String filePath; 47 private final boolean strictParse; 48 private final boolean optimize; 49 private final Args args; 50 private final DexOptions dexOptions; 51 52 static void dump(byte[] bytes, String filePath, Args args) { 53 new DotDumper(bytes, filePath, args).run(); 54 } 55 56 DotDumper(byte[] bytes, String filePath, Args args) { 57 this.bytes = bytes; 58 this.filePath = filePath; 59 this.strictParse = args.strictParse; 60 this.optimize = args.optimize; 61 this.args = args; 62 this.dexOptions = new DexOptions(); 63 } 64 65 private void run() { 66 ByteArray ba = new ByteArray(bytes); 67 68 /* 69 * First, parse the file completely, so we can safely refer to 70 * attributes, etc. 71 */ 72 classFile = new DirectClassFile(ba, filePath, strictParse); 73 classFile.setAttributeFactory(StdAttributeFactory.THE_ONE); 74 classFile.getMagic(); // Force parsing to happen. 75 76 // Next, reparse it and observe the process. 77 DirectClassFile liveCf = 78 new DirectClassFile(ba, filePath, strictParse); 79 liveCf.setAttributeFactory(StdAttributeFactory.THE_ONE); 80 liveCf.setObserver(this); 81 liveCf.getMagic(); // Force parsing to happen. 82 } 83 84 /** 85 * @param name method name 86 * @return true if this method should be dumped 87 */ 88 protected boolean shouldDumpMethod(String name) { 89 return args.method == null || args.method.equals(name); 90 } 91 92 @Override 93 public void changeIndent(int indentDelta) { 94 // This space intentionally left blank. 95 } 96 97 @Override 98 public void parsed(ByteArray bytes, int offset, int len, String human) { 99 // This space intentionally left blank. 100 } 101 102 /** {@inheritDoc} */ 103 @Override 104 public void startParsingMember(ByteArray bytes, int offset, String name, 105 String descriptor) { 106 // This space intentionally left blank. 107 } 108 109 @Override 110 public void endParsingMember(ByteArray bytes, int offset, String name, 111 String descriptor, Member member) { 112 if (!(member instanceof Method)) { 113 return; 114 } 115 116 if (!shouldDumpMethod(name)) { 117 return; 118 } 119 120 ConcreteMethod meth = new ConcreteMethod((Method) member, classFile, 121 true, true); 122 123 TranslationAdvice advice = DexTranslationAdvice.THE_ONE; 124 RopMethod rmeth = 125 Ropper.convert(meth, advice, classFile.getMethods(), dexOptions); 126 127 if (optimize) { 128 boolean isStatic = AccessFlags.isStatic(meth.getAccessFlags()); 129 rmeth = Optimizer.optimize(rmeth, 130 BaseDumper.computeParamWidth(meth, isStatic), isStatic, 131 true, advice); 132 } 133 134 System.out.println("digraph " + name + "{"); 135 136 System.out.println("\tfirst -> n" 137 + Hex.u2(rmeth.getFirstLabel()) + ";"); 138 139 BasicBlockList blocks = rmeth.getBlocks(); 140 141 int sz = blocks.size(); 142 for (int i = 0; i < sz; i++) { 143 BasicBlock bb = blocks.get(i); 144 int label = bb.getLabel(); 145 IntList successors = bb.getSuccessors(); 146 147 if (successors.size() == 0) { 148 System.out.println("\tn" + Hex.u2(label) + " -> returns;"); 149 } else if (successors.size() == 1) { 150 System.out.println("\tn" + Hex.u2(label) + " -> n" 151 + Hex.u2(successors.get(0)) + ";"); 152 } else { 153 System.out.print("\tn" + Hex.u2(label) + " -> {"); 154 for (int j = 0; j < successors.size(); j++ ) { 155 int successor = successors.get(j); 156 157 if (successor != bb.getPrimarySuccessor()) { 158 System.out.print(" n" + Hex.u2(successor) + " "); 159 } 160 161 } 162 System.out.println("};"); 163 164 System.out.println("\tn" + Hex.u2(label) + " -> n" 165 + Hex.u2(bb.getPrimarySuccessor()) 166 + " [label=\"primary\"];"); 167 168 169 } 170 } 171 172 System.out.println("}"); 173 } 174 } 175