Implement enough JDWP functionality that we can attach jdb.

You can also try "classes" and "classpath", though anything else
deadlocks because we're suspended but jdb thinks we aren't. I don't
think that's a new bug with this patch, though, so I'll look at that
next.

Change-Id: I54456b6a7fe72642be696c66aa485dc0c8a7f913
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 505f86f..3e547e4 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -818,6 +818,21 @@
   visitor(array_iftable_, arg);
 }
 
+void ClassLinker::VisitClasses(ClassVisitor* visitor, void* arg) const {
+  MutexLock mu(classes_lock_);
+  typedef Table::const_iterator It;  // TODO: C++0x auto
+  for (It it = classes_.begin(), end = classes_.end(); it != end; ++it) {
+    if (!visitor(it->second, arg)) {
+      return;
+    }
+  }
+  for (It it = image_classes_.begin(), end = image_classes_.end(); it != end; ++it) {
+    if (!visitor(it->second, arg)) {
+      return;
+    }
+  }
+}
+
 ClassLinker::~ClassLinker() {
   String::ResetClass();
   Field::ResetClass();
diff --git a/src/class_linker.h b/src/class_linker.h
index 478a260..5032d9f 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -40,6 +40,8 @@
 class InternTable;
 class ObjectLock;
 
+typedef bool (ClassVisitor)(Class* c, void* arg);
+
 class ClassLinker {
  public:
   // Creates the class linker by boot strapping from dex files.
@@ -205,6 +207,8 @@
     return boot_class_path_;
   }
 
+  void VisitClasses(ClassVisitor* visitor, void* arg) const;
+
   void VisitRoots(Heap::RootVisitor* visitor, void* arg) const;
 
   const DexFile& FindDexFile(const DexCache* dex_cache) const;
diff --git a/src/debugger.cc b/src/debugger.cc
index 6d3f80c..cdb17d3 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -64,6 +64,13 @@
     return map_.find(id) != map_.end();
   }
 
