Various things.

This patch:

1. adds good-enough implementations for a couple of Class native methods we
need.

2. removes a fixed TODO from image_test.

3. adds missing null checks to the Thread native methods, and forwards
Thread.nativeCreate to Thread::Create.

4. stops jni_compiler from overwriting already-registered native methods
with the dynamic lookup stub.

5. adds workarounds to Thread::CreatePeer so we can cope better without
code, attempts to initialize thread groups correctly, dumps thread state
from the managed peer (where one exists), and implements Thread::DumpStack
for debugging (and SIGQUIT dumps).

Change-Id: I3281226dd22ec852dca5165748fc75a254904ac0
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 046fbbe..b4fb7aa 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -76,6 +76,7 @@
 	src/image_writer.cc \
 	src/indirect_reference_table.cc \
 	src/intern_table.cc \
+	src/java_lang_Class.cc \
 	src/java_lang_Object.cc \
 	src/java_lang_Runtime.cc \
 	src/java_lang_String.cc \
diff --git a/src/image_test.cc b/src/image_test.cc
index 0bd79db..02a7d69 100644
--- a/src/image_test.cc
+++ b/src/image_test.cc
@@ -71,8 +71,7 @@
   ASSERT_TRUE(boot_space != NULL);
 
   // enable to display maps to debug boot_base and boot_limit checking problems below
