Home | History | Annotate | Download | only in Debug
      1 /*
      2  * [The "BSD licence"]
      3  * Copyright (c) 2010 Ben Gruver (JesusFreke)
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. The name of the author may not be used to endorse or promote products
     15  *    derived from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 package org.jf.dexlib.Debug;
     30 
     31 import org.jf.dexlib.DebugInfoItem;
     32 import org.jf.dexlib.DexFile;
     33 import org.jf.dexlib.StringIdItem;
     34 import org.jf.dexlib.TypeIdItem;
     35 import org.jf.dexlib.Util.ByteArrayInput;
     36 import org.jf.dexlib.Util.Input;
     37 
     38 public class DebugInstructionIterator {
     39     /**
     40      * This method decodes the debug instructions in the given byte array and iterates over them, calling
     41      * the ProcessDebugInstructionDelegate.ProcessDebugInstruction method for each instruction
     42      * @param in an Input object that the debug instructions can be read from
     43      * @param processDebugInstruction a <code>ProcessDebugInstructionDelegate</code> object that gets called
     44      * for each instruction that is encountered
     45      */
     46     public static void IterateInstructions(Input in, ProcessRawDebugInstructionDelegate processDebugInstruction) {
     47         int startDebugOffset;
     48 
     49         while(true)
     50         {
     51             startDebugOffset = in.getCursor();
     52             byte debugOpcode = in.readByte();
     53 
     54             switch (debugOpcode) {
     55                 case 0x00:
     56                 {
     57                     processDebugInstruction.ProcessEndSequence(startDebugOffset);
     58                     return;
     59                 }
     60                 case 0x01:
     61                 {
     62                     int codeAddressDiff = in.readUnsignedLeb128();
     63                     processDebugInstruction.ProcessAdvancePC(startDebugOffset, in.getCursor() - startDebugOffset,
     64                             codeAddressDiff);
     65                     break;
     66                 }
     67                 case 0x02:
     68                 {
     69                     int lineDiff = in.readSignedLeb128();
     70                     processDebugInstruction.ProcessAdvanceLine(startDebugOffset, in.getCursor() - startDebugOffset,
     71                             lineDiff);
     72                     break;
     73                 }
     74                 case 0x03:
     75                 {
     76                     int registerNum = in.readUnsignedOrSignedLeb128();
     77                     boolean isSignedRegister = false;
     78                     if (registerNum < 0) {
     79                         isSignedRegister = true;
     80                         registerNum = ~registerNum;
     81                     }
     82                     int nameIndex = in.readUnsignedLeb128() - 1;
     83                     int typeIndex = in.readUnsignedLeb128() - 1;
     84                     processDebugInstruction.ProcessStartLocal(startDebugOffset, in.getCursor() - startDebugOffset,
     85                             registerNum, nameIndex, typeIndex, isSignedRegister);
     86                     break;
     87                 }
     88                 case 0x04:
     89                 {
     90                     int registerNum = in.readUnsignedOrSignedLeb128();
     91                     boolean isSignedRegister = false;
     92                     if (registerNum < 0) {
     93                         isSignedRegister = true;
     94                         registerNum = ~registerNum;
     95                     }
     96                     int nameIndex = in.readUnsignedLeb128() - 1;
     97                     int typeIndex = in.readUnsignedLeb128() - 1;
     98                     int signatureIndex = in.readUnsignedLeb128() - 1;
     99                     processDebugInstruction.ProcessStartLocalExtended(startDebugOffset,
    100                             in.getCursor() - startDebugOffset, registerNum, nameIndex, typeIndex, signatureIndex,
    101                             isSignedRegister);
    102                     break;
    103                 }
    104                 case 0x05:
    105                 {
    106                     int registerNum = in.readUnsignedOrSignedLeb128();
    107                     boolean isSignedRegister = false;
    108                     if (registerNum < 0) {
    109                         isSignedRegister = true;
    110                         registerNum = ~registerNum;
    111                     }
    112                     processDebugInstruction.ProcessEndLocal(startDebugOffset, in.getCursor() - startDebugOffset,
    113                             registerNum, isSignedRegister);
    114                     break;
    115                 }
    116                 case 0x06:
    117                 {
    118                     int registerNum = in.readUnsignedOrSignedLeb128();
    119                     boolean isSignedRegister = false;
    120                     if (registerNum < 0) {
    121                         isSignedRegister = true;
    122                         registerNum = ~registerNum;
    123                     }
    124                     processDebugInstruction.ProcessRestartLocal(startDebugOffset, in.getCursor() - startDebugOffset,
    125                             registerNum, isSignedRegister);
    126                     break;
    127                 }
    128                 case 0x07:
    129                 {
    130                     processDebugInstruction.ProcessSetPrologueEnd(startDebugOffset);
    131                     break;
    132                 }
    133                 case 0x08:
    134                 {
    135                     processDebugInstruction.ProcessSetEpilogueBegin(startDebugOffset);
    136                     break;
    137                 }
    138                 case 0x09:
    139                 {
    140                     int nameIndex = in.readUnsignedLeb128();
    141                     processDebugInstruction.ProcessSetFile(startDebugOffset, in.getCursor() - startDebugOffset,
    142                             nameIndex);
    143                     break;
    144                 }
    145                 default:
    146                 {
    147                     int base = ((debugOpcode & 0xFF) - 0x0A);
    148                     processDebugInstruction.ProcessSpecialOpcode(startDebugOffset, debugOpcode, (base % 15) - 4, base / 15);
    149                 }
    150             }
    151         }
    152     }
    153 
    154     /**
    155      * This method decodes the debug instructions in the given byte array and iterates over them, calling
    156      * the ProcessDebugInstructionDelegate.ProcessDebugInstruction method for each instruction
    157      * @param debugInfoItem the <code>DebugInfoItem</code> to iterate over
    158      * @param registerCount the number of registers in the method that the given debug info is for
    159      * @param processDecodedDebugInstruction a <code>ProcessDebugInstructionDelegate</code> object that gets called
    160      * for each instruction that is encountered
    161      */
    162     public static void DecodeInstructions(DebugInfoItem debugInfoItem, int registerCount,
    163                                            ProcessDecodedDebugInstructionDelegate processDecodedDebugInstruction) {
    164         int startDebugOffset;
    165         int currentCodeAddress = 0;
    166         int line = debugInfoItem.getLineStart();
    167         Input in = new ByteArrayInput(debugInfoItem.getEncodedDebugInfo());
    168         DexFile dexFile = debugInfoItem.getDexFile();
    169 
    170         Local[] locals = new Local[registerCount];
    171 
    172         while(true)
    173         {
    174             startDebugOffset = in.getCursor();
    175             byte debugOpcode = in.readByte();
    176 
    177             switch (DebugOpcode.getDebugOpcodeByValue(debugOpcode)) {
    178                 case DBG_END_SEQUENCE:
    179                 {
    180                     return;
    181                 }
    182                 case DBG_ADVANCE_PC:
    183                 {
    184                     int codeAddressDiff = in.readUnsignedLeb128();
    185                     currentCodeAddress += codeAddressDiff;
    186                     break;
    187                 }
    188                 case DBG_ADVANCE_LINE:
    189                 {
    190                     int lineDiff = in.readSignedLeb128();
    191                     line += lineDiff;
    192                     break;
    193                 }
    194                 case DBG_START_LOCAL:
    195                 {
    196                     int registerNum = in.readUnsignedLeb128();
    197                     StringIdItem name = dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1);
    198                     TypeIdItem type = dexFile.TypeIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1);
    199                     locals[registerNum] = new Local(registerNum, name, type, null);
    200                     processDecodedDebugInstruction.ProcessStartLocal(currentCodeAddress,
    201                             in.getCursor() - startDebugOffset, registerNum, name, type);
    202                     break;
    203                 }
    204                 case DBG_START_LOCAL_EXTENDED:
    205                 {
    206                     int registerNum = in.readUnsignedLeb128();
    207                     StringIdItem name = dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1);
    208                     TypeIdItem type = dexFile.TypeIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1);
    209                     StringIdItem signature =
    210                             dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1);
    211                     locals[registerNum] = new Local(registerNum, name, type, signature);
    212                     processDecodedDebugInstruction.ProcessStartLocalExtended(currentCodeAddress,
    213                             in.getCursor() - startDebugOffset, registerNum, name, type, signature);
    214                     break;
    215                 }
    216                 case DBG_END_LOCAL:
    217                 {
    218                     int registerNum = in.readUnsignedLeb128();
    219                     Local local = locals[registerNum];
    220                     if (local == null) {
    221                         processDecodedDebugInstruction.ProcessEndLocal(currentCodeAddress, in.getCursor() - startDebugOffset, registerNum,
    222                                 null, null, null);
    223                     } else {
    224                         processDecodedDebugInstruction.ProcessEndLocal(currentCodeAddress, in.getCursor() - startDebugOffset, registerNum,
    225                                 local.name, local.type, local.signature);
    226                     }
    227                     break;
    228                 }
    229                 case DBG_RESTART_LOCAL:
    230                 {
    231                     int registerNum = in.readUnsignedLeb128();
    232                     Local local = locals[registerNum];
    233                     if (local == null) {
    234                         processDecodedDebugInstruction.ProcessRestartLocal(currentCodeAddress, in.getCursor() - startDebugOffset,
    235                                 registerNum, null, null, null);
    236                     } else {
    237                         processDecodedDebugInstruction.ProcessRestartLocal(currentCodeAddress, in.getCursor() - startDebugOffset,
    238                                 registerNum, local.name, local.type, local.signature);
    239                     }
    240 
    241                     break;
    242                 }
    243                 case DBG_SET_PROLOGUE_END:
    244                 {
    245                     processDecodedDebugInstruction.ProcessSetPrologueEnd(currentCodeAddress);
    246                     break;
    247                 }
    248                 case DBG_SET_EPILOGUE_BEGIN:
    249                 {
    250                     processDecodedDebugInstruction.ProcessSetEpilogueBegin(currentCodeAddress);
    251                     break;
    252                 }
    253                 case DBG_SET_FILE:
    254                 {
    255                     StringIdItem name = dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1);
    256                     processDecodedDebugInstruction.ProcessSetFile(currentCodeAddress, in.getCursor() - startDebugOffset, name);
    257                     break;
    258                 }
    259                 case DBG_SPECIAL_OPCODE:
    260                 {
    261                     int base = ((debugOpcode & 0xFF) - 0x0A);
    262                     currentCodeAddress += base / 15;
    263                     line += (base % 15) - 4;
    264                     processDecodedDebugInstruction.ProcessLineEmit(currentCodeAddress, line);
    265                 }
    266             }
    267         }
    268     }
    269 
    270     public static class ProcessRawDebugInstructionDelegate
    271     {
    272         //TODO: add javadocs
    273         public void ProcessEndSequence(int startDebugOffset) {
    274             ProcessStaticOpcode(DebugOpcode.DBG_END_SEQUENCE, startDebugOffset, 1);
    275         }
    276 
    277         public void ProcessAdvancePC(int startDebugOffset, int length, int codeAddressDiff) {
    278             ProcessStaticOpcode(DebugOpcode.DBG_ADVANCE_PC, startDebugOffset, length);
    279         }
    280 
    281         public void ProcessAdvanceLine(int startDebugOffset, int length, int lineDiff) {
    282             ProcessStaticOpcode(DebugOpcode.DBG_ADVANCE_LINE, startDebugOffset, length);
    283         }
    284 
    285         public void ProcessStartLocal(int startDebugOffset, int length, int registerNum, int nameIndex, int typeIndex,
    286                                       boolean registerIsSigned) {
    287         }
    288 
    289         public void ProcessStartLocalExtended(int startDebugOffset, int length, int registerNum, int nameIndex,
    290                                               int typeIndex,int signatureIndex, boolean registerIsSigned) {
    291         }
    292 
    293         public void ProcessEndLocal(int startDebugOffset, int length, int registerNum, boolean registerIsSigned) {
    294             ProcessStaticOpcode(DebugOpcode.DBG_END_LOCAL, startDebugOffset, length);
    295         }
    296 
    297         public void ProcessRestartLocal(int startDebugOffset, int length, int registerNum, boolean registerIsSigned) {
    298             ProcessStaticOpcode(DebugOpcode.DBG_RESTART_LOCAL, startDebugOffset, length);
    299         }
    300 
    301         public void ProcessSetPrologueEnd(int startDebugOffset) {
    302             ProcessStaticOpcode(DebugOpcode.DBG_SET_PROLOGUE_END, startDebugOffset, 1);
    303         }
    304 
    305         public void ProcessSetEpilogueBegin(int startDebugOffset) {
    306             ProcessStaticOpcode(DebugOpcode.DBG_SET_EPILOGUE_BEGIN, startDebugOffset, 1);
    307         }
    308 
    309         public void ProcessSetFile(int startDebugOffset, int length, int nameIndex) {
    310         }
    311 
    312         public void ProcessSpecialOpcode(int startDebugOffset, int debugOpcode, int lineDiff, int codeAddressDiff) {
    313             ProcessStaticOpcode(DebugOpcode.DBG_SPECIAL_OPCODE, startDebugOffset, 1);
    314         }
    315 
    316         public void ProcessStaticOpcode(DebugOpcode debugOpcode, int startDebugOffset, int length) {
    317         }
    318     }
    319 
    320     public static class ProcessDecodedDebugInstructionDelegate
    321     {
    322         public void ProcessStartLocal(int codeAddress, int length, int registerNum, StringIdItem name,
    323                                       TypeIdItem type) {
    324         }
    325 
    326         public void ProcessStartLocalExtended(int codeAddress, int length, int registerNum, StringIdItem name,
    327                                               TypeIdItem type, StringIdItem signature) {
    328         }
    329 
    330         public void ProcessEndLocal(int codeAddress, int length, int registerNum, StringIdItem name, TypeIdItem type,
    331                                     StringIdItem signature) {
    332         }
    333 
    334         public void ProcessRestartLocal(int codeAddress, int length, int registerNum, StringIdItem name,
    335                                         TypeIdItem type, StringIdItem signature) {
    336         }
    337 
    338         public void ProcessSetPrologueEnd(int codeAddress) {
    339         }
    340 
    341         public void ProcessSetEpilogueBegin(int codeAddress) {
    342         }
    343 
    344         public void ProcessSetFile(int codeAddress, int length, StringIdItem name) {
    345         }
    346 
    347         public void ProcessLineEmit(int codeAddress, int line) {
    348         }
    349     }
    350 
    351     private static class Local {
    352         public final int register;
    353         public final StringIdItem name;
    354         public final TypeIdItem type;
    355         public final StringIdItem signature;
    356         public Local(int register, StringIdItem name, TypeIdItem type, StringIdItem signature) {
    357             this.register = register;
    358             this.name = name;
    359             this.type = type;
    360             this.signature = signature;
    361         }
    362 
    363     }
    364 }
    365