+  template<typename T> T Get(JDWP::ObjectId id) {
+    MutexLock mu(lock_);
+    typedef std::map<JDWP::ObjectId, Object*>::iterator It; // C++0x auto
+    It it = map_.find(id);
+    return (it != map_.end()) ? reinterpret_cast<T>(it->second) : NULL;
+  }
+
   void VisitRoots(Heap::RootVisitor* visitor, void* arg) {
     MutexLock mu(lock_);
     typedef std::map<JDWP::ObjectId, Object*>::iterator It; // C++0x auto
@@ -265,7 +272,7 @@
   // If a debugger has already attached, send the "welcome" message.
   // This may cause us to suspend all threads.
   if (gJdwpState->IsActive()) {
-    //ScopedThreadStateChange(Thread::Current(), Thread::kRunnable);
+    //ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
     if (!gJdwpState->PostVMStart()) {
       LOG(WARNING) << "failed to post 'start' message to debugger";
     }
@@ -315,8 +322,21 @@
   gDebuggerConnected = true;
 }
 
-void Dbg::Active() {
-  UNIMPLEMENTED(FATAL);
+void Dbg::GoActive() {
+  // Enable all debugging features, including scans for breakpoints.
+  // This is a no-op if we're already active.
+  // Only called from the JDWP handler thread.
+  if (gDebuggerActive) {
+    return;
+  }
+
+  LOG(INFO) << "Debugger is active";
+
+  // TODO: CHECK we don't have any outstanding breakpoints.
+
+  gDebuggerActive = true;
+
+  //dvmEnableAllSubMode(kSubModeDebuggerActive);
 }
 
 void Dbg::Disconnected() {
@@ -369,9 +389,9 @@
   }
 }
 
-const char* Dbg::GetClassDescriptor(JDWP::RefTypeId id) {
-  UNIMPLEMENTED(FATAL);
-  return NULL;
+std::string Dbg::GetClassDescriptor(JDWP::RefTypeId classId) {
+  Class* c = gRegistry->Get<Class*>(classId);
+  return c->GetDescriptor()->ToModifiedUtf8();
 }
 
 JDWP::ObjectId Dbg::GetClassObject(JDWP::RefTypeId id) {
@@ -399,16 +419,55 @@
   return false;
 }
 
-void Dbg::GetClassList(uint32_t* pNumClasses, JDWP::RefTypeId** pClassRefBuf) {
-  UNIMPLEMENTED(FATAL);
+void Dbg::GetClassList(uint32_t* pClassCount, JDWP::RefTypeId** pClasses) {
+  // Get the complete list of reference classes (i.e. all classes except
+  // the primitive types).
+  // Returns a newly-allocated buffer full of RefTypeId values.
+  struct ClassListCreator {
+    static bool Visit(Class* c, void* arg) {
+      return reinterpret_cast<ClassListCreator*>(arg)->Visit(c);
+    }
+
+    bool Visit(Class* c) {
+      if (!c->IsPrimitive()) {
+        classes.push_back(static_cast<JDWP::RefTypeId>(gRegistry->Add(c)));
+      }
+      return true;
+    }
+
+    std::vector<JDWP::RefTypeId> classes;
+  };
+
+  ClassListCreator clc;
+  Runtime::Current()->GetClassLinker()->VisitClasses(ClassListCreator::Visit, &clc);
+  *pClassCount = clc.classes.size();
+  *pClasses = new JDWP::RefTypeId[clc.classes.size()];
+  for (size_t i = 0; i < clc.classes.size(); ++i) {
+    (*pClasses)[i] = clc.classes[i];
+  }
 }
 
 void Dbg::GetVisibleClassList(JDWP::ObjectId classLoaderId, uint32_t* pNumClasses, JDWP::RefTypeId** pClassRefBuf) {
   UNIMPLEMENTED(FATAL);
 }
 
-void Dbg::GetClassInfo(JDWP::RefTypeId classId, uint8_t* pTypeTag, uint32_t* pStatus, const char** pSignature) {
-  UNIMPLEMENTED(FATAL);
+void Dbg::GetClassInfo(JDWP::RefTypeId classId, uint8_t* pTypeTag, uint32_t* pStatus, std::string* pDescriptor) {
+  Class* c = gRegistry->Get<Class*>(classId);
+  if (c->IsArrayClass()) {
+    *pStatus = JDWP::CS_VERIFIED | JDWP::CS_PREPARED;
+    *pTypeTag = JDWP::TT_ARRAY;
+  } else {
+    if (c->IsErroneous()) {
+      *pStatus = JDWP::CS_ERROR;
+    } else {
+      *pStatus = JDWP::CS_VERIFIED | JDWP::CS_PREPARED | JDWP::CS_INITIALIZED;
+    }
+    *pTypeTag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS;
+  }
+
+  if (pDescriptor != NULL) {
+    *pDescriptor = c->GetDescriptor()->ToModifiedUtf8();
+  }
 }
 
 bool Dbg::FindLoadedClassBySignature(const char* classDescriptor, JDWP::RefTypeId* pRefTypeId) {
@@ -598,12 +657,52 @@
 
 //void Dbg::WaitForSuspend(JDWP::ObjectId threadId);
 
+void Dbg::GetThreadGroupThreadsImpl(Object* thread_group, JDWP::ObjectId** ppThreadIds, uint32_t* pThreadCount) {
+  struct ThreadListVisitor {
+    static void Visit(Thread* t, void* arg) {
+      reinterpret_cast<ThreadListVisitor*>(arg)->Visit(t);
+    }
+
+    void Visit(Thread* t) {
+      if (t == Dbg::GetDebugThread()) {
+        // Skip the JDWP thread. Some debuggers get bent out of shape when they can't suspend and
+        // query all threads, so it's easier if we just don't tell them about this thread.
+        return;
+      }
+      if (thread_group == NULL || t->GetThreadGroup() == thread_group) {
+        threads.push_back(gRegistry->Add(t->GetPeer()));
+      }
+    }
+
+    Object* thread_group;
+    std::vector<JDWP::ObjectId> threads;
+  };
+
+  ThreadListVisitor tlv;
+  tlv.thread_group = thread_group;
+
+  {
+    ScopedThreadListLock thread_list_lock;
+    Runtime::Current()->GetThreadList()->ForEach(ThreadListVisitor::Visit, &tlv);
+  }
+
+  *pThreadCount = tlv.threads.size();
+  if (*pThreadCount == 0) {
+    *ppThreadIds = NULL;
+  } else {
+    *ppThreadIds = new JDWP::ObjectId[*pThreadCount];
+    for (size_t i = 0; i < *pThreadCount; ++i) {
+      (*ppThreadIds)[i] = tlv.threads[i];
+    }
+  }
+}
+
 void Dbg::GetThreadGroupThreads(JDWP::ObjectId threadGroupId, JDWP::ObjectId** ppThreadIds, uint32_t* pThreadCount) {
-  UNIMPLEMENTED(FATAL);
+  GetThreadGroupThreadsImpl(gRegistry->Get<Object*>(threadGroupId), ppThreadIds, pThreadCount);
 }
 
 void Dbg::GetAllThreads(JDWP::ObjectId** ppThreadIds, uint32_t* pThreadCount) {
-  UNIMPLEMENTED(FATAL);
+  GetThreadGroupThreadsImpl(NULL, ppThreadIds, pThreadCount);
 }
 
 int Dbg::GetThreadFrameCount(JDWP::ObjectId threadId) {
@@ -621,6 +720,7 @@
 }
 
 void Dbg::SuspendVM() {
+  ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable); // TODO: do we really want to change back? should the JDWP thread be Runnable usually?
   Runtime::Current()->GetThreadList()->SuspendAll(true);
 }
 
@@ -800,7 +900,7 @@
   return true;
 }
 
-void DdmBroadcast(bool connect) {
+void Dbg::DdmBroadcast(bool connect) {
   LOG(VERBOSE) << "Broadcasting DDM " << (connect ? "connect" : "disconnect") << "...";
 
   Thread* self = Thread::Current();
@@ -822,11 +922,11 @@
 }
 
 void Dbg::DdmConnected() {
-  DdmBroadcast(true);
+  Dbg::DdmBroadcast(true);
 }
 
 void Dbg::DdmDisconnected() {
-  DdmBroadcast(false);
+  Dbg::DdmBroadcast(false);
   gDdmThreadNotification = false;
 }
 
@@ -859,7 +959,7 @@
   }
 }
 
-void DdmSendThreadStartCallback(Thread* t, void*) {
+static void DdmSendThreadStartCallback(Thread* t, void*) {
   Dbg::DdmSendThreadNotification(t, CHUNK_TYPE("THCR"));
 }
 
@@ -875,7 +975,7 @@
   }
 }
 
-void PostThreadStartOrStop(Thread* t, uint32_t type) {
+void Dbg::PostThreadStartOrStop(Thread* t, uint32_t type) {
   if (gDebuggerActive) {
     JDWP::ObjectId id = gRegistry->Add(t->GetPeer());
     gJdwpState->PostThreadChange(id, type == CHUNK_TYPE("THCR"));
@@ -884,11 +984,11 @@
 }
 
 void Dbg::PostThreadStart(Thread* t) {
-  PostThreadStartOrStop(t, CHUNK_TYPE("THCR"));
+  Dbg::PostThreadStartOrStop(t, CHUNK_TYPE("THCR"));
 }
 
 void Dbg::PostThreadDeath(Thread* t) {
-  PostThreadStartOrStop(t, CHUNK_TYPE("THDE"));
+  Dbg::PostThreadStartOrStop(t, CHUNK_TYPE("THDE"));
 }
 
 void Dbg::DdmSendChunk(uint32_t type, size_t byte_count, const uint8_t* buf) {
@@ -1067,7 +1167,13 @@
     Reset();
   }
 
+  static void HeapChunkCallback(const void* chunk_ptr, size_t chunk_len, const void* user_ptr, size_t user_len, void* arg) {
+    reinterpret_cast<HeapChunkContext*>(arg)->HeapChunkCallback(chunk_ptr, chunk_len, user_ptr, user_len);
+  }
+
  private:
+  enum { ALLOCATION_UNIT_SIZE = 8 };
+
   void Reset() {
     p = &buf[0];
     totalAllocationUnits = 0;
@@ -1075,105 +1181,92 @@
     pieceLenField = NULL;
   }
 
-  DISALLOW_COPY_AND_ASSIGN(HeapChunkContext);
-};
+  void HeapChunkCallback(const void* chunk_ptr, size_t chunk_len, const void* user_ptr, size_t user_len) {
+    CHECK_EQ((chunk_len & (ALLOCATION_UNIT_SIZE-1)), 0U);
 
-#define ALLOCATION_UNIT_SIZE 8
+    /* Make sure there's enough room left in the buffer.
+     * We need to use two bytes for every fractional 256
+     * allocation units used by the chunk.
+     */
+    {
+      size_t needed = (((chunk_len/ALLOCATION_UNIT_SIZE + 255) / 256) * 2);
+      size_t bytesLeft = buf.size() - (size_t)(p - &buf[0]);
+      if (bytesLeft < needed) {
+        Flush();
+      }
 
-uint8_t ExamineObject(const Object* o, bool is_native_heap) {
-  if (o == NULL) {
-    return HPSG_STATE(SOLIDITY_FREE, 0);
+      bytesLeft = buf.size() - (size_t)(p - &buf[0]);
+      if (bytesLeft < needed) {
+        LOG(WARNING) << "chunk is too big to transmit (chunk_len=" << chunk_len << ", " << needed << " bytes)";
+        return;
+      }
+    }
+
+    // OLD-TODO: notice when there's a gap and start a new heap, or at least a new range.
+    EnsureHeader(chunk_ptr);
+
+    // Determine the type of this chunk.
+    // OLD-TODO: if context.merge, see if this chunk is different from the last chunk.
+    // If it's the same, we should combine them.
+    uint8_t state = ExamineObject(reinterpret_cast<const Object*>(user_ptr), (type == CHUNK_TYPE("NHSG")));
+
+    // Write out the chunk description.
+    chunk_len /= ALLOCATION_UNIT_SIZE;   // convert to allocation units
+    totalAllocationUnits += chunk_len;
+    while (chunk_len > 256) {
+      *p++ = state | HPSG_PARTIAL;
+      *p++ = 255;     // length - 1
+      chunk_len -= 256;
+    }
+    *p++ = state;
+    *p++ = chunk_len - 1;
   }
 
-  // It's an allocated chunk. Figure out what it is.
+  uint8_t ExamineObject(const Object* o, bool is_native_heap) {
+    if (o == NULL) {
+      return HPSG_STATE(SOLIDITY_FREE, 0);
+    }
 
-  // If we're looking at the native heap, we'll just return
-  // (SOLIDITY_HARD, KIND_NATIVE) for all allocated chunks.
-  if (is_native_heap || !Heap::IsLiveObjectLocked(o)) {
-    return HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE);
-  }
+    // It's an allocated chunk. Figure out what it is.
 
-  Class* c = o->GetClass();
-  if (c == NULL) {
-    // The object was probably just created but hasn't been initialized yet.
+    // If we're looking at the native heap, we'll just return
+    // (SOLIDITY_HARD, KIND_NATIVE) for all allocated chunks.
+    if (is_native_heap || !Heap::IsLiveObjectLocked(o)) {
+      return HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE);
+    }
+
+    Class* c = o->GetClass();
+    if (c == NULL) {
+      // The object was probably just created but hasn't been initialized yet.
+      return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
+    }
+
+    if (!Heap::IsHeapAddress(c)) {
+      LOG(WARNING) << "invalid class for managed heap object: " << o << " " << c;
+      return HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
+    }
+
+    if (c->IsClassClass()) {
+      return HPSG_STATE(SOLIDITY_HARD, KIND_CLASS_OBJECT);
+    }
+
+    if (c->IsArrayClass()) {
+      if (o->IsObjectArray()) {
+        return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
+      }
+      switch (c->GetComponentSize()) {
+      case 1: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_1);
+      case 2: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_2);
+      case 4: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
+      case 8: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_8);
+      }
+    }
+
     return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
   }
 
