Merge "Misc codegen fixes related to register promotion" into dalvik-dev
diff --git a/Android.mk b/Android.mk
index 6c2fd7e..a27d526 100644
--- a/Android.mk
+++ b/Android.mk
@@ -156,18 +156,24 @@
 # oatdump targets
 
 .PHONY: dump-oat
-dump-oat: dump-core-oat dump-boot-oat
+dump-oat: dump-oat-core dump-oat-boot
 
-.PHONY: dump-core-oat
-dump-core-oat: $(TARGET_CORE_OAT) $(OATDUMP)
-	$(OATDUMP) $(addprefix --dex-file=,$(TARGET_CORE_DEX)) --image=$(TARGET_CORE_OAT) --strip-prefix=$(PRODUCT_OUT) --output=/tmp/core.oatdump.txt
+.PHONY: dump-oat-core
+dump-oat-core: $(TARGET_CORE_OAT) $(OATDUMP)
+	$(OATDUMP) $(addprefix --dex-file=,$(TARGET_CORE_DEX)) --image=$< --strip-prefix=$(PRODUCT_OUT) --output=/tmp/core.oatdump.txt
 	@echo Output in /tmp/core.oatdump.txt
 
-.PHONY: dump-boot-oat
-dump-boot-oat: $(TARGET_BOOT_OAT) $(OATDUMP)
-	$(OATDUMP) $(addprefix --dex-file=,$(TARGET_BOOT_DEX)) --image=$(TARGET_BOOT_OAT) --strip-prefix=$(PRODUCT_OUT) --output=/tmp/boot.oatdump.txt
+.PHONY: dump-oat-boot
+dump-oat-boot: $(TARGET_BOOT_OAT) $(OATDUMP)
+	$(OATDUMP) $(addprefix --dex-file=,$(TARGET_BOOT_DEX)) --image=$< --strip-prefix=$(PRODUCT_OUT) --output=/tmp/boot.oatdump.txt
 	@echo Output in /tmp/boot.oatdump.txt
 
+.PHONY: dump-oat-Calculator
+dump-oat-Calculator: $(TARGET_OUT_APPS)/Calculator.oat $(TARGET_BOOT_OAT) $(OATDUMP)
+	$(OATDUMP) --dex-file=$(TARGET_OUT_APPS)/Calculator.apk --image=$< $(addprefix --boot-dex-file=,$(TARGET_BOOT_DEX)) --boot=$(TARGET_BOOT_OAT) --strip-prefix=$(PRODUCT_OUT) --output=/tmp/Calculator.oatdump.txt
+	@echo Output in /tmp/Calculator.oatdump.txt
+
+
 ########################################################################
 # cpplint target
 
diff --git a/oat_process/Android.mk b/oat_process/Android.mk
index 9fb9401..0ebb052 100644
--- a/oat_process/Android.mk
+++ b/oat_process/Android.mk
@@ -6,18 +6,25 @@
 local_shared_libraries := \
 	libcutils \
 	libutils \
-	libbinder
+	libbinder \
+	libstlport
 
 include $(CLEAR_VARS)
+include external/stlport/libstlport.mk
 LOCAL_MODULE:= oat_process
 LOCAL_MODULE_TAGS:= optional
 LOCAL_SRC_FILES:= $(local_src_files)
-LOCAL_SHARED_LIBRARIES := liboat_runtimed $(local_shared_libraries)
+LOCAL_SHARED_LIBRARIES := liboat_runtime libart $(local_shared_libraries)
+LOCAL_C_INCLUDES += $(ART_C_INCLUDES)
+LOCAL_CFLAGS := $(ART_TARGET_CFLAGS)
 include $(BUILD_EXECUTABLE)
 
 include $(CLEAR_VARS)
+include external/stlport/libstlport.mk
 LOCAL_MODULE_TAGS:= optional
 LOCAL_MODULE:= oat_processd
 LOCAL_SRC_FILES:= $(local_src_files)
-LOCAL_SHARED_LIBRARIES := liboat_runtimed $(local_shared_libraries)
+LOCAL_SHARED_LIBRARIES := liboat_runtimed libartd $(local_shared_libraries)
+LOCAL_C_INCLUDES += $(ART_C_INCLUDES)
+LOCAL_CFLAGS := $(ART_TARGET_CFLAGS)
 include $(BUILD_EXECUTABLE)
diff --git a/oat_process/app_main.cpp b/oat_process/app_main.cpp
index ca80fc2..9ccdf16 100644
--- a/oat_process/app_main.cpp
+++ b/oat_process/app_main.cpp
@@ -7,6 +7,11 @@
 
 #define LOG_TAG "appproc"
 
+#include "class_loader.h"
+#include "jni_internal.h"
+#include "stringprintf.h"
+#include "thread.h"
+
 #include <binder/IPCThreadState.h>
 #include <binder/ProcessState.h>
 #include <utils/Log.h>
