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