-  if (!Heap::IsHeapAddress(c)) {
-    LOG(WARNING) << "invalid class for managed heap object: " << o << " " << c;
-    return HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
-  }
-
-  if (c->IsClassClass()) {
-    return HPSG_STATE(SOLIDITY_HARD, KIND_CLASS_OBJECT);
-  }
-
-  if (c->IsArrayClass()) {
-    if (o->IsObjectArray()) {
-      return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
-    }
-    switch (c->GetComponentSize()) {
-    case 1: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_1);
-    case 2: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_2);
-    case 4: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
-    case 8: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_8);
-    }
-  }
-
-  return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
-}
-
-static void HeapChunkCallback(const void* chunk_ptr, size_t chunk_len, const void* user_ptr, size_t user_len, void* arg) {
-  HeapChunkContext* context = reinterpret_cast<HeapChunkContext*>(arg);
-
-  CHECK_EQ((chunk_len & (ALLOCATION_UNIT_SIZE-1)), 0U);
-
-  /* Make sure there's enough room left in the buffer.
-   * We need to use two bytes for every fractional 256
-   * allocation units used by the chunk.
-   */
-  {
-    size_t needed = (((chunk_len/ALLOCATION_UNIT_SIZE + 255) / 256) * 2);
-    size_t bytesLeft = context->buf.size() - (size_t)(context->p - &context->buf[0]);
-    if (bytesLeft < needed) {
-      context->Flush();
-    }
-
-    bytesLeft = context->buf.size() - (size_t)(context->p - &context->buf[0]);
-    if (bytesLeft < needed) {
-      LOG(WARNING) << "chunk is too big to transmit (chunk_len=" << chunk_len << ", " << needed << " bytes)";
-      return;
-    }
-  }
-
-  // OLD-TODO: notice when there's a gap and start a new heap, or at least a new range.
-  context->EnsureHeader(chunk_ptr);
-
-  // Determine the type of this chunk.
-  // OLD-TODO: if context.merge, see if this chunk is different from the last chunk.
-  // If it's the same, we should combine them.
-  uint8_t state = ExamineObject(reinterpret_cast<const Object*>(user_ptr), (context->type == CHUNK_TYPE("NHSG")));
-
-  // Write out the chunk description.
-  chunk_len /= ALLOCATION_UNIT_SIZE;   // convert to allocation units
-  context->totalAllocationUnits += chunk_len;
-  while (chunk_len > 256) {
-    *context->p++ = state | HPSG_PARTIAL;
-    *context->p++ = 255;     // length - 1
-    chunk_len -= 256;
-  }
-  *context->p++ = state;
-  *context->p++ = chunk_len - 1;
-}
-
-static void WalkHeap(bool merge, bool native) {
-  HeapChunkContext context(merge, native);
-  if (native) {
-    dlmalloc_walk_heap(HeapChunkCallback, &context);
-  } else {
-    Heap::WalkHeap(HeapChunkCallback, &context);
-  }
-}
+  DISALLOW_COPY_AND_ASSIGN(HeapChunkContext);
+};
 
 void Dbg::DdmSendHeapSegments(bool native) {
   Dbg::HpsgWhen when;
@@ -1198,7 +1291,12 @@
   Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHST") : CHUNK_TYPE("HPST"), sizeof(heap_id), heap_id);
 
   // Send a series of heap segment chunks.
-  WalkHeap((what == HPSG_WHAT_MERGED_OBJECTS), native);
+  HeapChunkContext context((what == HPSG_WHAT_MERGED_OBJECTS), native);
+  if (native) {
+    dlmalloc_walk_heap(HeapChunkContext::HeapChunkCallback, &context);
+  } else {
+    Heap::WalkHeap(HeapChunkContext::HeapChunkCallback, &context);
+  }
 
   // Finally, send a heap end chunk.
   Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHEN") : CHUNK_TYPE("HPEN"), sizeof(heap_id), heap_id);
diff --git a/src/debugger.h b/src/debugger.h
index 35ef619..c2e8661 100644
--- a/src/debugger.h
+++ b/src/debugger.h
@@ -88,7 +88,7 @@
    * when the debugger attaches.
    */
   static void Connected();
-  static void Active();
+  static void GoActive();
   static void Disconnected();
 
   /*
@@ -125,7 +125,7 @@
   /*
    * Class, Object, Array
    */
-  static const char* GetClassDescriptor(JDWP::RefTypeId id);
+  static std::string GetClassDescriptor(JDWP::RefTypeId id);
   static JDWP::ObjectId GetClassObject(JDWP::RefTypeId id);
   static JDWP::RefTypeId GetSuperclass(JDWP::RefTypeId id);
   static JDWP::ObjectId GetClassLoader(JDWP::RefTypeId id);
@@ -133,7 +133,7 @@
   static bool IsInterface(JDWP::RefTypeId id);
   static void GetClassList(uint32_t* pNumClasses, JDWP::RefTypeId** pClassRefBuf);
   static void GetVisibleClassList(JDWP::ObjectId classLoaderId, uint32_t* pNumClasses, JDWP::RefTypeId** pClassRefBuf);
-  static void GetClassInfo(JDWP::RefTypeId classId, uint8_t* pTypeTag, uint32_t* pStatus, const char** pSignature);
+  static void GetClassInfo(JDWP::RefTypeId classId, uint8_t* pTypeTag, uint32_t* pStatus, std::string* pDescriptor);
   static bool FindLoadedClassBySignature(const char* classDescriptor, JDWP::RefTypeId* pRefTypeId);
   static void GetObjectType(JDWP::ObjectId objectId, uint8_t* pRefTypeTag, JDWP::RefTypeId* pRefTypeId);
   static uint8_t GetClassObjectType(JDWP::RefTypeId refTypeId);
@@ -273,6 +273,10 @@
   static void DdmSendHeapSegments(bool native);
 
  private:
+  static void DdmBroadcast(bool);
+  static void GetThreadGroupThreadsImpl(Object*, JDWP::ObjectId**, uint32_t*);
+  static void PostThreadStartOrStop(Thread*, uint32_t);
+
   static AllocRecord* recent_allocation_records_;
 };
 
