diff options
| -rw-r--r-- | src/dex_file.cc | 154 | ||||
| -rw-r--r-- | src/dex_file.h | 131 | ||||
| -rw-r--r-- | src/leb128.h | 8 | ||||
| -rw-r--r-- | src/object.h | 1 |
4 files changed, 293 insertions, 1 deletions
diff --git a/src/dex_file.cc b/src/dex_file.cc index 14c62f1312..b575bcf822 100644 --- a/src/dex_file.cc +++ b/src/dex_file.cc @@ -534,4 +534,158 @@ DexFile::ValueType DexFile::ReadEncodedValue(const byte** stream, return static_cast<ValueType>(type); } +String* DexFile::dexArtStringById(uint32_t idx) const { + return String::AllocFromModifiedUtf8(dexStringById(idx)); +} + +int32_t DexFile::GetLineNumFromPC(const art::Method* method, uint32_t rel_pc) const { + const CodeItem* code_item = GetCodeItem(method->code_off_); + DCHECK(code_item != NULL); + + // A method with no line number info should return -1 + LineNumFromPcContext context(rel_pc, -1); + dexDecodeDebugInfo(code_item, method, LineNumForPcCb, NULL, &context); + return context.line_num_; +} + +void DexFile::dexDecodeDebugInfo0(const CodeItem* code_item, const art::Method* method, + DexDebugNewPositionCb posCb, DexDebugNewLocalCb local_cb, + void* cnxt, const byte* stream, LocalInfo* local_in_reg) const { + uint32_t line = DecodeUnsignedLeb128(&stream); + uint32_t parameters_size = DecodeUnsignedLeb128(&stream); + uint16_t arg_reg = code_item->registers_size_ - code_item->ins_size_; + uint32_t address = 0; + + if (!method->IsStatic()) { + local_in_reg[arg_reg].name_ = String::AllocFromModifiedUtf8("this"); + local_in_reg[arg_reg].descriptor_ = method->GetDeclaringClass()->GetDescriptor(); + local_in_reg[arg_reg].signature_ = NULL; + local_in_reg[arg_reg].start_address_ = 0; + local_in_reg[arg_reg].is_live_ = true; + arg_reg++; + } + + ParameterIterator *it = GetParameterIterator(GetProtoId(method->proto_idx_)); + for (uint32_t i = 0; i < parameters_size && it->HasNext(); ++i, it->Next()) { + if (arg_reg >= code_item->registers_size_) { + LOG(FATAL) << "invalid stream"; + return; + } + + String* descriptor = String::AllocFromModifiedUtf8(it->GetDescriptor()); + String* name = dexArtStringById(DecodeUnsignedLeb128P1(&stream)); + + local_in_reg[arg_reg].name_ = name; + local_in_reg[arg_reg].descriptor_ = descriptor; + local_in_reg[arg_reg].signature_ = NULL; + local_in_reg[arg_reg].start_address_ = address; + local_in_reg[arg_reg].is_live_ = true; + switch (descriptor->CharAt(0)) { + case 'D': + case 'J': + arg_reg += 2; + break; + default: + arg_reg += 1; + break; + } + } + + if (it->HasNext()) { + LOG(FATAL) << "invalid stream"; + return; + } + + for (;;) { + uint8_t opcode = *stream++; + uint8_t adjopcode = opcode - DBG_FIRST_SPECIAL; + uint16_t reg; + + + switch (opcode) { + case DBG_END_SEQUENCE: + return; + + case DBG_ADVANCE_PC: + address += DecodeUnsignedLeb128(&stream); + break; + + case DBG_ADVANCE_LINE: + line += DecodeUnsignedLeb128(&stream); + break; + + case DBG_START_LOCAL: + case DBG_START_LOCAL_EXTENDED: + reg = DecodeUnsignedLeb128(&stream); + if (reg > code_item->registers_size_) { + LOG(FATAL) << "invalid stream"; + return; + } + + // Emit what was previously there, if anything + InvokeLocalCbIfLive(cnxt, reg, address, local_in_reg, local_cb); + + local_in_reg[reg].name_ = dexArtStringById(DecodeUnsignedLeb128P1(&stream)); + local_in_reg[reg].descriptor_ = dexArtStringByTypeIdx(DecodeUnsignedLeb128P1(&stream)); + if (opcode == DBG_START_LOCAL_EXTENDED) { + local_in_reg[reg].signature_ = dexArtStringById(DecodeUnsignedLeb128P1(&stream)); + } else { + local_in_reg[reg].signature_ = NULL; + } + local_in_reg[reg].start_address_ = address; + local_in_reg[reg].is_live_ = true; + break; + + case DBG_END_LOCAL: + reg = DecodeUnsignedLeb128(&stream); + if (reg > code_item->registers_size_) { + LOG(FATAL) << "invalid stream"; + return; + } + + InvokeLocalCbIfLive(cnxt, reg, address, local_in_reg, local_cb); + local_in_reg[reg].is_live_ = false; + break; + + case DBG_RESTART_LOCAL: + reg = DecodeUnsignedLeb128(&stream); + if (reg > code_item->registers_size_) { + LOG(FATAL) << "invalid stream"; + return; + } + + if (local_in_reg[reg].name_ == NULL + || local_in_reg[reg].descriptor_ == NULL) { + LOG(FATAL) << "invalid stream"; + return; + } + + // If the register is live, the "restart" is superfluous, + // and we don't want to mess with the existing start address. + if (!local_in_reg[reg].is_live_) { + local_in_reg[reg].start_address_ = address; + local_in_reg[reg].is_live_ = true; + } + break; + + case DBG_SET_PROLOGUE_END: + case DBG_SET_EPILOGUE_BEGIN: + case DBG_SET_FILE: + break; + + default: + address += adjopcode / DBG_LINE_RANGE; + line += DBG_LINE_BASE + (adjopcode % DBG_LINE_RANGE); + + if (posCb != NULL) { + if (posCb(cnxt, address, line)) { + // early exit + return; + } + } + break; + } + } +} + } // namespace art diff --git a/src/dex_file.h b/src/dex_file.h index b3f9bdd39a..ee5cc45c30 100644 --- a/src/dex_file.h +++ b/src/dex_file.h @@ -17,6 +17,8 @@ namespace art { union JValue; +class String; +class Method; // TODO: move all of the macro functionality into the DexCache class. class DexFile { @@ -484,6 +486,10 @@ class DexFile { // return the UTF-8 encoded string with the specified string_id index const char* dexStringById(uint32_t idx, int32_t* unicode_length) const { + if (idx == kDexNoIndex) { + *unicode_length = 0; + return NULL; + } const StringId& string_id = GetStringId(idx); return GetStringData(string_id, unicode_length); } @@ -493,6 +499,8 @@ class DexFile { return dexStringById(idx, &unicode_length); } + String* dexArtStringById(uint32_t idx) const; + // Get the descriptor string associated with a given type index. const char* dexStringByTypeIdx(uint32_t idx, int32_t* unicode_length) const { const TypeId& type_id = GetTypeId(idx); @@ -504,6 +512,11 @@ class DexFile { return dexStringById(type_id.descriptor_idx_); } + String* dexArtStringByTypeIdx(uint32_t idx) const { + const TypeId& type_id = GetTypeId(idx); + return dexArtStringById(type_id.descriptor_idx_); + } + // TODO: encoded_field is actually a stream of bytes void dexReadClassDataField(const byte** encoded_field, DexFile::Field* field, @@ -605,6 +618,124 @@ class DexFile { return -1; } + // Get the pointer to the start of the debugging data + const byte* dexGetDebugInfoStream(const CodeItem* code_item) const { + if (code_item->debug_info_off_ == 0) { + return NULL; + } else { + return base_ + code_item->debug_info_off_; + } + } + + // Callback for "new position table entry". + // Returning true causes the decoder to stop early. + typedef bool (*DexDebugNewPositionCb)(void *cnxt, uint32_t address, uint32_t line_num); + + // Callback for "new locals table entry". "signature" is an empty string + // if no signature is available for an entry. + typedef void (*DexDebugNewLocalCb)(void *cnxt, uint16_t reg, + uint32_t startAddress, + uint32_t endAddress, + const String* name, + const String* descriptor, + const String* signature); + + static bool LineNumForPcCb(void *cnxt, uint32_t address, uint32_t line_num) { + LineNumFromPcContext *context = (LineNumFromPcContext *)cnxt; + + // We know that this callback will be called in + // ascending address order, so keep going until we find + // a match or we've just gone past it. + if (address > context->address_) { + // The line number from the previous positions callback + // wil be the final result. + return true; + } else { + context->line_num_ = line_num; + return address == context->address_; + } + } + + + // Debug info opcodes and constants + enum { + DBG_END_SEQUENCE = 0x00, + DBG_ADVANCE_PC = 0x01, + DBG_ADVANCE_LINE = 0x02, + DBG_START_LOCAL = 0x03, + DBG_START_LOCAL_EXTENDED = 0x04, + DBG_END_LOCAL = 0x05, + DBG_RESTART_LOCAL = 0x06, + DBG_SET_PROLOGUE_END = 0x07, + DBG_SET_EPILOGUE_BEGIN = 0x08, + DBG_SET_FILE = 0x09, + DBG_FIRST_SPECIAL = 0x0a, + DBG_LINE_BASE = -4, + DBG_LINE_RANGE = 15, + }; + + struct LocalInfo { + LocalInfo() : name_(NULL), descriptor_(NULL), signature_(NULL), start_address_(0), is_live_(false) {} + + // E.g., list + const String* name_; + + // E.g., Ljava/util/LinkedList; + const String* descriptor_; + + // E.g., java.util.LinkedList<java.lang.Integer> + const String* signature_; + + // PC location where the local is first defined. + uint16_t start_address_; + + // Is the local defined and live. + bool is_live_; + }; + + struct LineNumFromPcContext { + LineNumFromPcContext(uint32_t address, uint32_t line_num) : + address_(address), line_num_(line_num) {} + uint32_t address_; + uint32_t line_num_; + }; + + void InvokeLocalCbIfLive(void *cnxt, int reg, uint32_t end_address, + LocalInfo *local_in_reg, DexDebugNewLocalCb local_cb) const { + if (local_cb != NULL && local_in_reg[reg].is_live_) { + local_cb(cnxt, reg, local_in_reg[reg].start_address_, end_address, + local_in_reg[reg].name_, local_in_reg[reg].descriptor_, + local_in_reg[reg].signature_); + } + } + + // Determine the source file line number based on the program counter. + // "pc" is an offset, in 16-bit units, from the start of the method's code. + // + // Returns -1 if no match was found (possibly because the source files were + // compiled without "-g", so no line number information is present). + // Returns -2 for native methods (as expected in exception traces). + // + // This is used by runtime; therefore use art::Method not art::DexFile::Method. + int32_t GetLineNumFromPC(const art::Method* method, uint32_t rel_pc) const; + + void dexDecodeDebugInfo0(const CodeItem* code_item, const art::Method* method, + DexDebugNewPositionCb posCb, DexDebugNewLocalCb local_cb, + void* cnxt, const byte* stream, LocalInfo* local_in_reg) const; + + void dexDecodeDebugInfo(const CodeItem* code_item, const art::Method *method, + DexDebugNewPositionCb posCb, DexDebugNewLocalCb local_cb, + void* cnxt) const { + const byte* stream = dexGetDebugInfoStream(code_item); + LocalInfo local_in_reg[code_item->registers_size_]; + + if (stream != NULL) { + dexDecodeDebugInfo0(code_item, method, posCb, local_cb, cnxt, stream, local_in_reg); + } + for (int reg = 0; reg < code_item->registers_size_; reg++) { + InvokeLocalCbIfLive(cnxt, reg, code_item->insns_size_, local_in_reg, local_cb); + } + } // TODO: const reference uint32_t dexGetIndexForClassDef(const ClassDef* class_def) const { diff --git a/src/leb128.h b/src/leb128.h index 51df6c92f8..bc5dd1a61a 100644 --- a/src/leb128.h +++ b/src/leb128.h @@ -35,6 +35,14 @@ static inline uint32_t DecodeUnsignedLeb128(const byte** data) { return (uint32_t)result; } +// Reads an unsigned LEB128 + 1 value. updating the given pointer to point +// just past the end of the read value. This function tolerates +// non-zero high-order bits in the fifth encoded byte. +// It is possible for this function to return -1. +static inline int32_t DecodeUnsignedLeb128P1(const byte** data) { + return DecodeUnsignedLeb128(data) - 1; +} + // Reads a signed LEB128 value, updating the given pointer to point // just past the end of the read value. This function tolerates // non-zero high-order bits in the fifth encoded byte. diff --git a/src/object.h b/src/object.h index 0bbb4f46ba..c8ec9cb882 100644 --- a/src/object.h +++ b/src/object.h @@ -5,7 +5,6 @@ #include "constants.h" #include "casts.h" -#include "dex_file.h" #include "globals.h" #include "heap.h" #include "logging.h" |