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