diff --git a/src/jdwp/jdwp_event.cc b/src/jdwp/jdwp_event.cc
index 6b5ef8c..8e0d4c3 100644
--- a/src/jdwp/jdwp_event.cc
+++ b/src/jdwp/jdwp_event.cc
@@ -109,7 +109,7 @@
  */
 struct ModBasket {
   const JdwpLocation* pLoc;           /* LocationOnly */
-  const char*         className;      /* ClassMatch/ClassExclude */
+  std::string         className;      /* ClassMatch/ClassExclude */
   ObjectId            threadId;       /* ThreadOnly */
   RefTypeId           classId;        /* ClassOnly */
   RefTypeId           excepClassId;   /* ExceptionOnly */
@@ -374,23 +374,22 @@
  *
  * ("Restricted name globbing" might have been a better term.)
  */
-static bool patternMatch(const char* pattern, const char* target) {
-  int patLen = strlen(pattern);
+static bool patternMatch(const char* pattern, const std::string& target) {
+  size_t patLen = strlen(pattern);
 
   if (pattern[0] == '*') {
-    int targetLen = strlen(target);
     patLen--;
     // TODO: remove printf when we find a test case to verify this
-    LOG(ERROR) << ">>> comparing '" << (pattern + 1) << "' to '" << (target + (targetLen-patLen)) << "'";
+    LOG(ERROR) << ">>> comparing '" << (pattern + 1) << "' to '" << (target.c_str() + (target.size()-patLen)) << "'";
 
-    if (targetLen < patLen) {
+    if (target.size() < patLen) {
       return false;
     }
-    return strcmp(pattern+1, target + (targetLen-patLen)) == 0;
+    return strcmp(pattern+1, target.c_str() + (target.size()-patLen)) == 0;
   } else if (pattern[patLen-1] == '*') {
-    return strncmp(pattern, target, patLen-1) == 0;
+    return strncmp(pattern, target.c_str(), patLen-1) == 0;
   } else {
-    return strcmp(pattern, target) == 0;
+    return strcmp(pattern, target.c_str()) == 0;
   }
 }
 
@@ -734,8 +733,8 @@
 
 // TODO: This doesn't behave like the real dvmDescriptorToName.
 // I'm hoping this isn't used to communicate with the debugger, and we can just use descriptors.
-char* dvmDescriptorToName(const char* descriptor) {
-  return strdup(descriptor);
+std::string dvmDescriptorToName(const std::string& descriptor) {
+  return descriptor;
 }
 
 /*
@@ -761,14 +760,13 @@
  */
 bool PostLocationEvent(JdwpState* state, const JdwpLocation* pLoc, ObjectId thisPtr, int eventFlags) {
   ModBasket basket;
-  char* nameAlloc = NULL;
 
   memset(&basket, 0, sizeof(basket));
   basket.pLoc = pLoc;
   basket.classId = pLoc->classId;
   basket.thisPtr = thisPtr;
   basket.threadId = Dbg::GetThreadSelfId();
-  basket.className = nameAlloc = dvmDescriptorToName(Dbg::GetClassDescriptor(pLoc->classId));
+  basket.className = dvmDescriptorToName(Dbg::GetClassDescriptor(pLoc->classId));
 
   /*
    * On rare occasions we may need to execute interpreted code in the VM
@@ -778,7 +776,6 @@
    */
   if (basket.threadId == state->debugThreadId) {
     LOG(VERBOSE) << "Ignoring location event in JDWP thread";
-    free(nameAlloc);
     return false;
   }
 
@@ -793,7 +790,6 @@
    */
   if (invokeInProgress(state)) {
     LOG(VERBOSE) << "Not checking breakpoints during invoke (" << basket.className << ")";
-    free(nameAlloc);
     return false;
   }
 
@@ -854,7 +850,6 @@
     Dbg::ThreadContinuing(old_state);
   }
 
-  free(nameAlloc);
   return matchCount != 0;
 }
 
@@ -963,13 +958,12 @@
     const JdwpLocation* pCatchLoc, ObjectId thisPtr)
 {
   ModBasket basket;
-  char* nameAlloc = NULL;
 
   memset(&basket, 0, sizeof(basket));
   basket.pLoc = pThrowLoc;
   basket.classId = pThrowLoc->classId;
   basket.threadId = Dbg::GetThreadSelfId();
-  basket.className = nameAlloc = dvmDescriptorToName(Dbg::GetClassDescriptor(basket.classId));
+  basket.className = dvmDescriptorToName(Dbg::GetClassDescriptor(basket.classId));
   basket.excepClassId = exceptionClassId;
   basket.caught = (pCatchLoc->classId != 0);
   basket.thisPtr = thisPtr;
@@ -977,7 +971,6 @@
   /* don't try to post an exception caused by the debugger */
   if (invokeInProgress(state)) {
     LOG(VERBOSE) << "Not posting exception hit during invoke (" << basket.className << ")";
-    free(nameAlloc);
     return false;
   }
 
@@ -998,14 +991,14 @@
                  << " 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),
+        Dbg::GetClassDescriptor(pThrowLoc->classId).c_str(),
         Dbg::GetMethodName(pThrowLoc->classId, pThrowLoc->methodId));
     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),
+          Dbg::GetClassDescriptor(pCatchLoc->classId).c_str(),
           Dbg::GetMethodName(pCatchLoc->classId, pCatchLoc->methodId));
     }
 
