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