@@ -72,11 +77,57 @@
         char* slashClassName = toSlashClassName(mClassName);
         mClass = env->FindClass(slashClassName);
         if (mClass == NULL) {
-            LOGE("ERROR: could not find class '%s'\n", mClassName);
+            LOG(ERROR) << StringPrintf("ERROR: could not find class '%s'\n", mClassName);
         }
         free(slashClassName);
 
         mClass = reinterpret_cast<jclass>(env->NewGlobalRef(mClass));
+
+        // TODO: remove this ClassLoader code
+        jclass ApplicationLoaders = env->FindClass("android/app/ApplicationLoaders");
+        jmethodID getDefault = env->GetStaticMethodID(ApplicationLoaders,
+                                                      "getDefault",
+                                                      "()Landroid/app/ApplicationLoaders;");
+        jfieldID mLoaders = env->GetFieldID(ApplicationLoaders, "mLoaders", "Ljava/util/Map;");
+        jclass BootClassLoader = env->FindClass("java/lang/BootClassLoader");
+        jmethodID getInstance = env->GetStaticMethodID(BootClassLoader,
+                                                       "getInstance",
+                                                       "()Ljava/lang/BootClassLoader;");
+        jclass ClassLoader = env->FindClass("java/lang/ClassLoader");
+        jfieldID parent = env->GetFieldID(ClassLoader, "parent", "Ljava/lang/ClassLoader;");
+        jclass Map = env->FindClass("java/util/Map");
+        jmethodID put = env->GetMethodID(Map,
+                                         "put",
+                                         "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+        jclass BaseDexClassLoader = env->FindClass("dalvik/system/BaseDexClassLoader");
+        jfieldID originalPath = env->GetFieldID(BaseDexClassLoader, "originalPath", "Ljava/lang/String;");
+        jfieldID pathList = env->GetFieldID(BaseDexClassLoader, "pathList", "Ldalvik/system/DexPathList;");
+        jclass DexPathList = env->FindClass("dalvik/system/DexPathList");
+        jmethodID init = env->GetMethodID(DexPathList,
+                                          "<init>",
+                                          "(Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/String;Ljava/io/File;)V");
+
+        // Set the parent of our pre-existing ClassLoader to the non-null BootClassLoader.getInstance()
+        const art::ClassLoader* class_loader_object = art::Thread::Current()->GetClassLoaderOverride();
+        jobject class_loader = art::AddLocalReference<jobject>(env, class_loader_object);
+        jobject boot_class_loader = env->CallStaticObjectMethod(BootClassLoader, getInstance);
+        env->SetObjectField(class_loader, parent, boot_class_loader);
+
+        // Create a DexPathList
+        jstring dex_path = env->NewStringUTF("/system/app/Calculator.apk");
+        jstring library_path = env->NewStringUTF("/data/data/com.android.calculator2/lib");
+        jobject dex_path_list = env->NewObject(DexPathList, init,
+                                               boot_class_loader, dex_path, library_path, NULL);
+
+        // Set DexPathList into our pre-existing ClassLoader
+        env->SetObjectField(class_loader, pathList, dex_path_list);
+        env->SetObjectField(class_loader, originalPath, dex_path);
+
+        // Stash our pre-existing ClassLoader into ApplicationLoaders.getDefault().mLoaders
+        // under the expected name.
+        jobject application_loaders = env->CallStaticObjectMethod(ApplicationLoaders, getDefault);
+        jobject loaders = env->GetObjectField(application_loaders, mLoaders);
+        env->CallObjectMethod(loaders, put, dex_path, class_loader);
     }
 
     virtual void onStarted()
@@ -150,11 +201,11 @@
 
     // ignore /system/bin/app_process when invoked via WrapperInit
     if (strcmp(argv[0], "/system/bin/app_process") == 0) {
-        LOGI("Removing /system/bin/app_process argument");
+        LOG(INFO) << "Removing /system/bin/app_process argument";
         argc--;
         argv++;
         for (int i = 0; i < argc; i++) {
-            LOGI("argv[%d]=%s", i, argv[i]);
+            LOG(INFO) << StringPrintf("argv[%d]=%s", i, argv[i]);
         }
     }
 
@@ -162,7 +213,7 @@
     int oatArgc = argc + 2;
     const char* oatArgv[oatArgc];
     if (strcmp(argv[0], "-Xbootimage:/system/framework/boot.oat") != 0) {
-        LOGI("Adding oat arguments");
+        LOG(INFO) << "Adding oat arguments";
         oatArgv[0] = "-Xbootimage:/system/framework/boot.oat";
         oatArgv[1] = "-Ximage:/system/app/Calculator.oat";
         setenv("CLASSPATH", "/system/app/Calculator.apk", 1);
@@ -170,12 +221,12 @@
         argv = oatArgv;
         argc = oatArgc;
         for (int i = 0; i < argc; i++) {
-            LOGI("argv[%d]=%s", i, argv[i]);
+            LOG(INFO) << StringPrintf("argv[%d]=%s", i, argv[i]);
         }
     }
 
     // TODO: remove the heap arguments when implicit garbage collection enabled