@@ -1047,7 +1040,6 @@
     Dbg::ThreadContinuing(old_state);
   }
 
-  free(nameAlloc);
   return matchCount != 0;
 }
 
@@ -1059,17 +1051,15 @@
  */
 bool PostClassPrepare(JdwpState* state, int tag, RefTypeId refTypeId, const char* signature, int status) {
   ModBasket basket;
-  char* nameAlloc = NULL;
 
   memset(&basket, 0, sizeof(basket));
   basket.classId = refTypeId;
   basket.threadId = Dbg::GetThreadSelfId();
-  basket.className = nameAlloc = dvmDescriptorToName(Dbg::GetClassDescriptor(basket.classId));
+  basket.className = dvmDescriptorToName(Dbg::GetClassDescriptor(basket.classId));
 
   /* suppress class prep caused by debugger */
   if (invokeInProgress(state)) {
     LOG(VERBOSE) << "Not posting class prep caused by invoke (" << basket.className << ")";
-    free(nameAlloc);
     return false;
   }
 
@@ -1114,7 +1104,7 @@
 
       expandBufAdd1(pReq, tag);
       expandBufAdd8BE(pReq, refTypeId);
-      expandBufAddUtf8String(pReq, (const uint8_t*) signature);
+      expandBufAddUtf8String(pReq, signature);
       expandBufAdd4BE(pReq, status);
     }
   }
