Home | History | Annotate | Download | only in dump
      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