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