-  // TODO: why does this dump show two attached threads?
-  if (true) {
+  if (false) {
     SignalCatcher::HandleSigQuit();
   }
 
diff --git a/src/java_lang_Class.cc b/src/java_lang_Class.cc
new file mode 100644
index 0000000..e068d2e
--- /dev/null
+++ b/src/java_lang_Class.cc
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jni_internal.h"
+#include "class_linker.h"
+#include "object.h"
+
+#include "JniConstants.h" // Last to avoid problems with LOG redefinition.
+
+namespace art {
+
+namespace {
+
+jclass Class_getComponentType(JNIEnv* env, jobject javaThis) {
+  Class* c = Decode<Class*>(env, javaThis);
+  if (!c->IsArrayClass()) {
+    return NULL;
+  }
+
+  /*
+   * We can't just return c->GetComponentType(), because that gives
+   * us the base type (e.g. X[][][] returns X).  If this is a multi-
+   * dimensional array, we have to do the lookup by name.
+   */
+  Class* result;
+  std::string descriptor(c->GetDescriptor()->ToModifiedUtf8());
+  if (descriptor[1] == '[') {
+    result = Runtime::Current()->GetClassLinker()->FindClass(descriptor.c_str() + 1, c->GetClassLoader());
+  } else {
+    result = c->GetComponentType();
+  }
+  return AddLocalReference<jclass>(env, result);
+
+}
+
+jobjectArray Class_getDeclaredClasses(JNIEnv* env, jclass java_lang_Class_class, jclass c, jboolean publicOnly) {
+  UNIMPLEMENTED(WARNING);
+  return env->NewObjectArray(0, java_lang_Class_class, NULL);
+}
+
+static JNINativeMethod gMethods[] = {
+  //NATIVE_METHOD(Class, classForName, "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"),
+  //NATIVE_METHOD(Class, desiredAssertionStatus, "()Z"),
+  //NATIVE_METHOD(Class, getClassLoader, "(Ljava/lang/Class;)Ljava/lang/ClassLoader;"),
+  NATIVE_METHOD(Class, getComponentType, "()Ljava/lang/Class;"),
+  //NATIVE_METHOD(Class, getDeclaredAnnotation, "(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;"),
+  //NATIVE_METHOD(Class, getDeclaredAnnotations, "()[Ljava/lang/annotation/Annotation;"),
+  NATIVE_METHOD(Class, getDeclaredClasses, "(Ljava/lang/Class;Z)[Ljava/lang/Class;"),
+  //NATIVE_METHOD(Class, getDeclaredConstructorOrMethod, "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Member;"),
+  //NATIVE_METHOD(Class, getDeclaredConstructors, "(Ljava/lang/Class;Z)[Ljava/lang/reflect/Constructor;"),
+  //NATIVE_METHOD(Class, getDeclaredField, "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/reflect/Field;"),
+  //NATIVE_METHOD(Class, getDeclaredFields, "(Ljava/lang/Class;Z)[Ljava/lang/reflect/Field;"),
+  //NATIVE_METHOD(Class, getDeclaredMethods, "(Ljava/lang/Class;Z)[Ljava/lang/reflect/Method;"),
+  //NATIVE_METHOD(Class, getDeclaringClass, "()Ljava/lang/Class;"),
+  //NATIVE_METHOD(Class, getEnclosingClass, "()Ljava/lang/Class;"),
+  //NATIVE_METHOD(Class, getEnclosingConstructor, "()Ljava/lang/reflect/Constructor;"),
+  //NATIVE_METHOD(Class, getEnclosingMethod, "()Ljava/lang/reflect/Method;"),
+  //NATIVE_METHOD(Class, getInnerClassName, "()Ljava/lang/String;"),
+  //NATIVE_METHOD(Class, getInterfaces, "()[Ljava/lang/Class;"),
+  //NATIVE_METHOD(Class, getModifiers, "(Ljava/lang/Class;Z)I"),
+  //NATIVE_METHOD(Class, getNameNative, "()Ljava/lang/String;"),
+  //NATIVE_METHOD(Class, getSignatureAnnotation, "()[Ljava/lang/Object;"),
+  //NATIVE_METHOD(Class, getSuperclass, "()Ljava/lang/Class;"),
+  //NATIVE_METHOD(Class, isAnonymousClass, "()Z"),
+  //NATIVE_METHOD(Class, isAssignableFrom, "(Ljava/lang/Class;)Z"),
+  //NATIVE_METHOD(Class, isDeclaredAnnotationPresent, "(Ljava/lang/Class;)Z"),
+  //NATIVE_METHOD(Class, isInstance, "(Ljava/lang/Object;)Z"),
+  //NATIVE_METHOD(Class, isInterface, "()Z"),
+  //NATIVE_METHOD(Class, isPrimitive, "()Z"),
+  //NATIVE_METHOD(Class, newInstanceImpl, "()Ljava/lang/Object;"),
+};
+
+}  // namespace
+
+void register_java_lang_Class(JNIEnv* env) {
+  jniRegisterNativeMethods(env, "java/lang/Class", gMethods, NELEM(gMethods));
+}
+
+}  // namespace art
diff --git a/src/java_lang_Thread.cc b/src/java_lang_Thread.cc
index 44578ee..286a211 100644
--- a/src/java_lang_Thread.cc
+++ b/src/java_lang_Thread.cc
@@ -26,8 +26,7 @@
 namespace {
 
 jobject Thread_currentThread(JNIEnv* env, jclass) {
-  Object* peer = Decode<Object*>(env, Thread::Current()->GetPeer());
-  return AddLocalReference<jobject>(env, peer);
+  return AddLocalReference<jobject>(env, Thread::Current()->GetPeer());
 }
 
 jboolean Thread_interrupted(JNIEnv* env, jclass) {
@@ -37,19 +36,19 @@
 jboolean Thread_isInterrupted(JNIEnv* env, jobject javaThread) {
   ThreadListLock lock;
   Thread* thread = Thread::FromManagedThread(env, javaThread);
-  return thread->IsInterrupted();
+  return (thread != NULL) ? thread->IsInterrupted() : JNI_FALSE;
 }
 
 void Thread_nativeCreate(JNIEnv* env, jclass, jobject javaThread, jlong stackSize) {
-  UNIMPLEMENTED(FATAL);
-  //Object* threadObj = dvmDecodeIndirectRef(env, javaThread);
-  //dvmCreateInterpThread(threadObj, (int) stackSize);
+  Object* managedThread = Decode<Object*>(env, javaThread);
+  Thread::Create(managedThread, stackSize);
 }
 
 jint Thread_nativeGetStatus(JNIEnv* env, jobject javaThread) {
   ThreadListLock lock;
   Thread* thread = Thread::FromManagedThread(env, javaThread);
-  return static_cast<jint>(thread->GetState());
+  Thread::State state = (thread != NULL) ? thread->GetState() : Thread::kUnknown;
+  return static_cast<jint>(state);
 }
 
 jboolean Thread_nativeHoldsLock(JNIEnv* env, jobject javaThread, jobject javaObject) {
@@ -93,7 +92,9 @@
 void Thread_nativeSetPriority(JNIEnv* env, jobject javaThread, jint newPriority) {
   ThreadListLock lock;
   Thread* thread = Thread::FromManagedThread(env, javaThread);
-  thread->SetNativePriority(newPriority);
+  if (thread != NULL) {
+    thread->SetNativePriority(newPriority);
+  }
 }
 
 /*
diff --git a/src/jni_compiler.cc b/src/jni_compiler.cc
index 9da1ab0..11b99c8 100644
--- a/src/jni_compiler.cc
+++ b/src/jni_compiler.cc
@@ -76,7 +76,9 @@
       jni_stub_ = x86::CreateJniStub();
     }
   }
-  native_method->RegisterNative(jni_stub_->GetData());
+  if (!native_method->IsRegistered()) {
+    native_method->RegisterNative(jni_stub_->GetData());
+  }
   // TODO: Need to make sure that the stub is copied into the image. I.e.,
   // ByteArray* needs to be reachable either as a root or from the object graph.
 
diff --git a/src/object.h b/src/object.h
index 94adf60..70a9b33 100644
--- a/src/object.h
+++ b/src/object.h
@@ -903,6 +903,10 @@
                return_pc_offset_in_bytes, false);
   }
 
+  bool IsRegistered() {
+    return GetFieldPtr<const void*>(OFFSET_OF_OBJECT_MEMBER(Method, native_method_), false) != NULL;
+  }
+
   void RegisterNative(const void* native_method) {
     CHECK(IsNative());
     CHECK(native_method != NULL);
diff --git a/src/runtime.cc b/src/runtime.cc
index 454a38c..a1157e6 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -413,7 +413,7 @@
   //REGISTER(register_dalvik_system_VMRuntime);
   REGISTER(register_dalvik_system_VMStack);
   //REGISTER(register_dalvik_system_Zygote);
-  //REGISTER(register_java_lang_Class);
+  REGISTER(register_java_lang_Class);
   REGISTER(register_java_lang_Object);
   REGISTER(register_java_lang_Runtime);
   REGISTER(register_java_lang_String);
diff --git a/src/thread.cc b/src/thread.cc
index 312bb30..05b4b9b 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -26,7 +26,7 @@
 pthread_key_t Thread::pthread_key_self_;
 
 // Temporary debugging hook for compiler.
-static void DebugMe(Method* method, uint32_t info) {
+void DebugMe(Method* method, uint32_t info) {
     LOG(INFO) << "DebugMe";
     if (method != NULL)
         LOG(INFO) << PrettyMethod(method);
@@ -40,8 +40,7 @@
  * callsite of the failed invoke-interface.  See comments in
  * compiler/runtime_support.S
  */
-extern "C" void artFailedInvokeInterface()
-{
+extern "C" void artFailedInvokeInterface() {
     UNIMPLEMENTED(FATAL) << "Unimplemented exception throw";
 }
 
@@ -62,19 +61,19 @@
 }
 
 // TODO: placeholder.  This is what generated code will call to throw
-static void ThrowException(Thread* thread, Throwable* exception) {
-    /*
-     * exception may be NULL, in which case this routine should
-     * throw NPE.  NOTE: this is a convenience for generated code,
-     * which previuosly did the null check inline and constructed
-     * and threw a NPE if NULL.  This routine responsible for setting
-     * exception_ in thread.
-     */
-    UNIMPLEMENTED(FATAL) << "Unimplemented exception throw";
+void ThrowException(Thread* thread, Throwable* exception) {
+  /*
+   * exception may be NULL, in which case this routine should
+   * throw NPE.  NOTE: this is a convenience for generated code,
+   * which previously did the null check inline and constructed
+   * and threw a NPE if NULL.  This routine responsible for setting
+   * exception_ in thread.
+   */
+  UNIMPLEMENTED(FATAL) << "Unimplemented exception throw: " << PrettyType(exception);
 }
 
 // TODO: placeholder.  Helper function to type
-static Class* InitializeTypeFromCode(uint32_t type_idx, Method* method) {
+Class* InitializeTypeFromCode(uint32_t type_idx, Method* method) {
   /*
    * Should initialize & fix up method->dex_cache_resolved_types_[].
    * Returns initialized type.  Does not return normally if an exception
@@ -86,7 +85,7 @@
 }
 
 // TODO: placeholder.  Helper function to resolve virtual method
-static void ResolveMethodFromCode(Method* method, uint32_t method_idx) {
+void ResolveMethodFromCode(Method* method, uint32_t method_idx) {
     /*
      * Slow-path handler on invoke virtual method path in which
      * base method is unresolved at compile-time.  Doesn't need to
@@ -98,9 +97,7 @@
 }
 
 // TODO: placeholder.  Helper function to alloc array for OP_FILLED_NEW_ARRAY
-static Array* CheckAndAllocFromCode(uint32_t type_index, Method* method,
-                                    int32_t component_count)
-{
+Array* CheckAndAllocFromCode(uint32_t type_index, Method* method, int32_t component_count) {
     /*
      * Just a wrapper around Array::AllocFromCode() that additionally
      * throws a runtime exception "bad Filled array req" for 'D' and 'J'.
@@ -110,7 +107,7 @@
 }
 
 // TODO: placeholder (throw on failure)
-static void CheckCastFromCode(const Class* a, const Class* b) {
+void CheckCastFromCode(const Class* a, const Class* b) {
     if (a->IsAssignableFrom(b)) {
         return;
     }
@@ -118,19 +115,19 @@
 }
 
 // TODO: placeholder
-static void UnlockObjectFromCode(Thread* thread, Object* obj) {
+void UnlockObjectFromCode(Thread* thread, Object* obj) {
     // TODO: throw and unwind if lock not held
     // TODO: throw and unwind on NPE
     obj->MonitorExit(thread);
 }
 
 // TODO: placeholder
-static void LockObjectFromCode(Thread* thread, Object* obj) {
+void LockObjectFromCode(Thread* thread, Object* obj) {
     obj->MonitorEnter(thread);
 }
 
 // TODO: placeholder
-static void CheckSuspendFromCode(Thread* thread) {
+void CheckSuspendFromCode(Thread* thread) {
     /*
      * Code is at a safe point, suspend if needed.
      * Also, this is where a pending safepoint callback
@@ -139,51 +136,51 @@
 }
 
 // TODO: placeholder
-static void StackOverflowFromCode(Method* method) {
-    //NOTE: to save code space, this handler needs to look up its own Thread*
-    UNIMPLEMENTED(FATAL) << "Stack overflow: " << PrettyMethod(method);
+void StackOverflowFromCode(Method* method) {
+  //NOTE: to save code space, this handler needs to look up its own Thread*
+  UNIMPLEMENTED(FATAL) << "Stack overflow: " << PrettyMethod(method);
 }
 
 // TODO: placeholder
-static void ThrowNullPointerFromCode() {
-    //NOTE: to save code space, this handler must look up caller's Method*
-    UNIMPLEMENTED(FATAL) << "Null pointer exception";
+void ThrowNullPointerFromCode() {
+  Thread::Current()->Dump(std::cerr);
+  //NOTE: to save code space, this handler must look up caller's Method*
+  UNIMPLEMENTED(FATAL) << "Null pointer exception";
 }
 
 // TODO: placeholder
-static void ThrowDivZeroFromCode() {
-    UNIMPLEMENTED(FATAL) << "Divide by zero";
+void ThrowDivZeroFromCode() {
+  UNIMPLEMENTED(FATAL) << "Divide by zero";
 }
 
 // TODO: placeholder
-static void ThrowArrayBoundsFromCode(int32_t index, int32_t limit) {
-    UNIMPLEMENTED(FATAL) << "Bound check exception, idx: " << index <<
-        ", limit: " << limit;
+void ThrowArrayBoundsFromCode(int32_t index, int32_t limit) {
+  UNIMPLEMENTED(FATAL) << "Bound check exception, idx: " << index << ", limit: " << limit;
 }
 
 // TODO: placeholder
-static void ThrowVerificationErrorFromCode(int32_t src1, int32_t ref) {
+void ThrowVerificationErrorFromCode(int32_t src1, int32_t ref) {
     UNIMPLEMENTED(FATAL) << "Verification error, src1: " << src1 <<
         " ref: " << ref;
 }
 
 // TODO: placeholder
-static void ThrowNegArraySizeFromCode(int32_t index) {
+void ThrowNegArraySizeFromCode(int32_t index) {
     UNIMPLEMENTED(FATAL) << "Negative array size: " << index;
 }
 
 // TODO: placeholder
-static void ThrowInternalErrorFromCode(int32_t errnum) {
+void ThrowInternalErrorFromCode(int32_t errnum) {
     UNIMPLEMENTED(FATAL) << "Internal error: " << errnum;
 }
 
 // TODO: placeholder
-static void ThrowRuntimeExceptionFromCode(int32_t errnum) {
+void ThrowRuntimeExceptionFromCode(int32_t errnum) {
     UNIMPLEMENTED(FATAL) << "Internal error: " << errnum;
 }
 
 // TODO: placeholder
-static void ThrowNoSuchMethodFromCode(int32_t method_idx) {
+void ThrowNoSuchMethodFromCode(int32_t method_idx) {
     UNIMPLEMENTED(FATAL) << "No such method, idx: " << method_idx;
 }
 
@@ -204,8 +201,7 @@
  *  ubyte  data[size*width] table of data values (may contain a single-byte
  *                          padding at the end)
  */
-static void HandleFillArrayDataFromCode(Array* array, const uint16_t* table)
-{
+void HandleFillArrayDataFromCode(Array* array, const uint16_t* table) {
     uint32_t size = (uint32_t)table[2] | (((uint32_t)table[3]) << 16);
     uint32_t size_in_bytes = size * table[1];
     if (static_cast<int32_t>(size) > array->GetLength()) {
@@ -220,8 +216,7 @@
  * Float/double conversion requires clamping to min and max of integer form.  If
  * target doesn't support this normally, use these.
  */
-static int64_t D2L(double d)
-{
+int64_t D2L(double d) {
     static const double kMaxLong = (double)(int64_t)0x7fffffffffffffffULL;
     static const double kMinLong = (double)(int64_t)0x8000000000000000ULL;
     if (d >= kMaxLong)
@@ -234,8 +229,7 @@
         return (int64_t)d;
 }
 
-static int64_t F2L(float f)
-{
+int64_t F2L(float f) {
     static const float kMaxLong = (float)(int64_t)0x7fffffffffffffffULL;
     static const float kMinLong = (float)(int64_t)0x8000000000000000ULL;
     if (f >= kMaxLong)
@@ -335,12 +329,15 @@
   return NULL;
 }
 
-Thread* Thread::Create(const Runtime* runtime) {
-  UNIMPLEMENTED(FATAL) << "need to pass in a java.lang.Thread";
+void Thread::Create(Object* peer, size_t stack_size) {
+  CHECK(peer != NULL);
 
-  size_t stack_size = runtime->GetDefaultStackSize();
+  if (stack_size == 0) {
+    stack_size = Runtime::Current()->GetDefaultStackSize();
+  }
 
-  Thread* new_thread = new Thread;
+  Thread* self = new Thread;
+  self->peer_ = peer;
 
   pthread_attr_t attr;
   errno = pthread_attr_init(&attr);
@@ -358,7 +355,7 @@
     PLOG(FATAL) << "pthread_attr_setstacksize(" << stack_size << ") failed";
   }
 
-  errno = pthread_create(&new_thread->pthread_, &attr, ThreadStart, new_thread);
+  errno = pthread_create(&self->pthread_, &attr, ThreadStart, self);
   if (errno != 0) {
     PLOG(FATAL) << "pthread_create failed";
   }
@@ -367,11 +364,6 @@
   if (errno != 0) {
     PLOG(FATAL) << "pthread_attr_destroy failed";
   }
-
-  // TODO: get the "daemon" field from the java.lang.Thread.
-  // new_thread->is_daemon_ = dvmGetFieldBoolean(threadObj, gDvm.offJavaLangThread_daemon);
-
-  return new_thread;
 }
 
 Thread* Thread::Attach(const Runtime* runtime, const char* name, bool as_daemon) {
@@ -379,7 +371,6 @@
 
   self->tid_ = ::art::GetTid();
   self->pthread_ = pthread_self();
-  self->is_daemon_ = as_daemon;
 
   self->InitStackHwm();
 
@@ -405,12 +396,22 @@
   return self;
 }
 
+jobject GetWellKnownThreadGroup(JNIEnv* env, const char* field_name) {
+  jclass thread_group_class = env->FindClass("java/lang/ThreadGroup");
+  jfieldID fid = env->GetStaticFieldID(thread_group_class, field_name, "Ljava/lang/ThreadGroup;");
+  jobject thread_group = env->GetStaticObjectField(thread_group_class, fid);
+  // This will be null in the compiler (and tests), but never in a running system.
+  //CHECK(thread_group != NULL) << "java.lang.ThreadGroup." << field_name << " not initialized";
+  return thread_group;
+}
+
 void Thread::CreatePeer(const char* name, bool as_daemon) {
   ScopedThreadStateChange tsc(Thread::Current(), Thread::kNative);
 
   JNIEnv* env = jni_env_;
 
-  jobject thread_group = NULL;
+  const char* field_name = (GetThinLockId() == ThreadList::kMainId) ? "mMain" : "mSystem";
+  jobject thread_group = GetWellKnownThreadGroup(env, field_name);
   jobject thread_name = env->NewStringUTF(name);
   jint thread_priority = GetNativePriority();
   jboolean thread_is_daemon = as_daemon;
@@ -419,7 +420,21 @@
   jmethodID mid = env->GetMethodID(c, "<init>", "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
 
   jobject peer = env->NewObject(c, mid, thread_group, thread_name, thread_priority, thread_is_daemon);
-  peer_ = env->NewGlobalRef(peer);
+
+  // Because we mostly run without code available (in the compiler, in tests), we
+  // manually assign the fields the constructor should have set.
+  // TODO: lose this.
+  jfieldID fid;
+  fid = env->GetFieldID(c, "group", "Ljava/lang/ThreadGroup;");
+  env->SetObjectField(peer, fid, thread_group);
+  fid = env->GetFieldID(c, "name", "Ljava/lang/String;");
+  env->SetObjectField(peer, fid, thread_name);
+  fid = env->GetFieldID(c, "priority", "I");
+  env->SetIntField(peer, fid, thread_priority);
+  fid = env->GetFieldID(c, "daemon", "Z");
+  env->SetBooleanField(peer, fid, thread_is_daemon);
+
+  peer_ = DecodeJObject(peer);
 }
 
 void Thread::InitStackHwm() {
@@ -456,33 +471,8 @@
 }
 
 void Thread::Dump(std::ostream& os) const {
-  /*
-   * Get the java.lang.Thread object.  This function gets called from
-   * some weird debug contexts, so it's possible that there's a GC in
-   * progress on some other thread.  To decrease the chances of the
-   * thread object being moved out from under us, we add the reference
-   * to the tracked allocation list, which pins it in place.
-   *
-   * If threadObj is NULL, the thread is still in the process of being
-   * attached to the VM, and there's really nothing interesting to
-   * say about it yet.
-   */
-  os << "TODO: pin Thread before dumping\n";
-#if 0
-  // TODO: dalvikvm had this limitation, but we probably still want to do our best.
-  if (peer_ == NULL) {
-    LOGI("Can't dump thread %d: threadObj not set", threadId);
-    return;
-  }
-  dvmAddTrackedAlloc(peer_, NULL);
-#endif
-
   DumpState(os);
   DumpStack(os);
-
-#if 0
-  dvmReleaseTrackedAlloc(peer_, NULL);
-#endif
 }
 
 std::string GetSchedulerGroup(pid_t tid) {
@@ -511,25 +501,46 @@
 }
 
 void Thread::DumpState(std::ostream& os) const {
-  std::string thread_name("unknown");
-  int priority = -1;
+  std::string thread_name("<native thread without managed peer>");
+  std::string group_name;
+  int priority;
+  bool is_daemon = false;
 
-#if 0 // TODO
-  nameStr = (StringObject*) dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_name);
-  threadName = dvmCreateCstrFromString(nameStr);
-  priority = dvmGetFieldInt(threadObj, gDvm.offJavaLangThread_priority);
-#else
-  {
-    // TODO: this may be truncated; we should use the java.lang.Thread 'name' field instead.
+  if (peer_ != NULL) {
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+
+    Class* boolean_class = class_linker->FindPrimitiveClass('Z');
+    Class* int_class = class_linker->FindPrimitiveClass('I');
+    Class* string_class = class_linker->FindSystemClass("Ljava/lang/String;");
+    Class* thread_class = class_linker->FindSystemClass("Ljava/lang/Thread;");
+    Class* thread_group_class = class_linker->FindSystemClass("Ljava/lang/ThreadGroup;");
+
+    Field* name_field = thread_class->FindDeclaredInstanceField("name", string_class);
+    Field* priority_field = thread_class->FindDeclaredInstanceField("priority", int_class);
+    Field* daemon_field = thread_class->FindDeclaredInstanceField("daemon", boolean_class);
+    Field* thread_group_field = thread_class->FindDeclaredInstanceField("group", thread_group_class);
+
+    String* thread_name_string = reinterpret_cast<String*>(name_field->GetObject(peer_));
+    thread_name = (thread_name_string != NULL) ? thread_name_string->ToModifiedUtf8() : "<null>";
+    priority = priority_field->GetInt(peer_);
+    is_daemon = daemon_field->GetBoolean(peer_);
+
+    Object* thread_group = thread_group_field->GetObject(peer_);
+    if (thread_group != NULL) {
+      Field* name_field = thread_group_class->FindDeclaredInstanceField("name", string_class);
+      String* group_name_string = reinterpret_cast<String*>(name_field->GetObject(thread_group));
+      group_name = (group_name_string != NULL) ? group_name_string->ToModifiedUtf8() : "<null>";
+    }
+  } else {
+    // This name may be truncated, but it's the best we can do in the absence of a managed peer.
     std::string stats;
     if (ReadFileToString(StringPrintf("/proc/self/task/%d/stat", GetTid()).c_str(), &stats)) {
       size_t start = stats.find('(') + 1;
       size_t end = stats.find(')') - start;
       thread_name = stats.substr(start, end);
     }
+    priority = GetNativePriority();
   }
-  priority = -1;
-#endif
 
   int policy;
   sched_param sp;
@@ -543,19 +554,8 @@
     scheduler_group = "default";
   }
 
-  std::string group_name("(null; initializing?)");
-#if 0
-  groupObj = (Object*) dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_group);
-  if (groupObj != NULL) {
-    nameStr = (StringObject*) dvmGetFieldObject(groupObj, gDvm.offJavaLangThreadGroup_name);
-    groupName = dvmCreateCstrFromString(nameStr);
-  }
-#else
-  group_name = "TODO";
-#endif
-
   os << '"' << thread_name << '"';
-  if (is_daemon_) {
+  if (is_daemon) {
     os << " daemon";
   }
   os << " prio=" << priority
@@ -605,8 +605,36 @@
      << " HZ=" << sysconf(_SC_CLK_TCK) << "\n";
 }
 
+struct StackDumpVisitor : public Thread::StackVisitor {
+  StackDumpVisitor(std::ostream& os) : os(os) {
+  }
+
+  ~StackDumpVisitor() {
+  }
+
+  void VisitFrame(const Frame& frame) {
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+
+    Method* m = frame.GetMethod();
+    Class* c = m->GetDeclaringClass();
+    const DexFile& dex_file = class_linker->FindDexFile(c->GetDexCache());
+
+    os << "  at " << PrettyMethod(m, false);
+    if (m->IsNative()) {
+      os << "(Native method)";
+    } else {
+      int line_number = dex_file.GetLineNumFromPC(m, m->ToDexPC(frame.GetPC()));
+      os << "(" << c->GetSourceFile()->ToModifiedUtf8() << ":" << line_number << ")";
+    }
+    os << "\n";
+  }
+
+  std::ostream& os;
+};
+
 void Thread::DumpStack(std::ostream& os) const {
-  os << "UNIMPLEMENTED: Thread::DumpStack\n";
+  StackDumpVisitor dumper(os);
+  WalkStack(&dumper);
 }
 
 void Thread::ThreadExitCallback(void* arg) {
@@ -687,10 +715,6 @@
   //dvmUnlockObject(self, lock);
   //lock = NULL;
 
-  // Delete our global reference to the java.lang.Thread.
-  jni_env_->DeleteGlobalRef(peer_);
-  peer_ = NULL;
-
   delete jni_env_;
   jni_env_ = NULL;
 
@@ -783,9 +807,9 @@
 class CountStackDepthVisitor : public Thread::StackVisitor {
  public:
   CountStackDepthVisitor() : depth_(0) {}
-  virtual bool VisitFrame(const Frame&) {
+
+  virtual void VisitFrame(const Frame&) {
     ++depth_;
-    return true;
   }
 
   int GetDepth() const {
@@ -817,11 +841,10 @@
 
   virtual ~BuildInternalStackTraceVisitor() {}
 
-  virtual bool VisitFrame(const Frame& frame) {
+  virtual void VisitFrame(const Frame& frame) {
     method_trace_->Set(count_, frame.GetMethod());
     pc_trace_->Set(count_, frame.GetPC());
     ++count_;
-    return true;
   }
 
   jobject GetInternalStackTrace() const {
@@ -841,7 +864,7 @@
 };
 
 void Thread::WalkStack(StackVisitor* visitor) const {
-  Frame frame = Thread::Current()->GetTopOfStack();
+  Frame frame = GetTopOfStack();
   // TODO: enable this CHECK after native_to_managed_record_ is initialized during startup.
   // CHECK(native_to_managed_record_ != NULL);
   NativeToManagedRecord* record = native_to_managed_record_;
@@ -1005,8 +1028,12 @@
 }
 
 void Thread::VisitRoots(Heap::RootVisitor* visitor, void* arg) const {
-  //(*visitor)(&thread->threadObj, threadId, ROOT_THREAD_OBJECT, arg);
-  //(*visitor)(&thread->exception, threadId, ROOT_NATIVE_STACK, arg);
+  if (exception_ != NULL) {
+    visitor(exception_, arg);
+  }
+  if (peer_ != NULL) {
+    visitor(peer_, arg);
+  }
   jni_env_->locals.VisitRoots(visitor, arg);
   jni_env_->monitors.VisitRoots(visitor, arg);
   // visitThreadStack(visitor, thread, arg);
diff --git a/src/thread.h b/src/thread.h
index ed4b1e4..9717a7c 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -208,11 +208,11 @@
   class StackVisitor {
    public:
     virtual ~StackVisitor() {}
-    virtual bool VisitFrame(const Frame& frame) = 0;
+    virtual void VisitFrame(const Frame& frame) = 0;
   };
 
   // Creates a new thread.
-  static Thread* Create(const Runtime* runtime);
+  static void Create(Object* peer, size_t stack_size);
 
   // Creates a new thread from the calling thread.
   static Thread* Attach(const Runtime* runtime, const char* name, bool as_daemon);
@@ -274,7 +274,7 @@
     return pthread_;
   }
 
-  jobject GetPeer() const {
+  Object* GetPeer() const {
     return peer_;
   }
 
@@ -495,10 +495,8 @@
   // Native thread handle.
   pthread_t pthread_;
 
-  bool is_daemon_;
-
   // Our managed peer (an instance of java.lang.Thread).
-  jobject peer_;
+  Object* peer_;
 
   // Guards the 'interrupted_' and 'wait_monitor_' members.
   mutable Mutex wait_mutex_;