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