@@ -1135,7 +1125,6 @@
     Dbg::ThreadContinuing(old_state);
   }
 
-  free(nameAlloc);
   return matchCount != 0;
 }
 
diff --git a/src/jdwp/jdwp_expand_buf.cc b/src/jdwp/jdwp_expand_buf.cc
index f5e24b2..30ebf00 100644
--- a/src/jdwp/jdwp_expand_buf.cc
+++ b/src/jdwp/jdwp_expand_buf.cc
@@ -153,8 +153,7 @@
   pBuf->curLen += sizeof(val);
 }
 
-static void SetUtf8String(uint8_t* buf, const uint8_t* str) {
-  uint32_t strLen = strlen((const char*)str);
+static void SetUtf8String(uint8_t* buf, const char* str, size_t strLen) {
   Set4BE(buf, strLen);
   memcpy(buf + sizeof(uint32_t), str, strLen);
 }
@@ -167,11 +166,11 @@
  * they can be null-terminated (either they don't have null bytes or they
  * have stored null bytes in a multi-byte encoding).
  */
-void expandBufAddUtf8String(ExpandBuf* pBuf, const uint8_t* str) {
-  int strLen = strlen((const char*)str);
+void expandBufAddUtf8String(ExpandBuf* pBuf, const char* str) {
+  int strLen = strlen(str);
 
   ensureSpace(pBuf, sizeof(uint32_t) + strLen);
-  SetUtf8String(pBuf->storage + pBuf->curLen, str);
+  SetUtf8String(pBuf->storage + pBuf->curLen, str, strLen);
   pBuf->curLen += sizeof(uint32_t) + strLen;
 }
 
diff --git a/src/jdwp/jdwp_expand_buf.h b/src/jdwp/jdwp_expand_buf.h
index 2c19f54..287f05e 100644
--- a/src/jdwp/jdwp_expand_buf.h
+++ b/src/jdwp/jdwp_expand_buf.h
@@ -56,7 +56,7 @@
 void expandBufAdd2BE(ExpandBuf* pBuf, uint16_t val);
 void expandBufAdd4BE(ExpandBuf* pBuf, uint32_t val);
 void expandBufAdd8BE(ExpandBuf* pBuf, uint64_t val);
-void expandBufAddUtf8String(ExpandBuf* pBuf, const uint8_t* str);
+void expandBufAddUtf8String(ExpandBuf* pBuf, const char* str);
 
 }  // namespace JDWP
 
diff --git a/src/jdwp/jdwp_handler.cc b/src/jdwp/jdwp_handler.cc
index 04c4734..e5e18bb 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), Dbg::GetMethodName(classId, methodId));
+  LOG(VERBOSE) << StringPrintf("        classId=%llx methodId=%x %s.%s", classId, methodId, Dbg::GetClassDescriptor(classId).c_str(), Dbg::GetMethodName(classId, methodId));
   LOG(VERBOSE) << StringPrintf("        %d args:", numArgs);
 
   uint64_t* argArray = NULL;
@@ -178,14 +178,14 @@
 static JdwpError handleVM_Version(JdwpState* state, const uint8_t* buf, int dataLen, ExpandBuf* pReply) {
   /* text information on runtime version */
   std::string version(StringPrintf("Android Runtime %s", Runtime::Current()->GetVersion()));
-  expandBufAddUtf8String(pReply, (const uint8_t*) version.c_str());
+  expandBufAddUtf8String(pReply, version.c_str());
   /* JDWP version numbers */
   expandBufAdd4BE(pReply, 1);        // major
   expandBufAdd4BE(pReply, 5);        // minor
   /* VM JRE version */
-  expandBufAddUtf8String(pReply, (const uint8_t*) "1.6.0");  /* e.g. 1.6.0_22 */
+  expandBufAddUtf8String(pReply, "1.6.0");  /* e.g. 1.6.0_22 */
   /* target VM name */
-  expandBufAddUtf8String(pReply, (const uint8_t*) "DalvikVM");
+  expandBufAddUtf8String(pReply, "DalvikVM");
 
   return ERR_NONE;
 }
@@ -382,10 +382,10 @@
   uint32_t classPaths = 1;
   uint32_t bootClassPaths = 0;
 
