Home | History | Annotate | Download | only in libdex
      1 /*
      2  * Copyright (C) 2011 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 /*
     18  * Handling of method debug info in a .dex file.
     19  */
     20 
     21 #include "DexDebugInfo.h"
     22 #include "DexProto.h"
     23 #include "Leb128.h"
     24 
     25 #include <stdlib.h>
     26 #include <string.h>
     27 
     28 /*
     29  * Reads a string index as encoded for the debug info format,
     30  * returning a string pointer or NULL as appropriate.
     31  */
     32 static const char* readStringIdx(const DexFile* pDexFile,
     33         const u1** pStream) {
     34     u4 stringIdx = readUnsignedLeb128(pStream);
     35 
     36     // Remember, encoded string indicies have 1 added to them.
     37     if (stringIdx == 0) {
     38         return NULL;
     39     } else {
     40         return dexStringById(pDexFile, stringIdx - 1);
     41     }
     42 }
     43 
     44 /*
     45  * Reads a type index as encoded for the debug info format, returning
     46  * a string pointer for its descriptor or NULL as appropriate.
     47  */
     48 static const char* readTypeIdx(const DexFile* pDexFile,
     49         const u1** pStream) {
     50     u4 typeIdx = readUnsignedLeb128(pStream);
     51 
     52     // Remember, encoded type indicies have 1 added to them.
     53     if (typeIdx == 0) {
     54         return NULL;
     55     } else {
     56         return dexStringByTypeIdx(pDexFile, typeIdx - 1);
     57     }
     58 }
     59 
     60 struct LocalInfo {
     61     const char *name;
     62     const char *descriptor;
     63     const char *signature;
     64     u2 startAddress;
     65     bool live;
     66 };
     67 
     68 static void emitLocalCbIfLive(void *cnxt, int reg, u4 endAddress,
     69         LocalInfo *localInReg, DexDebugNewLocalCb localCb)
     70 {
     71     if (localCb != NULL && localInReg[reg].live) {
     72         localCb(cnxt, reg, localInReg[reg].startAddress, endAddress,
     73                 localInReg[reg].name,
     74                 localInReg[reg].descriptor,
     75                 localInReg[reg].signature == NULL
     76                 ? "" : localInReg[reg].signature );
     77     }
     78 }
     79 
     80 static void invalidStream(const char* classDescriptor, const DexProto* proto) {
     81     IF_ALOGE() {
     82         char* methodDescriptor = dexProtoCopyMethodDescriptor(proto);
     83         ALOGE("Invalid debug info stream. class %s; proto %s",
     84                 classDescriptor, methodDescriptor);
     85         free(methodDescriptor);
     86     }
     87 }
     88 
     89 static void dexDecodeDebugInfo0(
     90             const DexFile* pDexFile,
     91             const DexCode* pCode,
     92             const char* classDescriptor,
     93             u4 protoIdx,
     94             u4 accessFlags,
     95             DexDebugNewPositionCb posCb, DexDebugNewLocalCb localCb,
     96             void* cnxt,
     97             const u1* stream,
     98             LocalInfo* localInReg)
     99 {
    100     DexProto proto = { pDexFile, protoIdx };
    101     u4 line = readUnsignedLeb128(&stream);
    102     u4 parametersSize = readUnsignedLeb128(&stream);
    103     u2 argReg = pCode->registersSize - pCode->insSize;
    104     u4 address = 0;
    105 
    106     if ((accessFlags & ACC_STATIC) == 0) {
    107         /*
    108          * The code is an instance method, which means that there is
    109          * an initial this parameter. Also, the proto list should
    110          * contain exactly one fewer argument word than the insSize
    111          * indicates.
    112          */
    113         assert(pCode->insSize == (dexProtoComputeArgsSize(&proto) + 1));
    114         localInReg[argReg].name = "this";
    115         localInReg[argReg].descriptor = classDescriptor;
    116         localInReg[argReg].startAddress = 0;
    117         localInReg[argReg].live = true;
    118         argReg++;
    119     } else {
    120         assert(pCode->insSize == dexProtoComputeArgsSize(&proto));
    121     }
    122 
    123     DexParameterIterator iterator;
    124     dexParameterIteratorInit(&iterator, &proto);
    125 
    126     while (parametersSize-- != 0) {
    127         const char* descriptor = dexParameterIteratorNextDescriptor(&iterator);
    128         const char *name;
    129         int reg;
    130 
    131         if ((argReg >= pCode->registersSize) || (descriptor == NULL)) {
    132             invalidStream(classDescriptor, &proto);
    133             return;
    134         }
    135 
    136         name = readStringIdx(pDexFile, &stream);
    137         reg = argReg;
    138 
    139         switch (descriptor[0]) {
    140             case 'D':
    141             case 'J':
    142                 argReg += 2;
    143                 break;
    144             default:
    145                 argReg += 1;
    146                 break;
    147         }
    148 
    149         if (name != NULL) {
    150             localInReg[reg].name = name;
    151             localInReg[reg].descriptor = descriptor;
    152             localInReg[reg].signature = NULL;
    153             localInReg[reg].startAddress = address;
    154             localInReg[reg].live = true;
    155         }
    156     }
    157 
    158     for (;;)  {
    159         u1 opcode = *stream++;
    160         u2 reg;
    161 
    162         switch (opcode) {
    163             case DBG_END_SEQUENCE:
    164                 return;
    165 
    166             case DBG_ADVANCE_PC:
    167                 address += readUnsignedLeb128(&stream);
    168                 break;
    169 
    170             case DBG_ADVANCE_LINE:
    171                 line += readSignedLeb128(&stream);
    172                 break;
    173 
    174             case DBG_START_LOCAL:
    175             case DBG_START_LOCAL_EXTENDED:
    176                 reg = readUnsignedLeb128(&stream);
    177                 if (reg > pCode->registersSize) {
    178                     invalidStream(classDescriptor, &proto);
    179                     return;
    180                 }
    181 
    182                 // Emit what was previously there, if anything
    183                 emitLocalCbIfLive(cnxt, reg, address,
    184                     localInReg, localCb);
    185 
    186                 localInReg[reg].name = readStringIdx(pDexFile, &stream);
    187                 localInReg[reg].descriptor = readTypeIdx(pDexFile, &stream);
    188                 if (opcode == DBG_START_LOCAL_EXTENDED) {
    189                     localInReg[reg].signature
    190                         = readStringIdx(pDexFile, &stream);
    191                 } else {
    192                     localInReg[reg].signature = NULL;
    193                 }
    194                 localInReg[reg].startAddress = address;
    195                 localInReg[reg].live = true;
    196                 break;
    197 
    198             case DBG_END_LOCAL:
    199                 reg = readUnsignedLeb128(&stream);
    200                 if (reg > pCode->registersSize) {
    201                     invalidStream(classDescriptor, &proto);
    202                     return;
    203                 }
    204 
    205                 emitLocalCbIfLive (cnxt, reg, address, localInReg, localCb);
    206                 localInReg[reg].live = false;
    207                 break;
    208 
    209             case DBG_RESTART_LOCAL:
    210                 reg = readUnsignedLeb128(&stream);
    211                 if (reg > pCode->registersSize) {
    212                     invalidStream(classDescriptor, &proto);
    213                     return;
    214                 }
    215 
    216                 if (localInReg[reg].name == NULL
    217                         || localInReg[reg].descriptor == NULL) {
    218                     invalidStream(classDescriptor, &proto);
    219                     return;
    220                 }
    221 
    222                 /*
    223                  * If the register is live, the "restart" is superfluous,
    224                  * and we don't want to mess with the existing start address.
    225                  */
    226                 if (!localInReg[reg].live) {
    227                     localInReg[reg].startAddress = address;
    228                     localInReg[reg].live = true;
    229                 }
    230                 break;
    231 
    232             case DBG_SET_PROLOGUE_END:
    233             case DBG_SET_EPILOGUE_BEGIN:
    234             case DBG_SET_FILE:
    235                 break;
    236 
    237             default: {
    238                 int adjopcode = opcode - DBG_FIRST_SPECIAL;
    239 
    240                 address += adjopcode / DBG_LINE_RANGE;
    241                 line += DBG_LINE_BASE + (adjopcode % DBG_LINE_RANGE);
    242 
    243                 if (posCb != NULL) {
    244                     int done;
    245                     done = posCb(cnxt, address, line);
    246 
    247                     if (done) {
    248                         // early exit
    249                         return;
    250                     }
    251                 }
    252                 break;
    253             }
    254         }
    255     }
    256 }
    257 
    258 // TODO optimize localCb == NULL case
    259 void dexDecodeDebugInfo(
    260             const DexFile* pDexFile,
    261             const DexCode* pCode,
    262             const char* classDescriptor,
    263             u4 protoIdx,
    264             u4 accessFlags,
    265             DexDebugNewPositionCb posCb, DexDebugNewLocalCb localCb,
    266             void* cnxt)
    267 {
    268     const u1* stream = dexGetDebugInfoStream(pDexFile, pCode);
    269     LocalInfo localInReg[pCode->registersSize];
    270 
    271     memset(localInReg, 0, sizeof(LocalInfo) * pCode->registersSize);
    272 
    273     if (stream != NULL) {
    274         dexDecodeDebugInfo0(pDexFile, pCode, classDescriptor, protoIdx, accessFlags,
    275             posCb, localCb, cnxt, stream, localInReg);
    276     }
    277 
    278     for (int reg = 0; reg < pCode->registersSize; reg++) {
    279         emitLocalCbIfLive(cnxt, reg, pCode->insnsSize, localInReg, localCb);
    280     }
    281 }
    282