• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.BasicBlocker;
20 import com.android.dx.cf.code.ByteBlock;
21 import com.android.dx.cf.code.ByteBlockList;
22 import com.android.dx.cf.code.ByteCatchList;
23 import com.android.dx.cf.code.BytecodeArray;
24 import com.android.dx.cf.code.ConcreteMethod;
25 import com.android.dx.cf.code.Ropper;
26 import com.android.dx.cf.direct.CodeObserver;
27 import com.android.dx.cf.direct.DirectClassFile;
28 import com.android.dx.cf.direct.StdAttributeFactory;
29 import com.android.dx.cf.iface.Member;
30 import com.android.dx.cf.iface.Method;
31 import com.android.dx.rop.code.AccessFlags;
32 import com.android.dx.rop.code.BasicBlock;
33 import com.android.dx.rop.code.BasicBlockList;
34 import com.android.dx.rop.code.DexTranslationAdvice;
35 import com.android.dx.rop.code.Insn;
36 import com.android.dx.rop.code.InsnList;
37 import com.android.dx.rop.code.RopMethod;
38 import com.android.dx.rop.code.TranslationAdvice;
39 import com.android.dx.rop.cst.CstType;
40 import com.android.dx.ssa.Optimizer;
41 import com.android.dx.util.ByteArray;
42 import com.android.dx.util.Hex;
43 import com.android.dx.util.IntList;
44 import java.io.PrintStream;
45 
46 /**
47  * Utility to dump basic block info from methods in a human-friendly form.
48  */
49 public class BlockDumper
50         extends BaseDumper {
51     /** whether or not to registerize (make rop blocks) */
52     private final boolean rop;
53 
54     /**
55      * {@code null-ok;} the class file object being constructed;
56      * becomes non-null during {@link #dump}
57      */
58     protected DirectClassFile classFile;
59 
60     /** whether or not to suppress dumping */
61     protected boolean suppressDump;
62 
63     /** whether this is the first method being dumped */
64     private boolean first;
65 
66     /** whether or not to run the ssa optimziations */
67     private final boolean optimize;
68 
69     /**
70      * Dumps the given array, interpreting it as a class file and dumping
71      * methods with indications of block-level stuff.
72      *
73      * @param bytes {@code non-null;} bytes of the (alleged) class file
74      * @param out {@code non-null;} where to dump to
75      * @param filePath the file path for the class, excluding any base
76      * directory specification
77      * @param rop whether or not to registerize (make rop blocks)
78      * @param args commandline parsedArgs
79      */
dump(byte[] bytes, PrintStream out, String filePath, boolean rop, Args args)80     public static void dump(byte[] bytes, PrintStream out,
81             String filePath, boolean rop, Args args) {
82         BlockDumper bd = new BlockDumper(bytes, out, filePath,
83                 rop, args);
84         bd.dump();
85     }
86 
87     /**
88      * Constructs an instance. This class is not publicly instantiable.
89      * Use {@link #dump}.
90      */
BlockDumper(byte[] bytes, PrintStream out, String filePath, boolean rop, Args args)91     BlockDumper(byte[] bytes, PrintStream out, String filePath,
92             boolean rop, Args args) {
93         super(bytes, out, filePath, args);
94 
95         this.rop = rop;
96         this.classFile = null;
97         this.suppressDump = true;
98         this.first = true;
99         this.optimize = args.optimize;
100     }
101 
102     /**
103      * Does the dumping.
104      */
dump()105     public void dump() {
106         byte[] bytes = getBytes();
107         ByteArray ba = new ByteArray(bytes);
108 
109         /*
110          * First, parse the file completely, so we can safely refer to
111          * attributes, etc.
112          */
113         classFile = new DirectClassFile(ba, getFilePath(), getStrictParse());
114         classFile.setAttributeFactory(StdAttributeFactory.THE_ONE);
115         classFile.getMagic(); // Force parsing to happen.
116 
117         // Next, reparse it and observe the process.
118         DirectClassFile liveCf =
119             new DirectClassFile(ba, getFilePath(), getStrictParse());
120         liveCf.setAttributeFactory(StdAttributeFactory.THE_ONE);
121         liveCf.setObserver(this);
122         liveCf.getMagic(); // Force parsing to happen.
123     }
124 
125     /** {@inheritDoc} */
126     @Override
changeIndent(int indentDelta)127     public void changeIndent(int indentDelta) {
128         if (!suppressDump) {
129             super.changeIndent(indentDelta);
130         }
131     }
132 
133     /** {@inheritDoc} */
134     @Override
parsed(ByteArray bytes, int offset, int len, String human)135     public void parsed(ByteArray bytes, int offset, int len, String human) {
136         if (!suppressDump) {
137             super.parsed(bytes, offset, len, human);
138         }
139     }
140 
141     /**
142      * @param name method name
143      * @return true if this method should be dumped
144      */
shouldDumpMethod(String name)145     protected boolean shouldDumpMethod(String name) {
146         return args.method == null || args.method.equals(name);
147     }
148 
149     /** {@inheritDoc} */
150     @Override
startParsingMember(ByteArray bytes, int offset, String name, String descriptor)151     public void startParsingMember(ByteArray bytes, int offset, String name,
152             String descriptor) {
153         if (descriptor.indexOf('(') < 0) {
154             // It's a field, not a method
155             return;
156         }
157 
158         if (!shouldDumpMethod(name)) {
159             return;
160         }
161 
162         suppressDump = false;
163 
164         if (first) {
165             first = false;
166         } else {
167             parsed(bytes, offset, 0, "\n");
168         }
169 
170         parsed(bytes, offset, 0, "method " + name + " " + descriptor);
171         suppressDump = true;
172     }
173 
174     /** {@inheritDoc} */
175     @Override
endParsingMember(ByteArray bytes, int offset, String name, String descriptor, Member member)176     public void endParsingMember(ByteArray bytes, int offset, String name,
177             String descriptor, Member member) {
178         if (!(member instanceof Method)) {
179             return;
180         }
181 
182         if (!shouldDumpMethod(name)) {
183             return;
184         }
185 
186         if ((member.getAccessFlags() & (AccessFlags.ACC_ABSTRACT |
187                 AccessFlags.ACC_NATIVE)) != 0) {
188             return;
189         }
190 
191         ConcreteMethod meth =
192             new ConcreteMethod((Method) member, classFile, true, true);
193 
194         if (rop) {
195             ropDump(meth);
196         } else {
197             regularDump(meth);
198         }
199     }
200 
201     /**
202      * Does a regular basic block dump.
203      *
204      * @param meth {@code non-null;} method data to dump
205      */
regularDump(ConcreteMethod meth)206     private void regularDump(ConcreteMethod meth) {
207         BytecodeArray code = meth.getCode();
208         ByteArray bytes = code.getBytes();
209         ByteBlockList list = BasicBlocker.identifyBlocks(meth);
210         int sz = list.size();
211         CodeObserver codeObserver = new CodeObserver(bytes, BlockDumper.this);
212 
213         suppressDump = false;
214 
215         int byteAt = 0;
216         for (int i = 0; i < sz; i++) {
217             ByteBlock bb = list.get(i);
218             int start = bb.getStart();
219             int end = bb.getEnd();
220 
221             if (byteAt < start) {
222                 parsed(bytes, byteAt, start - byteAt,
223                        "dead code " + Hex.u2(byteAt) + ".." + Hex.u2(start));
224             }
225 
226             parsed(bytes, start, 0,
227                     "block " + Hex.u2(bb.getLabel()) + ": " +
228                     Hex.u2(start) + ".." + Hex.u2(end));
229             changeIndent(1);
230 
231             int len;
232             for (int j = start; j < end; j += len) {
233                 len = code.parseInstruction(j, codeObserver);
234                 codeObserver.setPreviousOffset(j);
235             }
236 
237             IntList successors = bb.getSuccessors();
238             int ssz = successors.size();
239             if (ssz == 0) {
240                 parsed(bytes, end, 0, "returns");
241             } else {
242                 for (int j = 0; j < ssz; j++) {
243                     int succ = successors.get(j);
244                     parsed(bytes, end, 0, "next " + Hex.u2(succ));
245                 }
246             }
247 
248             ByteCatchList catches = bb.getCatches();
249             int csz = catches.size();
250             for (int j = 0; j < csz; j++) {
251                 ByteCatchList.Item one = catches.get(j);
252                 CstType exceptionClass = one.getExceptionClass();
253                 parsed(bytes, end, 0,
254                        "catch " +
255                        ((exceptionClass == CstType.OBJECT) ? "<any>" :
256                         exceptionClass.toHuman()) + " -> " +
257                        Hex.u2(one.getHandlerPc()));
258             }
259 
260             changeIndent(-1);
261             byteAt = end;
262         }
263 
264         int end = bytes.size();
265         if (byteAt < end) {
266             parsed(bytes, byteAt, end - byteAt,
267                     "dead code " + Hex.u2(byteAt) + ".." + Hex.u2(end));
268         }
269 
270         suppressDump = true;
271     }
272 
273     /**
274      * Does a registerizing dump.
275      *
276      * @param meth {@code non-null;} method data to dump
277      */
ropDump(ConcreteMethod meth)278     private void ropDump(ConcreteMethod meth) {
279         TranslationAdvice advice = DexTranslationAdvice.THE_ONE;
280         BytecodeArray code = meth.getCode();
281         ByteArray bytes = code.getBytes();
282         RopMethod rmeth = Ropper.convert(meth, advice, classFile.getMethods(), dexOptions);
283         StringBuilder sb = new StringBuilder(2000);
284 
285         if (optimize) {
286             boolean isStatic = AccessFlags.isStatic(meth.getAccessFlags());
287             int paramWidth = computeParamWidth(meth, isStatic);
288             rmeth =
289                 Optimizer.optimize(rmeth, paramWidth, isStatic, true, advice);
290         }
291 
292         BasicBlockList blocks = rmeth.getBlocks();
293         int[] order = blocks.getLabelsInOrder();
294 
295         sb.append("first " + Hex.u2(rmeth.getFirstLabel()) + "\n");
296 
297         for (int label : order) {
298             BasicBlock bb = blocks.get(blocks.indexOfLabel(label));
299             sb.append("block ");
300             sb.append(Hex.u2(label));
301             sb.append("\n");
302 
303             IntList preds = rmeth.labelToPredecessors(label);
304             int psz = preds.size();
305             for (int i = 0; i < psz; i++) {
306                 sb.append("  pred ");
307                 sb.append(Hex.u2(preds.get(i)));
308                 sb.append("\n");
309             }
310 
311             InsnList il = bb.getInsns();
312             int ilsz = il.size();
313             for (int i = 0; i < ilsz; i++) {
314                 Insn one = il.get(i);
315                 sb.append("  ");
316                 sb.append(il.get(i).toHuman());
317                 sb.append("\n");
318             }
319 
320             IntList successors = bb.getSuccessors();
321             int ssz = successors.size();
322             if (ssz == 0) {
323                 sb.append("  returns\n");
324             } else {
325                 int primary = bb.getPrimarySuccessor();
326                 for (int i = 0; i < ssz; i++) {
327                     int succ = successors.get(i);
328                     sb.append("  next ");
329                     sb.append(Hex.u2(succ));
330 
331                     if ((ssz != 1) && (succ == primary)) {
332                         sb.append(" *");
333                     }
334 
335                     sb.append("\n");
336                 }
337             }
338         }
339 
340         suppressDump = false;
341         parsed(bytes, 0, bytes.size(), sb.toString());
342         suppressDump = true;
343     }
344 }
345