-  expandBufAddUtf8String(pReply, (const uint8_t*) baseDir);
+  expandBufAddUtf8String(pReply, baseDir);
   expandBufAdd4BE(pReply, classPaths);
   for (uint32_t i = 0; i < classPaths; i++) {
-    expandBufAddUtf8String(pReply, (const uint8_t*) ".");
+    expandBufAddUtf8String(pReply, ".");
   }
 
   expandBufAdd4BE(pReply, bootClassPaths);
@@ -450,16 +450,16 @@
   expandBufAdd4BE(pReply, numClasses);
 
   for (uint32_t i = 0; i < numClasses; i++) {
-    static const uint8_t genericSignature[1] = "";
+    static const char genericSignature[1] = "";
     uint8_t refTypeTag;
-    const char* signature;
+    std::string descriptor;
     uint32_t status;
 
-    Dbg::GetClassInfo(classRefBuf[i], &refTypeTag, &status, &signature);
+    Dbg::GetClassInfo(classRefBuf[i], &refTypeTag, &status, &descriptor);
 
     expandBufAdd1(pReply, refTypeTag);
     expandBufAddRefTypeId(pReply, classRefBuf[i]);
-    expandBufAddUtf8String(pReply, (const uint8_t*) signature);
+    expandBufAddUtf8String(pReply, descriptor.c_str());
     expandBufAddUtf8String(pReply, genericSignature);
     expandBufAdd4BE(pReply, status);
   }
@@ -478,7 +478,7 @@
 
   LOG(VERBOSE) << StringPrintf("  Req for signature of refTypeId=0x%llx", refTypeId);
   const char* signature = Dbg::GetSignature(refTypeId);
-  expandBufAddUtf8String(pReply, (const uint8_t*) signature);
+  expandBufAddUtf8String(pReply, signature);
 
   return ERR_NONE;
 }