-    LOGI("Adding heap arguments");
+    LOG(INFO) << "Adding heap arguments";
     int heapArgc = argc + 2;
     const char* heapArgv[heapArgc];
     heapArgv[0] = "-Xms64m";
@@ -184,11 +235,11 @@
     argv = heapArgv;
     argc = heapArgc;
     for (int i = 0; i < argc; i++) {
-        LOGI("argv[%d]=%s", i, argv[i]);
+        LOG(INFO) << StringPrintf("argv[%d]=%s", i, argv[i]);
     }
 
     // TODO: change the system default to not perform preloading
-    LOGI("Disabling preloading");
+    LOG(INFO) << "Disabling preloading";
     for (int i = 0; i < argc; i++) {
         if (strcmp(argv[i], "preload") == 0) {
             argv[i] = "nopreload";
@@ -196,7 +247,7 @@
         }
     }
     for (int i = 0; i < argc; i++) {
-        LOGI("argv[%d]=%s", i, argv[i]);
+        LOG(INFO) << StringPrintf("argv[%d]=%s", i, argv[i]);
     }
 
     // Everything up to '--' or first non '-' arg goes to the vm
@@ -249,7 +300,7 @@
     } else {
         fprintf(stderr, "Error: no class name or --zygote supplied.\n");
         app_usage();
-        LOG_ALWAYS_FATAL("oat_process: no class name or --zygote supplied.");
+        LOG(FATAL) << "oat_process: no class name or --zygote supplied.";
         return 10;
     }
 }
diff --git a/src/asm_support.h b/src/asm_support.h
index 3a8a8ce..1ef10b8 100644
--- a/src/asm_support.h
+++ b/src/asm_support.h
@@ -9,13 +9,13 @@
 #define rLR r14
 #define SUSPEND_CHECK_INTERVAL (1000)
 // Offset of field Thread::top_of_managed_stack_ verified in InitCpu
-#define THREAD_TOP_OF_MANAGED_STACK_OFFSET 341
+#define THREAD_TOP_OF_MANAGED_STACK_OFFSET 276
 // Offset of field Thread::top_of_managed_stack_pc_ verified in InitCpu
-#define THREAD_TOP_OF_MANAGED_STACK_PC_OFFSET 345
+#define THREAD_TOP_OF_MANAGED_STACK_PC_OFFSET 280
 
 #elif defined(__i386__)
 // Offset of field Thread::self_ verified in InitCpu
-#define THREAD_SELF_OFFSET 365
+#define THREAD_SELF_OFFSET 372
 #endif
 
 #endif  // ART_SRC_ASM_SUPPORT_H_
diff --git a/src/check_jni.cc b/src/check_jni.cc
index 8a8862d..6ff0fd7 100644
--- a/src/check_jni.cc
+++ b/src/check_jni.cc
@@ -25,6 +25,10 @@
 #include "thread.h"
 #include "runtime.h"
 
