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