Start implementing jdb "locals".
This lets us show the names and types of the locals, but all the values
will show up as 0/null. We're going to have to walk the whole stack and
take callee-save frames into account to do that right.
Change-Id: Ic6e115513b6e65ae7ed4b7274e70bc514e83190a
diff --git a/src/debugger.cc b/src/debugger.cc
index aaabffb..1ad8cc2 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -520,9 +520,34 @@
return 0;
}
-int Dbg::GetTagWidth(int tag) {
- UNIMPLEMENTED(FATAL);
- return 0;
+size_t Dbg::GetTagWidth(int tag) {
+ switch (tag) {
+ case JDWP::JT_VOID:
+ return 0;
+ case JDWP::JT_BYTE:
+ case JDWP::JT_BOOLEAN:
+ return 1;
+ case JDWP::JT_CHAR:
+ case JDWP::JT_SHORT:
+ return 2;
+ case JDWP::JT_FLOAT:
+ case JDWP::JT_INT:
+ return 4;
+ case JDWP::JT_ARRAY:
+ case JDWP::JT_OBJECT:
+ case JDWP::JT_STRING:
+ case JDWP::JT_THREAD:
+ case JDWP::JT_THREAD_GROUP:
+ case JDWP::JT_CLASS_LOADER:
+ case JDWP::JT_CLASS_OBJECT:
+ return sizeof(JDWP::ObjectId);
+ case JDWP::JT_DOUBLE:
+ case JDWP::JT_LONG:
+ return 8;
+ default:
+ LOG(FATAL) << "unknown tag " << tag;
+ return -1;
+ }
}
int Dbg::GetArrayLength(JDWP::ObjectId arrayId) {
@@ -606,6 +631,77 @@
return accessFlags;
}
+static JDWP::JdwpTag TagFromClass(Class* c) {
+ if (c->IsArrayClass()) {
+ return JDWP::JT_ARRAY;
+ }
+
+ if (c->IsStringClass()) {
+ return JDWP::JT_STRING;
+ } else if (c->IsClassClass()) {
+ return JDWP::JT_CLASS_OBJECT;
+#if 0 // TODO
+ } else if (dvmInstanceof(clazz, gDvm.classJavaLangThread)) {
+ return JDWP::JT_THREAD;
+ } else if (dvmInstanceof(clazz, gDvm.classJavaLangThreadGroup)) {
+ return JDWP::JT_THREAD_GROUP;
+ } else if (dvmInstanceof(clazz, gDvm.classJavaLangClassLoader)) {
+ return JDWP::JT_CLASS_LOADER;
+#endif
+ } else {
+ return JDWP::JT_OBJECT;
+ }
+}
+
+/*
+ * Objects declared to hold Object might actually hold a more specific
+ * type. The debugger may take a special interest in these (e.g. it
+ * wants to display the contents of Strings), so we want to return an
+ * appropriate tag.
+ *
+ * Null objects are tagged JT_OBJECT.
+ */
+static JDWP::JdwpTag TagFromObject(const Object* o) {
+ return (o == NULL) ? JDWP::JT_OBJECT : TagFromClass(o->GetClass());
+}
+
+static const uint16_t kEclipseWorkaroundSlot = 1000;
+
+/*
+ * Eclipse appears to expect that the "this" reference is in slot zero.
+ * If it's not, the "variables" display will show two copies of "this",
+ * possibly because it gets "this" from SF.ThisObject and then displays
+ * all locals with nonzero slot numbers.
+ *
+ * So, we remap the item in slot 0 to 1000, and remap "this" to zero. On
+ * SF.GetValues / SF.SetValues we map them back.
+ */
+static uint16_t MangleSlot(uint16_t slot, const char* name) {
+ uint16_t newSlot = slot;
+ if (strcmp(name, "this") == 0) {
+ newSlot = 0;
+ } else if (slot == 0) {
+ newSlot = kEclipseWorkaroundSlot;
+ }
+ return newSlot;
+}
+
+/*
+ * Reverse Eclipse hack.
+ */
+static uint16_t DemangleSlot(uint16_t slot, Method** sp) {
+ int newSlot = slot;
+ if (slot == kEclipseWorkaroundSlot) {
+ newSlot = 0;
+ } else if (slot == 0) {
+ Frame f;
+ f.SetSP(sp);
+ Method* m = f.GetMethod();
+ newSlot = m->NumRegisters() - m->NumIns();
+ }
+ return newSlot;
+}
+
void Dbg::OutputDeclaredFields(JDWP::RefTypeId refTypeId, bool withGeneric, JDWP::ExpandBuf* pReply) {
Class* c = gRegistry->Get<Class*>(refTypeId);
CHECK(c != NULL);
@@ -706,8 +802,51 @@
JDWP::Set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems);
}
-void Dbg::OutputVariableTable(JDWP::RefTypeId refTypeId, JDWP::MethodId id, bool withGeneric, JDWP::ExpandBuf* pReply) {
- UNIMPLEMENTED(FATAL);
+void Dbg::OutputVariableTable(JDWP::RefTypeId refTypeId, JDWP::MethodId methodId, bool withGeneric, JDWP::ExpandBuf* pReply) {
+ struct DebugCallbackContext {
+ int numItems;
+ JDWP::ExpandBuf* pReply;
+ bool withGeneric;
+
+ static void Callback(void* context, uint16_t slot, uint32_t startAddress, uint32_t endAddress, const char *name, const char *descriptor, const char *signature) {
+ DebugCallbackContext* pContext = reinterpret_cast<DebugCallbackContext*>(context);
+
+ slot = MangleSlot(slot, name);
+
+ LOG(VERBOSE) << StringPrintf(" %2d: %d(%d) '%s' '%s' '%s' slot=%d", pContext->numItems, startAddress, endAddress - startAddress, name, descriptor, signature, slot);
+
+ expandBufAdd8BE(pContext->pReply, startAddress);
+ expandBufAddUtf8String(pContext->pReply, name);
+ expandBufAddUtf8String(pContext->pReply, descriptor);
+ if (pContext->withGeneric) {
+ expandBufAddUtf8String(pContext->pReply, signature);
+ }
+ expandBufAdd4BE(pContext->pReply, endAddress - startAddress);
+ expandBufAdd4BE(pContext->pReply, slot);
+
+ pContext->numItems++;
+ }
+ };
+
+ Method* m = FromMethodId(methodId);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ const DexFile& dex_file = class_linker->FindDexFile(m->GetDeclaringClass()->GetDexCache());
+ const DexFile::CodeItem* code_item = dex_file.GetCodeItem(m->GetCodeItemOffset());
+
+ expandBufAdd4BE(pReply, m->NumIns());
+
+ // Add numLocals later
+ size_t numLocalsOffset = expandBufGetLength(pReply);
+ expandBufAdd4BE(pReply, 0);
+
+ DebugCallbackContext context;
+ context.numItems = 0;
+ context.pReply = pReply;
+ context.withGeneric = withGeneric;
+
+ dex_file.DecodeDebugInfo(code_item, m, NULL, DebugCallbackContext::Callback, &context);
+
+ JDWP::Set4BE(expandBufGetBuffer(pReply) + numLocalsOffset, context.numItems);
}
uint8_t Dbg::GetFieldBasicTag(JDWP::ObjectId objId, JDWP::FieldId fieldId) {
@@ -978,11 +1117,87 @@
return false;
}
-void Dbg::GetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot, uint8_t tag, uint8_t* buf, int expectedLen) {
- UNIMPLEMENTED(FATAL);
+void Dbg::GetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot, JDWP::JdwpTag tag, uint8_t* buf, size_t expectedLen) {
+ Method** sp = reinterpret_cast<Method**>(frameId);
+ slot = DemangleSlot(slot, sp);
+
+ switch (tag) {
+ case JDWP::JT_BOOLEAN:
+ {
+ UNIMPLEMENTED(WARNING) << "get boolean local " << slot;
+ CHECK_EQ(expectedLen, 1U);
+ uint32_t intVal = 0; // framePtr[slot];
+ JDWP::Set1(buf+1, intVal != 0);
+ }
+ break;
+ case JDWP::JT_BYTE:
+ {
+ UNIMPLEMENTED(WARNING) << "get byte local " << slot;
+ CHECK_EQ(expectedLen, 1U);
+ uint32_t intVal = 0; // framePtr[slot];
+ JDWP::Set1(buf+1, intVal);
+ }
+ break;
+ case JDWP::JT_SHORT:
+ case JDWP::JT_CHAR:
+ {
+ UNIMPLEMENTED(WARNING) << "get 16-bit local " << slot;
+ CHECK_EQ(expectedLen, 2U);
+ uint32_t intVal = 0; // framePtr[slot];
+ JDWP::Set2BE(buf+1, intVal);
+ }
+ break;
+ case JDWP::JT_INT:
+ case JDWP::JT_FLOAT:
+ {
+ UNIMPLEMENTED(WARNING) << "get 32-bit local " << slot;
+ CHECK_EQ(expectedLen, 4U);
+ uint32_t intVal = 0; // framePtr[slot];
+ JDWP::Set4BE(buf+1, intVal);
+ }
+ break;
+ case JDWP::JT_ARRAY:
+ {
+ UNIMPLEMENTED(WARNING) << "get array local " << slot;
+ CHECK_EQ(expectedLen, sizeof(JDWP::ObjectId));
+ Object* o = NULL; // (Object*)framePtr[slot];
+ if (o != NULL && !Heap::IsHeapAddress(o)) {
+ LOG(FATAL) << "slot " << slot << " expected to hold array: " << o;
+ }
+ JDWP::SetObjectId(buf+1, gRegistry->Add(o));
+ }
+ break;
+ case JDWP::JT_OBJECT:
+ {
+ UNIMPLEMENTED(WARNING) << "get object local " << slot;
+ CHECK_EQ(expectedLen, sizeof(JDWP::ObjectId));
+ Object* o = NULL; // (Object*)framePtr[slot];
+ if (o != NULL && !Heap::IsHeapAddress(o)) {
+ LOG(FATAL) << "slot " << slot << " expected to hold object: " << o;
+ }
+ tag = TagFromObject(o);
+ JDWP::SetObjectId(buf+1, gRegistry->Add(o));
+ }
+ break;
+ case JDWP::JT_DOUBLE:
+ case JDWP::JT_LONG:
+ {
+ UNIMPLEMENTED(WARNING) << "get 64-bit local " << slot;
+ CHECK_EQ(expectedLen, 8U);
+ uint64_t longVal = 0; // memcpy(&longVal, &framePtr[slot], 8);
+ JDWP::Set8BE(buf+1, longVal);
+ }
+ break;
+ default:
+ LOG(FATAL) << "unknown tag " << tag;
+ break;
+ }
+
+ // Prepend tag, which may have been updated.
+ JDWP::Set1(buf, tag);
}
-void Dbg::SetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot, uint8_t tag, uint64_t value, int width) {
+void Dbg::SetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot, JDWP::JdwpTag tag, uint64_t value, size_t width) {
UNIMPLEMENTED(FATAL);
}