@@ -519,7 +519,7 @@
 
   const char* fileName = Dbg::GetSourceFile(refTypeId);
   if (fileName != NULL) {
-    expandBufAddUtf8String(pReply, (const uint8_t*) fileName);
+    expandBufAddUtf8String(pReply, fileName);
     return ERR_NONE;
   } else {
     return ERR_ABSENT_INFORMATION;
@@ -546,8 +546,7 @@
 static JdwpError handleRT_Interfaces(JdwpState* state, const uint8_t* buf, int dataLen, ExpandBuf* pReply) {
   RefTypeId refTypeId = ReadRefTypeId(&buf);
 
-  LOG(VERBOSE) << StringPrintf("  Req for interfaces in %llx (%s)", refTypeId,
-  Dbg::GetClassDescriptor(refTypeId));
+  LOG(VERBOSE) << StringPrintf("  Req for interfaces in %llx (%s)", refTypeId, Dbg::GetClassDescriptor(refTypeId).c_str());
 
   Dbg::OutputAllInterfaces(refTypeId, pReply);
 
@@ -582,17 +581,17 @@
  * Like RT_Signature but with the possibility of a "generic signature".
  */
 static JdwpError handleRT_SignatureWithGeneric(JdwpState* state, const uint8_t* buf, int dataLen, ExpandBuf* pReply) {
-  static const uint8_t genericSignature[1] = "";
+  static const char genericSignature[1] = "";
 
   RefTypeId refTypeId = ReadRefTypeId(&buf);
 
   LOG(VERBOSE) << StringPrintf("  Req for signature of refTypeId=0x%llx", refTypeId);
   const char* signature = Dbg::GetSignature(refTypeId);
   if (signature != NULL) {
-    expandBufAddUtf8String(pReply, (const uint8_t*) signature);
+    expandBufAddUtf8String(pReply, signature);
   } else {
     LOG(WARNING) << StringPrintf("No signature for refTypeId=0x%llx", refTypeId);
-    expandBufAddUtf8String(pReply, (const uint8_t*) "Lunknown;");
+    expandBufAddUtf8String(pReply, "Lunknown;");
   }
   expandBufAddUtf8String(pReply, genericSignature);
 
@@ -714,7 +713,7 @@
   RefTypeId arrayTypeId = ReadRefTypeId(&buf);
   uint32_t length = Read4BE(&buf);
 
-  LOG(VERBOSE) << StringPrintf("Creating array %s[%u]", Dbg::GetClassDescriptor(arrayTypeId), length);
+  LOG(VERBOSE) << StringPrintf("Creating array %s[%u]", Dbg::GetClassDescriptor(arrayTypeId).c_str(), length);
   ObjectId objectId = Dbg::CreateArrayObject(arrayTypeId, length);
   if (objectId == 0) {
     return ERR_OUT_OF_MEMORY;
@@ -731,7 +730,7 @@
   RefTypeId refTypeId = ReadRefTypeId(&buf);
   MethodId methodId = ReadMethodId(&buf);
 
-  LOG(VERBOSE) << StringPrintf("  Req for line table in %s.%s", Dbg::GetClassDescriptor(refTypeId), Dbg::GetMethodName(refTypeId,methodId));
+  LOG(VERBOSE) << StringPrintf("  Req for line table in %s.%s", Dbg::GetClassDescriptor(refTypeId).c_str(), Dbg::GetMethodName(refTypeId,methodId));
 
   Dbg::OutputLineTable(refTypeId, methodId, pReply);
 
@@ -745,9 +744,7 @@
   RefTypeId classId = ReadRefTypeId(&buf);
   MethodId methodId = ReadMethodId(&buf);
 
-  LOG(VERBOSE) << StringPrintf("  Req for LocalVarTab in class=%s method=%s",
-  Dbg::GetClassDescriptor(classId),
-  Dbg::GetMethodName(classId, methodId));
+  LOG(VERBOSE) << StringPrintf("  Req for LocalVarTab in class=%s method=%s", Dbg::GetClassDescriptor(classId).c_str(), Dbg::GetMethodName(classId, methodId));
 
   /*
    * We could return ERR_ABSENT_INFORMATION here if the DEX file was
@@ -884,7 +881,7 @@
 
   LOG(VERBOSE) << StringPrintf("  Req for str %llx --> '%s'", stringObject, str);
 
-  expandBufAddUtf8String(pReply, (uint8_t*) str);
+  expandBufAddUtf8String(pReply, str);
   free(str);
 
   return ERR_NONE;
@@ -901,7 +898,7 @@
   if (name == NULL) {
     return ERR_INVALID_THREAD;
   }
-  expandBufAddUtf8String(pReply, (uint8_t*) name);
+  expandBufAddUtf8String(pReply, name);
   free(name);
 
   return ERR_NONE;
@@ -1086,9 +1083,9 @@
 
   char* name = Dbg::GetThreadGroupName(threadGroupId);
   if (name != NULL) {
-    expandBufAddUtf8String(pReply, (uint8_t*) name);
+    expandBufAddUtf8String(pReply, name);
   } else {
-    expandBufAddUtf8String(pReply, (uint8_t*) "BAD-GROUP-ID");
+    expandBufAddUtf8String(pReply, "BAD-GROUP-ID");
     LOG(VERBOSE) << StringPrintf("bad thread group ID");
   }
 
@@ -1310,7 +1307,7 @@
     case MK_CLASS_ONLY:     /* for ClassPrepare, MethodEntry */
       {
         RefTypeId clazzId = ReadRefTypeId(&buf);
-        LOG(VERBOSE) << StringPrintf("    ClassOnly: %llx (%s)", clazzId, Dbg::GetClassDescriptor(clazzId));
+        LOG(VERBOSE) << StringPrintf("    ClassOnly: %llx (%s)", clazzId, Dbg::GetClassDescriptor(clazzId).c_str());
         pEvent->mods[idx].classOnly.refTypeId = clazzId;
       }
       break;
@@ -1356,7 +1353,7 @@
         caught = Read1(&buf);
         uncaught = Read1(&buf);
         LOG(VERBOSE) << StringPrintf("    ExceptionOnly: type=%llx(%s) caught=%d uncaught=%d",
-            exceptionOrNull, (exceptionOrNull == 0) ? "null" : Dbg::GetClassDescriptor(exceptionOrNull), caught, uncaught);
+            exceptionOrNull, (exceptionOrNull == 0) ? "null" : Dbg::GetClassDescriptor(exceptionOrNull).c_str(), caught, uncaught);
 
         pEvent->mods[idx].exceptionOnly.refTypeId = exceptionOrNull;
         pEvent->mods[idx].exceptionOnly.caught = caught;
@@ -1524,7 +1521,7 @@
 static JdwpError handleCOR_ReflectedType(JdwpState* state, const uint8_t* buf, int dataLen, ExpandBuf* pReply) {
   RefTypeId classObjectId = ReadRefTypeId(&buf);
 
-  LOG(VERBOSE) << StringPrintf("  Req for refTypeId for class=%llx (%s)", classObjectId, Dbg::GetClassDescriptor(classObjectId));
+  LOG(VERBOSE) << StringPrintf("  Req for refTypeId for class=%llx (%s)", classObjectId, Dbg::GetClassDescriptor(classObjectId).c_str());
 
   /* just hand the type back to them */
   if (Dbg::IsInterface(classObjectId)) {
@@ -1732,7 +1729,7 @@
      * active debugger session, and zero out the last-activity timestamp
      * so waitForDebugger() doesn't return if we stall for a bit here.
      */
-    Dbg::Active();
+    Dbg::GoActive();
     QuasiAtomicSwap64(0, &lastActivityWhen);
   }
 
diff --git a/src/thread.cc b/src/thread.cc
index b8df84f..e6dc2ff 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -407,7 +407,7 @@
     priority = gThread_priority->GetInt(peer_);
     is_daemon = gThread_daemon->GetBoolean(peer_);
 
-    Object* thread_group = gThread_group->GetObject(peer_);
+    Object* thread_group = GetThreadGroup();
     if (thread_group != NULL) {
       String* group_name_string = reinterpret_cast<String*>(gThreadGroup_name->GetObject(thread_group));
       group_name = (group_name_string != NULL) ? group_name_string->ToModifiedUtf8() : "<null>";
@@ -840,7 +840,7 @@
   Object* handler = gThread_uncaughtHandler->GetObject(peer_);
   if (handler == NULL) {
     // Otherwise use the thread group's default handler.
-    handler = gThread_group->GetObject(peer_);
+    handler = GetThreadGroup();
   }
 
   // Call the handler.
@@ -854,10 +854,14 @@
   ClearException();
 }
 
+Object* Thread::GetThreadGroup() const {
+  return gThread_group->GetObject(peer_);
+}
+
 void Thread::RemoveFromThreadGroup() {
   // this.group.removeThread(this);
   // group can be null if we're in the compiler or a test.
-  Object* group = gThread_group->GetObject(peer_);
+  Object* group = GetThreadGroup();
   if (group != NULL) {
     Method* m = group->GetClass()->FindVirtualMethodForVirtualOrInterface(gThreadGroup_removeThread);
     Object* args = peer_;
diff --git a/src/thread.h b/src/thread.h
index 7b8c8b9..a4fd3bc 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -233,6 +233,8 @@
     return peer_;
   }
 
+  Object* GetThreadGroup() const;
+
   RuntimeStats* GetStats() {
     return &stats_;
   }