Initial implementation for adding StackTraceElement support.
Line # information added.
Change-Id: I5a68383e74a19fa28d82a33bb1db649ef570f3b0
diff --git a/src/dex_file.cc b/src/dex_file.cc
index 14c62f1..b575bcf 100644
--- a/src/dex_file.cc
+++ b/src/dex_file.cc
@@ -534,4 +534,158 @@
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 b3f9bdd..ee5cc45 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 @@
// 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 @@
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 @@
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 @@
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 51df6c9..bc5dd1a 100644
--- a/src/leb128.h
+++ b/src/leb128.h
@@ -35,6 +35,14 @@
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 0bbb4f4..c8ec9cb 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"