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