+#define LIBCORE_CPP_JNI_HELPERS
+#include <JNIHelp.h> // from libcore
+#undef LIBCORE_CPP_JNI_HELPERS
+
 namespace art {
 
 void JniAbort(const char* jni_function_name) {
@@ -32,7 +36,7 @@
   const Method* current_method = self->GetCurrentMethod();
 
   std::stringstream os;
-  os << "JNI app bug detected";
+  os << "Aborting because JNI app bug detected (see above for details)";
 
   if (jni_function_name != NULL) {
     os << "\n             in call to " << jni_function_name;
@@ -678,8 +682,8 @@
      */
     if ((flags & kFlag_ExcepOkay) == 0 && self->IsExceptionPending()) {
       LOG(ERROR) << "JNI ERROR: JNI method called with exception pending";
-      LOG(ERROR) << "Pending exception is: TODO"; // TODO
-      // TODO: dvmLogExceptionStackTrace();
+      LOG(ERROR) << "Pending exception is:";
+      LOG(ERROR) << jniGetStackTrace(mEnv);
       JniAbort();
       return;
     }
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 2c683e9..becd6d5 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -1444,7 +1444,9 @@
 bool ClassLinker::HasSameMethodDescriptorClasses(const Method* method,
                                                  const Class* klass1,
                                                  const Class* klass2) {
-  if (method->IsMiranda()) { return true; }
+  if (method->IsMiranda()) {
+      return true;
+  }
   const DexFile& dex_file = FindDexFile(method->GetDeclaringClass()->GetDexCache());
   const DexFile::ProtoId& proto_id = dex_file.GetProtoId(method->GetProtoIdx());
   DexFile::ParameterIterator *it;
@@ -1463,7 +1465,7 @@
   // Check the return type
   const char* descriptor = dex_file.GetReturnTypeDescriptor(proto_id);
   if (descriptor[0] == 'L' || descriptor[0] == '[') {
-    if (HasSameDescriptorClasses(descriptor, klass1, klass2)) {
+    if (!HasSameDescriptorClasses(descriptor, klass1, klass2)) {
       return false;
     }
   }
@@ -1529,7 +1531,6 @@
 
   Thread* self = Thread::Current();
   ScopedThreadStateChange tsc(self, Thread::kRunnable);
-  LOG(INFO) << "initializing " << PrettyClass(c) << " from " << *self;
   c->MonitorEnter(self);
   InitializeClass(c, can_run_clinit);
   c->MonitorExit(self);
diff --git a/src/dex_file.cc b/src/dex_file.cc
index 924953f..519cdc2 100644
--- a/src/dex_file.cc
+++ b/src/dex_file.cc
@@ -287,17 +287,15 @@
     // lock. If somebody else is working on it, we'll block here until
     // they complete.  Because we're waiting on an external resource,
     // we go into native mode.
-    // Note that current_thread can be NULL if we're parsing the bootclasspath
+    // Note that self can be NULL if we're parsing the bootclasspath
     // during JNI_CreateJavaVM.
-    Thread* current_thread = Thread::Current();
-    Thread::State old(Thread::kUnknown);
-    if (current_thread != NULL) {
-        old = current_thread->SetState(Thread::kNative);
+    Thread* self = Thread::Current();
+    UniquePtr<ScopedThreadStateChange> state_changer;
+    if (self != NULL) {
+      state_changer.reset(new ScopedThreadStateChange(self, Thread::kNative));
     }
     UniquePtr<LockedFd> fd(LockedFd::CreateAndLock(cache_path_tmp, 0644));
-    if (current_thread != NULL) {
-        current_thread->SetState(old);
-    }
+    state_changer.reset(NULL);
     if (fd.get() == NULL) {
       return NULL;
     }
diff --git a/src/java_lang_Thread.cc b/src/java_lang_Thread.cc
index b1da60d..9a91f77 100644
--- a/src/java_lang_Thread.cc
+++ b/src/java_lang_Thread.cc
@@ -48,8 +48,10 @@
 jint Thread_nativeGetStatus(JNIEnv* env, jobject javaThread) {
   ThreadListLock lock;
   Thread* thread = Thread::FromManagedThread(env, javaThread);
-  Thread::State state = (thread != NULL) ? thread->GetState() : Thread::kUnknown;
-  return static_cast<jint>(state);
+  if (thread == NULL) {
+    return -1;
+  }
+  return static_cast<jint>(thread->GetState());
 }
 
 jboolean Thread_nativeHoldsLock(JNIEnv* env, jobject javaThread, jobject javaObject) {
diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc
index 725d9aa..74fdb0a 100644
--- a/src/jni_internal_test.cc
+++ b/src/jni_internal_test.cc
@@ -69,10 +69,6 @@
 }
 
 TEST_F(JniInternalTest, FindClass) {
-  // TODO: when these tests start failing because you're calling FindClass
-  // with a pending exception, fix EXPECT_CLASS_NOT_FOUND to assert that an
-  // exception was thrown and clear the exception.
-
   // Reference types...
   EXPECT_CLASS_FOUND("java/lang/String");
   // ...for arrays too, where you must include "L;".
diff --git a/src/monitor.cc b/src/monitor.cc
index 7d6cde4..4e71e45 100644
--- a/src/monitor.cc
+++ b/src/monitor.cc
@@ -27,6 +27,7 @@
 #include "mutex.h"
 #include "object.h"
 #include "thread.h"
+#include "thread_list.h"
 
 namespace art {
 
@@ -638,6 +639,7 @@
 
   // Allocate and acquire a new monitor.
   Monitor* m = new Monitor(obj);
+  LOG(INFO) << "created monitor " << m << " for object " << obj;
   // Replace the head of the list with the new monitor.
   do {
     m->next_ = gMonitorList;
@@ -699,6 +701,7 @@
     } else {
       LOG(INFO) << StringPrintf("(%d) spin on lock %p: %#x (%#x) %#x", threadId, thinp, 0, *thinp, thin);
       // The lock is owned by another thread. Notify the VM that we are about to wait.
+      self->monitor_enter_object_ = obj;
       Thread::State oldStatus = self->SetState(Thread::kBlocked);
       // Spin until the thin lock is released or inflated.
       sleepDelayNs = 0;
@@ -736,12 +739,14 @@
           // The thin lock was inflated by another thread. Let the VM know we are no longer
           // waiting and try again.
           LOG(INFO) << "(" << threadId << ") lock " << (void*) thinp << " surprise-fattened";
+          self->monitor_enter_object_ = NULL;
           self->SetState(oldStatus);
           goto retry;
         }
       }
       LOG(INFO) << StringPrintf("(%d) spin on lock done %p: %#x (%#x) %#x", threadId, thinp, 0, *thinp, thin);
       // We have acquired the thin lock. Let the VM know that we are no longer waiting.
+      self->monitor_enter_object_ = NULL;
       self->SetState(oldStatus);
       // Fatten the lock.
       Inflate(self, obj);
@@ -885,4 +890,39 @@
   }
 }
 
+void Monitor::DescribeWait(std::ostream& os, const Thread* thread) {
+  Thread::State state = thread->GetState();
+
+  Object* object = NULL;
+  uint32_t lock_owner = ThreadList::kInvalidId;
+  if (state == Thread::kWaiting || state == Thread::kTimedWaiting) {
+    os << "  - waiting on ";
+    Monitor* monitor = thread->wait_monitor_;
+    if (monitor != NULL) {
+      object = monitor->obj_;
+    }
+    lock_owner = Thread::LockOwnerFromThreadLock(object);
+  } else if (state == Thread::kBlocked) {
+    os << "  - waiting to lock ";
+    object = thread->monitor_enter_object_;
+    if (object != NULL) {
+      lock_owner = object->GetLockOwner();
+    }
+  } else {
+    // We're not waiting on anything.
+    return;
+  }
+  os << "<" << object << ">";
+
+  // - waiting on <0x613f83d8> (a java.lang.ThreadLock) held by thread 5
+  // - waiting on <0x6008c468> (a java.lang.Class<java.lang.ref.ReferenceQueue>)
+  os << " (a " << PrettyTypeOf(object) << ")";
+
+  if (lock_owner != ThreadList::kInvalidId) {
+    os << " held by thread " << lock_owner;
+  }
+
+  os << "\n";
+}
+
 }  // namespace art
diff --git a/src/monitor.h b/src/monitor.h
index 8c22fb1..fefc43e 100644
--- a/src/monitor.h
+++ b/src/monitor.h
@@ -20,6 +20,8 @@
 #include <pthread.h>
 #include <stdint.h>
 
+#include <iosfwd>
+
 #include "mutex.h"
 
 namespace art {
@@ -71,6 +73,8 @@
 
   static void FreeMonitorList();
 
+  static void DescribeWait(std::ostream& os, const Thread* thread);
+
  private:
   Monitor(Object* obj);
 
diff --git a/src/oatdump.cc b/src/oatdump.cc
index 4cb9cf0..e5911c0 100644
--- a/src/oatdump.cc
+++ b/src/oatdump.cc
@@ -126,6 +126,9 @@
     if (obj->IsClass()) {
       Class* klass = obj->AsClass();
       StringAppendF(&summary, "CLASS %s", klass->GetDescriptor()->ToModifiedUtf8().c_str());
+      std::stringstream ss;
+      ss << " (" << klass->GetStatus() << ")";
+      summary += ss.str();
     } else if (obj->IsMethod()) {
       Method* method = obj->AsMethod();
       StringAppendF(&summary, "METHOD %s", PrettyMethod(method).c_str());
@@ -151,21 +154,32 @@
     //               object_bytes, RoundUp(object_bytes, kObjectAlignment) - object_bytes);
     if (obj->IsMethod()) {
       Method* method = obj->AsMethod();
-      const ByteArray* code = method->GetCodeArray();
       if (!method->IsPhony()) {
-        size_t code_bytes = code->GetLength();
-        if (method->IsNative()) {
-          state->stats_.managed_to_native_code_bytes += code_bytes;
-        } else {
-          state->stats_.managed_code_bytes += code_bytes;
+        const ByteArray* code = method->GetCodeArray();
+        const int8_t* code_base = NULL;
+        const int8_t* code_limit = NULL;
+        if (code != NULL) {
+          size_t code_bytes = code->GetLength();
+          code_base = code->GetData();
+          code_limit = code_base + code_bytes;
+          if (method->IsNative()) {
+            state->stats_.managed_to_native_code_bytes += code_bytes;
+          } else {
+            state->stats_.managed_code_bytes += code_bytes;
+          }
         }
-        StringAppendF(&summary, "\tCODE     %p-%p\n", code->GetData(), code->GetData() + code_bytes);
+        StringAppendF(&summary, "\tCODE     %p-%p\n", code_base, code_limit);
 
         const ByteArray* invoke = method->GetInvokeStubArray();
-        size_t native_to_managed_code_bytes = invoke->GetLength();
-        state->stats_.native_to_managed_code_bytes += native_to_managed_code_bytes;
-        StringAppendF(&summary, "\tJNI STUB %p-%p\n",
-                      invoke->GetData(), invoke->GetData() + native_to_managed_code_bytes);
+        const int8_t* invoke_base = NULL;
+        const int8_t* invoke_limit = NULL;
+        if (invoke != NULL) {
+          size_t native_to_managed_code_bytes = invoke->GetLength();
+          invoke_base = invoke->GetData();
+          invoke_limit = invoke_base + native_to_managed_code_bytes;
+          state->stats_.native_to_managed_code_bytes += native_to_managed_code_bytes;
+          StringAppendF(&summary, "\tJNI STUB %p-%p\n", invoke_base, invoke_limit);
+        }
       }
       if (method->IsNative()) {
         if (method->IsRegistered()) {
@@ -191,8 +205,10 @@
                                      method->GetRegisterMapData()->GetLength());
         state->stats_.register_map_bytes += register_map_bytes;
 
-        size_t pc_mapping_table_bytes = method->GetMappingTable()->GetLength();
-        state->stats_.pc_mapping_table_bytes += pc_mapping_table_bytes;
+        if (method->GetMappingTable() != NULL) {
+          size_t pc_mapping_table_bytes = method->GetMappingTable()->GetLength();
+          state->stats_.pc_mapping_table_bytes += pc_mapping_table_bytes;
+        }
 
         ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
         class DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache();
diff --git a/src/object.cc b/src/object.cc
index 8bdc295..cfc18bf 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -855,9 +855,9 @@
   }
   if (NumStaticFields() > 0) {
     os << "  static fields (" << NumStaticFields() << " entries):\n";
-    if (IsLoaded() || IsErroneous()) {
+    if (IsResolved() || IsErroneous()) {
       for (size_t i = 0; i < NumStaticFields(); ++i) {
-//        os << StringPrintf("    %2d: %s\n", i, PrettyField(GetStaticField(i)).c_str());
+        os << StringPrintf("    %2d: %s\n", i, PrettyField(GetStaticField(i)).c_str());
       }
     } else {
       os << "    <not yet available>";
@@ -865,9 +865,9 @@
   }
   if (NumInstanceFields() > 0) {
     os << "  instance fields (" << NumInstanceFields() << " entries):\n";
-    if (IsLoaded() || IsErroneous()) {
+    if (IsResolved() || IsErroneous()) {
       for (size_t i = 0; i < NumInstanceFields(); ++i) {
-//        os << StringPrintf("    %2d: %s\n", i, PrettyField(GetInstanceField(i)).c_str());
+        os << StringPrintf("    %2d: %s\n", i, PrettyField(GetInstanceField(i)).c_str());
       }
     } else {
       os << "    <not yet available>";
diff --git a/src/thread.cc b/src/thread.cc
index fb2ba8d..4e3e8d1 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -30,6 +30,7 @@
 #include "context.h"
 #include "heap.h"
 #include "jni_internal.h"
+#include "monitor.h"
 #include "object.h"
 #include "runtime.h"
 #include "runtime_support.h"
@@ -41,6 +42,7 @@
 
 pthread_key_t Thread::pthread_key_self_;
 
+static Class* gThreadLock = NULL;
 static Class* gThrowable = NULL;
 static Field* gThread_daemon = NULL;
 static Field* gThread_group = NULL;
@@ -50,6 +52,7 @@
 static Field* gThread_uncaughtHandler = NULL;
 static Field* gThread_vmData = NULL;
 static Field* gThreadGroup_name = NULL;
+static Field* gThreadLock_thread = NULL;
 static Method* gThread_run = NULL;
 static Method* gThreadGroup_removeThread = NULL;
 static Method* gUncaughtExceptionHandler_uncaughtException = NULL;
@@ -818,7 +821,8 @@
 }
 
 struct StackDumpVisitor : public Thread::StackVisitor {
-  StackDumpVisitor(std::ostream& os) : os(os) {
+  StackDumpVisitor(std::ostream& os, const Thread* thread)
+      : os(os), thread(thread), frame_count(0) {
   }
 
   virtual ~StackDumpVisitor() {
@@ -828,10 +832,10 @@
     if (!frame.HasMethod()) {
       return;
     }
-    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
 
     Method* m = frame.GetMethod();
     Class* c = m->GetDeclaringClass();
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
     const DexFile& dex_file = class_linker->FindDexFile(c->GetDexCache());
 
     os << "  at " << PrettyMethod(m, false);
@@ -842,13 +846,19 @@
       os << "(" << c->GetSourceFile()->ToModifiedUtf8() << ":" << line_number << ")";
     }
     os << "\n";
+
+    if (frame_count++ == 0) {
+      Monitor::DescribeWait(os, thread);
+    }
   }
 
   std::ostream& os;
+  const Thread* thread;
+  int frame_count;
 };
 
 void Thread::DumpStack(std::ostream& os) const {
-  StackDumpVisitor dumper(os);
+  StackDumpVisitor dumper(os, this);
   WalkStack(&dumper);
 }
 
@@ -962,29 +972,61 @@
   }
 }
 
+// TODO: make more accessible?
+Class* FindPrimitiveClassOrDie(ClassLinker* class_linker, char descriptor) {
+  Class* c = class_linker->FindPrimitiveClass(descriptor);
+  CHECK(c != NULL) << descriptor;
+  return c;
+}
+
+// TODO: make more accessible?
+Class* FindClassOrDie(ClassLinker* class_linker, const char* descriptor) {
+  Class* c = class_linker->FindSystemClass(descriptor);
+  CHECK(c != NULL) << descriptor;
+  return c;
+}
+
+// TODO: make more accessible?
+Field* FindFieldOrDie(Class* c, const char* name, Class* type) {
+  Field* f = c->FindDeclaredInstanceField(name, type);
+  CHECK(f != NULL) << PrettyClass(c) << " " << name << " " << PrettyClass(type);
+  return f;
+}
+
+// TODO: make more accessible?
+Method* FindMethodOrDie(Class* c, const char* name, const char* signature) {
+  Method* m = c->FindVirtualMethod(name, signature);
+  CHECK(m != NULL) << PrettyClass(c) << " " << name << " " << signature;
+  return m;
+}
+
 void Thread::FinishStartup() {
   // Now the ClassLinker is ready, we can find the various Class*, Field*, and Method*s we need.
   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* ThreadGroup_class = class_linker->FindSystemClass("Ljava/lang/ThreadGroup;");
-  Class* ThreadLock_class = class_linker->FindSystemClass("Ljava/lang/ThreadLock;");
-  Class* UncaughtExceptionHandler_class = class_linker->FindSystemClass("Ljava/lang/Thread$UncaughtExceptionHandler;");
-  gThrowable = class_linker->FindSystemClass("Ljava/lang/Throwable;");
-  gThread_daemon = Thread_class->FindDeclaredInstanceField("daemon", boolean_class);
-  gThread_group = Thread_class->FindDeclaredInstanceField("group", ThreadGroup_class);
-  gThread_lock = Thread_class->FindDeclaredInstanceField("lock", ThreadLock_class);
-  gThread_name = Thread_class->FindDeclaredInstanceField("name", String_class);
-  gThread_priority = Thread_class->FindDeclaredInstanceField("priority", int_class);
-  gThread_run = Thread_class->FindVirtualMethod("run", "()V");
-  gThread_uncaughtHandler = Thread_class->FindDeclaredInstanceField("uncaughtHandler", UncaughtExceptionHandler_class);
-  gThread_vmData = Thread_class->FindDeclaredInstanceField("vmData", int_class);
-  gThreadGroup_name = ThreadGroup_class->FindDeclaredInstanceField("name", String_class);
-  gThreadGroup_removeThread = ThreadGroup_class->FindVirtualMethod("removeThread", "(Ljava/lang/Thread;)V");
-  gUncaughtExceptionHandler_uncaughtException =
-      UncaughtExceptionHandler_class->FindVirtualMethod("uncaughtException", "(Ljava/lang/Thread;Ljava/lang/Throwable;)V");
+
+  Class* boolean_class = FindPrimitiveClassOrDie(class_linker, 'Z');
+  Class* int_class = FindPrimitiveClassOrDie(class_linker, 'I');
+  Class* String_class = FindClassOrDie(class_linker, "Ljava/lang/String;");
+  Class* Thread_class = FindClassOrDie(class_linker, "Ljava/lang/Thread;");
+  Class* ThreadGroup_class = FindClassOrDie(class_linker, "Ljava/lang/ThreadGroup;");
+  Class* UncaughtExceptionHandler_class = FindClassOrDie(class_linker, "Ljava/lang/Thread$UncaughtExceptionHandler;");
+  gThreadLock = FindClassOrDie(class_linker, "Ljava/lang/ThreadLock;");
+  gThrowable = FindClassOrDie(class_linker, "Ljava/lang/Throwable;");
+
+  gThread_daemon = FindFieldOrDie(Thread_class, "daemon", boolean_class);
+  gThread_group = FindFieldOrDie(Thread_class, "group", ThreadGroup_class);
+  gThread_lock = FindFieldOrDie(Thread_class, "lock", gThreadLock);
+  gThread_name = FindFieldOrDie(Thread_class, "name", String_class);
+  gThread_priority = FindFieldOrDie(Thread_class, "priority", int_class);
+  gThread_uncaughtHandler = FindFieldOrDie(Thread_class, "uncaughtHandler", UncaughtExceptionHandler_class);
+  gThread_vmData = FindFieldOrDie(Thread_class, "vmData", int_class);
+  gThreadGroup_name = FindFieldOrDie(ThreadGroup_class, "name", String_class);
+  gThreadLock_thread = FindFieldOrDie(gThreadLock, "thread", Thread_class);
+
+  gThread_run = FindMethodOrDie(Thread_class, "run", "()V");
+  gThreadGroup_removeThread = FindMethodOrDie(ThreadGroup_class, "removeThread", "(Ljava/lang/Thread;)V");
+  gUncaughtExceptionHandler_uncaughtException = FindMethodOrDie(UncaughtExceptionHandler_class,
+      "uncaughtException", "(Ljava/lang/Thread;Ljava/lang/Throwable;)V");
 
   // Finish attaching the main thread.
   Thread::Current()->CreatePeer("main", false);
@@ -994,27 +1036,45 @@
   CHECK_PTHREAD_CALL(pthread_key_delete, (Thread::pthread_key_self_), "self key");
 }
 
+uint32_t Thread::LockOwnerFromThreadLock(Object* thread_lock) {
+  if (thread_lock == NULL || thread_lock->GetClass() != gThreadLock) {
+    return ThreadList::kInvalidId;
+  }
+  Object* managed_thread = gThreadLock_thread->GetObject(thread_lock);
+  if (managed_thread == NULL) {
+    return ThreadList::kInvalidId;
+  }
+  uintptr_t vmData = static_cast<uintptr_t>(gThread_vmData->GetInt(managed_thread));
+  Thread* thread = reinterpret_cast<Thread*>(vmData);
+  if (thread == NULL) {
+    return ThreadList::kInvalidId;
+  }
+  return thread->GetThinLockId();
+}
+
 Thread::Thread()
     : peer_(NULL),
+      top_of_managed_stack_(),
+      top_of_managed_stack_pc_(0),
       wait_mutex_(new Mutex("Thread wait mutex")),
       wait_cond_(new ConditionVariable("Thread wait condition variable")),
       wait_monitor_(NULL),
       interrupted_(false),
       wait_next_(NULL),
+      monitor_enter_object_(NULL),
       card_table_(0),
       stack_end_(NULL),
-      top_of_managed_stack_(),
-      top_of_managed_stack_pc_(0),
       native_to_managed_record_(NULL),
       top_sirt_(NULL),
       jni_env_(NULL),
-      state_(Thread::kUnknown),
+      state_(Thread::kNative),
       self_(NULL),
       runtime_(NULL),
       exception_(NULL),
       suspend_count_(0),
       class_loader_override_(NULL),
       long_jump_context_(NULL) {
+  CHECK((sizeof(Thread) % 4) == 0) << sizeof(Thread);
 }
 
 void MonitorExitVisitor(const Object* object, void*) {
@@ -1537,7 +1597,7 @@
   "Suspended",
 };
 std::ostream& operator<<(std::ostream& os, const Thread::State& state) {
-  int int_state = static_cast<int>(state);
+  int32_t int_state = static_cast<int32_t>(state);
   if (state >= Thread::kTerminated && state <= Thread::kSuspended) {
     os << kStateNames[int_state];
   } else {
diff --git a/src/thread.h b/src/thread.h
index a9cc78f..01cc1cc 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -151,8 +151,6 @@
     kMaxPriority = 10,
   };
   enum State {
-    kUnknown = -1,
-
     // These match up with JDWP values.
     kTerminated   = 0,        // TERMINATED
     kRunnable     = 1,        // RUNNABLE or running now
@@ -257,6 +255,7 @@
   }
 
   static Thread* FromManagedThread(JNIEnv* env, jobject thread);
+  static uint32_t LockOwnerFromThreadLock(Object* thread_lock);
 
   void Dump(std::ostream& os) const;
 
@@ -577,15 +576,28 @@
   // Our managed peer (an instance of java.lang.Thread).
   Object* peer_;
 
+  // The top_of_managed_stack_ and top_of_managed_stack_pc_ fields are accessed from
+  // compiled code, so we keep them early in the structure to (a) avoid having to keep
+  // fixing the assembler offsets and (b) improve the chances that these will still be aligned.
+
+  // Top of the managed stack, written out prior to the state transition from
+  // kRunnable to kNative. Uses include to give the starting point for scanning
+  // a managed stack when a thread is in native code.
+  Frame top_of_managed_stack_;
+  // PC corresponding to the call out of the top_of_managed_stack_ frame
+  uintptr_t top_of_managed_stack_pc_;
+
   // Guards the 'interrupted_' and 'wait_monitor_' members.
   mutable Mutex* wait_mutex_;
   ConditionVariable* wait_cond_;
   // Pointer to the monitor lock we're currently waiting on (or NULL), guarded by wait_mutex_.
   Monitor* wait_monitor_;
   // Thread "interrupted" status; stays raised until queried or thrown, guarded by wait_mutex_.
-  bool interrupted_;
+  uint32_t interrupted_;
   // The next thread in the wait set this thread is part of.
   Thread* wait_next_;
+  // If we're blocked in MonitorEnter, this is the object we're trying to lock.
+  Object* monitor_enter_object_;
 
   friend class Monitor;
 
@@ -604,14 +616,6 @@
   // The "lowest addressable byte" of the stack
   byte* stack_base_;
 
-  // Top of the managed stack, written out prior to the state transition from
-  // kRunnable to kNative. Uses include to give the starting point for scanning
-  // a managed stack when a thread is in native code.
-  Frame top_of_managed_stack_;
-
-  // PC corresponding to the call out of the top_of_managed_stack_ frame
-  uintptr_t top_of_managed_stack_pc_;
-
   // A linked list (of stack allocated records) recording transitions from
   // native to managed code.
   NativeToManagedRecord* native_to_managed_record_;
diff --git a/src/thread_list.h b/src/thread_list.h
index e5d9eab..35b23e7 100644
--- a/src/thread_list.h
+++ b/src/thread_list.h
@@ -87,12 +87,10 @@
       // Try to get it from TLS.
       self = Thread::Current();
     }
+    // self may still be NULL during VM shutdown.
     Thread::State old_state;
     if (self != NULL) {
       old_state = self->SetState(Thread::kVmWait);
-    } else {
-      // This happens during VM shutdown.
-      old_state = Thread::kUnknown;
     }
     Runtime::Current()->GetThreadList()->thread_list_lock_.Lock();
     if (self != NULL) {