Implement debugger support for getting thread stacks.
You can now "where all" in jdb.
Change-Id: Iccff0427f39b2f2ab3786f05b89850d50b87adb2
diff --git a/src/debugger.cc b/src/debugger.cc
index 7918bfb..aaabffb 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -498,9 +498,16 @@
return c->GetDescriptor()->ToModifiedUtf8();
}
-const char* Dbg::GetSourceFile(JDWP::RefTypeId refTypeId) {
- UNIMPLEMENTED(FATAL);
- return NULL;
+bool Dbg::GetSourceFile(JDWP::RefTypeId refTypeId, std::string& result) {
+ Class* c = gRegistry->Get<Class*>(refTypeId);
+ CHECK(c != NULL);
+
+ String* source_file = c->GetSourceFile();
+ if (source_file == NULL) {
+ return false;
+ }
+ result = source_file->ToModifiedUtf8();
+ return true;
}
const char* Dbg::GetObjectTypeName(JDWP::ObjectId objectId) {
@@ -558,9 +565,32 @@
return false;
}
-const char* Dbg::GetMethodName(JDWP::RefTypeId refTypeId, JDWP::MethodId id) {
+JDWP::FieldId ToFieldId(Field* f) {
+#ifdef MOVING_GARBAGE_COLLECTOR
UNIMPLEMENTED(FATAL);
- return NULL;
+#else
+ return static_cast<JDWP::FieldId>(reinterpret_cast<uintptr_t>(f));
+#endif
+}
+
+JDWP::MethodId ToMethodId(Method* m) {
+#ifdef MOVING_GARBAGE_COLLECTOR
+ UNIMPLEMENTED(FATAL);
+#else
+ return static_cast<JDWP::MethodId>(reinterpret_cast<uintptr_t>(m));
+#endif
+}
+
+Method* FromMethodId(JDWP::MethodId mid) {
+#ifdef MOVING_GARBAGE_COLLECTOR
+ UNIMPLEMENTED(FATAL);
+#else
+ return reinterpret_cast<Method*>(static_cast<uintptr_t>(mid));
+#endif
+}
+
+std::string Dbg::GetMethodName(JDWP::RefTypeId refTypeId, JDWP::MethodId methodId) {
+ return FromMethodId(methodId)->GetName()->ToModifiedUtf8();
}
/*
@@ -576,14 +606,6 @@
return accessFlags;
}
-JDWP::FieldId ToFieldId(Field* f) {
- return static_cast<JDWP::FieldId>(reinterpret_cast<uintptr_t>(f));
-}
-
-JDWP::MethodId ToMethodId(Method* m) {
- return static_cast<JDWP::MethodId>(reinterpret_cast<uintptr_t>(m));
-}
-
void Dbg::OutputDeclaredFields(JDWP::RefTypeId refTypeId, bool withGeneric, JDWP::ExpandBuf* pReply) {
Class* c = gRegistry->Get<Class*>(refTypeId);
CHECK(c != NULL);
@@ -641,7 +663,47 @@
}
void Dbg::OutputLineTable(JDWP::RefTypeId refTypeId, JDWP::MethodId methodId, JDWP::ExpandBuf* pReply) {
- UNIMPLEMENTED(FATAL);
+ struct DebugCallbackContext {
+ int numItems;
+ JDWP::ExpandBuf* pReply;
+
+ static bool Callback(void* context, uint32_t address, uint32_t lineNum) {
+ DebugCallbackContext* pContext = reinterpret_cast<DebugCallbackContext*>(context);
+ expandBufAdd8BE(pContext->pReply, address);
+ expandBufAdd4BE(pContext->pReply, lineNum);
+ pContext->numItems++;
+ return true;
+ }
+ };
+
+ 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());
+
+ uint64_t start, end;
+ if (m->IsNative()) {
+ start = -1;
+ end = -1;
+ } else {
+ start = 0;
+ end = code_item->insns_size_in_code_units_; // TODO: what are the units supposed to be? *2?
+ }
+
+ expandBufAdd8BE(pReply, start);
+ expandBufAdd8BE(pReply, end);
+
+ // Add numLines later
+ size_t numLinesOffset = expandBufGetLength(pReply);
+ expandBufAdd4BE(pReply, 0);
+
+ DebugCallbackContext context;
+ context.numItems = 0;
+ context.pReply = pReply;
+
+ dex_file.DecodeDebugInfo(code_item, m, DebugCallbackContext::Callback, NULL, &context);
+
+ JDWP::Set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems);
}
void Dbg::OutputVariableTable(JDWP::RefTypeId refTypeId, JDWP::MethodId id, bool withGeneric, JDWP::ExpandBuf* pReply) {
@@ -835,9 +897,10 @@
}
int Dbg::GetThreadFrameCount(JDWP::ObjectId threadId) {
+ ScopedThreadListLock thread_list_lock;
struct CountStackDepthVisitor : public Thread::StackVisitor {
CountStackDepthVisitor() : depth(0) {}
- virtual void VisitFrame(const Frame& frame, uintptr_t pc) {
+ virtual void VisitFrame(const Frame&, uintptr_t) {
++depth;
}
size_t depth;
@@ -847,9 +910,42 @@
return visitor.depth;
}
-bool Dbg::GetThreadFrame(JDWP::ObjectId threadId, int num, JDWP::FrameId* pFrameId, JDWP::JdwpLocation* pLoc) {
- UNIMPLEMENTED(FATAL);
- return false;
+bool Dbg::GetThreadFrame(JDWP::ObjectId threadId, int desired_frame_number, JDWP::FrameId* pFrameId, JDWP::JdwpLocation* pLoc) {
+ ScopedThreadListLock thread_list_lock;
+ struct GetFrameVisitor : public Thread::StackVisitor {
+ GetFrameVisitor(int desired_frame_number, JDWP::FrameId* pFrameId, JDWP::JdwpLocation* pLoc)
+ : found(false) ,depth(0), desired_frame_number(desired_frame_number), pFrameId(pFrameId), pLoc(pLoc) {
+ }
+ virtual void VisitFrame(const Frame& f, uintptr_t pc) {
+ if (!f.HasMethod()) {
+ return; // These don't count?
+ }
+
+ if (depth == desired_frame_number) {
+ *pFrameId = reinterpret_cast<JDWP::FrameId>(f.GetSP());
+
+ Method* m = f.GetMethod();
+ Class* c = m->GetDeclaringClass();
+
+ pLoc->typeTag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS;
+ pLoc->classId = gRegistry->Add(c);
+ pLoc->methodId = ToMethodId(m);
+ pLoc->idx = m->IsNative() ? -1 : m->ToDexPC(pc);
+
+ found = true;
+ }
+ ++depth;
+ }
+ bool found;
+ int depth;
+ int desired_frame_number;
+ JDWP::FrameId* pFrameId;
+ JDWP::JdwpLocation* pLoc;
+ };
+ GetFrameVisitor visitor(desired_frame_number, pFrameId, pLoc);
+ visitor.desired_frame_number = desired_frame_number;
+ DecodeThread(threadId)->WalkStack(&visitor);
+ return visitor.found;
}
JDWP::ObjectId Dbg::GetThreadSelfId() {
diff --git a/src/debugger.h b/src/debugger.h
index ca39316..d9961b7 100644
--- a/src/debugger.h
+++ b/src/debugger.h
@@ -138,7 +138,7 @@
static void GetObjectType(JDWP::ObjectId objectId, uint8_t* pRefTypeTag, JDWP::RefTypeId* pRefTypeId);
static uint8_t GetClassObjectType(JDWP::RefTypeId refTypeId);
static std::string GetSignature(JDWP::RefTypeId refTypeId);
- static const char* GetSourceFile(JDWP::RefTypeId refTypeId);
+ static bool GetSourceFile(JDWP::RefTypeId refTypeId, std::string& source_file);
static const char* GetObjectTypeName(JDWP::ObjectId objectId);
static uint8_t GetObjectTag(JDWP::ObjectId objectId);
static int GetTagWidth(int tag);
@@ -157,7 +157,7 @@
/*
* Method and Field
*/
- static const char* GetMethodName(JDWP::RefTypeId refTypeId, JDWP::MethodId id);
+ static std::string GetMethodName(JDWP::RefTypeId refTypeId, JDWP::MethodId id);
static void OutputDeclaredFields(JDWP::RefTypeId refTypeId, bool withGeneric, JDWP::ExpandBuf* pReply);
static void OutputDeclaredMethods(JDWP::RefTypeId refTypeId, bool withGeneric, JDWP::ExpandBuf* pReply);
static void OutputDeclaredInterfaces(JDWP::RefTypeId refTypeId, JDWP::ExpandBuf* pReply);
diff --git a/src/dex_file.h b/src/dex_file.h
index d48067b..8e07dde 100644
--- a/src/dex_file.h
+++ b/src/dex_file.h
@@ -592,10 +592,6 @@
// This is used by runtime; therefore use art::Method not art::DexFile::Method.
int32_t GetLineNumFromPC(const Method* method, uint32_t rel_pc) const;
- void DecodeDebugInfo0(const CodeItem* code_item, const Method* method,
- DexDebugNewPositionCb posCb, DexDebugNewLocalCb local_cb,
- void* cnxt, const byte* stream, LocalInfo* local_in_reg) const;
-
void DecodeDebugInfo(const CodeItem* code_item, const Method* method,
DexDebugNewPositionCb posCb, DexDebugNewLocalCb local_cb,
void* cnxt) const;
@@ -661,6 +657,11 @@
// Returns true if the header magic is of the expected value.
bool IsMagicValid();
+ void DecodeDebugInfo0(const CodeItem* code_item, const Method* method,
+ DexDebugNewPositionCb posCb, DexDebugNewLocalCb local_cb,
+ void* cnxt, const byte* stream, LocalInfo* local_in_reg) const;
+
+
// The index of descriptors to class definition indexes.
// TODO: given type_ids are sorted by string_id index, and string_ids are alphabetically, class
// lookup can be done with a binary search. Is the index necessary?
diff --git a/src/jdwp/jdwp.h b/src/jdwp/jdwp.h
index 41d8d8f..1b766bd 100644
--- a/src/jdwp/jdwp.h
+++ b/src/jdwp/jdwp.h
@@ -75,6 +75,7 @@
MethodId methodId; /* method in which "idx" resides */
uint64_t idx; /* relative index into code block */
};
+std::ostream& operator<<(std::ostream& os, const JdwpLocation& rhs);
/*
* How we talk to the debugger.
diff --git a/src/jdwp/jdwp_event.cc b/src/jdwp/jdwp_event.cc
index aa9d487..9440066 100644
--- a/src/jdwp/jdwp_event.cc
+++ b/src/jdwp/jdwp_event.cc
@@ -956,17 +956,11 @@
<< " thread=" << (void*) basket.threadId
<< " exceptId=" << (void*) exceptionId
<< " caught=" << basket.caught << ")";
- LOG(VERBOSE) << StringPrintf(" throw: %d %llx %x %lld (%s.%s)", pThrowLoc->typeTag,
- pThrowLoc->classId, pThrowLoc->methodId, pThrowLoc->idx,
- Dbg::GetClassDescriptor(pThrowLoc->classId).c_str(),
- Dbg::GetMethodName(pThrowLoc->classId, pThrowLoc->methodId));
+ LOG(VERBOSE) << " throw: " << *pThrowLoc;
if (pCatchLoc->classId == 0) {
LOG(VERBOSE) << " catch: (not caught)";
} else {
- LOG(VERBOSE) << StringPrintf(" catch: %d %llx %x %lld (%s.%s)", pCatchLoc->typeTag,
- pCatchLoc->classId, pCatchLoc->methodId, pCatchLoc->idx,
- Dbg::GetClassDescriptor(pCatchLoc->classId).c_str(),
- Dbg::GetMethodName(pCatchLoc->classId, pCatchLoc->methodId));
+ LOG(VERBOSE) << " catch: " << *pCatchLoc;
}
suspendPolicy = scanSuspendPolicy(matchList, matchCount);
diff --git a/src/jdwp/jdwp_handler.cc b/src/jdwp/jdwp_handler.cc
index 84e1afc..5bf95f1 100644
--- a/src/jdwp/jdwp_handler.cc
+++ b/src/jdwp/jdwp_handler.cc
@@ -109,7 +109,7 @@
uint32_t numArgs = Read4BE(&buf);
LOG(VERBOSE) << StringPrintf(" --> threadId=%llx objectId=%llx", threadId, objectId);
- LOG(VERBOSE) << StringPrintf(" classId=%llx methodId=%x %s.%s", classId, methodId, Dbg::GetClassDescriptor(classId).c_str(), Dbg::GetMethodName(classId, methodId));
+ LOG(VERBOSE) << StringPrintf(" classId=%llx methodId=%x %s.%s", classId, methodId, Dbg::GetClassDescriptor(classId).c_str(), Dbg::GetMethodName(classId, methodId).c_str());
LOG(VERBOSE) << StringPrintf(" %d args:", numArgs);
uint64_t* argArray = NULL;
@@ -516,14 +516,12 @@
*/
static JdwpError handleRT_SourceFile(JdwpState* state, const uint8_t* buf, int dataLen, ExpandBuf* pReply) {
RefTypeId refTypeId = ReadRefTypeId(&buf);
-
- const char* fileName = Dbg::GetSourceFile(refTypeId);
- if (fileName != NULL) {
- expandBufAddUtf8String(pReply, fileName);
- return ERR_NONE;
- } else {
+ std::string source_file;
+ if (!Dbg::GetSourceFile(refTypeId, source_file)) {
return ERR_ABSENT_INFORMATION;
}
+ expandBufAddUtf8String(pReply, source_file.c_str());
+ return ERR_NONE;
}
/*
@@ -730,7 +728,7 @@
RefTypeId refTypeId = ReadRefTypeId(&buf);
MethodId methodId = ReadMethodId(&buf);
- LOG(VERBOSE) << StringPrintf(" Req for line table in %s.%s", Dbg::GetClassDescriptor(refTypeId).c_str(), Dbg::GetMethodName(refTypeId,methodId));
+ LOG(VERBOSE) << StringPrintf(" Req for line table in %s.%s", Dbg::GetClassDescriptor(refTypeId).c_str(), Dbg::GetMethodName(refTypeId,methodId).c_str());
Dbg::OutputLineTable(refTypeId, methodId, pReply);
@@ -744,7 +742,7 @@
RefTypeId classId = ReadRefTypeId(&buf);
MethodId methodId = ReadMethodId(&buf);
- LOG(VERBOSE) << StringPrintf(" Req for LocalVarTab in class=%s method=%s", Dbg::GetClassDescriptor(classId).c_str(), Dbg::GetMethodName(classId, methodId));
+ LOG(VERBOSE) << StringPrintf(" Req for LocalVarTab in class=%s method=%s", Dbg::GetClassDescriptor(classId).c_str(), Dbg::GetMethodName(classId, methodId).c_str());
/*
* We could return ERR_ABSENT_INFORMATION here if the DEX file was
diff --git a/src/jdwp/jdwp_main.cc b/src/jdwp/jdwp_main.cc
index 12b689c..3bc0d41 100644
--- a/src/jdwp/jdwp_main.cc
+++ b/src/jdwp/jdwp_main.cc
@@ -450,6 +450,13 @@
return os;
}
+std::ostream& operator<<(std::ostream& os, const JdwpLocation& rhs) {
+ // TODO: do we really want the Class* and Method* as pointers?
+ os << rhs.typeTag << " " << (void*) rhs.classId << " " << (void*) rhs.methodId << " " << rhs.idx
+ << " (" << Dbg::GetClassDescriptor(rhs.classId) << "." << Dbg::GetMethodName(rhs.classId, rhs.methodId) << ")";
+ return os;
+}
+
} // namespace JDWP
} // namespace art