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 17package com.android.dx.command.dump; 18 19import com.android.dx.cf.code.ConcreteMethod; 20import com.android.dx.cf.code.Ropper; 21import com.android.dx.cf.direct.DirectClassFile; 22import com.android.dx.cf.direct.StdAttributeFactory; 23import com.android.dx.cf.iface.Member; 24import com.android.dx.cf.iface.Method; 25import com.android.dx.cf.iface.ParseObserver; 26import com.android.dx.rop.code.BasicBlock; 27import com.android.dx.rop.code.BasicBlockList; 28import com.android.dx.rop.code.RopMethod; 29import com.android.dx.rop.code.DexTranslationAdvice; 30import com.android.dx.rop.code.TranslationAdvice; 31import com.android.dx.rop.code.AccessFlags; 32import com.android.dx.ssa.Optimizer; 33import com.android.dx.util.ByteArray; 34import com.android.dx.util.Hex; 35import com.android.dx.util.IntList; 36 37/** 38 * Dumps the pred/succ graph of methods into a format compatible 39 * with the popular graph utility "dot". 40 */ 41public class DotDumper implements ParseObserver { 42 private DirectClassFile classFile; 43 44 private final byte[] bytes; 45 private final String filePath; 46 private final boolean strictParse; 47 private final boolean optimize; 48 private final Args args; 49 50 static void dump(byte[] bytes, String filePath, Args args) { 51 new DotDumper(bytes, filePath, args).run(); 52 } 53 54 DotDumper(byte[] bytes, String filePath, Args args) { 55 this.bytes = bytes; 56 this.filePath = filePath; 57 this.strictParse = args.strictParse; 58 this.optimize = args.optimize; 59 this.args = args; 60 } 61 62 private void run() { 63 ByteArray ba = new ByteArray(bytes); 64 65 /* 66 * First, parse the file completely, so we can safely refer to 67 * attributes, etc. 68 */ 69 classFile = new DirectClassFile(ba, filePath, strictParse); 70 classFile.setAttributeFactory(StdAttributeFactory.THE_ONE); 71 classFile.getMagic(); // Force parsing to happen. 72 73 // Next, reparse it and observe the process. 74 DirectClassFile liveCf = 75 new DirectClassFile(ba, filePath, strictParse); 76 liveCf.setAttributeFactory(StdAttributeFactory.THE_ONE); 77 liveCf.setObserver(this); 78 liveCf.getMagic(); // Force parsing to happen. 79 } 80 81 /** 82 * @param name method name 83 * @return true if this method should be dumped 84 */ 85 protected boolean shouldDumpMethod(String name) { 86 return args.method == null || args.method.equals(name); 87 } 88 89 public void changeIndent(int indentDelta) { 90 // This space intentionally left blank. 91 } 92 93 public void parsed(ByteArray bytes, int offset, int len, String human) { 94 // This space intentionally left blank. 95 } 96 97 /** {@inheritDoc} */ 98 public void startParsingMember(ByteArray bytes, int offset, String name, 99 String descriptor) { 100 // This space intentionally left blank. 101 } 102 103 public void endParsingMember(ByteArray bytes, int offset, String name, 104 String descriptor, Member member) { 105 if (!(member instanceof Method)) { 106 return; 107 } 108 109 if (!shouldDumpMethod(name)) { 110 return; 111 } 112 113 ConcreteMethod meth = new ConcreteMethod((Method) member, classFile, 114 true, true); 115 116 TranslationAdvice advice = DexTranslationAdvice.THE_ONE; 117 RopMethod rmeth = 118 Ropper.convert(meth, advice); 119 120 if (optimize) { 121 boolean isStatic = AccessFlags.isStatic(meth.getAccessFlags()); 122 rmeth = Optimizer.optimize(rmeth, 123 BaseDumper.computeParamWidth(meth, isStatic), isStatic, 124 true, advice); 125 } 126 127 System.out.println("digraph " + name + "{"); 128 129 System.out.println("\tfirst -> n" 130 + Hex.u2(rmeth.getFirstLabel()) + ";"); 131 132 BasicBlockList blocks = rmeth.getBlocks(); 133 134 int sz = blocks.size(); 135 for (int i = 0; i < sz; i++) { 136 BasicBlock bb = blocks.get(i); 137 int label = bb.getLabel(); 138 IntList successors = bb.getSuccessors(); 139 140 if (successors.size() == 0) { 141 System.out.println("\tn" + Hex.u2(label) + " -> returns;"); 142 } else if (successors.size() == 1) { 143 System.out.println("\tn" + Hex.u2(label) + " -> n" 144 + Hex.u2(successors.get(0)) + ";"); 145 } else { 146 System.out.print("\tn" + Hex.u2(label) + " -> {"); 147 for (int j = 0; j < successors.size(); j++ ) { 148 int successor = successors.get(j); 149 150 if (successor != bb.getPrimarySuccessor()) { 151 System.out.print(" n" + Hex.u2(successor) + " "); 152 } 153 154 } 155 System.out.println("};"); 156 157 System.out.println("\tn" + Hex.u2(label) + " -> n" 158 + Hex.u2(bb.getPrimarySuccessor()) 159 + " [label=\"primary\"];"); 160 161 162 } 163 } 164 165 System.out.println("}"); 166 } 167} 168