summaryrefslogtreecommitdiffstats
path: root/libdex/DexDebugInfo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libdex/DexDebugInfo.cpp')
-rw-r--r--libdex/DexDebugInfo.cpp334
1 files changed, 334 insertions, 0 deletions
diff --git a/libdex/DexDebugInfo.cpp b/libdex/DexDebugInfo.cpp
new file mode 100644
index 000000000..c33a1e49f
--- /dev/null
+++ b/libdex/DexDebugInfo.cpp
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Handling of method debug info in a .dex file.
+ */
+
+#include "DexDebugInfo.h"
+#include "DexProto.h"
+#include "Leb128.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * Decode the arguments in a method signature, which looks something
+ * like "(ID[Ljava/lang/String;)V".
+ *
+ * Returns the type signature letter for the next argument, or ')' if
+ * there are no more args. Advances "pSig" to point to the character
+ * after the one returned.
+ */
+static char decodeSignature(const char** pSig)
+{
+ const char* sig = *pSig;
+
+ if (*sig == '(')
+ sig++;
+
+ if (*sig == 'L') {
+ /* object ref */
+ while (*++sig != ';')
+ ;
+ *pSig = sig+1;
+ return 'L';
+ }
+ if (*sig == '[') {
+ /* array; advance past array type */
+ while (*++sig == '[')
+ ;
+ if (*sig == 'L') {
+ while (*++sig != ';')
+ ;
+ }
+ *pSig = sig+1;
+ return '[';
+ }
+ if (*sig == '\0')
+ return *sig; /* don't advance further */
+
+ *pSig = sig+1;
+ return *sig;
+}
+
+/*
+ * returns the length of a type string, given the start of the
+ * type string. Used for the case where the debug info format
+ * references types that are inside a method type signature.
+ */
+static int typeLength(const char *type) {
+ // Assumes any leading '(' has already been gobbled
+ const char *end = type;
+ decodeSignature(&end);
+ return end - type;
+}
+
+/*
+ * Reads a string index as encoded for the debug info format,
+ * returning a string pointer or NULL as appropriate.
+ */
+static const char* readStringIdx(const DexFile* pDexFile,
+ const u1** pStream) {
+ u4 stringIdx = readUnsignedLeb128(pStream);
+
+ // Remember, encoded string indicies have 1 added to them.
+ if (stringIdx == 0) {
+ return NULL;
+ } else {
+ return dexStringById(pDexFile, stringIdx - 1);
+ }
+}
+
+/*
+ * Reads a type index as encoded for the debug info format, returning
+ * a string pointer for its descriptor or NULL as appropriate.
+ */
+static const char* readTypeIdx(const DexFile* pDexFile,
+ const u1** pStream) {
+ u4 typeIdx = readUnsignedLeb128(pStream);
+
+ // Remember, encoded type indicies have 1 added to them.
+ if (typeIdx == 0) {
+ return NULL;
+ } else {
+ return dexStringByTypeIdx(pDexFile, typeIdx - 1);
+ }
+}
+
+typedef struct LocalInfo {
+ const char *name;
+ const char *descriptor;
+ const char *signature;
+ u2 startAddress;
+ bool live;
+} LocalInfo;
+
+static void emitLocalCbIfLive(void *cnxt, int reg, u4 endAddress,
+ LocalInfo *localInReg, DexDebugNewLocalCb localCb)
+{
+ if (localCb != NULL && localInReg[reg].live) {
+ localCb(cnxt, reg, localInReg[reg].startAddress, endAddress,
+ localInReg[reg].name,
+ localInReg[reg].descriptor,
+ localInReg[reg].signature == NULL
+ ? "" : localInReg[reg].signature );
+ }
+}
+
+static void invalidStream(const char* classDescriptor, const DexProto* proto) {
+ IF_LOGE() {
+ char* methodDescriptor = dexProtoCopyMethodDescriptor(proto);
+ LOGE("Invalid debug info stream. class %s; proto %s",
+ classDescriptor, methodDescriptor);
+ free(methodDescriptor);
+ }
+}
+
+static void dexDecodeDebugInfo0(
+ const DexFile* pDexFile,
+ const DexCode* pCode,
+ const char* classDescriptor,
+ u4 protoIdx,
+ u4 accessFlags,
+ DexDebugNewPositionCb posCb, DexDebugNewLocalCb localCb,
+ void* cnxt,
+ const u1* stream,
+ LocalInfo* localInReg)
+{
+ DexProto proto = { pDexFile, protoIdx };
+ u4 insnsSize = pCode->insnsSize;
+ u4 line = readUnsignedLeb128(&stream);
+ u4 parametersSize = readUnsignedLeb128(&stream);
+ u2 argReg = pCode->registersSize - pCode->insSize;
+ u4 address = 0;
+
+ if ((accessFlags & ACC_STATIC) == 0) {
+ /*
+ * The code is an instance method, which means that there is
+ * an initial this parameter. Also, the proto list should
+ * contain exactly one fewer argument word than the insSize
+ * indicates.
+ */
+ assert(pCode->insSize == (dexProtoComputeArgsSize(&proto) + 1));
+ localInReg[argReg].name = "this";
+ localInReg[argReg].descriptor = classDescriptor;
+ localInReg[argReg].startAddress = 0;
+ localInReg[argReg].live = true;
+ argReg++;
+ } else {
+ assert(pCode->insSize == dexProtoComputeArgsSize(&proto));
+ }
+
+ DexParameterIterator iterator;
+ dexParameterIteratorInit(&iterator, &proto);
+
+ while (parametersSize-- != 0) {
+ const char* descriptor = dexParameterIteratorNextDescriptor(&iterator);
+ const char *name;
+ int reg;
+
+ if ((argReg >= pCode->registersSize) || (descriptor == NULL)) {
+ invalidStream(classDescriptor, &proto);
+ return;
+ }
+
+ name = readStringIdx(pDexFile, &stream);
+ reg = argReg;
+
+ switch (descriptor[0]) {
+ case 'D':
+ case 'J':
+ argReg += 2;
+ break;
+ default:
+ argReg += 1;
+ break;
+ }
+
+ if (name != NULL) {
+ localInReg[reg].name = name;
+ localInReg[reg].descriptor = descriptor;
+ localInReg[reg].signature = NULL;
+ localInReg[reg].startAddress = address;
+ localInReg[reg].live = true;
+ }
+ }
+
+ for (;;) {
+ u1 opcode = *stream++;
+ u2 reg;
+
+ switch (opcode) {
+ case DBG_END_SEQUENCE:
+ return;
+
+ case DBG_ADVANCE_PC:
+ address += readUnsignedLeb128(&stream);
+ break;
+
+ case DBG_ADVANCE_LINE:
+ line += readSignedLeb128(&stream);
+ break;
+
+ case DBG_START_LOCAL:
+ case DBG_START_LOCAL_EXTENDED:
+ reg = readUnsignedLeb128(&stream);
+ if (reg > pCode->registersSize) {
+ invalidStream(classDescriptor, &proto);
+ return;
+ }
+
+ // Emit what was previously there, if anything
+ emitLocalCbIfLive(cnxt, reg, address,
+ localInReg, localCb);
+
+ localInReg[reg].name = readStringIdx(pDexFile, &stream);
+ localInReg[reg].descriptor = readTypeIdx(pDexFile, &stream);
+ if (opcode == DBG_START_LOCAL_EXTENDED) {
+ localInReg[reg].signature
+ = readStringIdx(pDexFile, &stream);
+ } else {
+ localInReg[reg].signature = NULL;
+ }
+ localInReg[reg].startAddress = address;
+ localInReg[reg].live = true;
+ break;
+
+ case DBG_END_LOCAL:
+ reg = readUnsignedLeb128(&stream);
+ if (reg > pCode->registersSize) {
+ invalidStream(classDescriptor, &proto);
+ return;
+ }
+
+ emitLocalCbIfLive (cnxt, reg, address, localInReg, localCb);
+ localInReg[reg].live = false;
+ break;
+
+ case DBG_RESTART_LOCAL:
+ reg = readUnsignedLeb128(&stream);
+ if (reg > pCode->registersSize) {
+ invalidStream(classDescriptor, &proto);
+ return;
+ }
+
+ if (localInReg[reg].name == NULL
+ || localInReg[reg].descriptor == NULL) {
+ invalidStream(classDescriptor, &proto);
+ return;
+ }
+
+ /*
+ * If the register is live, the "restart" is superfluous,
+ * and we don't want to mess with the existing start address.
+ */
+ if (!localInReg[reg].live) {
+ localInReg[reg].startAddress = address;
+ localInReg[reg].live = true;
+ }
+ break;
+
+ case DBG_SET_PROLOGUE_END:
+ case DBG_SET_EPILOGUE_BEGIN:
+ case DBG_SET_FILE:
+ break;
+
+ default: {
+ int adjopcode = opcode - DBG_FIRST_SPECIAL;
+
+ address += adjopcode / DBG_LINE_RANGE;
+ line += DBG_LINE_BASE + (adjopcode % DBG_LINE_RANGE);
+
+ if (posCb != NULL) {
+ int done;
+ done = posCb(cnxt, address, line);
+
+ if (done) {
+ // early exit
+ return;
+ }
+ }
+ break;
+ }
+ }
+ }
+}
+
+// TODO optimize localCb == NULL case
+void dexDecodeDebugInfo(
+ const DexFile* pDexFile,
+ const DexCode* pCode,
+ const char* classDescriptor,
+ u4 protoIdx,
+ u4 accessFlags,
+ DexDebugNewPositionCb posCb, DexDebugNewLocalCb localCb,
+ void* cnxt)
+{
+ const u1* stream = dexGetDebugInfoStream(pDexFile, pCode);
+ LocalInfo localInReg[pCode->registersSize];
+
+ memset(localInReg, 0, sizeof(LocalInfo) * pCode->registersSize);
+
+ if (stream != NULL) {
+ dexDecodeDebugInfo0(pDexFile, pCode, classDescriptor, protoIdx, accessFlags,
+ posCb, localCb, cnxt, stream, localInReg);
+ }
+
+ for (int reg = 0; reg < pCode->registersSize; reg++) {
+ emitLocalCbIfLive(cnxt, reg, pCode->insnsSize, localInReg, localCb);
+ }
+}