Interpreter entries and instrumentation as a listener.

Make the instrumentation responsible for whether we want method entry/exit
stubs, and allow it to use interpreter entry stubs when instruction by
instruction instrumentation is required. Improve deoptimization so more JDWP
test cases are passing.

Refactor exception debug posting, in particular improve reporting in the
interpreter. Improve class linker exception throwing so that broken dex files
are more likely to be reported. Fixes the performance issue Bug: 8410519.

Fix some error reporting lock level errors for the large object space. Make
fast object verification faster.

Add some debug mode robustness to finding dex PCs in GC maps.

Add printf attributes to JniAbortF and fix errors.

Expand run-test 044 to test return behaviors and fix issues with not throwing
appropriate exceptions for proxies.

Ensure causes are reported with a class linker NoClassDefFoundError and JNI
NoSuchFieldError.

Remove unused debugMe and updateDebuggerFromCode.

There's a minor sizing tweak to the arg array builder, and an extra reference
array check in the interpreter.

Some clean-up of trace code.

Fix reg type cache destructor if it is called after the reg type cache is
shutdown (as is the case in oatdump).

Change-Id: I6519c7b35df77f978d011999354c864f4918e8ce
diff --git a/Android.mk b/Android.mk
index 145f8b0..27cce22 100644
--- a/Android.mk
+++ b/Android.mk
@@ -305,7 +305,7 @@
 # oatdump targets
 
 .PHONY: dump-oat
-dump-oat: dump-oat-core dump-oat-boot dump-oat-Calculator
+dump-oat: dump-oat-core dump-oat-boot
 
 .PHONY: dump-oat-core
 dump-oat-core: dump-oat-core-host dump-oat-core-target
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 2472158..7faf452 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -246,6 +246,7 @@
 	src/thread.cc \
 	src/thread_list.cc \
 	src/thread_pool.cc \
+	src/throw_location.cc \
 	src/trace.cc \
 	src/utf.cc \
 	src/utils.cc \
@@ -263,7 +264,6 @@
 	src/oat/runtime/context.cc \
 	src/oat/runtime/support_alloc.cc \
 	src/oat/runtime/support_cast.cc \
-	src/oat/runtime/support_debug.cc \
 	src/oat/runtime/support_deoptimize.cc \
 	src/oat/runtime/support_dexcache.cc \
 	src/oat/runtime/support_field.cc \
diff --git a/src/base/mutex-inl.h b/src/base/mutex-inl.h
index 3cb43a8..f911054 100644
--- a/src/base/mutex-inl.h
+++ b/src/base/mutex-inl.h
@@ -97,8 +97,9 @@
       BaseMutex* held_mutex = self->GetHeldMutex(static_cast<LockLevel>(i));
       if (UNLIKELY(held_mutex != NULL)) {
         LOG(ERROR) << "Lock level violation: holding \"" << held_mutex->name_ << "\" "
-                   << "(level " << LockLevel(i) << ") while locking \"" << name_ << "\" "
-                   << "(level " << level_ << ")";
+                   << "(level " << LockLevel(i) << " - " << i
+                   << ") while locking \"" << name_ << "\" "
+                   << "(level " << level_ << " - " << static_cast<int>(level_) << ")";
         if (i > kAbortLock) {
           // Only abort in the check below if this is more than abort level lock.
           bad_mutexes_held = true;
diff --git a/src/check_jni.cc b/src/check_jni.cc
index 57ce432..30d5099 100644
--- a/src/check_jni.cc
+++ b/src/check_jni.cc
@@ -44,7 +44,7 @@
 static void JniAbort(const char* jni_function_name, const char* msg) {
   Thread* self = Thread::Current();
   ScopedObjectAccess soa(self);
-  mirror::AbstractMethod* current_method = self->GetCurrentMethod();
+  mirror::AbstractMethod* current_method = self->GetCurrentMethod(NULL);
 
   std::ostringstream os;
   os << "JNI DETECTED ERROR IN APPLICATION: " << msg;
@@ -401,8 +401,7 @@
    *
    * Use the kFlag_NullableUtf flag where 'u' field(s) are nullable.
    */
-  void Check(bool entry, const char* fmt0, ...)
-      SHARED_LOCKS_REQUIRED (Locks::mutator_lock_) {
+  void Check(bool entry, const char* fmt0, ...) SHARED_LOCKS_REQUIRED (Locks::mutator_lock_) {
     va_list ap;
 
     const mirror::AbstractMethod* traceMethod = NULL;
@@ -411,7 +410,7 @@
       // use DetachCurrentThread or GetEnv on a thread that's not yet attached.
       Thread* self = Thread::Current();
       if ((flags_ & kFlag_Invocation) == 0 || self != NULL) {
-        traceMethod = self->GetCurrentMethod();
+        traceMethod = self->GetCurrentMethod(NULL);
       }
     }
 
@@ -812,14 +811,19 @@
     // Verify that, if an exception has been raised, the native code doesn't
     // make any JNI calls other than the Exception* methods.
     if ((flags & kFlag_ExcepOkay) == 0 && self->IsExceptionPending()) {
-      std::string type(PrettyTypeOf(self->GetException()));
+      ThrowLocation throw_location;
+      mirror::Throwable* exception = self->GetException(&throw_location);
+      std::string type(PrettyTypeOf(exception));
       // TODO: write native code that doesn't require allocation for dumping an exception.
       // TODO: do we care any more? art always dumps pending exceptions on aborting threads.
-      if (type != "java.lang.OutOfMemoryError") {
-        JniAbortF(function_name_, "JNI %s called with pending exception: %s",
-                  function_name_, type.c_str(), jniGetStackTrace(soa_.Env()).c_str());
+      bool with_stack_trace = (type != "java.lang.OutOfMemoryError");
+      if (with_stack_trace) {
+        JniAbortF(function_name_, "JNI %s called with pending exception '%s' thrown in %s\n%s",
+                  function_name_, type.c_str(), throw_location.Dump().c_str(),
+                  jniGetStackTrace(soa_.Env()).c_str());
       } else {
-        JniAbortF(function_name_, "JNI %s called with %s pending", function_name_, type.c_str());
+        JniAbortF(function_name_, "JNI %s called with pending exception '%s' thrown in %s",
+                  function_name_, type.c_str(), throw_location.Dump().c_str());
       }
       return;
     }
@@ -1772,7 +1776,7 @@
       JniAbortF(__FUNCTION__, "non-nullable address is NULL");
     }
     if (capacity <= 0) {
-      JniAbortF(__FUNCTION__, "capacity must be greater than 0: %d", capacity);
+      JniAbortF(__FUNCTION__, "capacity must be greater than 0: %lld", capacity);
     }
     return CHECK_JNI_EXIT("L", baseEnv(env)->NewDirectByteBuffer(env, address, capacity));
   }
diff --git a/src/class_linker.cc b/src/class_linker.cc
index a9e17b2..4774c63 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -80,51 +80,9 @@
 static void ThrowNoClassDefFoundError(const char* fmt, ...) {
   va_list args;
   va_start(args, fmt);
-  Thread::Current()->ThrowNewExceptionV("Ljava/lang/NoClassDefFoundError;", fmt, args);
-  va_end(args);
-}
-
-static void ThrowClassFormatError(const char* fmt, ...)
-    __attribute__((__format__(__printf__, 1, 2)))
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-static void ThrowClassFormatError(const char* fmt, ...) {
-  va_list args;
-  va_start(args, fmt);
-  Thread::Current()->ThrowNewExceptionV("Ljava/lang/ClassFormatError;", fmt, args);
-  va_end(args);
-}
-
-static void ThrowLinkageError(const char* fmt, ...)
-    __attribute__((__format__(__printf__, 1, 2)))
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-static void ThrowLinkageError(const char* fmt, ...) {
-  va_list args;
-  va_start(args, fmt);
-  Thread::Current()->ThrowNewExceptionV("Ljava/lang/LinkageError;", fmt, args);
-  va_end(args);
-}
-
-static void ThrowNoSuchFieldError(const StringPiece& scope, mirror::Class* c, const StringPiece& type,
-                                  const StringPiece& name)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ClassHelper kh(c);
-  std::ostringstream msg;
-  msg << "No " << scope << "field " << name << " of type " << type
-      << " in class " << kh.GetDescriptor() << " or its superclasses";
-  std::string location(kh.GetLocation());
-  if (!location.empty()) {
-    msg << " (defined in " << location << ")";
-  }
-  Thread::Current()->ThrowNewException("Ljava/lang/NoSuchFieldError;", msg.str().c_str());
-}
-
-static void ThrowNullPointerException(const char* fmt, ...)
-    __attribute__((__format__(__printf__, 1, 2)))
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-static void ThrowNullPointerException(const char* fmt, ...) {
-  va_list args;
-  va_start(args, fmt);
-  Thread::Current()->ThrowNewExceptionV("Ljava/lang/NullPointerException;", fmt, args);
+  Thread* self = Thread::Current();
+  ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+  self->ThrowNewExceptionV(throw_location, "Ljava/lang/NoClassDefFoundError;", fmt, args);
   va_end(args);
 }
 
@@ -139,18 +97,19 @@
   }
 
   CHECK(c->IsErroneous()) << PrettyClass(c) << " " << c->GetStatus();
+  Thread* self = Thread::Current();
+  ThrowLocation throw_location = self->GetCurrentLocationForThrow();
   if (c->GetVerifyErrorClass() != NULL) {
     // TODO: change the verifier to store an _instance_, with a useful detail message?
     ClassHelper ve_ch(c->GetVerifyErrorClass());
-    std::string error_descriptor(ve_ch.GetDescriptor());
-    Thread::Current()->ThrowNewException(error_descriptor.c_str(), PrettyDescriptor(c).c_str());
+    self->ThrowNewException(throw_location, ve_ch.GetDescriptor(), PrettyDescriptor(c).c_str());
   } else {
-    ThrowNoClassDefFoundError("%s", PrettyDescriptor(c).c_str());
+    self->ThrowNewException(throw_location, "Ljava/lang/NoClassDefFoundError;",
+                            PrettyDescriptor(c).c_str());
   }
 }
 
-static void WrapExceptionInInitializer()
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+static void WrapExceptionInInitializer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   Thread* self = Thread::Current();
   JNIEnv* env = self->GetJniEnv();
 
@@ -163,7 +122,8 @@
 
   // We only wrap non-Error exceptions; an Error can just be used as-is.
   if (!is_error) {
-    self->ThrowNewWrappedException("Ljava/lang/ExceptionInInitializerError;", NULL);
+    ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+    self->ThrowNewWrappedException(throw_location, "Ljava/lang/ExceptionInInitializerError;", NULL);
   }
 }
 
@@ -1244,8 +1204,7 @@
     ObjectLock lock(self, klass);
     // Check for circular dependencies between classes.
     if (!klass->IsResolved() && klass->GetClinitThreadId() == self->GetTid()) {
-      self->ThrowNewException("Ljava/lang/ClassCircularityError;",
-          PrettyDescriptor(klass).c_str());
+      ThrowClassCircularityError(klass);
       klass->SetStatus(mirror::Class::kStatusError);
       return NULL;
     }
@@ -1260,9 +1219,7 @@
   }
   // Return the loaded class.  No exceptions should be pending.
   CHECK(klass->IsResolved()) << PrettyClass(klass);
-  CHECK(!self->IsExceptionPending())
-      << PrettyClass(klass) << " " << PrettyTypeOf(self->GetException()) << "\n"
-      << self->GetException()->Dump();
+  self->AssertNoPendingException();
   return klass;
 }
 
@@ -1335,13 +1292,13 @@
                                                WellKnownClasses::java_lang_ClassLoader_loadClass,
                                                class_name_object.get()));
     }
-    if (soa.Env()->ExceptionCheck()) {
+    if (soa.Self()->IsExceptionPending()) {
       // If the ClassLoader threw, pass that exception up.
       return NULL;
     } else if (result.get() == NULL) {
       // broken loader - throw NPE to be compatible with Dalvik
-      ThrowNullPointerException("ClassLoader.loadClass returned null for %s",
-                                class_name_string.c_str());
+      ThrowNullPointerException(NULL, StringPrintf("ClassLoader.loadClass returned null for %s",
+                                                   class_name_string.c_str()).c_str());
       return NULL;
     } else {
       // success, return mirror::Class*
@@ -1560,7 +1517,7 @@
 
 // Special case to get oat code without overwriting a trampoline.
 const void* ClassLinker::GetOatCodeFor(const mirror::AbstractMethod* method) {
-  CHECK(Runtime::Current()->IsCompiler() || method->GetDeclaringClass()->IsInitializing());
+  CHECK(!method->IsAbstract()) << PrettyMethod(method);
   const void* result = GetOatMethodFor(method).GetCode();
   if (result == NULL) {
     // No code? You must mean to go into the interpreter.
@@ -1587,7 +1544,8 @@
   if (class_data == NULL) {
     return;  // no fields or methods - for example a marker interface
   }
-  if (!Runtime::Current()->IsStarted() || Runtime::Current()->UseCompileTimeClassPath()) {
+  Runtime* runtime = Runtime::Current();
+  if (!runtime->IsStarted() || runtime->UseCompileTimeClassPath()) {
     // OAT file unavailable
     return;
   }
@@ -1603,32 +1561,26 @@
   }
   size_t method_index = 0;
   // Link the code of methods skipped by LinkCode
-  const void* trampoline = Runtime::Current()->GetResolutionStubArray(Runtime::kStaticMethod)->GetData();
   for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) {
     mirror::AbstractMethod* method = klass->GetDirectMethod(i);
-    if (Runtime::Current()->IsMethodTracingActive()) {
-      Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
-      if (instrumentation->GetSavedCodeFromMap(method) == trampoline) {
-        const void* code = oat_class->GetOatMethod(method_index).GetCode();
-        instrumentation->ResetSavedCode(method);
-        method->SetCode(code);
-        instrumentation->SaveAndUpdateCode(method);
-      }
-    } else if (method->GetCode() == trampoline) {
+    if (method->IsStatic()) {
       const void* code = oat_class->GetOatMethod(method_index).GetCode();
       if (code == NULL) {
         // No code? You must mean to go into the interpreter.
         code = GetInterpreterEntryPoint();
       }
-      method->SetCode(code);
+      runtime->GetInstrumentation()->UpdateMethodsCode(method, code);
     }
     method_index++;
   }
+  // Ignore virtual methods on the iterator.
 }
 
 static void LinkCode(SirtRef<mirror::AbstractMethod>& method, const OatFile::OatClass* oat_class,
                      uint32_t method_index)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  // Method shouldn't have already been linked.
+  DCHECK(method->GetCode() == NULL);
   // Every kind of method should at least get an invoke stub from the oat_method.
   // non-abstract methods also get their code pointers.
   const OatFile::OatMethod oat_method = oat_class->GetOatMethod(method_index);
@@ -1641,23 +1593,22 @@
   }
 
   if (method->IsStatic() && !method->IsConstructor()) {
-    // For static methods excluding the class initializer, install the trampoline
+    // For static methods excluding the class initializer, install the trampoline.
     method->SetCode(runtime->GetResolutionStubArray(Runtime::kStaticMethod)->GetData());
   }
-  if (method->IsNative()) {
-    // unregistering restores the dlsym lookup stub
-    method->UnregisterNative(Thread::Current());
-  }
 
-  if (Runtime::Current()->IsMethodTracingActive()) {
-    Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
-    instrumentation->SaveAndUpdateCode(method.get());
+  if (method->IsNative()) {
+    // Unregistering restores the dlsym lookup stub.
+    method->UnregisterNative(Thread::Current());
   }
 
   if (method->GetCode() == NULL) {
     // No code? You must mean to go into the interpreter.
     method->SetCode(GetInterpreterEntryPoint());
   }
+
+  // Allow instrumentation its chance to hijack code.
+  runtime->GetInstrumentation()->UpdateMethodsCode(method.get(), method->GetCode());
 }
 
 void ClassLinker::LoadClass(const DexFile& dex_file,
@@ -2229,7 +2180,6 @@
 
   // Verify super class.
   mirror::Class* super = klass->GetSuperClass();
-  std::string error_msg;
   if (super != NULL) {
     // Acquire lock to prevent races on verifying the super class.
     ObjectLock lock(self, super);
@@ -2238,18 +2188,17 @@
       Runtime::Current()->GetClassLinker()->VerifyClass(super);
     }
     if (!super->IsCompileTimeVerified()) {
-      error_msg = "Rejecting class ";
-      error_msg += PrettyDescriptor(klass);
-      error_msg += " that attempts to sub-class erroneous class ";
-      error_msg += PrettyDescriptor(super);
+      std::string error_msg(StringPrintf("Rejecting class %s that attempts to sub-class erroneous class %s",
+                                         PrettyDescriptor(klass).c_str(),
+                                         PrettyDescriptor(super).c_str()));
       LOG(ERROR) << error_msg  << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8();
-      SirtRef<mirror::Throwable> cause(self, self->GetException());
+      SirtRef<mirror::Throwable> cause(self, self->GetException(NULL));
       if (cause.get() != NULL) {
         self->ClearException();
       }
-      self->ThrowNewException("Ljava/lang/VerifyError;", error_msg.c_str());
+      ThrowVerifyError(klass, "%s", error_msg.c_str());
       if (cause.get() != NULL) {
-        self->GetException()->SetCause(cause.get());
+        self->GetException(NULL)->SetCause(cause.get());
       }
       klass->SetStatus(mirror::Class::kStatusError);
       return;
@@ -2264,13 +2213,12 @@
   if (oat_file_class_status == mirror::Class::kStatusError) {
     LOG(WARNING) << "Skipping runtime verification of erroneous class " << PrettyDescriptor(klass)
                  << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8();
-    error_msg = "Rejecting class ";
-    error_msg += PrettyDescriptor(klass);
-    error_msg += " because it failed compile-time verification";
-    Thread::Current()->ThrowNewException("Ljava/lang/VerifyError;", error_msg.c_str());
+    ThrowVerifyError(klass, "Rejecting class %s because it failed compile-time verification",
+                     PrettyDescriptor(klass).c_str());
     klass->SetStatus(mirror::Class::kStatusError);
     return;
   }
+  std::string error_msg;
   if (!preverified) {
     verifier_failure = verifier::MethodVerifier::VerifyClass(klass, error_msg);
   }
@@ -2301,7 +2249,7 @@
         << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8()
         << " because: " << error_msg;
     self->AssertNoPendingException();
-    self->ThrowNewException("Ljava/lang/VerifyError;", error_msg.c_str());
+    ThrowVerifyError(klass, "%s", error_msg.c_str());
     klass->SetStatus(mirror::Class::kStatusError);
   }
 }
@@ -2463,7 +2411,7 @@
 
   klass->SetSuperClass(proxy_class);  // The super class is java.lang.reflect.Proxy
   klass->SetStatus(mirror::Class::kStatusLoaded);  // Class is now effectively in the loaded state
-  DCHECK(!Thread::Current()->IsExceptionPending());
+  self->AssertNoPendingException();
 
   // Link the fields and virtual methods, creating vtable and iftables
   if (!LinkClass(klass, interfaces)) {
@@ -2798,7 +2746,7 @@
       const mirror::AbstractMethod* method = klass->GetVTable()->Get(i);
       if (method != super->GetVTable()->Get(i) &&
           !IsSameMethodSignatureInDifferentClassContexts(method, super, klass)) {
-        ThrowLinkageError("Class %s method %s resolves differently in superclass %s",
+        ThrowLinkageError(klass, "Class %s method %s resolves differently in superclass %s",
                           PrettyDescriptor(klass).c_str(), PrettyMethod(method).c_str(),
                           PrettyDescriptor(super).c_str());
         return false;
@@ -2813,7 +2761,7 @@
         const mirror::AbstractMethod* method = iftable->GetMethodArray(i)->Get(j);
         if (!IsSameMethodSignatureInDifferentClassContexts(method, interface,
                                                            method->GetDeclaringClass())) {
-          ThrowLinkageError("Class %s method %s resolves differently in interface %s",
+          ThrowLinkageError(klass, "Class %s method %s resolves differently in interface %s",
                             PrettyDescriptor(method->GetDeclaringClass()).c_str(),
                             PrettyMethod(method).c_str(),
                             PrettyDescriptor(interface).c_str());
@@ -2996,10 +2944,9 @@
     }
     // Verify
     if (!klass->CanAccess(super_class)) {
-      Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
-          "Class %s extended by class %s is inaccessible",
-          PrettyDescriptor(super_class).c_str(),
-          PrettyDescriptor(klass.get()).c_str());
+      ThrowIllegalAccessError(klass.get(), "Class %s extended by class %s is inaccessible",
+                              PrettyDescriptor(super_class).c_str(),
+                              PrettyDescriptor(klass.get()).c_str());
       return false;
     }
     klass->SetSuperClass(super_class);
@@ -3016,10 +2963,9 @@
       // Verify
       if (!klass->CanAccess(interface)) {
         // TODO: the RI seemed to ignore this in my testing.
-        Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
-            "Interface %s implemented by class %s is inaccessible",
-            PrettyDescriptor(interface).c_str(),
-            PrettyDescriptor(klass.get()).c_str());
+        ThrowIllegalAccessError(klass.get(), "Interface %s implemented by class %s is inaccessible",
+                                PrettyDescriptor(interface).c_str(),
+                                PrettyDescriptor(klass.get()).c_str());
         return false;
       }
     }
@@ -3034,31 +2980,28 @@
   mirror::Class* super = klass->GetSuperClass();
   if (klass.get() == GetClassRoot(kJavaLangObject)) {
     if (super != NULL) {
-      Thread::Current()->ThrowNewExceptionF("Ljava/lang/ClassFormatError;",
-          "java.lang.Object must not have a superclass");
+      ThrowClassFormatError(klass.get(), "java.lang.Object must not have a superclass");
       return false;
     }
     return true;
   }
   if (super == NULL) {
-    ThrowLinkageError("No superclass defined for class %s", PrettyDescriptor(klass.get()).c_str());
+    ThrowLinkageError(klass.get(), "No superclass defined for class %s",
+                      PrettyDescriptor(klass.get()).c_str());
     return false;
   }
   // Verify
   if (super->IsFinal() || super->IsInterface()) {
-    Thread* self = Thread::Current();
-    self->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
-        "Superclass %s of %s is %s",
-        PrettyDescriptor(super).c_str(),
-        PrettyDescriptor(klass.get()).c_str(),
-        super->IsFinal() ? "declared final" : "an interface");
+    ThrowIncompatibleClassChangeError(klass.get(), "Superclass %s of %s is %s",
+                                      PrettyDescriptor(super).c_str(),
+                                      PrettyDescriptor(klass.get()).c_str(),
+                                      super->IsFinal() ? "declared final" : "an interface");
     return false;
   }
   if (!klass->CanAccess(super)) {
-    Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
-        "Superclass %s is inaccessible by %s",
-        PrettyDescriptor(super).c_str(),
-        PrettyDescriptor(klass.get()).c_str());
+    ThrowIllegalAccessError(klass.get(), "Superclass %s is inaccessible to class %s",
+                            PrettyDescriptor(super).c_str(),
+                            PrettyDescriptor(klass.get()).c_str());
     return false;
   }
 
@@ -3074,8 +3017,9 @@
   }
   // Disallow custom direct subclasses of java.lang.ref.Reference.
   if (init_done_ && super == GetClassRoot(kJavaLangRefReference)) {
-    ThrowLinkageError("Class %s attempts to subclass java.lang.ref.Reference, which is not allowed",
-        PrettyDescriptor(klass.get()).c_str());
+    ThrowLinkageError(klass.get(),
+                      "Class %s attempts to subclass java.lang.ref.Reference, which is not allowed",
+                      PrettyDescriptor(klass.get()).c_str());
     return false;
   }
 
@@ -3096,7 +3040,7 @@
     // No vtable.
     size_t count = klass->NumVirtualMethods();
     if (!IsUint(16, count)) {
-      ThrowClassFormatError("Too many methods on interface: %zd", count);
+      ThrowClassFormatError(klass.get(), "Too many methods on interface: %zd", count);
       return false;
     }
     for (size_t i = 0; i < count; ++i) {
@@ -3133,7 +3077,7 @@
         if (local_mh.HasSameNameAndSignature(&super_mh)) {
           if (klass->CanAccessMember(super_method->GetDeclaringClass(), super_method->GetAccessFlags())) {
             if (super_method->IsFinal()) {
-              ThrowLinkageError("Method %s overrides final method in class %s",
+              ThrowLinkageError(klass.get(), "Method %s overrides final method in class %s",
                                 PrettyMethod(local_method).c_str(),
                                 super_mh.GetDeclaringClassDescriptor());
               return false;
@@ -3156,7 +3100,7 @@
       }
     }
     if (!IsUint(16, actual_count)) {
-      ThrowClassFormatError("Too many methods defined on class: %zd", actual_count);
+      ThrowClassFormatError(klass.get(), "Too many methods defined on class: %zd", actual_count);
       return false;
     }
     // Shrink vtable if possible
@@ -3169,7 +3113,7 @@
     CHECK(klass.get() == GetClassRoot(kJavaLangObject));
     uint32_t num_virtual_methods = klass->NumVirtualMethods();
     if (!IsUint(16, num_virtual_methods)) {
-      ThrowClassFormatError("Too many methods: %d", num_virtual_methods);
+      ThrowClassFormatError(klass.get(), "Too many methods: %d", num_virtual_methods);
       return false;
     }
     SirtRef<mirror::ObjectArray<mirror::AbstractMethod> >
@@ -3238,10 +3182,9 @@
     DCHECK(interface != NULL);
     if (!interface->IsInterface()) {
       ClassHelper ih(interface);
-      self->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
-          "Class %s implements non-interface class %s",
-          PrettyDescriptor(klass.get()).c_str(),
-          PrettyDescriptor(ih.GetDescriptor()).c_str());
+      ThrowIncompatibleClassChangeError(klass.get(), "Class %s implements non-interface class %s",
+                                        PrettyDescriptor(klass.get()).c_str(),
+                                        PrettyDescriptor(ih.GetDescriptor()).c_str());
       return false;
     }
     // Check if interface is already in iftable
@@ -3297,7 +3240,7 @@
           AllocMethodArray(self, num_methods);
       iftable->SetMethodArray(i, method_array);
       mirror::ObjectArray<mirror::AbstractMethod>* vtable = klass->GetVTableDuringLinking();
-      for (size_t j = 0; j < interface->NumVirtualMethods(); ++j) {
+      for (size_t j = 0; j < num_methods; ++j) {
         mirror::AbstractMethod* interface_method = interface->GetVirtualMethod(j);
         interface_mh.ChangeMethod(interface_method);
         int32_t k;
@@ -3314,9 +3257,10 @@
           vtable_mh.ChangeMethod(vtable_method);
           if (interface_mh.HasSameNameAndSignature(&vtable_mh)) {
             if (!vtable_method->IsAbstract() && !vtable_method->IsPublic()) {
-              self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
-                                       "Implementation not public: %s",
-                                       PrettyMethod(vtable_method).c_str());
+              ThrowIllegalAccessError(klass.get(),
+                                      "Method '%s' implementing interface method '%s' is not public",
+                                      PrettyMethod(vtable_method).c_str(),
+                                      PrettyMethod(interface_method).c_str());
               return false;
             }
             method_array->Set(j, vtable_method);
@@ -3657,12 +3601,15 @@
       //       same name to be loaded simultaneously by different loaders
       dex_cache->SetResolvedType(type_idx, resolved);
     } else {
-      CHECK(Thread::Current()->IsExceptionPending())
+      Thread* self = Thread::Current();
+      CHECK(self->IsExceptionPending())
           << "Expected pending exception for failed resolution of: " << descriptor;
-      // Convert a ClassNotFoundException to a NoClassDefFoundError
-      if (Thread::Current()->GetException()->InstanceOf(GetClassRoot(kJavaLangClassNotFoundException))) {
+      // Convert a ClassNotFoundException to a NoClassDefFoundError.
+      SirtRef<mirror::Throwable> cause(self, self->GetException(NULL));
+      if (cause->InstanceOf(GetClassRoot(kJavaLangClassNotFoundException))) {
         Thread::Current()->ClearException();
         ThrowNoClassDefFoundError("Failed resolution of: %s", descriptor);
+        self->GetException(NULL)->SetCause(cause.get());
       }
     }
   }
@@ -3779,7 +3726,7 @@
           if (resolved != NULL) {
             ThrowIncompatibleClassChangeError(type, kInterface, resolved, referrer);
           } else {
-            ThrowNoSuchMethodError(type, klass, name, signature, referrer);
+            ThrowNoSuchMethodError(type, klass, name, signature);
           }
         }
         break;
@@ -3791,12 +3738,12 @@
           if (resolved != NULL) {
             ThrowIncompatibleClassChangeError(type, kVirtual, resolved, referrer);
           } else {
-            ThrowNoSuchMethodError(type, klass, name, signature, referrer);
+            ThrowNoSuchMethodError(type, klass, name, signature);
           }
         }
         break;
       case kSuper:
-        ThrowNoSuchMethodError(type, klass, name, signature, referrer);
+        ThrowNoSuchMethodError(type, klass, name, signature);
         break;
       case kVirtual:
         if (resolved != NULL) {
@@ -3806,7 +3753,7 @@
           if (resolved != NULL) {
             ThrowIncompatibleClassChangeError(type, kInterface, resolved, referrer);
           } else {
-            ThrowNoSuchMethodError(type, klass, name, signature, referrer);
+            ThrowNoSuchMethodError(type, klass, name, signature);
           }
         }
         break;
diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc
index 6b8083f..c47ce4a 100644
--- a/src/class_linker_test.cc
+++ b/src/class_linker_test.cc
@@ -46,7 +46,7 @@
     EXPECT_TRUE(class_linker_->FindSystemClass(descriptor.c_str()) == NULL);
     Thread* self = Thread::Current();
     EXPECT_TRUE(self->IsExceptionPending());
-    Object* exception = self->GetException();
+    Object* exception = self->GetException(NULL);
     self->ClearException();
     Class* exception_class = class_linker_->FindSystemClass("Ljava/lang/NoClassDefFoundError;");
     EXPECT_TRUE(exception->InstanceOf(exception_class));
diff --git a/src/common_throws.cc b/src/common_throws.cc
index 8673d11..0bb9da2 100644
--- a/src/common_throws.cc
+++ b/src/common_throws.cc
@@ -32,18 +32,7 @@
 
 namespace art {
 
-static void AddReferrerLocation(std::ostream& os, const mirror::AbstractMethod* referrer)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  if (referrer != NULL) {
-    ClassHelper kh(referrer->GetDeclaringClass());
-    std::string location(kh.GetLocation());
-    if (!location.empty()) {
-      os << " (accessed from " << location << ")";
-    }
-  }
-}
-
-static void AddReferrerLocationFromClass(std::ostream& os, mirror::Class* referrer)
+static void AddReferrerLocation(std::ostream& os, const mirror::Class* referrer)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   if (referrer != NULL) {
     ClassHelper kh(referrer);
@@ -55,112 +44,86 @@
   }
 }
 
-// NullPointerException
-
-void ThrowNullPointerExceptionForFieldAccess(mirror::Field* field, bool is_read) {
+static void ThrowException(const ThrowLocation* throw_location, const char* exception_descriptor,
+                           const mirror::Class* referrer, const char* fmt, va_list* args = NULL)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   std::ostringstream msg;
-  msg << "Attempt to " << (is_read ? "read from" : "write to")
-      << " field '" << PrettyField(field, true) << "' on a null object reference";
-  Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", msg.str().c_str());
-}
-
-void ThrowNullPointerExceptionForMethodAccess(mirror::AbstractMethod* caller, uint32_t method_idx,
-                                              InvokeType type) {
-  mirror::DexCache* dex_cache = caller->GetDeclaringClass()->GetDexCache();
-  const DexFile& dex_file = *dex_cache->GetDexFile();
-  std::ostringstream msg;
-  msg << "Attempt to invoke " << type << " method '"
-      << PrettyMethod(method_idx, dex_file, true) << "' on a null object reference";
-  Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", msg.str().c_str());
-}
-
-void ThrowNullPointerExceptionFromDexPC(mirror::AbstractMethod* throw_method, uint32_t dex_pc) {
-  const DexFile::CodeItem* code = MethodHelper(throw_method).GetCodeItem();
-  CHECK_LT(dex_pc, code->insns_size_in_code_units_);
-  const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);
-  DecodedInstruction dec_insn(instr);
-  switch (instr->Opcode()) {
-    case Instruction::INVOKE_DIRECT:
-    case Instruction::INVOKE_DIRECT_RANGE:
-      ThrowNullPointerExceptionForMethodAccess(throw_method, dec_insn.vB, kDirect);
-      break;
-    case Instruction::INVOKE_VIRTUAL:
-    case Instruction::INVOKE_VIRTUAL_RANGE:
-      ThrowNullPointerExceptionForMethodAccess(throw_method, dec_insn.vB, kVirtual);
-      break;
-    case Instruction::INVOKE_INTERFACE:
-    case Instruction::INVOKE_INTERFACE_RANGE:
-      ThrowNullPointerExceptionForMethodAccess(throw_method, dec_insn.vB, kInterface);
-      break;
-    case Instruction::IGET:
-    case Instruction::IGET_WIDE:
-    case Instruction::IGET_OBJECT:
-    case Instruction::IGET_BOOLEAN:
-    case Instruction::IGET_BYTE:
-    case Instruction::IGET_CHAR:
-    case Instruction::IGET_SHORT: {
-      mirror::Field* field =
-          Runtime::Current()->GetClassLinker()->ResolveField(dec_insn.vC, throw_method, false);
-      ThrowNullPointerExceptionForFieldAccess(field, true /* read */);
-      break;
-    }
-    case Instruction::IPUT:
-    case Instruction::IPUT_WIDE:
-    case Instruction::IPUT_OBJECT:
-    case Instruction::IPUT_BOOLEAN:
-    case Instruction::IPUT_BYTE:
-    case Instruction::IPUT_CHAR:
-    case Instruction::IPUT_SHORT: {
-      mirror::Field* field =
-          Runtime::Current()->GetClassLinker()->ResolveField(dec_insn.vC, throw_method, false);
-      ThrowNullPointerExceptionForFieldAccess(field, false /* write */);
-      break;
-    }
-    case Instruction::AGET:
-    case Instruction::AGET_WIDE:
-    case Instruction::AGET_OBJECT:
-    case Instruction::AGET_BOOLEAN:
-    case Instruction::AGET_BYTE:
-    case Instruction::AGET_CHAR:
-    case Instruction::AGET_SHORT:
-      Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;",
-                                           "Attempt to read from null array");
-      break;
-    case Instruction::APUT:
-    case Instruction::APUT_WIDE:
-    case Instruction::APUT_OBJECT:
-    case Instruction::APUT_BOOLEAN:
-    case Instruction::APUT_BYTE:
-    case Instruction::APUT_CHAR:
-    case Instruction::APUT_SHORT:
-      Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;",
-                                           "Attempt to write to null array");
-      break;
-    case Instruction::ARRAY_LENGTH:
-      Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;",
-                                           "Attempt to get length of null array");
-      break;
-    default: {
-      // TODO: We should have covered all the cases where we expect a NPE above, this
-      //       message/logging is so we can improve any cases we've missed in the future.
-      const DexFile& dex_file = *throw_method->GetDeclaringClass()->GetDexCache()->GetDexFile();
-      std::string message("Null pointer exception during instruction '");
-      message += instr->DumpString(&dex_file);
-      message += "'";
-      Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", message.c_str());
-      break;
-    }
+  if (args != NULL) {
+    std::string vmsg;
+    StringAppendV(&vmsg, fmt, *args);
+    msg << vmsg;
+  } else {
+    msg << fmt;
+  }
+  AddReferrerLocation(msg, referrer);
+  Thread* self = Thread::Current();
+  if (throw_location == NULL) {
+    ThrowLocation computed_throw_location = self->GetCurrentLocationForThrow();
+    self->ThrowNewException(computed_throw_location, exception_descriptor, msg.str().c_str());
+  } else {
+    self->ThrowNewException(*throw_location, exception_descriptor, msg.str().c_str());
   }
 }
 
+// ArithmeticException
+
+void ThrowArithmeticExceptionDivideByZero(Thread* self) {
+  ThrowException(NULL, "Ljava/lang/ArithmeticException;", NULL, "divide by zero");
+}
+
+// ArrayIndexOutOfBoundsException
+
+void ThrowArrayIndexOutOfBoundsException(int index, int length) {
+  ThrowException(NULL, "Ljava/lang/ArrayIndexOutOfBoundsException;", NULL,
+                 StringPrintf("length=%d; index=%d", length, index).c_str());
+}
+
+// ArrayStoreException
+
+void ThrowArrayStoreException(const mirror::Class* element_class,
+                              const mirror::Class* array_class) {
+  ThrowException(NULL, "Ljava/lang/ArrayStoreException;", NULL,
+                 StringPrintf("%s cannot be stored in an array of type %s",
+                              PrettyDescriptor(element_class).c_str(),
+                              PrettyDescriptor(array_class).c_str()).c_str());
+}
+
+// ClassCastException
+
+void ThrowClassCastException(const mirror::Class* dest_type, const mirror::Class* src_type) {
+  ThrowException(NULL, "Ljava/lang/ClassCastException;", NULL,
+                 StringPrintf("%s cannot be cast to %s",
+                              PrettyDescriptor(src_type).c_str(),
+                              PrettyDescriptor(dest_type).c_str()).c_str());
+}
+
+void ThrowClassCastException(const ThrowLocation* throw_location, const char* msg) {
+  ThrowException(throw_location, "Ljava/lang/ClassCastException;", NULL, msg);
+}
+
+// ClassCircularityError
+
+void ThrowClassCircularityError(mirror::Class* c) {
+  std::ostringstream msg;
+  msg << PrettyDescriptor(c);
+  ThrowException(NULL, "Ljava/lang/ClassCircularityError;", c, msg.str().c_str());
+}
+
+// ClassFormatError
+
+void ThrowClassFormatError(const mirror::Class* referrer, const char* fmt, ...) {
+  va_list args;
+  va_start(args, fmt);
+  ThrowException(NULL, "Ljava/lang/ClassFormatError;", referrer, fmt, &args);
+  va_end(args);}
+
 // IllegalAccessError
 
 void ThrowIllegalAccessErrorClass(mirror::Class* referrer, mirror::Class* accessed) {
   std::ostringstream msg;
   msg << "Illegal class access: '" << PrettyDescriptor(referrer) << "' attempting to access '"
       << PrettyDescriptor(accessed) << "'";
-  AddReferrerLocationFromClass(msg, referrer);
-  Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessError;", msg.str().c_str());
+  ThrowException(NULL, "Ljava/lang/IllegalAccessError;", referrer, msg.str().c_str());
 }
 
 void ThrowIllegalAccessErrorClassForMethodDispatch(mirror::Class* referrer, mirror::Class* accessed,
@@ -171,24 +134,21 @@
   msg << "Illegal class access ('" << PrettyDescriptor(referrer) << "' attempting to access '"
       << PrettyDescriptor(accessed) << "') in attempt to invoke " << type
       << " method " << PrettyMethod(called).c_str();
-  AddReferrerLocation(msg, caller);
-  Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessError;", msg.str().c_str());
+  ThrowException(NULL, "Ljava/lang/IllegalAccessError;", referrer, msg.str().c_str());
 }
 
 void ThrowIllegalAccessErrorMethod(mirror::Class* referrer, mirror::AbstractMethod* accessed) {
   std::ostringstream msg;
   msg << "Method '" << PrettyMethod(accessed) << "' is inaccessible to class '"
       << PrettyDescriptor(referrer) << "'";
-  AddReferrerLocationFromClass(msg, referrer);
-  Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessError;", msg.str().c_str());
+  ThrowException(NULL, "Ljava/lang/IllegalAccessError;", referrer, msg.str().c_str());
 }
 
 void ThrowIllegalAccessErrorField(mirror::Class* referrer, mirror::Field* accessed) {
   std::ostringstream msg;
   msg << "Field '" << PrettyField(accessed, false) << "' is inaccessible to class '"
       << PrettyDescriptor(referrer) << "'";
-  AddReferrerLocationFromClass(msg, referrer);
-  Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessError;", msg.str().c_str());
+  ThrowException(NULL, "Ljava/lang/IllegalAccessError;", referrer, msg.str().c_str());
 }
 
 void ThrowIllegalAccessErrorFinalField(const mirror::AbstractMethod* referrer,
@@ -196,10 +156,24 @@
   std::ostringstream msg;
   msg << "Final field '" << PrettyField(accessed, false) << "' cannot be written to by method '"
       << PrettyMethod(referrer) << "'";
-  AddReferrerLocation(msg, referrer);
-  Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessError;", msg.str().c_str());
+  ThrowException(NULL, "Ljava/lang/IllegalAccessError;", referrer != NULL ? referrer->GetClass() : NULL,
+                 msg.str().c_str());
 }
 
+void ThrowIllegalAccessError(mirror::Class* referrer, const char* fmt, ...){
+  va_list args;
+  va_start(args, fmt);
+  ThrowException(NULL, "Ljava/lang/IllegalAccessError;", referrer, fmt, &args);
+  va_end(args);
+}
+
+// IllegalArgumentException
+
+void ThrowIllegalArgumentException(const ThrowLocation* throw_location, const char* msg) {
+  ThrowException(throw_location, "Ljava/lang/IllegalArgumentException;", NULL, msg);
+}
+
+
 // IncompatibleClassChangeError
 
 void ThrowIncompatibleClassChangeError(InvokeType expected_type, InvokeType found_type,
@@ -208,9 +182,9 @@
   std::ostringstream msg;
   msg << "The method '" << PrettyMethod(method) << "' was expected to be of type "
       << expected_type << " but instead was found to be of type " << found_type;
-  AddReferrerLocation(msg, referrer);
-  Thread::Current()->ThrowNewException("Ljava/lang/IncompatibleClassChangeError;",
-                                       msg.str().c_str());
+  ThrowException(NULL, "Ljava/lang/IncompatibleClassChangeError;",
+                 referrer != NULL ? referrer->GetClass() : NULL,
+                 msg.str().c_str());
 }
 
 void ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(const mirror::AbstractMethod* interface_method,
@@ -224,9 +198,9 @@
       << "' does not implement interface '"
       << PrettyDescriptor(interface_method->GetDeclaringClass())
       << "' in call to '" << PrettyMethod(interface_method) << "'";
-  AddReferrerLocation(msg, referrer);
-  Thread::Current()->ThrowNewException("Ljava/lang/IncompatibleClassChangeError;",
-                                       msg.str().c_str());
+  ThrowException(NULL, "Ljava/lang/IncompatibleClassChangeError;",
+                 referrer != NULL ? referrer->GetClass() : NULL,
+                 msg.str().c_str());
 }
 
 void ThrowIncompatibleClassChangeErrorField(const mirror::Field* resolved_field, bool is_static,
@@ -235,30 +209,192 @@
   msg << "Expected '" << PrettyField(resolved_field) << "' to be a "
       << (is_static ? "static" : "instance") << " field" << " rather than a "
       << (is_static ? "instance" : "static") << " field";
-  AddReferrerLocation(msg, referrer);
-  Thread::Current()->ThrowNewException("Ljava/lang/IncompatibleClassChangeError;",
-                                       msg.str().c_str());
+  ThrowException(NULL, "Ljava/lang/IncompatibleClassChangeError;", referrer->GetClass(),
+                 msg.str().c_str());
+}
+
+void ThrowIncompatibleClassChangeError(const mirror::Class* referrer, const char* fmt, ...){
+  va_list args;
+  va_start(args, fmt);
+  ThrowException(NULL, "Ljava/lang/IncompatibleClassChangeError;", referrer, fmt, &args);
+  va_end(args);
+}
+
+// LinkageError
+
+void ThrowLinkageError(const mirror::Class* referrer, const char* fmt, ...) {
+  va_list args;
+  va_start(args, fmt);
+  ThrowException(NULL, "Ljava/lang/LinkageError;", referrer, fmt, &args);
+  va_end(args);
+}
+
+// NegativeArraySizeException
+
+void ThrowNegativeArraySizeException(int size) {
+  ThrowException(NULL, "Ljava/lang/NegativeArraySizeException;", NULL, StringPrintf("%d", size).c_str());
+}
+
+void ThrowNegativeArraySizeException(const char* msg) {
+  ThrowException(NULL, "Ljava/lang/NegativeArraySizeException;", NULL, msg);
+}
+
+// NoSuchFieldError
+
+void ThrowNoSuchFieldError(const StringPiece& scope, mirror::Class* c,
+                           const StringPiece& type, const StringPiece& name)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ClassHelper kh(c);
+  std::ostringstream msg;
+  msg << "No " << scope << "field " << name << " of type " << type
+      << " in class " << kh.GetDescriptor() << " or its superclasses";
+  ThrowException(NULL, "Ljava/lang/NoSuchFieldError;", c, msg.str().c_str());
 }
 
 // NoSuchMethodError
 
 void ThrowNoSuchMethodError(InvokeType type, mirror::Class* c, const StringPiece& name,
-                            const StringPiece& signature, const mirror::AbstractMethod* referrer) {
+                            const StringPiece& signature) {
   std::ostringstream msg;
   ClassHelper kh(c);
   msg << "No " << type << " method " << name << signature
       << " in class " << kh.GetDescriptor() << " or its super classes";
-  AddReferrerLocation(msg, referrer);
-  Thread::Current()->ThrowNewException("Ljava/lang/NoSuchMethodError;", msg.str().c_str());
+  ThrowException(NULL, "Ljava/lang/NoSuchMethodError;", c, msg.str().c_str());
 }
 
-void ThrowNoSuchMethodError(uint32_t method_idx, const mirror::AbstractMethod* referrer) {
-  mirror::DexCache* dex_cache = referrer->GetDeclaringClass()->GetDexCache();
+void ThrowNoSuchMethodError(uint32_t method_idx) {
+  Thread* self = Thread::Current();
+  ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+  mirror::DexCache* dex_cache = throw_location.GetMethod()->GetDeclaringClass()->GetDexCache();
   const DexFile& dex_file = *dex_cache->GetDexFile();
   std::ostringstream msg;
   msg << "No method '" << PrettyMethod(method_idx, dex_file, true) << "'";
-  AddReferrerLocation(msg, referrer);
-  Thread::Current()->ThrowNewException("Ljava/lang/NoSuchMethodError;", msg.str().c_str());
+  ThrowException(&throw_location, "Ljava/lang/NoSuchMethodError;",
+                 throw_location.GetMethod()->GetDeclaringClass(), msg.str().c_str());
+}
+
+// NullPointerException
+
+void ThrowNullPointerExceptionForFieldAccess(const ThrowLocation& throw_location,
+                                             mirror::Field* field, bool is_read) {
+  std::ostringstream msg;
+  msg << "Attempt to " << (is_read ? "read from" : "write to")
+      << " field '" << PrettyField(field, true) << "' on a null object reference";
+  ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL, msg.str().c_str());
+}
+
+void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location, uint32_t method_idx,
+                                              InvokeType type) {
+  mirror::DexCache* dex_cache = throw_location.GetMethod()->GetDeclaringClass()->GetDexCache();
+  const DexFile& dex_file = *dex_cache->GetDexFile();
+  std::ostringstream msg;
+  msg << "Attempt to invoke " << type << " method '"
+      << PrettyMethod(method_idx, dex_file, true) << "' on a null object reference";
+  ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL, msg.str().c_str());
+}
+
+void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location) {
+  const DexFile::CodeItem* code = MethodHelper(throw_location.GetMethod()).GetCodeItem();
+  uint32_t throw_dex_pc = throw_location.GetDexPc();
+  CHECK_LT(throw_dex_pc, code->insns_size_in_code_units_);
+  const Instruction* instr = Instruction::At(&code->insns_[throw_dex_pc]);
+  DecodedInstruction dec_insn(instr);
+  switch (instr->Opcode()) {
+    case Instruction::INVOKE_DIRECT:
+    case Instruction::INVOKE_DIRECT_RANGE:
+      ThrowNullPointerExceptionForMethodAccess(throw_location, dec_insn.vB, kDirect);
+      break;
+    case Instruction::INVOKE_VIRTUAL:
+    case Instruction::INVOKE_VIRTUAL_RANGE:
+      ThrowNullPointerExceptionForMethodAccess(throw_location, dec_insn.vB, kVirtual);
+      break;
+    case Instruction::INVOKE_INTERFACE:
+    case Instruction::INVOKE_INTERFACE_RANGE:
+      ThrowNullPointerExceptionForMethodAccess(throw_location, dec_insn.vB, kInterface);
+      break;
+    case Instruction::IGET:
+    case Instruction::IGET_WIDE:
+    case Instruction::IGET_OBJECT:
+    case Instruction::IGET_BOOLEAN:
+    case Instruction::IGET_BYTE:
+    case Instruction::IGET_CHAR:
+    case Instruction::IGET_SHORT: {
+      mirror::Field* field =
+          Runtime::Current()->GetClassLinker()->ResolveField(dec_insn.vC,
+                                                             throw_location.GetMethod(), false);
+      ThrowNullPointerExceptionForFieldAccess(throw_location, field, true /* read */);
+      break;
+    }
+    case Instruction::IPUT:
+    case Instruction::IPUT_WIDE:
+    case Instruction::IPUT_OBJECT:
+    case Instruction::IPUT_BOOLEAN:
+    case Instruction::IPUT_BYTE:
+    case Instruction::IPUT_CHAR:
+    case Instruction::IPUT_SHORT: {
+      mirror::Field* field =
+          Runtime::Current()->GetClassLinker()->ResolveField(dec_insn.vC,
+                                                             throw_location.GetMethod(), false);
+      ThrowNullPointerExceptionForFieldAccess(throw_location, field, false /* write */);
+      break;
+    }
+    case Instruction::AGET:
+    case Instruction::AGET_WIDE:
+    case Instruction::AGET_OBJECT:
+    case Instruction::AGET_BOOLEAN:
+    case Instruction::AGET_BYTE:
+    case Instruction::AGET_CHAR:
+    case Instruction::AGET_SHORT:
+      ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL,
+                     "Attempt to read from null array");
+      break;
+    case Instruction::APUT:
+    case Instruction::APUT_WIDE:
+    case Instruction::APUT_OBJECT:
+    case Instruction::APUT_BOOLEAN:
+    case Instruction::APUT_BYTE:
+    case Instruction::APUT_CHAR:
+    case Instruction::APUT_SHORT:
+      ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL,
+                     "Attempt to write to null array");
+      break;
+    case Instruction::ARRAY_LENGTH:
+      ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL,
+                     "Attempt to get length of null array");
+      break;
+    default: {
+      // TODO: We should have covered all the cases where we expect a NPE above, this
+      //       message/logging is so we can improve any cases we've missed in the future.
+      const DexFile& dex_file =
+          *throw_location.GetMethod()->GetDeclaringClass()->GetDexCache()->GetDexFile();
+      ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL,
+                     StringPrintf("Null pointer exception during instruction '%s'",
+                                  instr->DumpString(&dex_file).c_str()).c_str());
+      break;
+    }
+  }
+}
+
+void ThrowNullPointerException(const ThrowLocation* throw_location, const char* msg) {
+  ThrowException(throw_location, "Ljava/lang/NullPointerException;", NULL, msg);
+}
+
+// RuntimeException
+
+void ThrowRuntimeException(const char* fmt, ...) {
+  va_list args;
+  va_start(args, fmt);
+  ThrowException(NULL, "Ljava/lang/RuntimeException;", NULL, fmt, &args);
+  va_end(args);
+}
+
+// VerifyError
+
+void ThrowVerifyError(const mirror::Class* referrer, const char* fmt, ...) {
+  va_list args;
+  va_start(args, fmt);
+  ThrowException(NULL, "Ljava/lang/VerifyError;", referrer, fmt, &args);
+  va_end(args);
 }
 
 }  // namespace art
diff --git a/src/common_throws.h b/src/common_throws.h
index 9e28bd7..5555435 100644
--- a/src/common_throws.h
+++ b/src/common_throws.h
@@ -28,17 +28,39 @@
 class Object;
 }  // namespace mirror
 class StringPiece;
+class ThrowLocation;
 
-// NullPointerException
+// ArithmeticException
 
-void ThrowNullPointerExceptionForFieldAccess(mirror::Field* field, bool is_read)
+void ThrowArithmeticExceptionDivideByZero(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+// ArrayIndexOutOfBoundsException
+
+void ThrowArrayIndexOutOfBoundsException(int index, int length)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-void ThrowNullPointerExceptionForMethodAccess(mirror::AbstractMethod* caller, uint32_t method_idx,
-                                              InvokeType type)
+// ArrayStoreException
+
+void ThrowArrayStoreException(const mirror::Class* element_class,
+                              const mirror::Class* array_class)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-void ThrowNullPointerExceptionFromDexPC(mirror::AbstractMethod* throw_method, uint32_t dex_pc)
+// ClassCircularityError
+
+void ThrowClassCircularityError(mirror::Class* c) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+// ClassCastException
+
+void ThrowClassCastException(const mirror::Class* dest_type, const mirror::Class* src_type)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+void ThrowClassCastException(const ThrowLocation* throw_location, const char* msg)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+// ClassFormatError
+
+void ThrowClassFormatError(const mirror::Class* referrer, const char* fmt, ...)
+    __attribute__((__format__(__printf__, 2, 3)))
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 // IllegalAccessError
@@ -62,6 +84,15 @@
                                        mirror::Field* accessed)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+void ThrowIllegalAccessError(mirror::Class* referrer, const char* fmt, ...)
+    __attribute__((__format__(__printf__, 2, 3)))
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+// IllegalArgumentException
+
+void ThrowIllegalArgumentException(const ThrowLocation* throw_location, const char* msg)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
 // IncompatibleClassChangeError
 
 void ThrowIncompatibleClassChangeError(InvokeType expected_type, InvokeType found_type,
@@ -78,13 +109,66 @@
                                             const mirror::AbstractMethod* referrer)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+void ThrowIncompatibleClassChangeError(const mirror::Class* referrer, const char* fmt, ...)
+    __attribute__((__format__(__printf__, 2, 3)))
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+// LinkageError
+
+void ThrowLinkageError(const mirror::Class* referrer, const char* fmt, ...)
+    __attribute__((__format__(__printf__, 2, 3)))
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+// NegativeArraySizeException
+
+void ThrowNegativeArraySizeException(int size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+void ThrowNegativeArraySizeException(const char* msg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+
+// NoSuchFieldError
+
+void ThrowNoSuchFieldError(const StringPiece& scope, mirror::Class* c,
+                           const StringPiece& type, const StringPiece& name)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
 // NoSuchMethodError
 
 void ThrowNoSuchMethodError(InvokeType type, mirror::Class* c, const StringPiece& name,
-                            const StringPiece& signature, const mirror::AbstractMethod* referrer)
+                            const StringPiece& signature)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-void ThrowNoSuchMethodError(uint32_t method_idx, const mirror::AbstractMethod* referrer)
+void ThrowNoSuchMethodError(uint32_t method_idx)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+// NullPointerException
+
+void ThrowNullPointerExceptionForFieldAccess(const ThrowLocation& throw_location,
+                                             mirror::Field* field,
+                                             bool is_read)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location,
+                                              uint32_t method_idx,
+                                              InvokeType type)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+void ThrowNullPointerException(const ThrowLocation* throw_location, const char* msg)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+// RuntimeException
+
+void ThrowRuntimeException(const char* fmt, ...)
+    __attribute__((__format__(__printf__, 1, 2)))
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+// VerifyError
+
+void ThrowVerifyError(const mirror::Class* referrer, const char* fmt, ...)
+    __attribute__((__format__(__printf__, 2, 3)))
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 }  // namespace art
diff --git a/src/compiler/dex/frontend.h b/src/compiler/dex/frontend.h
index 2e62dc8..49e0852 100644
--- a/src/compiler/dex/frontend.h
+++ b/src/compiler/dex/frontend.h
@@ -55,7 +55,6 @@
 
 // Force code generation paths for testing.
 enum debugControlVector {
-  kDebugDisplayMissingTargets,
   kDebugVerbose,
   kDebugDumpCFG,
   kDebugSlowFieldPath,
diff --git a/src/compiler/dex/quick/gen_common.cc b/src/compiler/dex/quick/gen_common.cc
index c13e797..4fadc9d 100644
--- a/src/compiler/dex/quick/gen_common.cc
+++ b/src/compiler/dex/quick/gen_common.cc
@@ -507,17 +507,6 @@
   }
 }
 
-
-// Debugging routine - if null target, branch to DebugMe
-void Mir2Lir::GenShowTarget()
-{
-  DCHECK_NE(cu_->instruction_set, kX86) << "unimplemented GenShowTarget";
-  LIR* branch_over = OpCmpImmBranch(kCondNe, TargetReg(kInvokeTgt), 0, NULL);
-  LoadWordDisp(TargetReg(kSelf), ENTRYPOINT_OFFSET(pDebugMe), TargetReg(kInvokeTgt));
-  LIR* target = NewLIR0(kPseudoTargetLabel);
-  branch_over->target = target;
-}
-
 void Mir2Lir::HandleSuspendLaunchPads()
 {
   LIR** suspend_label = reinterpret_cast<LIR**>(suspend_launchpads_.elem_list);
diff --git a/src/compiler/dex/quick/gen_invoke.cc b/src/compiler/dex/quick/gen_invoke.cc
index 3e946f8..c0bae29 100644
--- a/src/compiler/dex/quick/gen_invoke.cc
+++ b/src/compiler/dex/quick/gen_invoke.cc
@@ -1375,9 +1375,6 @@
                              vtable_idx, direct_code, direct_method,
                              original_type);
   }
-  if (cu_->enable_debug & (1 << kDebugDisplayMissingTargets)) {
-    GenShowTarget();
-  }
   LIR* call_inst;
   if (cu_->instruction_set != kX86) {
     call_inst = OpReg(kOpBlx, TargetReg(kInvokeTgt));
diff --git a/src/compiler/dex/quick/mir_to_lir.h b/src/compiler/dex/quick/mir_to_lir.h
index 69ebc7e..aec0cc1 100644
--- a/src/compiler/dex/quick/mir_to_lir.h
+++ b/src/compiler/dex/quick/mir_to_lir.h
@@ -396,7 +396,6 @@
                  bool is_long_or_double, bool is_object);
     void GenSget(uint32_t field_idx, RegLocation rl_dest,
                  bool is_long_or_double, bool is_object);
-    void GenShowTarget();
     void GenIGet(uint32_t field_idx, int opt_flags, OpSize size,
                  RegLocation rl_dest, RegLocation rl_obj, bool is_long_or_double, bool is_object);
     void GenIPut(uint32_t field_idx, int opt_flags, OpSize size,
diff --git a/src/compiler/driver/compiler_driver.cc b/src/compiler/driver/compiler_driver.cc
index 700936c..4e8ebbd 100644
--- a/src/compiler/driver/compiler_driver.cc
+++ b/src/compiler/driver/compiler_driver.cc
@@ -1202,9 +1202,8 @@
       manager->GetClassLinker()->FindClass(descriptor,
                                            soa.Decode<mirror::ClassLoader*>(manager->GetClassLoader()));
   if (klass == NULL) {
-    Thread* self = Thread::Current();
-    CHECK(self->IsExceptionPending());
-    self->ClearException();
+    CHECK(soa.Self()->IsExceptionPending());
+    soa.Self()->ClearException();
 
     /*
      * At compile time, we can still structurally verify the class even if FindClass fails.
@@ -1230,13 +1229,13 @@
 
   if (klass->IsErroneous()) {
     // ClassLinker::VerifyClass throws, which isn't useful in the compiler.
-    CHECK(Thread::Current()->IsExceptionPending());
-    Thread::Current()->ClearException();
+    CHECK(soa.Self()->IsExceptionPending());
+    soa.Self()->ClearException();
   }
 
   CHECK(klass->IsCompileTimeVerified() || klass->IsErroneous())
       << PrettyDescriptor(klass) << ": state=" << klass->GetStatus();
-  CHECK(!Thread::Current()->IsExceptionPending()) << PrettyTypeOf(Thread::Current()->GetException());
+  soa.Self()->AssertNoPendingException();
 }
 
 void CompilerDriver::VerifyDexFile(jobject class_loader, const DexFile& dex_file,
@@ -1435,7 +1434,6 @@
   mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(manager->GetClassLoader());
   const char* descriptor = manager->GetDexFile()->GetClassDescriptor(class_def);
   mirror::Class* klass = manager->GetClassLinker()->FindClass(descriptor, class_loader);
-  Thread* self = Thread::Current();
   bool compiling_boot = Runtime::Current()->GetHeap()->GetSpaces().size() == 1;
   bool can_init_static_fields = compiling_boot &&
       manager->GetCompiler()->IsImageClass(descriptor);
@@ -1447,9 +1445,9 @@
     // its parents, whose locks are acquired. This leads to a parent-to-child and a child-to-parent
     // lock ordering and consequent potential deadlock.
     static Mutex lock1("Initializer lock", kMonitorLock);
-    MutexLock mu(self, lock1);
+    MutexLock mu(soa.Self(), lock1);
     // The lock required to initialize the class.
-    ObjectLock lock2(self, klass);
+    ObjectLock lock2(soa.Self(), klass);
     // Only try to initialize classes that were successfully verified.
     if (klass->IsVerified()) {
       manager->GetClassLinker()->EnsureInitialized(klass, false, can_init_static_fields);
@@ -1473,7 +1471,7 @@
             } else {
               manager->GetClassLinker()->EnsureInitialized(klass, true, can_init_static_fields);
             }
-            CHECK(!self->IsExceptionPending()) << self->GetException()->Dump();
+            soa.Self()->AssertNoPendingException();
           }
         }
       }
@@ -1494,7 +1492,7 @@
     }
   }
   // Clear any class not found or verification exceptions.
-  self->ClearException();
+  soa.Self()->ClearException();
 }
 
 void CompilerDriver::InitializeClasses(jobject jni_class_loader, const DexFile& dex_file,
@@ -1651,7 +1649,7 @@
   if (self->IsExceptionPending()) {
     ScopedObjectAccess soa(self);
     LOG(FATAL) << "Unexpected exception compiling: " << PrettyMethod(method_idx, dex_file) << "\n"
-        << self->GetException()->Dump();
+        << self->GetException(NULL)->Dump();
   }
 }
 
diff --git a/src/compiler/llvm/runtime_support_llvm.cc b/src/compiler/llvm/runtime_support_llvm.cc
index d9b879a..d6c8181 100644
--- a/src/compiler/llvm/runtime_support_llvm.cc
+++ b/src/compiler/llvm/runtime_support_llvm.cc
@@ -135,15 +135,16 @@
   obj->MonitorExit(thread);
 }
 
-void art_portable_test_suspend_from_code(Thread* thread)
+void art_portable_test_suspend_from_code(Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  CheckSuspend(thread);
-  if (thread->ReadFlag(kEnterInterpreter)) {
+  CheckSuspend(self);
+  if (Runtime::Current()->GetInstrumentation()->ShouldPortableCodeDeoptimize()) {
     // Save out the shadow frame to the heap
-    ShadowFrameCopyVisitor visitor(thread);
+    ShadowFrameCopyVisitor visitor(self);
     visitor.WalkStack(true);
-    thread->SetDeoptimizationShadowFrame(visitor.GetShadowFrameCopy(), JValue());
-    thread->SetException(reinterpret_cast<mirror::Throwable*>(-1));
+    self->SetDeoptimizationShadowFrame(visitor.GetShadowFrameCopy());
+    self->SetDeoptimizationReturnValue(JValue());
+    self->SetException(ThrowLocation(), reinterpret_cast<mirror::Throwable*>(-1));
   }
 }
 
@@ -175,51 +176,59 @@
 }
 
 void art_portable_throw_div_zero_from_code() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  Thread::Current()->ThrowNewException("Ljava/lang/ArithmeticException;",
-                                       "divide by zero");
+  ThrowArithmeticExceptionDivideByZero(Thread::Current());
 }
 
 void art_portable_throw_array_bounds_from_code(int32_t index, int32_t length)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  Thread::Current()->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;",
-                                        "length=%d; index=%d", length, index);
+  ThrowArrayIndexOutOfBoundsException(index, length);
 }
 
 void art_portable_throw_no_such_method_from_code(int32_t method_idx)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  // We need the calling method as context for the method_idx.
-  mirror::AbstractMethod* method = Thread::Current()->GetCurrentMethod();
-  ThrowNoSuchMethodError(method_idx, method);
+  ThrowNoSuchMethodError(method_idx);
 }
 
 void art_portable_throw_null_pointer_exception_from_code(uint32_t dex_pc)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  mirror::AbstractMethod* throw_method =
-      Thread::Current()->GetManagedStack()->GetTopShadowFrame()->GetMethod();
-  ThrowNullPointerExceptionFromDexPC(throw_method, dex_pc);
+  // TODO: remove dex_pc argument from caller.
+  UNUSED(dex_pc);
+  Thread* self = Thread::Current();
+  ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+  ThrowNullPointerExceptionFromDexPC(throw_location);
 }
 
 void art_portable_throw_stack_overflow_from_code() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   ThrowStackOverflowError(Thread::Current());
 }
 
-void art_portable_throw_exception_from_code(mirror::Object* exception) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  Thread::Current()->DeliverException(static_cast<mirror::Throwable*>(exception));
+void art_portable_throw_exception_from_code(mirror::Throwable* exception)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  Thread* self = Thread::Current();
+  ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+  if (exception == NULL) {
+    ThrowNullPointerException(NULL, "throw with null exception");
+  } else {
+    self->SetException(throw_location, exception);
+  }
 }
 
 void* art_portable_get_and_clear_exception(Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   DCHECK(self->IsExceptionPending());
-  mirror::Throwable* exception = self->GetException();
+  // TODO: make this inline.
+  mirror::Throwable* exception = self->GetException(NULL);
   self->ClearException();
   return exception;
 }
 
 int32_t art_portable_find_catch_block_from_code(mirror::AbstractMethod* current_method, uint32_t ti_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  mirror::Throwable* exception = Thread::Current()->GetException();
-  // Check for magic deoptimization exception.
-  if (reinterpret_cast<int32_t>(exception) == -1) {
+  Thread* self = Thread::Current();  // TODO: make an argument.
+  ThrowLocation throw_location;
+  mirror::Throwable* exception = self->GetException(&throw_location);
+  // Check for special deoptimization exception.
+  if (UNLIKELY(reinterpret_cast<int32_t>(exception) == -1)) {
     return -1;
   }
   mirror::Class* exception_type = exception->GetClass();
@@ -229,26 +238,40 @@
   const DexFile::TryItem* try_item = DexFile::GetTryItems(*code_item, ti_offset);
 
   int iter_index = 0;
+  int result = -1;
+  uint32_t catch_dex_pc = -1;
   // Iterate over the catch handlers associated with dex_pc
   for (CatchHandlerIterator it(*code_item, *try_item); it.HasNext(); it.Next()) {
     uint16_t iter_type_idx = it.GetHandlerTypeIndex();
     // Catch all case
     if (iter_type_idx == DexFile::kDexNoIndex16) {
-      return iter_index;
+      catch_dex_pc = it.GetHandlerAddress();
+      result = iter_index;
+      break;
     }
     // Does this catch exception type apply?
     mirror::Class* iter_exception_type = mh.GetDexCacheResolvedType(iter_type_idx);
-    if (iter_exception_type == NULL) {
-      // The verifier should take care of resolving all exception classes early
+    if (UNLIKELY(iter_exception_type == NULL)) {
+      // TODO: check, the verifier (class linker?) should take care of resolving all exception
+      //       classes early.
       LOG(WARNING) << "Unresolved exception class when finding catch block: "
           << mh.GetTypeDescriptorFromTypeIdx(iter_type_idx);
     } else if (iter_exception_type->IsAssignableFrom(exception_type)) {
-      return iter_index;
+      catch_dex_pc = it.GetHandlerAddress();
+      result = iter_index;
+      break;
     }
     ++iter_index;
   }
-  // Handler not found
-  return -1;
+  if (result != -1) {
+    // Handler found.
+    Runtime::Current()->GetInstrumentation()->ExceptionCaughtEvent(self,
+                                                                   throw_location,
+                                                                   current_method,
+                                                                   catch_dex_pc,
+                                                                   exception);
+  }
+  return result;
 }
 
 
@@ -640,14 +663,12 @@
   DCHECK(dest_type->IsClass()) << PrettyClass(dest_type);
   DCHECK(src_type->IsClass()) << PrettyClass(src_type);
   if (UNLIKELY(!dest_type->IsAssignableFrom(src_type))) {
-    Thread::Current()->ThrowNewExceptionF("Ljava/lang/ClassCastException;",
-                                          "%s cannot be cast to %s",
-                                          PrettyDescriptor(src_type).c_str(),
-                                          PrettyDescriptor(dest_type).c_str());
+    ThrowClassCastException(dest_type, src_type);
   }
 }
 
-void art_portable_check_put_array_element_from_code(const mirror::Object* element, const mirror::Object* array)
+void art_portable_check_put_array_element_from_code(const mirror::Object* element,
+                                                    const mirror::Object* array)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   if (element == NULL) {
     return;
@@ -658,10 +679,7 @@
   mirror::Class* component_type = array_class->GetComponentType();
   mirror::Class* element_class = element->GetClass();
   if (UNLIKELY(!component_type->IsAssignableFrom(element_class))) {
-    Thread::Current()->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
-                                          "%s cannot be stored in an array of type %s",
-                                          PrettyDescriptor(element_class).c_str(),
-                                          PrettyDescriptor(array_class).c_str());
+    ThrowArrayStoreException(element_class, array_class);
   }
   return;
 }
diff --git a/src/debugger.cc b/src/debugger.cc
index 080288f..9bd1eb5 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -46,6 +46,7 @@
 #include "sirt_ref.h"
 #include "stack_indirect_reference_table.h"
 #include "thread_list.h"
+#include "throw_location.h"
 #include "utf.h"
 #include "well_known_classes.h"
 
@@ -104,6 +105,55 @@
   int stack_depth;
 };
 
+class DebugInstrumentationListener : public instrumentation::InstrumentationListener {
+ public:
+  DebugInstrumentationListener() {}
+  virtual ~DebugInstrumentationListener() {}
+
+  virtual void MethodEntered(Thread* thread, mirror::Object* this_object,
+                             const mirror::AbstractMethod* method, uint32_t dex_pc)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    if (method->IsNative()) {
+      // TODO: post location events is a suspension point and native method entry stubs aren't.
+      return;
+    }
+    Dbg::PostLocationEvent(method, 0, this_object, Dbg::kMethodEntry);
+  }
+
+  virtual void MethodExited(Thread* thread, mirror::Object* this_object,
+                            const mirror::AbstractMethod* method,
+                            uint32_t dex_pc, const JValue& return_value)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    UNUSED(return_value);
+    if (method->IsNative()) {
+      // TODO: post location events is a suspension point and native method entry stubs aren't.
+      return;
+    }
+    Dbg::PostLocationEvent(method, dex_pc, this_object, Dbg::kMethodExit);
+  }
+
+  virtual void MethodUnwind(Thread* thread, const mirror::AbstractMethod* method,
+                            uint32_t dex_pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    // We're not recorded to listen to this kind of event, so complain.
+    LOG(ERROR) << "Unexpected method unwind event in debugger " << PrettyMethod(method)
+        << " " << dex_pc;
+  }
+
+  virtual void DexPcMoved(Thread* thread, mirror::Object* this_object,
+                          const mirror::AbstractMethod* method, uint32_t new_dex_pc)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    Dbg::UpdateDebugger(thread, this_object, method, new_dex_pc);
+  }
+
+  virtual void ExceptionCaught(Thread* thread, const ThrowLocation& throw_location,
+                               mirror::AbstractMethod* catch_method, uint32_t catch_dex_pc,
+                               mirror::Throwable* exception_object)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    Dbg::PostException(thread, throw_location, catch_method, catch_dex_pc, exception_object);
+  }
+
+} gDebugInstrumentationListener;
+
 // JDWP is allowed unless the Zygote forbids it.
 static bool gJdwpAllowed = true;
 
@@ -140,7 +190,7 @@
 static std::vector<Breakpoint> gBreakpoints GUARDED_BY(Locks::breakpoint_lock_);
 static SingleStepControl gSingleStepControl GUARDED_BY(Locks::breakpoint_lock_);
 
-static bool IsBreakpoint(mirror::AbstractMethod* m, uint32_t dex_pc)
+static bool IsBreakpoint(const mirror::AbstractMethod* m, uint32_t dex_pc)
     LOCKS_EXCLUDED(Locks::breakpoint_lock_)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   MutexLock mu(Thread::Current(), *Locks::breakpoint_lock_);
@@ -466,15 +516,6 @@
   return gDisposed;
 }
 
-static void SetDebuggerUpdatesEnabledCallback(Thread* t, void* user_data) {
-  t->SetDebuggerUpdatesEnabled(*reinterpret_cast<bool*>(user_data));
-}
-
-static void SetDebuggerUpdatesEnabled(bool enabled) {
-  MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
-  Runtime::Current()->GetThreadList()->ForEach(SetDebuggerUpdatesEnabledCallback, &enabled);
-}
-
 void Dbg::GoActive() {
   // Enable all debugging features, including scans for breakpoints.
   // This is a no-op if we're already active.
@@ -483,16 +524,26 @@
     return;
   }
 
-  LOG(INFO) << "Debugger is active";
-
   {
     // TODO: dalvik only warned if there were breakpoints left over. clear in Dbg::Disconnected?
     MutexLock mu(Thread::Current(), *Locks::breakpoint_lock_);
     CHECK_EQ(gBreakpoints.size(), 0U);
   }
 
+  Runtime* runtime = Runtime::Current();
+  runtime->GetThreadList()->SuspendAll();
+  Thread* self = Thread::Current();
+  ThreadState old_state = self->SetStateUnsafe(kRunnable);
+  CHECK_NE(old_state, kRunnable);
+  runtime->GetInstrumentation()->AddListener(&gDebugInstrumentationListener,
+                                             instrumentation::Instrumentation::kMethodEntered |
+                                             instrumentation::Instrumentation::kMethodExited |
+                                             instrumentation::Instrumentation::kDexPcMoved);
   gDebuggerActive = true;
-  SetDebuggerUpdatesEnabled(true);
+  CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable);
+  runtime->GetThreadList()->ResumeAll();
+
+  LOG(INFO) << "Debugger is active";
 }
 
 void Dbg::Disconnected() {
@@ -500,11 +551,22 @@
 
   LOG(INFO) << "Debugger is no longer active";
 
+  // Suspend all threads and exclusively acquire the mutator lock. Set the state of the thread
+  // to kRunnable to avoid scoped object access transitions. Remove the debugger as a listener
+  // and clear the object registry.
+  Runtime* runtime = Runtime::Current();
+  runtime->GetThreadList()->SuspendAll();
+  Thread* self = Thread::Current();
+  ThreadState old_state = self->SetStateUnsafe(kRunnable);
+  runtime->GetInstrumentation()->RemoveListener(&gDebugInstrumentationListener,
+                                                instrumentation::Instrumentation::kMethodEntered |
+                                                instrumentation::Instrumentation::kMethodExited |
+                                                instrumentation::Instrumentation::kDexPcMoved);
   gDebuggerActive = false;
-  SetDebuggerUpdatesEnabled(false);
-
   gRegistry->Clear();
   gDebuggerConnected = false;
+  CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable);
+  runtime->GetThreadList()->ResumeAll();
 }
 
 bool Dbg::IsDebuggerActive() {
@@ -1902,34 +1964,16 @@
   virtual bool VisitFrame() NO_THREAD_SAFETY_ANALYSIS {
     if (frame_id != GetFrameId()) {
       return true;  // continue
-    }
-    mirror::AbstractMethod* m = GetMethod();
-    if (m->IsNative() || m->IsStatic()) {
-      this_object = NULL;
     } else {
-      uint16_t reg = DemangleSlot(0, m);
-      this_object = reinterpret_cast<mirror::Object*>(GetVReg(m, reg, kReferenceVReg));
+      this_object = GetThisObject();
+      return false;
     }
-    return false;
   }
 
   mirror::Object* this_object;
   JDWP::FrameId frame_id;
 };
 
-static mirror::Object* GetThis(Thread* self, mirror::AbstractMethod* m, size_t frame_id)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  // TODO: should we return the 'this' we passed through to non-static native methods?
-  if (m->IsNative() || m->IsStatic()) {
-    return NULL;
-  }
-
-  UniquePtr<Context> context(Context::Create());
-  GetThisVisitor visitor(self, context.get(), frame_id);
-  visitor.WalkStack();
-  return visitor.this_object;
-}
-
 JDWP::JdwpError Dbg::GetThisObject(JDWP::ObjectId thread_id, JDWP::FrameId frame_id,
                                    JDWP::ObjectId* result) {
   ScopedObjectAccessUnchecked soa(Thread::Current());
@@ -2176,7 +2220,8 @@
   visitor.WalkStack();
 }
 
-void Dbg::PostLocationEvent(const mirror::AbstractMethod* m, int dex_pc, mirror::Object* this_object, int event_flags) {
+void Dbg::PostLocationEvent(const mirror::AbstractMethod* m, int dex_pc,
+                            mirror::Object* this_object, int event_flags) {
   mirror::Class* c = m->GetDeclaringClass();
 
   JDWP::JdwpLocation location;
@@ -2194,29 +2239,25 @@
   gJdwpState->PostLocationEvent(&location, this_id, event_flags);
 }
 
-void Dbg::PostException(Thread* thread,
-                        JDWP::FrameId throw_frame_id, mirror::AbstractMethod* throw_method,
-                        uint32_t throw_dex_pc, mirror::AbstractMethod* catch_method,
+void Dbg::PostException(Thread* thread, const ThrowLocation& throw_location,
+                        mirror::AbstractMethod* catch_method,
                         uint32_t catch_dex_pc, mirror::Throwable* exception_object) {
   if (!IsDebuggerActive()) {
     return;
   }
 
-  JDWP::JdwpLocation throw_location;
-  SetLocation(throw_location, throw_method, throw_dex_pc);
+  JDWP::JdwpLocation jdwp_throw_location;
+  SetLocation(jdwp_throw_location, throw_location.GetMethod(), throw_location.GetDexPc());
   JDWP::JdwpLocation catch_location;
   SetLocation(catch_location, catch_method, catch_dex_pc);
 
   // We need 'this' for InstanceOnly filters.
-  UniquePtr<Context> context(Context::Create());
-  GetThisVisitor visitor(thread, context.get(), throw_frame_id);
-  visitor.WalkStack();
-  JDWP::ObjectId this_id = gRegistry->Add(visitor.this_object);
-
+  JDWP::ObjectId this_id = gRegistry->Add(throw_location.GetThis());
   JDWP::ObjectId exception_id = gRegistry->Add(exception_object);
   JDWP::RefTypeId exception_class_id = gRegistry->AddRefType(exception_object->GetClass());
 
-  gJdwpState->PostException(&throw_location, exception_id, exception_class_id, &catch_location, this_id);
+  gJdwpState->PostException(&jdwp_throw_location, exception_id, exception_class_id, &catch_location,
+                            this_id);
 }
 
 void Dbg::PostClassPrepare(mirror::Class* c) {
@@ -2232,20 +2273,9 @@
   gJdwpState->PostClassPrepare(tag, gRegistry->Add(c), ClassHelper(c).GetDescriptor(), state);
 }
 
-void Dbg::UpdateDebugger(int32_t dex_pc, Thread* self) {
-  if (!IsDebuggerActive() || dex_pc == -2 /* fake method exit */) {
-    return;
-  }
-
-  size_t frame_id;
-  mirror::AbstractMethod* m = self->GetCurrentMethod(NULL, &frame_id);
-  //LOG(INFO) << "UpdateDebugger " << PrettyMethod(m) << "@" << dex_pc << " frame " << frame_id;
-
-  if (dex_pc == -1) {
-    // We use a pc of -1 to represent method entry, since we might branch back to pc 0 later.
-    // This means that for this special notification, there can't be anything else interesting
-    // going on, so we're done already.
-    Dbg::PostLocationEvent(m, 0, GetThis(self, m, frame_id), kMethodEntry);
+void Dbg::UpdateDebugger(Thread* thread, mirror::Object* this_object,
+                         const mirror::AbstractMethod* m, uint32_t dex_pc) {
+  if (!IsDebuggerActive() || dex_pc == static_cast<uint32_t>(-2) /* fake method exit */) {
     return;
   }
 
@@ -2259,7 +2289,7 @@
     // If the debugger is single-stepping one of our threads, check to
     // see if we're that thread and we've reached a step point.
     MutexLock mu(Thread::Current(), *Locks::breakpoint_lock_);
-    if (gSingleStepControl.is_active && gSingleStepControl.thread == self) {
+    if (gSingleStepControl.is_active && gSingleStepControl.thread == thread) {
       CHECK(!m->IsNative());
       if (gSingleStepControl.step_depth == JDWP::SD_INTO) {
         // Step into method calls.  We break when the line number
@@ -2282,7 +2312,7 @@
         // might get unrolled past it by an exception, and it's tricky
         // to identify recursion.)
 
-        int stack_depth = GetStackDepth(self);
+        int stack_depth = GetStackDepth(thread);
 
         if (stack_depth < gSingleStepControl.stack_depth) {
           // popped up one or more frames, always trigger
@@ -2307,7 +2337,7 @@
         // with the PC at the next instruction in the returned-to
         // function, rather than the end of the returning function.
 
-        int stack_depth = GetStackDepth(self);
+        int stack_depth = GetStackDepth(thread);
         if (stack_depth < gSingleStepControl.stack_depth) {
           event_flags |= kSingleStep;
           VLOG(jdwp) << "SS method pop";
@@ -2316,27 +2346,10 @@
     }
   }
 
-  // Check to see if this is a "return" instruction.  JDWP says we should
-  // send the event *after* the code has been executed, but it also says
-  // the location we provide is the last instruction.  Since the "return"
-  // instruction has no interesting side effects, we should be safe.
-  // (We can't just move this down to the returnFromMethod label because
-  // we potentially need to combine it with other events.)
-  // We're also not supposed to generate a method exit event if the method
-  // terminates "with a thrown exception".
-  if (dex_pc >= 0) {
-    const DexFile::CodeItem* code_item = MethodHelper(m).GetCodeItem();
-    CHECK(code_item != NULL) << PrettyMethod(m) << " @" << dex_pc;
-    CHECK_LT(dex_pc, static_cast<int32_t>(code_item->insns_size_in_code_units_));
-    if (Instruction::At(&code_item->insns_[dex_pc])->IsReturn()) {
-      event_flags |= kMethodExit;
-    }
-  }
-
   // If there's something interesting going on, see if it matches one
   // of the debugger filters.
   if (event_flags != 0) {
-    Dbg::PostLocationEvent(m, dex_pc, GetThis(self, m, frame_id), event_flags);
+    Dbg::PostLocationEvent(m, dex_pc, this_object, event_flags);
   }
 }
 
@@ -2706,8 +2719,19 @@
 
   // We can be called while an exception is pending. We need
   // to preserve that across the method invocation.
-  SirtRef<mirror::Throwable> old_exception(soa.Self(), soa.Self()->GetException());
-  soa.Self()->ClearException();
+  SirtRef<mirror::Object> old_throw_this_object(soa.Self(), NULL);
+  SirtRef<mirror::AbstractMethod> old_throw_method(soa.Self(), NULL);
+  SirtRef<mirror::Throwable> old_exception(soa.Self(), NULL);
+  uint32_t old_throw_dex_pc;
+  {
+    ThrowLocation old_throw_location;
+    mirror::Throwable* old_exception_obj = soa.Self()->GetException(&old_throw_location);
+    old_throw_this_object.reset(old_throw_location.GetThis());
+    old_throw_method.reset(old_throw_location.GetMethod());
+    old_exception.reset(old_exception_obj);
+    old_throw_dex_pc = old_throw_location.GetDexPc();
+    soa.Self()->ClearException();
+  }
 
   // Translate the method through the vtable, unless the debugger wants to suppress it.
   mirror::AbstractMethod* m = pReq->method_;
@@ -2731,12 +2755,13 @@
   arg_array.BuildArgArray(soa, pReq->receiver_, reinterpret_cast<jvalue*>(pReq->arg_values_));
   InvokeWithArgArray(soa, m, &arg_array, &pReq->result_value, mh.GetShorty()[0]);
 
-  pReq->exception = gRegistry->Add(soa.Self()->GetException());
+  mirror::Throwable* exception = soa.Self()->GetException(NULL);
+  soa.Self()->ClearException();
+  pReq->exception = gRegistry->Add(exception);
   pReq->result_tag = BasicTagFromDescriptor(MethodHelper(m).GetShorty());
   if (pReq->exception != 0) {
-    mirror::Object* exc = soa.Self()->GetException();
-    VLOG(jdwp) << "  JDWP invocation returning with exception=" << exc << " " << PrettyTypeOf(exc);
-    soa.Self()->ClearException();
+    VLOG(jdwp) << "  JDWP invocation returning with exception=" << exception
+        << " " << exception->Dump();
     pReq->result_value.SetJ(0);
   } else if (pReq->result_tag == JDWP::JT_OBJECT) {
     /* if no exception thrown, examine object result more closely */
@@ -2759,7 +2784,9 @@
   }
 
   if (old_exception.get() != NULL) {
-    soa.Self()->SetException(old_exception.get());
+    ThrowLocation gc_safe_throw_location(old_throw_this_object.get(), old_throw_method.get(),
+                                         old_throw_dex_pc);
+    soa.Self()->SetException(gc_safe_throw_location, old_exception.get());
   }
 }
 
@@ -2943,9 +2970,6 @@
     ScopedObjectAccessUnchecked soa(Thread::Current());
     JDWP::ObjectId id = gRegistry->Add(t->GetPeer());
     gJdwpState->PostThreadChange(id, type == CHUNK_TYPE("THCR"));
-    // If this thread's just joined the party while we're already debugging, make sure it knows
-    // to give us updates when it's running.
-    t->SetDebuggerUpdatesEnabled(true);
   }
   Dbg::DdmSendThreadNotification(t, type);
 }
@@ -3326,9 +3350,9 @@
     Heap* heap = Runtime::Current()->GetHeap();
     const Spaces& spaces = heap->GetSpaces();
     Thread* self = Thread::Current();
+    ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
     for (Spaces::const_iterator cur = spaces.begin(); cur != spaces.end(); ++cur) {
       if ((*cur)->IsAllocSpace()) {
-        ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
         (*cur)->AsAllocSpace()->Walk(HeapChunkContext::HeapChunkCallback, &context);
       }
     }
diff --git a/src/debugger.h b/src/debugger.h
index ad01011..eb17695 100644
--- a/src/debugger.h
+++ b/src/debugger.h
@@ -39,6 +39,7 @@
 }  // namespace mirror
 struct AllocRecord;
 class Thread;
+class ThrowLocation;
 
 /*
  * Invoke-during-breakpoint support.
@@ -101,8 +102,8 @@
    * when the debugger attaches.
    */
   static void Connected();
-  static void GoActive() LOCKS_EXCLUDED(Locks::breakpoint_lock_);
-  static void Disconnected() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  static void GoActive() LOCKS_EXCLUDED(Locks::breakpoint_lock_, Locks::mutator_lock_);
+  static void Disconnected() LOCKS_EXCLUDED(Locks::mutator_lock_);
   static void Disposed();
 
   // Returns true if we're actually debugging with a real debugger, false if it's
@@ -326,9 +327,8 @@
   static void PostLocationEvent(const mirror::AbstractMethod* method, int pcOffset,
                                 mirror::Object* thisPtr, int eventFlags)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static void PostException(Thread* thread, JDWP::FrameId throw_frame_id,
-                            mirror::AbstractMethod* throw_method,
-                            uint32_t throw_dex_pc, mirror::AbstractMethod* catch_method,
+  static void PostException(Thread* thread, const ThrowLocation& throw_location,
+                            mirror::AbstractMethod* catch_method,
                             uint32_t catch_dex_pc, mirror::Throwable* exception)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static void PostThreadStart(Thread* t)
@@ -338,7 +338,8 @@
   static void PostClassPrepare(mirror::Class* c)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  static void UpdateDebugger(int32_t dex_pc, Thread* self)
+  static void UpdateDebugger(Thread* thread, mirror::Object* this_object,
+                             const mirror::AbstractMethod* method, uint32_t new_dex_pc)
       LOCKS_EXCLUDED(Locks::breakpoint_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
diff --git a/src/dex_file.h b/src/dex_file.h
index 2da3e32..002d79c 100644
--- a/src/dex_file.h
+++ b/src/dex_file.h
@@ -866,10 +866,10 @@
   Index index_;
 
   // The base address of the memory mapping.
-  const byte* begin_;
+  const byte* const begin_;
 
   // The size of the underlying memory allocation in bytes.
-  size_t size_;
+  const size_t size_;
 
   // Typically the dex file name when available, alternatively some identifying string.
   //
@@ -883,6 +883,7 @@
   UniquePtr<MemMap> mem_map_;
 
   // A cached com.android.dex.Dex instance, possibly NULL. Use GetDexObject.
+  // TODO: this is mutable as it shouldn't be here. We should move it to the dex cache or similar.
   mutable jobject dex_object_;
 
   // Points to the header section.
diff --git a/src/gc/large_object_space.cc b/src/gc/large_object_space.cc
index df7e68d..c3bf382 100644
--- a/src/gc/large_object_space.cc
+++ b/src/gc/large_object_space.cc
@@ -50,7 +50,7 @@
 
 LargeObjectMapSpace::LargeObjectMapSpace(const std::string& name)
     : LargeObjectSpace(name),
-      lock_("large object space lock", kAllocSpaceLock)
+      lock_("large object map space lock", kAllocSpaceLock)
 {
 
 }
diff --git a/src/gc/mark_sweep.cc b/src/gc/mark_sweep.cc
index 055a7e7..35e75cb 100644
--- a/src/gc/mark_sweep.cc
+++ b/src/gc/mark_sweep.cc
@@ -135,8 +135,8 @@
 MarkSweep::MarkSweep(Heap* heap, bool is_concurrent)
     : GarbageCollector(heap),
       gc_barrier_(new Barrier(0)),
-      large_object_lock_("large object lock"),
-      mark_stack_expand_lock_("mark stack expand lock"),
+      large_object_lock_("mark sweep large object lock", kMarkSweepLargeObjectLock),
+      mark_stack_expand_lock_("mark sweep mark stack expand lock"),
       timings_(GetName(), true),
       cumulative_timings_(GetName()),
       is_concurrent_(is_concurrent) {
diff --git a/src/gc_map.h b/src/gc_map.h
index 8e4dbdb..473b39a 100644
--- a/src/gc_map.h
+++ b/src/gc_map.h
@@ -66,8 +66,11 @@
   const uint8_t* FindBitMap(uintptr_t native_pc_offset) {
     size_t num_entries = NumEntries();
     size_t index = Hash(native_pc_offset) % num_entries;
+    size_t misses = 0;
     while (GetNativePcOffset(index) != native_pc_offset) {
       index = (index + 1) % num_entries;
+      misses++;
+      DCHECK_LT(misses, num_entries) << "Failed to find offset: " << native_pc_offset;
     }
     return GetBitMap(index);
   }
diff --git a/src/heap.cc b/src/heap.cc
index 2f7cb24..468e800 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -611,44 +611,46 @@
 }
 
 void Heap::VerifyObjectBody(const mirror::Object* obj) {
-  if (!IsAligned<kObjectAlignment>(obj)) {
+  if (UNLIKELY(!IsAligned<kObjectAlignment>(obj))) {
     LOG(FATAL) << "Object isn't aligned: " << obj;
   }
+  if (UNLIKELY(GetObjectsAllocated() <= 10)) {  // Ignore early dawn of the universe verifications.
+    return;
+  }
+  const byte* raw_addr = reinterpret_cast<const byte*>(obj) +
+      mirror::Object::ClassOffset().Int32Value();
+  const mirror::Class* c = *reinterpret_cast<mirror::Class* const *>(raw_addr);
+  if (UNLIKELY(c == NULL)) {
+    LOG(FATAL) << "Null class in object: " << obj;
+  } else if (UNLIKELY(!IsAligned<kObjectAlignment>(c))) {
+    LOG(FATAL) << "Class isn't aligned: " << c << " in object: " << obj;
+  }
+  // Check obj.getClass().getClass() == obj.getClass().getClass().getClass()
+  // Note: we don't use the accessors here as they have internal sanity checks
+  // that we don't want to run
+  raw_addr = reinterpret_cast<const byte*>(c) + mirror::Object::ClassOffset().Int32Value();
+  const mirror::Class* c_c = *reinterpret_cast<mirror::Class* const *>(raw_addr);
+  raw_addr = reinterpret_cast<const byte*>(c_c) + mirror::Object::ClassOffset().Int32Value();
+  const mirror::Class* c_c_c = *reinterpret_cast<mirror::Class* const *>(raw_addr);
+  CHECK_EQ(c_c, c_c_c);
 
-  // TODO: the bitmap tests below are racy if VerifyObjectBody is called without the
-  //       heap_bitmap_lock_.
-  if (!GetLiveBitmap()->Test(obj)) {
-    // Check the allocation stack / live stack.
-    if (!std::binary_search(live_stack_->Begin(), live_stack_->End(), obj) &&
-        std::find(allocation_stack_->Begin(), allocation_stack_->End(), obj) ==
-            allocation_stack_->End()) {
-      if (large_object_space_->GetLiveObjects()->Test(obj)) {
-        DumpSpaces();
-        LOG(FATAL) << "Object is dead: " << obj;
+  if (verify_object_mode_ != kVerifyAllFast) {
+    // TODO: the bitmap tests below are racy if VerifyObjectBody is called without the
+    //       heap_bitmap_lock_.
+    if (!GetLiveBitmap()->Test(obj)) {
+      // Check the allocation stack / live stack.
+      if (!std::binary_search(live_stack_->Begin(), live_stack_->End(), obj) &&
+          std::find(allocation_stack_->Begin(), allocation_stack_->End(), obj) ==
+              allocation_stack_->End()) {
+        if (large_object_space_->GetLiveObjects()->Test(obj)) {
+          DumpSpaces();
+          LOG(FATAL) << "Object is dead: " << obj;
+        }
       }
     }
-  }
-
-  // Ignore early dawn of the universe verifications
-  if (verify_object_mode_ != kVerifyAllFast && GetObjectsAllocated() > 10) {
-    const byte* raw_addr = reinterpret_cast<const byte*>(obj) +
-        mirror::Object::ClassOffset().Int32Value();
-    const mirror::Class* c = *reinterpret_cast<mirror::Class* const *>(raw_addr);
-    if (c == NULL) {
-      LOG(FATAL) << "Null class in object: " << obj;
-    } else if (!IsAligned<kObjectAlignment>(c)) {
-      LOG(FATAL) << "Class isn't aligned: " << c << " in object: " << obj;
-    } else if (!GetLiveBitmap()->Test(c)) {
+    if (!GetLiveBitmap()->Test(c)) {
       LOG(FATAL) << "Class of object is dead: " << c << " in object: " << obj;
     }
-    // Check obj.getClass().getClass() == obj.getClass().getClass().getClass()
-    // Note: we don't use the accessors here as they have internal sanity checks
-    // that we don't want to run
-    raw_addr = reinterpret_cast<const byte*>(c) + mirror::Object::ClassOffset().Int32Value();
-    const mirror::Class* c_c = *reinterpret_cast<mirror::Class* const *>(raw_addr);
-    raw_addr = reinterpret_cast<const byte*>(c_c) + mirror::Object::ClassOffset().Int32Value();
-    const mirror::Class* c_c_c = *reinterpret_cast<mirror::Class* const *>(raw_addr);
-    CHECK_EQ(c_c, c_c_c);
   }
 }
 
diff --git a/src/hprof/hprof.cc b/src/hprof/hprof.cc
index 472cc67..7539066 100644
--- a/src/hprof/hprof.cc
+++ b/src/hprof/hprof.cc
@@ -41,6 +41,7 @@
 #include "base/stringprintf.h"
 #include "base/unix_file/fd_file.h"
 #include "class_linker.h"
+#include "common_throws.h"
 #include "debugger.h"
 #include "dex_file-inl.h"
 #include "globals.h"
@@ -450,16 +451,14 @@
       if (fd_ >= 0) {
         out_fd = dup(fd_);
         if (out_fd < 0) {
-          self->ThrowNewExceptionF("Ljava/lang/RuntimeException;",
-                                   "Couldn't dump heap; dup(%d) failed: %s", fd_, strerror(errno));
+          ThrowRuntimeException("Couldn't dump heap; dup(%d) failed: %s", fd_, strerror(errno));
           return;
         }
       } else {
         out_fd = open(filename_.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0644);
         if (out_fd < 0) {
-          self->ThrowNewExceptionF("Ljava/lang/RuntimeException;",
-                                   "Couldn't dump heap; open(\"%s\") failed: %s", filename_.c_str(),
-                                   strerror(errno));
+          ThrowRuntimeException("Couldn't dump heap; open(\"%s\") failed: %s", filename_.c_str(),
+                                strerror(errno));
           return;
         }
       }
@@ -470,7 +469,7 @@
       if (!okay) {
         std::string msg(StringPrintf("Couldn't dump heap; writing \"%s\" failed: %s",
                                      filename_.c_str(), strerror(errno)));
-        self->ThrowNewException("Ljava/lang/RuntimeException;", msg.c_str());
+        ThrowRuntimeException("%s", msg.c_str());
         LOG(ERROR) << msg;
       }
     }
diff --git a/src/instrumentation.cc b/src/instrumentation.cc
index 81fe637..55e93cb 100644
--- a/src/instrumentation.cc
+++ b/src/instrumentation.cc
@@ -18,14 +18,17 @@
 
 #include <sys/uio.h>
 
+#include "atomic_integer.h"
 #include "base/unix_file/fd_file.h"
 #include "class_linker.h"
 #include "debugger.h"
+#include "dex_file-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/dex_cache.h"
 #include "mirror/abstract_method-inl.h"
 #include "mirror/object_array-inl.h"
 #include "mirror/object-inl.h"
+#include "nth_caller_visitor.h"
 #if !defined(ART_USE_PORTABLE_COMPILER)
 #include "oat/runtime/oat_support_entrypoints.h"
 #endif
@@ -34,205 +37,521 @@
 #include "scoped_thread_state_change.h"
 #include "thread.h"
 #include "thread_list.h"
-#include "trace.h"
 
 namespace art {
+namespace instrumentation {
 
-static bool InstallStubsClassVisitor(mirror::Class* klass, void*)
+static bool InstallStubsClassVisitor(mirror::Class* klass, void* arg)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+  Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg);
+  return instrumentation->InstallStubsForClass(klass);
+}
+
+bool Instrumentation::InstallStubsForClass(mirror::Class* klass) {
+  bool uninstall = !entry_exit_stubs_installed_ && !interpreter_stubs_installed_;
+  ClassLinker* class_linker = NULL;
+  if (uninstall) {
+    class_linker = Runtime::Current()->GetClassLinker();
+  }
+  bool is_initialized = klass->IsInitialized();
   for (size_t i = 0; i < klass->NumDirectMethods(); i++) {
     mirror::AbstractMethod* method = klass->GetDirectMethod(i);
-    if (instrumentation->GetSavedCodeFromMap(method) == NULL) {
-      instrumentation->SaveAndUpdateCode(method);
+    if (!method->IsAbstract()) {
+      const void* new_code;
+      if (uninstall) {
+        if (is_initialized || !method->IsStatic() || method->IsConstructor()) {
+          new_code = class_linker->GetOatCodeFor(method);
+        } else {
+          new_code = Runtime::Current()->GetResolutionStubArray(Runtime::kStaticMethod)->GetData();
+        }
+      } else {  // !uninstall
+        if (!interpreter_stubs_installed_ || method->IsNative()) {
+          new_code = GetInstrumentationEntryPoint();
+        } else {
+          new_code = GetInterpreterEntryPoint();
+        }
+      }
+      method->SetCode(new_code);
     }
   }
-
   for (size_t i = 0; i < klass->NumVirtualMethods(); i++) {
     mirror::AbstractMethod* method = klass->GetVirtualMethod(i);
-    if (instrumentation->GetSavedCodeFromMap(method) == NULL) {
-      instrumentation->SaveAndUpdateCode(method);
+    if (!method->IsAbstract()) {
+      const void* new_code;
+      if (uninstall) {
+        new_code = class_linker->GetOatCodeFor(method);
+      } else {  // !uninstall
+        if (!interpreter_stubs_installed_ || method->IsNative()) {
+          new_code = GetInstrumentationEntryPoint();
+        } else {
+          new_code = GetInterpreterEntryPoint();
+        }
+      }
+      method->SetCode(new_code);
     }
   }
   return true;
 }
 
-static bool UninstallStubsClassVisitor(mirror::Class* klass, void*)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
-  for (size_t i = 0; i < klass->NumDirectMethods(); i++) {
-    mirror::AbstractMethod* method = klass->GetDirectMethod(i);
-    if (instrumentation->GetSavedCodeFromMap(method) != NULL) {
-      instrumentation->ResetSavedCode(method);
-    }
-  }
-
-  for (size_t i = 0; i < klass->NumVirtualMethods(); i++) {
-    mirror::AbstractMethod* method = klass->GetVirtualMethod(i);
-    if (instrumentation->GetSavedCodeFromMap(method) != NULL) {
-      instrumentation->ResetSavedCode(method);
-    }
-  }
-  return true;
-}
-
-void InstrumentationInstallStack(Thread* self, void* arg)
+// Places the instrumentation exit pc as the return PC for every quick frame. This also allows
+// deoptimization of quick frames to interpreter frames.
+static void InstrumentationInstallStack(Thread* thread, void* arg)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   struct InstallStackVisitor : public StackVisitor {
-    InstallStackVisitor(Thread* self, uintptr_t instrumentation_exit_pc)
-        : StackVisitor(self, NULL),  self_(self),
-          instrumentation_exit_pc_(instrumentation_exit_pc) {}
+    InstallStackVisitor(Thread* thread, Context* context, uintptr_t instrumentation_exit_pc)
+        : StackVisitor(thread, context),  instrumentation_stack_(thread->GetInstrumentationStack()),
+          instrumentation_exit_pc_(instrumentation_exit_pc), last_return_pc_(0) {}
 
     virtual bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+      mirror::AbstractMethod* m = GetMethod();
       if (GetCurrentQuickFrame() == NULL) {
+        if (kVerboseInstrumentation) {
+          LOG(INFO) << "  Ignoring a shadow frame. Frame " << GetFrameId()
+              << " Method=" << PrettyMethod(m);
+        }
         return true;  // Ignore shadow frames.
       }
-      mirror::AbstractMethod* m = GetMethod();
       if (m == NULL) {
+        if (kVerboseInstrumentation) {
+          LOG(INFO) << "  Skipping upcall. Frame " << GetFrameId();
+        }
+        last_return_pc_ = 0;
         return true; // Ignore upcalls.
       }
-      if (m->GetDexMethodIndex() == DexFile::kDexNoIndex16) {
+      if (m->IsRuntimeMethod()) {
+        if (kVerboseInstrumentation) {
+          LOG(INFO) << "  Skipping runtime method. Frame " << GetFrameId();
+        }
+        last_return_pc_ = GetReturnPc();
         return true;  // Ignore unresolved methods since they will be instrumented after resolution.
       }
-      uintptr_t pc = GetReturnPc();
-      InstrumentationStackFrame instrumentation_frame(m, pc, GetFrameId());
-      self_->PushBackInstrumentationStackFrame(instrumentation_frame);
+      if (kVerboseInstrumentation) {
+        LOG(INFO) << "  Installing exit stub in " << DescribeLocation();
+      }
+      uintptr_t return_pc = GetReturnPc();
+      CHECK_NE(return_pc, instrumentation_exit_pc_);
+      CHECK_NE(return_pc, 0U);
+      InstrumentationStackFrame instrumentation_frame(GetThisObject(), m, return_pc, GetFrameId());
+      if (kVerboseInstrumentation) {
+        LOG(INFO) << "Pushing frame " << instrumentation_frame.Dump();
+      }
+      instrumentation_stack_->push_back(instrumentation_frame);
+      dex_pcs_.push_back(m->ToDexPc(last_return_pc_));
       SetReturnPc(instrumentation_exit_pc_);
+      last_return_pc_ = return_pc;
       return true;  // Continue.
     }
-    Thread* const self_;
+    std::deque<InstrumentationStackFrame>* const instrumentation_stack_;
+    std::vector<uint32_t> dex_pcs_;
     const uintptr_t instrumentation_exit_pc_;
+    uintptr_t last_return_pc_;
   };
-  uintptr_t instrumentation_exit_pc = GetInstrumentationExitPc();
-  InstallStackVisitor visitor(self, instrumentation_exit_pc);
-  visitor.WalkStack(true);
-  Trace* trace = reinterpret_cast<Trace*>(arg);
-  if (trace != NULL) {
-    std::deque<InstrumentationStackFrame>::const_reverse_iterator it =
-        self->GetInstrumentationStack()->rbegin();
-    std::deque<InstrumentationStackFrame>::const_reverse_iterator end =
-        self->GetInstrumentationStack()->rend();
-    for (; it != end; ++it) {
-      trace->LogMethodTraceEvent(self, (*it).method_, Trace::kMethodTraceEnter);
-    }
+  if (kVerboseInstrumentation) {
+    std::string thread_name;
+    thread->GetThreadName(thread_name);
+    LOG(INFO) << "Installing exit stubs in " << thread_name;
   }
+  UniquePtr<Context> context(Context::Create());
+  uintptr_t instrumentation_exit_pc = GetInstrumentationExitPc();
+  InstallStackVisitor visitor(thread, context.get(), instrumentation_exit_pc);
+  visitor.WalkStack(true);
+
+  // Create method enter events for all methods current on the thread's stack.
+  Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg);
+  typedef std::deque<InstrumentationStackFrame>::const_reverse_iterator It;
+  for (It it = thread->GetInstrumentationStack()->rbegin(),
+       end = thread->GetInstrumentationStack()->rend(); it != end; ++it) {
+    mirror::Object* this_object = (*it).this_object_;
+    mirror::AbstractMethod* method = (*it).method_;
+    uint32_t dex_pc = visitor.dex_pcs_.back();
+    visitor.dex_pcs_.pop_back();
+    instrumentation->MethodEnterEvent(thread, this_object, method, dex_pc);
+  }
+  thread->VerifyStack();
 }
 
-static void InstrumentationRestoreStack(Thread* self, void*)
+// Removes the instrumentation exit pc as the return PC for every quick frame.
+static void InstrumentationRestoreStack(Thread* thread, void* arg)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   struct RestoreStackVisitor : public StackVisitor {
-    RestoreStackVisitor(Thread* self, uintptr_t instrumentation_exit_pc)
-        : StackVisitor(self, NULL), self_(self),
-          instrumentation_exit_pc_(instrumentation_exit_pc) {}
+    RestoreStackVisitor(Thread* thread, uintptr_t instrumentation_exit_pc,
+                        Instrumentation* instrumentation)
+        : StackVisitor(thread, NULL), thread_(thread),
+          instrumentation_exit_pc_(instrumentation_exit_pc),
+          instrumentation_(instrumentation),
+          instrumentation_stack_(thread->GetInstrumentationStack()),
+          frames_removed_(0) {}
 
     virtual bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-      if (self_->IsInstrumentationStackEmpty()) {
+      if (instrumentation_stack_->size() == 0) {
         return false;  // Stop.
       }
       mirror::AbstractMethod* m = GetMethod();
+      if (GetCurrentQuickFrame() == NULL) {
+        if (kVerboseInstrumentation) {
+          LOG(INFO) << "  Ignoring a shadow frame. Frame " << GetFrameId() << " Method=" << PrettyMethod(m);
+        }
+        return true;  // Ignore shadow frames.
+      }
       if (m == NULL) {
+        if (kVerboseInstrumentation) {
+          LOG(INFO) << "  Skipping upcall. Frame " << GetFrameId();
+        }
         return true;  // Ignore upcalls.
       }
-      uintptr_t pc = GetReturnPc();
-      if (pc == instrumentation_exit_pc_) {
-        InstrumentationStackFrame instrumentation_frame = self_->PopInstrumentationStackFrame();
-        SetReturnPc(instrumentation_frame.return_pc_);
-        CHECK(m == instrumentation_frame.method_);
-        CHECK_EQ(GetFrameId(), instrumentation_frame.frame_id_);
-        Runtime* runtime = Runtime::Current();
-        if (runtime->IsMethodTracingActive()) {
-          Trace* trace = runtime->GetInstrumentation()->GetTrace();
-          trace->LogMethodTraceEvent(self_, m, Trace::kMethodTraceExit);
+      typedef std::deque<instrumentation::InstrumentationStackFrame>::const_iterator It; // TODO: C++0x auto
+      bool removed_stub = false;
+      // TODO: make this search more efficient?
+      for (It it = instrumentation_stack_->begin(), end = instrumentation_stack_->end(); it != end;
+          ++it) {
+        InstrumentationStackFrame instrumentation_frame =  *it;
+        if (instrumentation_frame.frame_id_ == GetFrameId()) {
+          if (kVerboseInstrumentation) {
+            LOG(INFO) << "  Removing exit stub in " << DescribeLocation();
+          }
+          CHECK(m == instrumentation_frame.method_) << PrettyMethod(m);
+          SetReturnPc(instrumentation_frame.return_pc_);
+          // Create the method exit events. As the methods didn't really exit the result is 0.
+          instrumentation_->MethodExitEvent(thread_, instrumentation_frame.this_object_, m,
+                                            GetDexPc(), JValue());
+          frames_removed_++;
+          removed_stub = true;
+          break;
+        }
+      }
+      if (!removed_stub) {
+        if (kVerboseInstrumentation) {
+          LOG(INFO) << "  No exit stub in " << DescribeLocation();
+          DescribeStack(thread_);
         }
       }
       return true;  // Continue.
     }
-    Thread* const self_;
+    Thread* const thread_;
     const uintptr_t instrumentation_exit_pc_;
+    Instrumentation* const instrumentation_;
+    std::deque<instrumentation::InstrumentationStackFrame>* const instrumentation_stack_;
+    size_t frames_removed_;
   };
-  uintptr_t instrumentation_exit_pc = GetInstrumentationExitPc();
-  RestoreStackVisitor visitor(self, instrumentation_exit_pc);
-  visitor.WalkStack(true);
-}
-
-Instrumentation::~Instrumentation() {
-  delete trace_;
-}
-
-void Instrumentation::InstallStubs() {
-  Thread* self = Thread::Current();
-  Locks::thread_list_lock_->AssertNotHeld(self);
-  Runtime::Current()->GetClassLinker()->VisitClasses(InstallStubsClassVisitor, NULL);
-  MutexLock mu(self, *Locks::thread_list_lock_);
-  Runtime::Current()->GetThreadList()->ForEach(InstrumentationInstallStack, GetTrace());
-}
-
-void Instrumentation::UninstallStubs() {
-  Thread* self = Thread::Current();
-  Locks::thread_list_lock_->AssertNotHeld(self);
-  Runtime::Current()->GetClassLinker()->VisitClasses(UninstallStubsClassVisitor, NULL);
-  MutexLock mu(self, *Locks::thread_list_lock_);
-  Runtime::Current()->GetThreadList()->ForEach(InstrumentationRestoreStack, NULL);
-}
-
-void Instrumentation::AddSavedCodeToMap(const mirror::AbstractMethod* method, const void* code) {
-  saved_code_map_.Put(method, code);
-}
-
-void Instrumentation::RemoveSavedCodeFromMap(const mirror::AbstractMethod* method) {
-  saved_code_map_.erase(method);
-}
-
-const void* Instrumentation::GetSavedCodeFromMap(const mirror::AbstractMethod* method) {
-  typedef SafeMap<const mirror::AbstractMethod*, const void*>::const_iterator It; // TODO: C++0x auto
-  It it = saved_code_map_.find(method);
-  if (it == saved_code_map_.end()) {
-    return NULL;
-  } else {
-    return it->second;
+  if (kVerboseInstrumentation) {
+    std::string thread_name;
+    thread->GetThreadName(thread_name);
+    LOG(INFO) << "Removing exit stubs in " << thread_name;
+  }
+  std::deque<instrumentation::InstrumentationStackFrame>* stack = thread->GetInstrumentationStack();
+  if (stack->size() > 0) {
+    Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg);
+    uintptr_t instrumentation_exit_pc = GetInstrumentationExitPc();
+    RestoreStackVisitor visitor(thread, instrumentation_exit_pc, instrumentation);
+    visitor.WalkStack(true);
+    CHECK_EQ(visitor.frames_removed_, stack->size());
+    while (stack->size() > 0) {
+      stack->pop_front();
+    }
   }
 }
 
-void Instrumentation::SaveAndUpdateCode(mirror::AbstractMethod* method) {
-#if defined(ART_USE_PORTABLE_COMPILER)
-  UNUSED(method);
-  UNIMPLEMENTED(FATAL);
-#else
-  void* instrumentation_stub = GetInstrumentationEntryPoint();
-  CHECK(GetSavedCodeFromMap(method) == NULL);
-  AddSavedCodeToMap(method, method->GetCode());
-  method->SetCode(instrumentation_stub);
-#endif
+void Instrumentation::AddListener(InstrumentationListener* listener, uint32_t events) {
+  Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
+  bool require_entry_exit_stubs = false;
+  bool require_interpreter = false;
+  if ((events & kMethodEntered) != 0) {
+    method_entry_listeners_.push_back(listener);
+    require_entry_exit_stubs = true;
+    have_method_entry_listeners_ = true;
+  }
+  if ((events & kMethodExited) != 0) {
+    method_exit_listeners_.push_back(listener);
+    require_entry_exit_stubs = true;
+    have_method_exit_listeners_ = true;
+  }
+  if ((events & kMethodUnwind) != 0) {
+    method_unwind_listeners_.push_back(listener);
+    have_method_unwind_listeners_ = true;
+  }
+  if ((events & kDexPcMoved) != 0) {
+    dex_pc_listeners_.push_back(listener);
+    require_interpreter = true;
+    have_dex_pc_listeners_ = true;
+  }
+  ConfigureStubs(require_entry_exit_stubs, require_interpreter);
 }
 
-void Instrumentation::ResetSavedCode(mirror::AbstractMethod* method) {
-  CHECK(GetSavedCodeFromMap(method) != NULL);
-  method->SetCode(GetSavedCodeFromMap(method));
-  RemoveSavedCodeFromMap(method);
+void Instrumentation::RemoveListener(InstrumentationListener* listener, uint32_t events) {
+  Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
+  bool require_entry_exit_stubs = false;
+  bool require_interpreter = false;
+
+  if ((events & kMethodEntered) != 0) {
+    bool contains = std::find(method_entry_listeners_.begin(), method_entry_listeners_.end(),
+                              listener) != method_entry_listeners_.end();
+    if (contains) {
+      method_entry_listeners_.remove(listener);
+    }
+    have_method_entry_listeners_ = method_entry_listeners_.size() > 0;
+    require_entry_exit_stubs |= have_method_entry_listeners_;
+  }
+  if ((events & kMethodExited) != 0) {
+    bool contains = std::find(method_exit_listeners_.begin(), method_exit_listeners_.end(),
+                              listener) != method_exit_listeners_.end();
+    if (contains) {
+      method_exit_listeners_.remove(listener);
+    }
+    have_method_exit_listeners_ = method_exit_listeners_.size() > 0;
+    require_entry_exit_stubs |= have_method_exit_listeners_;
+  }
+  if ((events & kMethodUnwind) != 0) {
+    method_unwind_listeners_.remove(listener);
+  }
+  if ((events & kDexPcMoved) != 0) {
+    bool contains = std::find(dex_pc_listeners_.begin(), dex_pc_listeners_.end(),
+                              listener) != dex_pc_listeners_.end();
+    if (contains) {
+      dex_pc_listeners_.remove(listener);
+    }
+    have_dex_pc_listeners_ = dex_pc_listeners_.size() > 0;
+    require_interpreter |= have_dex_pc_listeners_;
+  }
+  ConfigureStubs(require_entry_exit_stubs, require_interpreter);
 }
 
-Trace* Instrumentation::GetTrace() const {
-  return trace_;
+void Instrumentation::ConfigureStubs(bool require_entry_exit_stubs, bool require_interpreter) {
+  interpret_only_ = require_interpreter || forced_interpret_only_;
+  // Compute what level of instrumentation is required and compare to current.
+  int desired_level, current_level;
+  if (require_interpreter) {
+    desired_level = 2;
+  } else if (require_entry_exit_stubs) {
+    desired_level = 1;
+  } else {
+    desired_level = 0;
+  }
+  if (interpreter_stubs_installed_) {
+    current_level = 2;
+  } else if (entry_exit_stubs_installed_) {
+    current_level = 1;
+  } else {
+    current_level = 0;
+  }
+  if (desired_level == current_level) {
+    // We're already set.
+    return;
+  }
+  Thread* self = Thread::Current();
+  Runtime* runtime = Runtime::Current();
+  Locks::thread_list_lock_->AssertNotHeld(self);
+  if (desired_level > 0) {
+    if (require_interpreter) {
+      interpreter_stubs_installed_ = true;
+    } else {
+      CHECK(require_entry_exit_stubs);
+      entry_exit_stubs_installed_ = true;
+    }
+    runtime->GetClassLinker()->VisitClasses(InstallStubsClassVisitor, this);
+    instrumentation_stubs_installed_ = true;
+    MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
+    runtime->GetThreadList()->ForEach(InstrumentationInstallStack, this);
+  } else {
+    interpreter_stubs_installed_ = false;
+    entry_exit_stubs_installed_ = false;
+    runtime->GetClassLinker()->VisitClasses(InstallStubsClassVisitor, this);
+    instrumentation_stubs_installed_ = false;
+    MutexLock mu(self, *Locks::thread_list_lock_);
+    Runtime::Current()->GetThreadList()->ForEach(InstrumentationRestoreStack, this);
+  }
 }
 
-void Instrumentation::SetTrace(Trace* trace) {
-  trace_ = trace;
+void Instrumentation::UpdateMethodsCode(mirror::AbstractMethod* method, const void* code) const {
+  if (LIKELY(!instrumentation_stubs_installed_)) {
+    method->SetCode(code);
+  }
 }
 
-void Instrumentation::RemoveTrace() {
-  delete trace_;
-  trace_ = NULL;
+const void* Instrumentation::GetQuickCodeFor(const mirror::AbstractMethod* method) const {
+  Runtime* runtime = Runtime::Current();
+  if (LIKELY(!instrumentation_stubs_installed_)) {
+    const void* code = method->GetCode();
+    DCHECK(code != NULL);
+    if (LIKELY(code != runtime->GetResolutionStubArray(Runtime::kStaticMethod)->GetData())) {
+      return code;
+    }
+  }
+  return runtime->GetClassLinker()->GetOatCodeFor(method);
 }
 
-uint32_t InstrumentationMethodUnwindFromCode(Thread* self) {
-  Trace* trace = Runtime::Current()->GetInstrumentation()->GetTrace();
-  InstrumentationStackFrame instrumentation_frame = self->PopInstrumentationStackFrame();
+void Instrumentation::MethodEnterEventImpl(Thread* thread, mirror::Object* this_object,
+                                           const mirror::AbstractMethod* method,
+                                           uint32_t dex_pc) const {
+  typedef std::list<InstrumentationListener*>::const_iterator It; // TODO: C++0x auto
+  for (It it = method_entry_listeners_.begin(), end = method_entry_listeners_.end(); it != end;
+      ++it) {
+    (*it)->MethodEntered(thread, this_object, method, dex_pc);
+  }
+}
+
+void Instrumentation::MethodExitEventImpl(Thread* thread, mirror::Object* this_object,
+                                          const mirror::AbstractMethod* method,
+                                          uint32_t dex_pc, const JValue& return_value) const {
+  typedef std::list<InstrumentationListener*>::const_iterator It; // TODO: C++0x auto
+  for (It it = method_exit_listeners_.begin(), end = method_exit_listeners_.end(); it != end;
+      ++it) {
+    (*it)->MethodExited(thread, this_object, method, dex_pc, return_value);
+  }
+}
+
+void Instrumentation::MethodUnwindEvent(Thread* thread, mirror::Object* this_object,
+                                        const mirror::AbstractMethod* method,
+                                        uint32_t dex_pc) const {
+  if (have_method_unwind_listeners_) {
+    typedef std::list<InstrumentationListener*>::const_iterator It; // TODO: C++0x auto
+    for (It it = method_unwind_listeners_.begin(), end = method_unwind_listeners_.end(); it != end;
+        ++it) {
+      (*it)->MethodUnwind(thread, method, dex_pc);
+    }
+  }
+}
+
+void Instrumentation::DexPcMovedEventImpl(Thread* thread, mirror::Object* this_object,
+                                          const mirror::AbstractMethod* method,
+                                          uint32_t dex_pc) const {
+  // TODO: STL copy-on-write collection? The copy below is due to the debug listener having an
+  // action where it can remove itself as a listener and break the iterator. The copy only works
+  // around the problem and in general we may have to move to something like reference counting to
+  // ensure listeners are deleted correctly.
+  std::list<InstrumentationListener*> copy(dex_pc_listeners_);
+  typedef std::list<InstrumentationListener*>::const_iterator It; // TODO: C++0x auto
+  for (It it = copy.begin(), end = copy.end(); it != end; ++it) {
+    (*it)->DexPcMoved(thread, this_object, method, dex_pc);
+  }
+}
+
+void Instrumentation::ExceptionCaughtEvent(Thread* thread, const ThrowLocation& throw_location,
+                                           mirror::AbstractMethod* catch_method,
+                                           uint32_t catch_dex_pc,
+                                           mirror::Throwable* exception_object) {
+  if (have_exception_caught_listeners_) {
+    typedef std::list<InstrumentationListener*>::const_iterator It; // TODO: C++0x auto
+    for (It it = exception_caught_listeners_.begin(), end = exception_caught_listeners_.end();
+        it != end; ++it) {
+      (*it)->ExceptionCaught(thread, throw_location, catch_method, catch_dex_pc, exception_object);
+    }
+  }
+}
+
+static void CheckStackDepth(Thread* self, const InstrumentationStackFrame& instrumentation_frame,
+                            int delta)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  size_t frame_id = StackVisitor::ComputeNumFrames(self) + delta;
+  if (frame_id != instrumentation_frame.frame_id_) {
+    LOG(ERROR) << "Expected frame_id=" << frame_id << " but found "
+        << instrumentation_frame.frame_id_;
+    StackVisitor::DescribeStack(self);
+    CHECK_EQ(frame_id, instrumentation_frame.frame_id_);
+  }
+}
+
+void Instrumentation::PushInstrumentationStackFrame(Thread* self, mirror::Object* this_object,
+                                                    mirror::AbstractMethod* method,
+                                                    uintptr_t lr) {
+  // We have a callee-save frame meaning this value is guaranteed to never be 0.
+  size_t frame_id = StackVisitor::ComputeNumFrames(self);
+  std::deque<instrumentation::InstrumentationStackFrame>* stack = self->GetInstrumentationStack();
+  if (kVerboseInstrumentation) {
+    LOG(INFO) << "Entering " << PrettyMethod(method) << " from PC " << (void*)lr;
+  }
+  instrumentation::InstrumentationStackFrame instrumentation_frame(this_object, method, lr,
+                                                                   frame_id);
+  stack->push_front(instrumentation_frame);
+
+  MethodEnterEvent(self, this_object, method, 0);
+}
+
+uint64_t Instrumentation::PopInstrumentationStackFrame(Thread* self, uintptr_t* return_pc,
+                                                       uint64_t gpr_result, uint64_t fpr_result) {
+  // Do the pop.
+  std::deque<instrumentation::InstrumentationStackFrame>* stack = self->GetInstrumentationStack();
+  CHECK_GT(stack->size(), 0U);
+  InstrumentationStackFrame instrumentation_frame = stack->front();
+  stack->pop_front();
+
+  // Set return PC and check the sanity of the stack.
+  *return_pc = instrumentation_frame.return_pc_;
+  CheckStackDepth(self, instrumentation_frame, 0);
+
   mirror::AbstractMethod* method = instrumentation_frame.method_;
-  uint32_t lr = instrumentation_frame.return_pc_;
+  char return_shorty = MethodHelper(method).GetShorty()[0];
+  JValue return_value;
+  if (return_shorty == 'V') {
+    return_value.SetJ(0);
+  } else if (return_shorty == 'F' || return_shorty == 'D') {
+    return_value.SetJ(fpr_result);
+  } else {
+    return_value.SetJ(gpr_result);
+  }
+  // TODO: improve the dex pc information here, requires knowledge of current PC as opposed to
+  //       return_pc.
+  uint32_t dex_pc = DexFile::kDexNoIndex;
+  mirror::Object* this_object = instrumentation_frame.this_object_;
+  MethodExitEvent(self, this_object, instrumentation_frame.method_, dex_pc, return_value);
 
-  trace->LogMethodTraceEvent(self, method, Trace::kMethodTraceUnwind);
-
-  return lr;
+  bool deoptimize = false;
+  if (interpreter_stubs_installed_) {
+    // Deoptimize unless we're returning to an upcall.
+    NthCallerVisitor visitor(self, 1, true);
+    visitor.WalkStack(true);
+    deoptimize = visitor.caller != NULL;
+    if (deoptimize && kVerboseInstrumentation) {
+      LOG(INFO) << "Deoptimizing into " << PrettyMethod(visitor.caller);
+    }
+  }
+  if (deoptimize) {
+    if (kVerboseInstrumentation) {
+      LOG(INFO) << "Deoptimizing from " << PrettyMethod(method)
+          << " result is " << std::hex << return_value.GetJ();
+    }
+    self->SetDeoptimizationReturnValue(return_value);
+    return static_cast<uint64_t>(GetDeoptimizationEntryPoint()) |
+        (static_cast<uint64_t>(*return_pc) << 32);
+  } else {
+    if (kVerboseInstrumentation) {
+      LOG(INFO) << "Returning from " << PrettyMethod(method) << " to PC " << (void*)(*return_pc);
+    }
+    return *return_pc;
+  }
 }
 
+void Instrumentation::PopMethodForUnwind(Thread* self, bool is_deoptimization) const {
+  // Do the pop.
+  std::deque<instrumentation::InstrumentationStackFrame>* stack = self->GetInstrumentationStack();
+  CHECK_GT(stack->size(), 0U);
+  InstrumentationStackFrame instrumentation_frame = stack->front();
+  // TODO: bring back CheckStackDepth(self, instrumentation_frame, 2);
+  stack->pop_front();
+
+  mirror::AbstractMethod* method = instrumentation_frame.method_;
+  if (is_deoptimization) {
+    if (kVerboseInstrumentation) {
+      LOG(INFO) << "Popping for deoptimization " << PrettyMethod(method);
+    }
+  } else {
+    if (kVerboseInstrumentation) {
+      LOG(INFO) << "Popping for unwind " << PrettyMethod(method);
+    }
+
+    // Notify listeners of method unwind.
+    // TODO: improve the dex pc information here, requires knowledge of current PC as opposed to
+    //       return_pc.
+    uint32_t dex_pc = DexFile::kDexNoIndex;
+    MethodUnwindEvent(self, instrumentation_frame.this_object_, method, dex_pc);
+  }
+}
+
+std::string InstrumentationStackFrame::Dump() const {
+  std::ostringstream os;
+  os << "Frame " << frame_id_ << " " << PrettyMethod(method_) << ":"
+      << reinterpret_cast<void*>(return_pc_) << " this=" << reinterpret_cast<void*>(this_object_);
+  return os.str();
+}
+
+}  // namespace instrumentation
 }  // namespace art
diff --git a/src/instrumentation.h b/src/instrumentation.h
index fb49bf8..6a4a142 100644
--- a/src/instrumentation.h
+++ b/src/instrumentation.h
@@ -18,61 +18,261 @@
 #define ART_SRC_INSTRUMENTATION_H_
 
 #include "base/macros.h"
-#include "safe_map.h"
+#include "locks.h"
 
 #include <stdint.h>
+#include <list>
 
 namespace art {
-
 namespace mirror {
 class AbstractMethod;
-}
+class Class;
+class Object;
+class Throwable;
+}  // namespace mirror
+union JValue;
 class Thread;
-class Trace;
+class ThrowLocation;
 
-uint32_t InstrumentationMethodUnwindFromCode(Thread* self);
+namespace instrumentation {
 
-struct InstrumentationStackFrame {
-  InstrumentationStackFrame() : method_(NULL), return_pc_(0), frame_id_(0) {}
-  InstrumentationStackFrame(mirror::AbstractMethod* method, uintptr_t return_pc, size_t frame_id)
-      : method_(method), return_pc_(return_pc), frame_id_(frame_id) {
-  }
-  mirror::AbstractMethod* method_;
-  uintptr_t return_pc_;
-  size_t frame_id_;
+const bool kVerboseInstrumentation = false;
+
+// Instrumentation event listener API. Registered listeners will get the appropriate call back for
+// the events they are listening for. The call backs supply the thread, method and dex_pc the event
+// occurred upon. The thread may or may not be Thread::Current().
+struct InstrumentationListener {
+  InstrumentationListener() {}
+  virtual ~InstrumentationListener() {}
+
+  // Call-back for when a method is entered.
+  virtual void MethodEntered(Thread* thread, mirror::Object* this_object,
+                             const mirror::AbstractMethod* method,
+                             uint32_t dex_pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0;
+
+  // Call-back for when a method is exited.
+  // TODO: its likely passing the return value would be useful, however, we may need to get and
+  //       parse the shorty to determine what kind of register holds the result.
+  virtual void MethodExited(Thread* thread, mirror::Object* this_object,
+                            const mirror::AbstractMethod* method, uint32_t dex_pc,
+                            const JValue& return_value)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0;
+
+  // Call-back for when a method is popped due to an exception throw. A method will either cause a
+  // MethodExited call-back or a MethodUnwind call-back when its activation is removed.
+  virtual void MethodUnwind(Thread* thread, const mirror::AbstractMethod* method,
+                            uint32_t dex_pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0;
+
+  // Call-back for when the dex pc moves in a method.
+  virtual void DexPcMoved(Thread* thread, mirror::Object* this_object,
+                          const mirror::AbstractMethod* method, uint32_t new_dex_pc)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0;
+
+  // Call-back when an exception is caught.
+  virtual void ExceptionCaught(Thread* thread, const ThrowLocation& throw_location,
+                               mirror::AbstractMethod* catch_method, uint32_t catch_dex_pc,
+                               mirror::Throwable* exception_object)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0;
 };
 
+// Instrumentation is a catch-all for when extra information is required from the runtime. The
+// typical use for instrumentation is for profiling and debugging. Instrumentation may add stubs
+// to method entry and exit, it may also force execution to be switched to the interpreter and
+// trigger deoptimization.
 class Instrumentation {
  public:
-  Instrumentation() {}
-  ~Instrumentation();
+  enum InstrumentationEvent {
+    kMethodEntered = 1,
+    kMethodExited = 2,
+    kMethodUnwind = 4,
+    kDexPcMoved = 8,
+    kExceptionCaught = 16
+  };
 
-  // Replaces code of each method with a pointer to a stub for method tracing.
-  void InstallStubs() LOCKS_EXCLUDED(Locks::thread_list_lock_);
+  Instrumentation() :
+      instrumentation_stubs_installed_(false), entry_exit_stubs_installed_(false),
+      interpreter_stubs_installed_(false),
+      interpret_only_(false), forced_interpret_only_(false),
+      have_method_entry_listeners_(false), have_method_exit_listeners_(false),
+      have_method_unwind_listeners_(false), have_dex_pc_listeners_(false),
+      have_exception_caught_listeners_(false) {}
 
-  // Restores original code for each method and fixes the return values of each thread's stack.
-  void UninstallStubs() LOCKS_EXCLUDED(Locks::thread_list_lock_);
+  // Add a listener to be notified of the masked together sent of instrumentation events. This
+  // suspend the runtime to install stubs. You are expected to hold the mutator lock as a proxy
+  // for saying you should have suspended all threads (installing stubs while threads are running
+  // will break).
+  void AddListener(InstrumentationListener* listener, uint32_t events)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
+      LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_);
 
-  const void* GetSavedCodeFromMap(const mirror::AbstractMethod* method);
-  void SaveAndUpdateCode(mirror::AbstractMethod* method);
-  void ResetSavedCode(mirror::AbstractMethod* method);
+  // Removes a listener possibly removing instrumentation stubs.
+  void RemoveListener(InstrumentationListener* listener, uint32_t events)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
+      LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_);
 
-  Trace* GetTrace() const;
-  void SetTrace(Trace* trace);
-  void RemoveTrace();
+  // Update the code of a method respecting any installed stubs.
+  void UpdateMethodsCode(mirror::AbstractMethod* method, const void* code) const;
+
+  // Get the quick code for the given method. More efficient than asking the class linker as it
+  // will short-cut to GetCode if instrumentation and static method resolution stubs aren't
+  // installed.
+  const void* GetQuickCodeFor(const mirror::AbstractMethod* method) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  void ForceInterpretOnly() {
+    interpret_only_ = true;
+    forced_interpret_only_ = true;
+  }
+
+  // Called by AbstractMethod::Invoke to determine dispatch mechanism.
+  bool InterpretOnly() const {
+    return interpret_only_;
+  }
+
+  bool ShouldPortableCodeDeoptimize() const {
+    return instrumentation_stubs_installed_;
+  }
+
+  bool AreExitStubsInstalled() const {
+    return instrumentation_stubs_installed_;
+  }
+
+  // Inform listeners that a method has been entered. A dex PC is provided as we may install
+  // listeners into executing code and get method enter events for methods already on the stack.
+  void MethodEnterEvent(Thread* thread, mirror::Object* this_object,
+                        const mirror::AbstractMethod* method, uint32_t dex_pc) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    if (have_method_entry_listeners_) {
+      MethodEnterEventImpl(thread, this_object, method, dex_pc);
+    }
+  }
+
+  // Inform listeners that a method has been exited.
+  void MethodExitEvent(Thread* thread, mirror::Object* this_object,
+                       const mirror::AbstractMethod* method, uint32_t dex_pc,
+                       const JValue& return_value) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    if (have_method_exit_listeners_) {
+      MethodExitEventImpl(thread, this_object, method, dex_pc, return_value);
+    }
+  }
+
+  // Inform listeners that a method has been exited due to an exception.
+  void MethodUnwindEvent(Thread* thread, mirror::Object* this_object,
+                         const mirror::AbstractMethod* method, uint32_t dex_pc) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Inform listeners that the dex pc has moved (only supported by the interpreter).
+  void DexPcMovedEvent(Thread* thread, mirror::Object* this_object,
+                       const mirror::AbstractMethod* method, uint32_t dex_pc) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    if (have_dex_pc_listeners_) {
+      DexPcMovedEventImpl(thread, this_object, method, dex_pc);
+    }
+  }
+
+  // Inform listeners that an exception was caught.
+  void ExceptionCaughtEvent(Thread* thread, const ThrowLocation& throw_location,
+                            mirror::AbstractMethod* catch_method, uint32_t catch_dex_pc,
+                            mirror::Throwable* exception_object)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Called when an instrumented method is entered. The intended link register (lr) is saved so
+  // that returning causes a branch to the method exit stub. Generates method enter events.
+  void PushInstrumentationStackFrame(Thread* self, mirror::Object* this_object,
+                                     mirror::AbstractMethod* method, uintptr_t lr)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Called when an instrumented method is exited. Removes the pushed instrumentation frame
+  // returning the intended link register. Generates method exit events.
+  uint64_t PopInstrumentationStackFrame(Thread* self, uintptr_t* return_pc, uint64_t gpr_result,
+                                        uint64_t fpr_result)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Pops an instrumentation frame from the current thread and generate an unwind event.
+  void PopMethodForUnwind(Thread* self, bool is_deoptimization) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Call back for configure stubs.
+  bool InstallStubsForClass(mirror::Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
  private:
-  void AddSavedCodeToMap(const mirror::AbstractMethod* method, const void* code);
-  void RemoveSavedCodeFromMap(const mirror::AbstractMethod* method);
+  // Does the job of installing or removing instrumentation code within methods.
+  void ConfigureStubs(bool require_entry_exit_stubs, bool require_interpreter)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
+      LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_);
 
-  // Maps a method to its original code pointer.
-  SafeMap<const mirror::AbstractMethod*, const void*> saved_code_map_;
+  void MethodEnterEventImpl(Thread* thread, mirror::Object* this_object,
+                            const mirror::AbstractMethod* method, uint32_t dex_pc) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void MethodExitEventImpl(Thread* thread, mirror::Object* this_object,
+                           const mirror::AbstractMethod* method,
+                           uint32_t dex_pc, const JValue& return_value) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void DexPcMovedEventImpl(Thread* thread, mirror::Object* this_object,
+                           const mirror::AbstractMethod* method, uint32_t dex_pc) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  Trace* trace_;
+  // Have we hijacked AbstractMethod::code_ so that it calls instrumentation/interpreter code?
+  bool instrumentation_stubs_installed_;
+
+  // Have we hijacked AbstractMethod::code_ to reference the enter/exit stubs?
+  bool entry_exit_stubs_installed_;
+
+  // Have we hijacked AbstractMethod::code_ to reference the enter interpreter stub?
+  bool interpreter_stubs_installed_;
+
+  // Do we need the fidelity of events that we only get from running within the interpreter?
+  bool interpret_only_;
+
+  // Did the runtime request we only run in the interpreter? ie -Xint mode.
+  bool forced_interpret_only_;
+
+  // Do we have any listeners for method entry events? Short-cut to avoid taking the
+  // instrumentation_lock_.
+  bool have_method_entry_listeners_;
+
+  // Do we have any listeners for method exit events? Short-cut to avoid taking the
+  // instrumentation_lock_.
+  bool have_method_exit_listeners_;
+
+  // Do we have any listeners for method unwind events? Short-cut to avoid taking the
+  // instrumentation_lock_.
+  bool have_method_unwind_listeners_;
+
+  // Do we have any listeners for dex move events? Short-cut to avoid taking the
+  // instrumentation_lock_.
+  bool have_dex_pc_listeners_;
+
+  // Do we have any exception caught listeners? Short-cut to avoid taking the instrumentation_lock_.
+  bool have_exception_caught_listeners_;
+
+  // The event listeners, written to with the mutator_lock_ exclusively held.
+  std::list<InstrumentationListener*> method_entry_listeners_ GUARDED_BY(Locks::mutator_lock_);
+  std::list<InstrumentationListener*> method_exit_listeners_ GUARDED_BY(Locks::mutator_lock_);
+  std::list<InstrumentationListener*> method_unwind_listeners_ GUARDED_BY(Locks::mutator_lock_);
+  std::list<InstrumentationListener*> dex_pc_listeners_ GUARDED_BY(Locks::mutator_lock_);
+  std::list<InstrumentationListener*> exception_caught_listeners_ GUARDED_BY(Locks::mutator_lock_);
 
   DISALLOW_COPY_AND_ASSIGN(Instrumentation);
 };
 
+// An element in the instrumentation side stack maintained in art::Thread.
+struct InstrumentationStackFrame {
+  InstrumentationStackFrame(mirror::Object* this_object, mirror::AbstractMethod* method,
+                            uintptr_t return_pc, size_t frame_id)
+      : this_object_(this_object), method_(method), return_pc_(return_pc), frame_id_(frame_id) {
+  }
+
+  std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  mirror::Object* this_object_;
+  mirror::AbstractMethod* method_;
+  const uintptr_t return_pc_;
+  const size_t frame_id_;
+};
+
+}  // namespace instrumentation
 }  // namespace art
 
 #endif  // ART_SRC_INSTRUMENTATION_H_
diff --git a/src/interpreter/interpreter.cc b/src/interpreter/interpreter.cc
index 91b381c..b82c632 100644
--- a/src/interpreter/interpreter.cc
+++ b/src/interpreter/interpreter.cc
@@ -21,7 +21,6 @@
 #include "base/logging.h"
 #include "class_linker-inl.h"
 #include "common_throws.h"
-#include "debugger.h"
 #include "dex_file-inl.h"
 #include "dex_instruction.h"
 #include "gc/card_table-inl.h"
@@ -50,10 +49,6 @@
 static const int64_t kMaxLong = std::numeric_limits<int64_t>::max();
 static const int64_t kMinLong = std::numeric_limits<int64_t>::min();
 
-static JDWP::FrameId throw_frame_id_ = 0;
-static AbstractMethod* throw_method_ = NULL;
-static uint32_t throw_dex_pc_ = 0;
-
 static void UnstartedRuntimeInvoke(Thread* self, AbstractMethod* target_method,
                                    Object* receiver, uint32_t* args, JValue* result)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -441,7 +436,7 @@
     } else {
       obj = shadow_frame.GetVRegReference(dec_insn.vB);
       if (UNLIKELY(obj == NULL)) {
-        ThrowNullPointerExceptionForFieldAccess(f, true);
+        ThrowNullPointerExceptionForFieldAccess(shadow_frame.GetCurrentLocationForThrow(), f, true);
         return;
       }
     }
@@ -488,7 +483,8 @@
     } else {
       obj = shadow_frame.GetVRegReference(dec_insn.vB);
       if (UNLIKELY(obj == NULL)) {
-        ThrowNullPointerExceptionForFieldAccess(f, false);
+        ThrowNullPointerExceptionForFieldAccess(shadow_frame.GetCurrentLocationForThrow(),
+                                                f, false);
         return;
       }
     }
@@ -523,7 +519,7 @@
 static void DoIntDivide(Thread* self, ShadowFrame& shadow_frame, size_t result_reg,
     int32_t dividend, int32_t divisor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   if (UNLIKELY(divisor == 0)) {
-    self->ThrowNewException("Ljava/lang/ArithmeticException;", "divide by zero");
+    ThrowArithmeticExceptionDivideByZero(self);
   } else if (UNLIKELY(dividend == kMinInt && divisor == -1)) {
     shadow_frame.SetVReg(result_reg, kMinInt);
   } else {
@@ -534,7 +530,7 @@
 static void DoIntRemainder(Thread* self, ShadowFrame& shadow_frame, size_t result_reg,
     int32_t dividend, int32_t divisor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   if (UNLIKELY(divisor == 0)) {
-    self->ThrowNewException("Ljava/lang/ArithmeticException;", "divide by zero");
+    ThrowArithmeticExceptionDivideByZero(self);
   } else if (UNLIKELY(dividend == kMinInt && divisor == -1)) {
     shadow_frame.SetVReg(result_reg, 0);
   } else {
@@ -545,7 +541,7 @@
 static void DoLongDivide(Thread* self, ShadowFrame& shadow_frame, size_t result_reg,
     int64_t dividend, int64_t divisor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   if (UNLIKELY(divisor == 0)) {
-    self->ThrowNewException("Ljava/lang/ArithmeticException;", "divide by zero");
+    ThrowArithmeticExceptionDivideByZero(self);
   } else if (UNLIKELY(dividend == kMinLong && divisor == -1)) {
     shadow_frame.SetVRegLong(result_reg, kMinLong);
   } else {
@@ -556,7 +552,7 @@
 static void DoLongRemainder(Thread* self, ShadowFrame& shadow_frame, size_t result_reg,
     int64_t dividend, int64_t divisor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   if (UNLIKELY(divisor == 0)) {
-    self->ThrowNewException("Ljava/lang/ArithmeticException;", "divide by zero");
+    ThrowArithmeticExceptionDivideByZero(self);
   } else if (UNLIKELY(dividend == kMinLong && divisor == -1)) {
     shadow_frame.SetVRegLong(result_reg, 0);
   } else {
@@ -567,41 +563,46 @@
 static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item,
                       ShadowFrame& shadow_frame, JValue result_register)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  if (UNLIKELY(!shadow_frame.HasReferenceArray())) {
+    LOG(FATAL) << "Invalid shadow frame for interpreter use";
+    return JValue();
+  }
+  self->VerifyStack();
+  instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
   const uint16_t* insns = code_item->insns_;
   const Instruction* inst = Instruction::At(insns + shadow_frame.GetDexPC());
-  bool entry = (inst->GetDexPc(insns) == 0);
+  if (inst->GetDexPc(insns) == 0) {  // We are entering the method as opposed to deoptimizing..
+    instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(), shadow_frame.GetMethod(),
+                                      0);
+  }
   while (true) {
     CheckSuspend(self);
     uint32_t dex_pc = inst->GetDexPc(insns);
     shadow_frame.SetDexPC(dex_pc);
-    if (entry) {
-      Dbg::UpdateDebugger(-1, self);
-    }
-    entry = false;
-    Dbg::UpdateDebugger(dex_pc, self);
+    instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(), shadow_frame.GetMethod(),
+                                     dex_pc);
     DecodedInstruction dec_insn(inst);
     const bool kTracing = false;
     if (kTracing) {
-      LOG(INFO) << PrettyMethod(shadow_frame.GetMethod())
-                << StringPrintf("\n0x%x: %s\nReferences:",
-                                inst->GetDexPc(insns), inst->DumpString(&mh.GetDexFile()).c_str());
+#define TRACE_LOG std::cerr
+      TRACE_LOG << PrettyMethod(shadow_frame.GetMethod())
+                << StringPrintf("\n0x%x: ", inst->GetDexPc(insns))
+                << inst->DumpString(&mh.GetDexFile()) << "\n";
       for (size_t i = 0; i < shadow_frame.NumberOfVRegs(); ++i) {
-        Object* o = shadow_frame.GetVRegReference(i);
-        if (o != NULL) {
-          if (o->GetClass()->IsStringClass() && o->AsString()->GetCharArray() != NULL) {
-            LOG(INFO) << i << ": java.lang.String " << static_cast<void*>(o)
-                  << " \"" << o->AsString()->ToModifiedUtf8() << "\"";
+        uint32_t raw_value = shadow_frame.GetVReg(i);
+        Object* ref_value = shadow_frame.GetVRegReference(i);
+        TRACE_LOG << StringPrintf(" vreg%d=0x%08X", i, raw_value);
+        if (ref_value != NULL) {
+          if (ref_value->GetClass()->IsStringClass() &&
+              ref_value->AsString()->GetCharArray() != NULL) {
+            TRACE_LOG << "/java.lang.String \"" << ref_value->AsString()->ToModifiedUtf8() << "\"";
           } else {
-            LOG(INFO) << i << ": " << PrettyTypeOf(o) << " " << static_cast<void*>(o);
+            TRACE_LOG << "/" << PrettyTypeOf(ref_value);
           }
-        } else {
-          LOG(INFO) << i << ": null";
         }
       }
-      LOG(INFO) << "vregs:";
-      for (size_t i = 0; i < shadow_frame.NumberOfVRegs(); ++i) {
-        LOG(INFO) << StringPrintf("%d: %08x", i, shadow_frame.GetVReg(i));
-      }
+      TRACE_LOG << "\n";
+#undef TRACE_LOG
     }
     const Instruction* next_inst = inst->Next();
     switch (dec_insn.opcode) {
@@ -632,31 +633,42 @@
         shadow_frame.SetVRegReference(dec_insn.vA, result_register.GetL());
         break;
       case Instruction::MOVE_EXCEPTION: {
-        Throwable* exception = self->GetException();
+        Throwable* exception = self->GetException(NULL);
         self->ClearException();
         shadow_frame.SetVRegReference(dec_insn.vA, exception);
         break;
       }
       case Instruction::RETURN_VOID: {
         JValue result;
-        result.SetJ(0);
+        instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(),
+                                         shadow_frame.GetMethod(), shadow_frame.GetDexPC(),
+                                         result);
         return result;
       }
       case Instruction::RETURN: {
         JValue result;
         result.SetJ(0);
         result.SetI(shadow_frame.GetVReg(dec_insn.vA));
+        instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(),
+                                         shadow_frame.GetMethod(), shadow_frame.GetDexPC(),
+                                         result);
         return result;
       }
       case Instruction::RETURN_WIDE: {
         JValue result;
         result.SetJ(shadow_frame.GetVRegLong(dec_insn.vA));
+        instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(),
+                                         shadow_frame.GetMethod(), shadow_frame.GetDexPC(),
+                                         result);
         return result;
       }
       case Instruction::RETURN_OBJECT: {
         JValue result;
         result.SetJ(0);
         result.SetL(shadow_frame.GetVRegReference(dec_insn.vA));
+        instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(),
+                                         shadow_frame.GetMethod(), shadow_frame.GetDexPC(),
+                                         result);
         return result;
       }
       case Instruction::CONST_4: {
@@ -721,7 +733,7 @@
       case Instruction::MONITOR_ENTER: {
         Object* obj = shadow_frame.GetVRegReference(dec_insn.vA);
         if (UNLIKELY(obj == NULL)) {
-          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns));
+          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
         } else {
           DoMonitorEnter(self, obj);
         }
@@ -730,7 +742,7 @@
       case Instruction::MONITOR_EXIT: {
         Object* obj = shadow_frame.GetVRegReference(dec_insn.vA);
         if (UNLIKELY(obj == NULL)) {
-          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns));
+          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
         } else {
           DoMonitorExit(self, obj);
         }
@@ -743,10 +755,7 @@
         } else {
           Object* obj = shadow_frame.GetVRegReference(dec_insn.vA);
           if (UNLIKELY(obj != NULL && !obj->InstanceOf(c))) {
-            self->ThrowNewExceptionF("Ljava/lang/ClassCastException;",
-                "%s cannot be cast to %s",
-                PrettyDescriptor(obj->GetClass()).c_str(),
-                PrettyDescriptor(c).c_str());
+            ThrowClassCastException(c, obj->GetClass());
           }
         }
         break;
@@ -764,7 +773,7 @@
       case Instruction::ARRAY_LENGTH:  {
         Object* array = shadow_frame.GetVRegReference(dec_insn.vB);
         if (UNLIKELY(array == NULL)) {
-          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns));
+          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
           break;
         }
         shadow_frame.SetVReg(dec_insn.vA, array->AsArray()->GetLength());
@@ -787,7 +796,7 @@
         int32_t length = dec_insn.vA;
         CHECK(is_range || length <= 5);
         if (UNLIKELY(length < 0)) {
-          self->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d", length);
+          ThrowNegativeArraySizeException(length);
           break;
         }
         Class* arrayClass = ResolveVerifyAndClinit(dec_insn.vB, shadow_frame.GetMethod(), self, false, true);
@@ -799,11 +808,11 @@
         Class* componentClass = arrayClass->GetComponentType();
         if (UNLIKELY(componentClass->IsPrimitive() && !componentClass->IsPrimitiveInt())) {
           if (componentClass->IsPrimitiveLong() || componentClass->IsPrimitiveDouble()) {
-            self->ThrowNewExceptionF("Ljava/lang/RuntimeException;",
-                                     "Bad filled array request for type %s",
-                                     PrettyDescriptor(componentClass).c_str());
+            ThrowRuntimeException("Bad filled array request for type %s",
+                                  PrettyDescriptor(componentClass).c_str());
           } else {
-            self->ThrowNewExceptionF("Ljava/lang/InternalError;",
+            self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(),
+                                     "Ljava/lang/InternalError;",
                                      "Found type %s; filled-new-array not implemented for anything but \'int\'",
                                      PrettyDescriptor(componentClass).c_str());
           }
@@ -902,9 +911,12 @@
         break;
       }
       case Instruction::THROW: {
-        Object* o = shadow_frame.GetVRegReference(dec_insn.vA);
-        Throwable* t = (o == NULL) ? NULL : o->AsThrowable();
-        self->DeliverException(t);
+        Object* exception = shadow_frame.GetVRegReference(dec_insn.vA);
+        if (exception == NULL) {
+          ThrowNullPointerException(NULL, "throw with null exception");
+        } else {
+          self->SetException(shadow_frame.GetCurrentLocationForThrow(), exception->AsThrowable());
+        }
         break;
       }
       case Instruction::GOTO:
@@ -962,8 +974,7 @@
       case Instruction::FILL_ARRAY_DATA: {
         Object* obj = shadow_frame.GetVRegReference(dec_insn.vA);
         if (UNLIKELY(obj == NULL)) {
-          Thread::Current()->ThrowNewExceptionF("Ljava/lang/NullPointerException;",
-              "null array in FILL_ARRAY_DATA");
+          ThrowNullPointerException(NULL, "null array in FILL_ARRAY_DATA");
           break;
         }
         Array* array = obj->AsArray();
@@ -972,9 +983,10 @@
         const Instruction::ArrayDataPayload* payload =
             reinterpret_cast<const Instruction::ArrayDataPayload*>(insns + dex_pc + dec_insn.vB);
         if (UNLIKELY(static_cast<int32_t>(payload->element_count) > array->GetLength())) {
-          Thread::Current()->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;",
-                                                "failed FILL_ARRAY_DATA; length=%d, index=%d",
-                                                array->GetLength(), payload->element_count);
+          self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(),
+                                   "Ljava/lang/ArrayIndexOutOfBoundsException;",
+                                   "failed FILL_ARRAY_DATA; length=%d, index=%d",
+                                   array->GetLength(), payload->element_count);
           break;
         }
         uint32_t size_in_bytes = payload->element_count * payload->element_width;
@@ -1068,7 +1080,7 @@
       case Instruction::AGET_BOOLEAN: {
         Object* a = shadow_frame.GetVRegReference(dec_insn.vB);
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns));
+          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
           break;
         }
         int32_t index = shadow_frame.GetVReg(dec_insn.vC);
@@ -1078,7 +1090,7 @@
       case Instruction::AGET_BYTE: {
         Object* a = shadow_frame.GetVRegReference(dec_insn.vB);
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns));
+          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
           break;
         }
         int32_t index = shadow_frame.GetVReg(dec_insn.vC);
@@ -1088,7 +1100,7 @@
       case Instruction::AGET_CHAR: {
         Object* a = shadow_frame.GetVRegReference(dec_insn.vB);
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns));
+          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
           break;
         }
         int32_t index = shadow_frame.GetVReg(dec_insn.vC);
@@ -1098,7 +1110,7 @@
       case Instruction::AGET_SHORT: {
         Object* a = shadow_frame.GetVRegReference(dec_insn.vB);
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns));
+          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
           break;
         }
         int32_t index = shadow_frame.GetVReg(dec_insn.vC);
@@ -1108,7 +1120,7 @@
       case Instruction::AGET: {
         Object* a = shadow_frame.GetVRegReference(dec_insn.vB);
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns));
+          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
           break;
         }
         int32_t index = shadow_frame.GetVReg(dec_insn.vC);
@@ -1118,7 +1130,7 @@
       case Instruction::AGET_WIDE:  {
         Object* a = shadow_frame.GetVRegReference(dec_insn.vB);
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns));
+          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
           break;
         }
         int32_t index = shadow_frame.GetVReg(dec_insn.vC);
@@ -1128,7 +1140,7 @@
       case Instruction::AGET_OBJECT: {
         Object* a = shadow_frame.GetVRegReference(dec_insn.vB);
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns));
+          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
           break;
         }
         int32_t index = shadow_frame.GetVReg(dec_insn.vC);
@@ -1139,7 +1151,7 @@
         uint8_t val = shadow_frame.GetVReg(dec_insn.vA);
         Object* a = shadow_frame.GetVRegReference(dec_insn.vB);
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns));
+          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
           break;
         }
         int32_t index = shadow_frame.GetVReg(dec_insn.vC);
@@ -1150,7 +1162,7 @@
         int8_t val = shadow_frame.GetVReg(dec_insn.vA);
         Object* a = shadow_frame.GetVRegReference(dec_insn.vB);
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns));
+          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
           break;
         }
         int32_t index = shadow_frame.GetVReg(dec_insn.vC);
@@ -1161,7 +1173,7 @@
         uint16_t val = shadow_frame.GetVReg(dec_insn.vA);
         Object* a = shadow_frame.GetVRegReference(dec_insn.vB);
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns));
+          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
           break;
         }
         int32_t index = shadow_frame.GetVReg(dec_insn.vC);
@@ -1172,7 +1184,7 @@
         int16_t val = shadow_frame.GetVReg(dec_insn.vA);
         Object* a = shadow_frame.GetVRegReference(dec_insn.vB);
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns));
+          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
           break;
         }
         int32_t index = shadow_frame.GetVReg(dec_insn.vC);
@@ -1183,7 +1195,7 @@
         int32_t val = shadow_frame.GetVReg(dec_insn.vA);
         Object* a = shadow_frame.GetVRegReference(dec_insn.vB);
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns));
+          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
           break;
         }
         int32_t index = shadow_frame.GetVReg(dec_insn.vC);
@@ -1194,7 +1206,7 @@
         int64_t val = shadow_frame.GetVRegLong(dec_insn.vA);
         Object* a = shadow_frame.GetVRegReference(dec_insn.vB);
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns));
+          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
           break;
         }
         int32_t index = shadow_frame.GetVReg(dec_insn.vC);
@@ -1205,7 +1217,7 @@
         Object* val = shadow_frame.GetVRegReference(dec_insn.vA);
         Object* a = shadow_frame.GetVRegReference(dec_insn.vB);
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns));
+          ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
           break;
         }
         int32_t index = shadow_frame.GetVReg(dec_insn.vC);
@@ -1777,22 +1789,21 @@
         break;
     }
     if (UNLIKELY(self->IsExceptionPending())) {
-      if (throw_frame_id_ == 0) {
-        throw_method_ = shadow_frame.GetMethod();
-        throw_dex_pc_ = dex_pc;
-      }
-      throw_frame_id_++;
+      self->VerifyStack();
+      ThrowLocation throw_location;
+      mirror::Throwable* exception = self->GetException(&throw_location);
       uint32_t found_dex_pc =
-          shadow_frame.GetMethod()->FindCatchBlock(self->GetException()->GetClass(),
-                                                   inst->GetDexPc(insns));
+          shadow_frame.GetMethod()->FindCatchBlock(exception->GetClass(), inst->GetDexPc(insns));
       if (found_dex_pc == DexFile::kDexNoIndex) {
         JValue result;
         result.SetJ(0);
+        instrumentation->MethodUnwindEvent(self, shadow_frame.GetThisObject(),
+                                           shadow_frame.GetMethod(), shadow_frame.GetDexPC());
         return result;  // Handler in caller.
       } else {
-        Dbg::PostException(self, throw_frame_id_, throw_method_, throw_dex_pc_,
-                           shadow_frame.GetMethod(), found_dex_pc, self->GetException());
-        throw_frame_id_ = 0;
+        Runtime::Current()->GetInstrumentation()->ExceptionCaughtEvent(self, throw_location,
+                                                                       shadow_frame.GetMethod(),
+                                                                       found_dex_pc, exception);
         next_inst = Instruction::At(insns + found_dex_pc);
       }
     }
@@ -1816,8 +1827,9 @@
     num_regs =  code_item->registers_size_;
     num_ins = code_item->ins_size_;
   } else if (method->IsAbstract()) {
-    self->ThrowNewExceptionF("Ljava/lang/AbstractMethodError;", "abstract method \"%s\"",
-                             PrettyMethod(method).c_str());
+    ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+    self->ThrowNewExceptionF(throw_location, "Ljava/lang/AbstractMethodError;",
+                             "abstract method \"%s\"", PrettyMethod(method).c_str());
     return;
   } else {
     DCHECK(method->IsNative());
@@ -1884,23 +1896,18 @@
   self->PopShadowFrame();
 }
 
-JValue EnterInterpreterFromDeoptimize(Thread* self, ShadowFrame& shadow_frame, JValue ret_val)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  MethodHelper mh(shadow_frame.GetMethod());
-  const DexFile::CodeItem* code_item = mh.GetCodeItem();
-  return Execute(self, mh, code_item, shadow_frame, ret_val);
-}
-
-void EnterInterpreterFromLLVM(Thread* self, ShadowFrame* shadow_frame, JValue* ret_val)
+void EnterInterpreterFromDeoptimize(Thread* self, ShadowFrame* shadow_frame, JValue* ret_val)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   JValue value;
-  MethodHelper mh(shadow_frame->GetMethod());
-  const DexFile::CodeItem* code_item = mh.GetCodeItem();
+  value.SetJ(ret_val->GetJ());  // Set value to last known result in case the shadow frame chain is empty.
+  MethodHelper mh;
   while (shadow_frame != NULL) {
+    self->SetTopOfShadowStack(shadow_frame);
+    mh.ChangeMethod(shadow_frame->GetMethod());
+    const DexFile::CodeItem* code_item = mh.GetCodeItem();
     value = Execute(self, mh, code_item, *shadow_frame, value);
     ShadowFrame* old_frame = shadow_frame;
     shadow_frame = shadow_frame->GetLink();
-    mh.ChangeMethod(shadow_frame->GetMethod());
     delete old_frame;
   }
   ret_val->SetJ(value.GetJ());
diff --git a/src/interpreter/interpreter.h b/src/interpreter/interpreter.h
index 556b044..cf47b68 100644
--- a/src/interpreter/interpreter.h
+++ b/src/interpreter/interpreter.h
@@ -33,15 +33,13 @@
 
 namespace interpreter {
 
+// Called by AbstractMethod::Invoke, shadow frames arguments are taken from the args array.
 extern void EnterInterpreterFromInvoke(Thread* self, mirror::AbstractMethod* method,
                                        mirror::Object* receiver, uint32_t* args, JValue* result)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-extern JValue EnterInterpreterFromDeoptimize(Thread* self, ShadowFrame& shadow_frame,
-                                             JValue ret_val)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-void EnterInterpreterFromLLVM(Thread* self, ShadowFrame* shadow_frame, JValue* result)
+extern void EnterInterpreterFromDeoptimize(Thread* self, ShadowFrame* shadow_frame,
+                                           JValue* ret_val)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 extern JValue EnterInterpreterFromStub(Thread* self, MethodHelper& mh,
diff --git a/src/invoke_arg_array_builder.h b/src/invoke_arg_array_builder.h
index aca1091..a6e99a5 100644
--- a/src/invoke_arg_array_builder.h
+++ b/src/invoke_arg_array_builder.h
@@ -43,13 +43,24 @@
  public:
   explicit ArgArray(const char* shorty, uint32_t shorty_len)
       : shorty_(shorty), shorty_len_(shorty_len), num_bytes_(0) {
-    // TODO: This code is conservative. The multiply by 2 is to handle the case where all args are
-    // doubles or longs. We could scan the shorty to use the arg array more often.
-    if (shorty_len * 2 <= kSmallArgArraySize) {
+    size_t num_slots = shorty_len + 1;  // +1 in case of receiver.
+    if (LIKELY((num_slots * 2) < kSmallArgArraySize)) {
+      // We can trivially use the small arg array.
       arg_array_ = small_arg_array_;
     } else {
-      large_arg_array_.reset(new uint32_t[shorty_len_ * 2]);
-      arg_array_ = large_arg_array_.get();
+      // Analyze shorty to see if we need the large arg array.
+      for (size_t i = 1; i < shorty_len; ++i) {
+        char c = shorty[i];
+        if (c == 'J' || c == 'D') {
+          num_slots++;
+        }
+      }
+      if (num_slots <= kSmallArgArraySize) {
+        arg_array_ = small_arg_array_;
+      } else {
+        large_arg_array_.reset(new uint32_t[num_slots]);
+        arg_array_ = large_arg_array_.get();
+      }
     }
   }
 
diff --git a/src/jdwp/jdwp_event.cc b/src/jdwp/jdwp_event.cc
index eb385fc..5b65aa4 100644
--- a/src/jdwp/jdwp_event.cc
+++ b/src/jdwp/jdwp_event.cc
@@ -1069,21 +1069,31 @@
   // Try to avoid blocking GC during a send, but only safe when not using mutexes at a lower-level
   // than mutator for lock ordering reasons.
   Thread* self = Thread::Current();
-  bool safe_to_release_mutator_lock_over_send;
-  for (size_t i=0; i < kMutatorLock; ++i) {
-    if (self->GetHeldMutex(static_cast<LockLevel>(i)) != NULL) {
-      safe_to_release_mutator_lock_over_send = false;
-      break;
+  bool safe_to_release_mutator_lock_over_send = !Locks::mutator_lock_->IsExclusiveHeld(self);
+  if (safe_to_release_mutator_lock_over_send) {
+    for (size_t i=0; i < kMutatorLock; ++i) {
+      if (self->GetHeldMutex(static_cast<LockLevel>(i)) != NULL) {
+        safe_to_release_mutator_lock_over_send = false;
+        break;
+      }
     }
   }
+  bool success;
   if (safe_to_release_mutator_lock_over_send) {
     // Change state to waiting to allow GC, ... while we're sending.
     self->TransitionFromRunnableToSuspended(kWaitingForDebuggerSend);
-    (*transport_->sendBufferedRequest)(this, wrapiov, iov_count + 1);
+    success = (*transport_->sendBufferedRequest)(this, wrapiov, iov_count + 1);
     self->TransitionFromSuspendedToRunnable();
   } else {
     // Send and possibly block GC...
-    (*transport_->sendBufferedRequest)(this, wrapiov, iov_count + 1);
+    success = (*transport_->sendBufferedRequest)(this, wrapiov, iov_count + 1);
+  }
+  if (!success) {
+    LOG(INFO) << StringPrintf("JDWP send of type %c%c%c%c failed.",
+                              static_cast<uint8_t>(type >> 24),
+                              static_cast<uint8_t>(type >> 16),
+                              static_cast<uint8_t>(type >> 8),
+                              static_cast<uint8_t>(type));
   }
 }
 
diff --git a/src/jdwp/jdwp_main.cc b/src/jdwp/jdwp_main.cc
index 4e738ff..1f121f8 100644
--- a/src/jdwp/jdwp_main.cc
+++ b/src/jdwp/jdwp_main.cc
@@ -334,6 +334,7 @@
 
   /* set the thread state to kWaitingInMainDebuggerLoop so GCs don't wait for us */
   CHECK_EQ(thread_->GetState(), kNative);
+  Locks::mutator_lock_->AssertNotHeld(thread_);
   thread_->SetState(kWaitingInMainDebuggerLoop);
 
   /*
@@ -421,10 +422,9 @@
 
       // Release session state, e.g. remove breakpoint instructions.
       ResetState();
-
-      // Tell the rest of the runtime that the debugger is no longer around.
-      Dbg::Disconnected();
     }
+    // Tell the rest of the runtime that the debugger is no longer around.
+    Dbg::Disconnected();
 
     /* if we had threads suspended, resume them now */
     Dbg::UndoDebuggerSuspensions();
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 6df03e9..a6c9fa1 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -112,7 +112,7 @@
       CHECK(self->IsExceptionPending());
       LOG(ERROR) << "Internal error: unresolvable type for argument type in JNI invoke: "
           << mh.GetTypeDescriptorFromTypeIdx(type_idx) << "\n"
-          << self->GetException()->Dump();
+          << self->GetException(NULL)->Dump();
       self->ClearException();
       ++error_count;
     } else if (!param_type->IsPrimitive()) {
@@ -214,8 +214,10 @@
 static void ThrowNoSuchMethodError(ScopedObjectAccess& soa, Class* c,
                                    const char* name, const char* sig, const char* kind)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  soa.Self()->ThrowNewExceptionF("Ljava/lang/NoSuchMethodError;",
-      "no %s method \"%s.%s%s\"", kind, ClassHelper(c).GetDescriptor(), name, sig);
+  ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
+  soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/NoSuchMethodError;",
+                                 "no %s method \"%s.%s%s\"",
+                                 kind, ClassHelper(c).GetDescriptor(), name, sig);
 }
 
 static jmethodID FindMethodID(ScopedObjectAccess& soa, jclass jni_class,
@@ -248,7 +250,7 @@
 
 static ClassLoader* GetClassLoader(const ScopedObjectAccess& soa)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  AbstractMethod* method = soa.Self()->GetCurrentMethod();
+  AbstractMethod* method = soa.Self()->GetCurrentMethod(NULL);
   if (method == NULL ||
       method == soa.DecodeMethod(WellKnownClasses::java_lang_Runtime_nativeLoad)) {
     return soa.Self()->GetClassLoaderOverride();
@@ -276,10 +278,14 @@
   if (field_type == NULL) {
     // Failed to find type from the signature of the field.
     DCHECK(soa.Self()->IsExceptionPending());
+    ThrowLocation throw_location;
+    SirtRef<mirror::Throwable> cause(soa.Self(), soa.Self()->GetException(&throw_location));
     soa.Self()->ClearException();
-    soa.Self()->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;",
-        "no type \"%s\" found and so no field \"%s\" could be found in class "
-        "\"%s\" or its superclasses", sig, name, ClassHelper(c).GetDescriptor());
+    soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/NoSuchFieldError;",
+                                   "no type \"%s\" found and so no field \"%s\" could be found in class "
+                                   "\"%s\" or its superclasses", sig, name,
+                                   ClassHelper(c).GetDescriptor());
+    soa.Self()->GetException(NULL)->SetCause(cause.get());
     return NULL;
   }
   if (is_static) {
@@ -288,9 +294,10 @@
     field = c->FindInstanceField(name, ClassHelper(field_type).GetDescriptor());
   }
   if (field == NULL) {
-    soa.Self()->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;",
-        "no \"%s\" field \"%s\" in class \"%s\" or its superclasses", sig,
-        name, ClassHelper(c).GetDescriptor());
+    ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
+    soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/NoSuchFieldError;",
+                                   "no \"%s\" field \"%s\" in class \"%s\" or its superclasses",
+                                   sig, name, ClassHelper(c).GetDescriptor());
     return NULL;
   }
   return soa.EncodeField(field);
@@ -314,16 +321,19 @@
                         jsize length, const char* identifier)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   std::string type(PrettyTypeOf(array));
-  soa.Self()->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;",
-      "%s offset=%d length=%d %s.length=%d",
-      type.c_str(), start, length, identifier, array->GetLength());
+  ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
+  soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/ArrayIndexOutOfBoundsException;",
+                                 "%s offset=%d length=%d %s.length=%d",
+                                 type.c_str(), start, length, identifier, array->GetLength());
 }
 
 static void ThrowSIOOBE(ScopedObjectAccess& soa, jsize start, jsize length,
                         jsize array_length)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  soa.Self()->ThrowNewExceptionF("Ljava/lang/StringIndexOutOfBoundsException;",
-      "offset=%d length=%d string.length()=%d", start, length, array_length);
+  ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
+  soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/StringIndexOutOfBoundsException;",
+                                 "offset=%d length=%d string.length()=%d", start, length,
+                                 array_length);
 }
 
 int ThrowNewException(JNIEnv* env, jclass exception_class, const char* msg, jobject cause)
@@ -362,10 +372,9 @@
   if (exception.get() == NULL) {
     return JNI_ERR;
   }
-
   ScopedObjectAccess soa(env);
-  soa.Self()->SetException(soa.Decode<Throwable*>(exception.get()));
-
+  ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
+  soa.Self()->SetException(throw_location, soa.Decode<Throwable*>(exception.get()));
   return JNI_OK;
 }
 
@@ -678,7 +687,8 @@
     if (exception == NULL) {
       return JNI_ERR;
     }
-    soa.Self()->SetException(exception);
+    ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
+    soa.Self()->SetException(throw_location, exception);
     return JNI_OK;
   }
 
@@ -697,31 +707,42 @@
   static void ExceptionDescribe(JNIEnv* env) {
     ScopedObjectAccess soa(env);
 
-    Thread* self = soa.Self();
-    Throwable* original_exception = self->GetException();
-    self->ClearException();
-
-    ScopedLocalRef<jthrowable> exception(env, soa.AddLocalReference<jthrowable>(original_exception));
+    SirtRef<mirror::Object> old_throw_this_object(soa.Self(), NULL);
+    SirtRef<mirror::AbstractMethod> old_throw_method(soa.Self(), NULL);
+    SirtRef<mirror::Throwable> old_exception(soa.Self(), NULL);
+    uint32_t old_throw_dex_pc;
+    {
+      ThrowLocation old_throw_location;
+      mirror::Throwable* old_exception_obj = soa.Self()->GetException(&old_throw_location);
+      old_throw_this_object.reset(old_throw_location.GetThis());
+      old_throw_method.reset(old_throw_location.GetMethod());
+      old_exception.reset(old_exception_obj);
+      old_throw_dex_pc = old_throw_location.GetDexPc();
+      soa.Self()->ClearException();
+    }
+    ScopedLocalRef<jthrowable> exception(env, soa.AddLocalReference<jthrowable>(old_exception.get()));
     ScopedLocalRef<jclass> exception_class(env, env->GetObjectClass(exception.get()));
     jmethodID mid = env->GetMethodID(exception_class.get(), "printStackTrace", "()V");
     if (mid == NULL) {
       LOG(WARNING) << "JNI WARNING: no printStackTrace()V in "
-                   << PrettyTypeOf(original_exception);
+                   << PrettyTypeOf(old_exception.get());
     } else {
       env->CallVoidMethod(exception.get(), mid);
-      if (self->IsExceptionPending()) {
-        LOG(WARNING) << "JNI WARNING: " << PrettyTypeOf(self->GetException())
+      if (soa.Self()->IsExceptionPending()) {
+        LOG(WARNING) << "JNI WARNING: " << PrettyTypeOf(soa.Self()->GetException(NULL))
                      << " thrown while calling printStackTrace";
-        self->ClearException();
+        soa.Self()->ClearException();
       }
     }
+    ThrowLocation gc_safe_throw_location(old_throw_this_object.get(), old_throw_method.get(),
+                                         old_throw_dex_pc);
 
-    self->SetException(original_exception);
+    soa.Self()->SetException(gc_safe_throw_location, old_exception.get());
   }
 
   static jthrowable ExceptionOccurred(JNIEnv* env) {
     ScopedObjectAccess soa(env);
-    Object* exception = soa.Self()->GetException();
+    Object* exception = soa.Self()->GetException(NULL);
     return soa.AddLocalReference<jthrowable>(exception);
   }
 
@@ -2134,10 +2155,10 @@
 
   static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) {
     if (capacity < 0) {
-      JniAbortF("NewDirectByteBuffer", "negative buffer capacity: %d", capacity);
+      JniAbortF("NewDirectByteBuffer", "negative buffer capacity: %lld", capacity);
     }
     if (address == NULL && capacity != 0) {
-      JniAbortF("NewDirectByteBuffer", "non-zero capacity for NULL pointer: %d", capacity);
+      JniAbortF("NewDirectByteBuffer", "non-zero capacity for NULL pointer: %lld", capacity);
     }
 
     // At the moment, the Java side is limited to 32 bits.
@@ -2676,7 +2697,7 @@
       force_copy(false), // TODO: add a way to enable this
       trace(options->jni_trace_),
       work_around_app_jni_bugs(false),
-      pins_lock("JNI pin table lock"),
+      pins_lock("JNI pin table lock", kPinTableLock),
       pin_table("pin table", kPinTableInitial, kPinTableMax),
       globals_lock("JNI global reference table lock"),
       globals(gGlobalsInitial, gGlobalsMax, kGlobal),
@@ -2889,9 +2910,10 @@
     MutexLock mu(self, libraries_lock);
     native_method = libraries->FindNativeMethod(m, detail);
   }
-  // throwing can cause libraries_lock to be reacquired
+  // Throwing can cause libraries_lock to be reacquired.
   if (native_method == NULL) {
-    self->ThrowNewException("Ljava/lang/UnsatisfiedLinkError;", detail.c_str());
+    ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+    self->ThrowNewException(throw_location, "Ljava/lang/UnsatisfiedLinkError;", detail.c_str());
   }
   return native_method;
 }
diff --git a/src/jni_internal.h b/src/jni_internal.h
index 9c067de..131032a 100644
--- a/src/jni_internal.h
+++ b/src/jni_internal.h
@@ -49,7 +49,8 @@
 class Thread;
 
 void SetJniGlobalsMax(size_t max);
-void JniAbortF(const char* jni_function_name, const char* fmt, ...);
+void JniAbortF(const char* jni_function_name, const char* fmt, ...)
+    __attribute__((__format__(__printf__, 2, 3)));
 void* FindNativeMethod(Thread* thread);
 void RegisterNativeMethods(JNIEnv* env, const char* jni_class_name, const JNINativeMethod* methods,
                            size_t method_count);
diff --git a/src/jvalue.h b/src/jvalue.h
index fa85937..66cd93e 100644
--- a/src/jvalue.h
+++ b/src/jvalue.h
@@ -19,6 +19,8 @@
 
 #include "base/macros.h"
 
+#include <stdint.h>
+
 namespace art {
 namespace mirror {
 class Object;
diff --git a/src/locks.cc b/src/locks.cc
index 27b9d4b..eb0620c 100644
--- a/src/locks.cc
+++ b/src/locks.cc
@@ -29,6 +29,7 @@
 Mutex* Locks::runtime_shutdown_lock_ = NULL;
 Mutex* Locks::thread_list_lock_ = NULL;
 Mutex* Locks::thread_suspend_count_lock_ = NULL;
+Mutex* Locks::trace_lock_ = NULL;
 Mutex* Locks::unexpected_signal_lock_ = NULL;
 
 void Locks::Init() {
@@ -42,6 +43,7 @@
     DCHECK(mutator_lock_ != NULL);
     DCHECK(thread_list_lock_ != NULL);
     DCHECK(thread_suspend_count_lock_ != NULL);
+    DCHECK(trace_lock_ != NULL);
     DCHECK(unexpected_signal_lock_ != NULL);
   } else {
     logging_lock_ = new Mutex("logging lock", kLoggingLock, true);
@@ -61,6 +63,8 @@
     thread_list_lock_ = new Mutex("thread list lock", kThreadListLock);
     DCHECK(thread_suspend_count_lock_ == NULL);
     thread_suspend_count_lock_ = new Mutex("thread suspend count lock", kThreadSuspendCountLock);
+    DCHECK(trace_lock_ == NULL);
+    trace_lock_ = new Mutex("trace lock", kTraceLock);
     DCHECK(unexpected_signal_lock_ == NULL);
     unexpected_signal_lock_ = new Mutex("unexpected signal lock", kUnexpectedSignalLock, true);
   }
diff --git a/src/locks.h b/src/locks.h
index c0f6ae5..568f950 100644
--- a/src/locks.h
+++ b/src/locks.h
@@ -37,17 +37,20 @@
   kThreadSuspendCountLock,
   kAbortLock,
   kDefaultMutexLevel,
+  kJdwpSerialLock,
   kAllocSpaceLock,
+  kMarkSweepLargeObjectLock,
+  kPinTableLock,
   kLoadLibraryLock,
   kClassLinkerClassesLock,
   kBreakpointLock,
+  kJdwpObjectRegistryLock,
   kThreadListLock,
   kBreakpointInvokeLock,
-  kJdwpObjectRegistryLock,
+  kTraceLock,
   kJdwpEventListLock,
   kJdwpAttachLock,
   kJdwpStartLock,
-  kJdwpSerialLock,
   kRuntimeShutdownLock,
   kHeapBitmapLock,
   kMonitorLock,
@@ -136,8 +139,11 @@
   // Guards breakpoints and single-stepping.
   static Mutex* breakpoint_lock_ ACQUIRED_AFTER(thread_list_lock_);
 
+  // Guards trace requests.
+  static Mutex* trace_lock_ ACQUIRED_AFTER(breakpoint_lock_);
+
   // Guards lists of classes within the class linker.
-  static Mutex* classlinker_classes_lock_ ACQUIRED_AFTER(breakpoint_lock_);
+  static Mutex* classlinker_classes_lock_ ACQUIRED_AFTER(trace_lock_);
 
   // When declaring any Mutex add DEFAULT_MUTEX_ACQUIRED_AFTER to use annotalysis to check the code
   // doesn't try to hold a higher level Mutex.
diff --git a/src/mirror/abstract_method-inl.h b/src/mirror/abstract_method-inl.h
index 2049748..fd02474 100644
--- a/src/mirror/abstract_method-inl.h
+++ b/src/mirror/abstract_method-inl.h
@@ -20,6 +20,7 @@
 #include "abstract_method.h"
 
 #include "dex_file.h"
+#include "oat/runtime/oat_support_entrypoints.h"
 #include "object_array.h"
 #include "runtime.h"
 
@@ -113,6 +114,9 @@
   if (IsNative() || IsRuntimeMethod() || IsProxyMethod()) {
     return;
   }
+  if (GetCode() == GetInterpreterEntryPoint()) {
+    return;
+  }
   Runtime* runtime = Runtime::Current();
   if (GetCode() == runtime->GetResolutionStubArray(Runtime::kStaticMethod)->GetData()) {
       return;
diff --git a/src/mirror/abstract_method.cc b/src/mirror/abstract_method.cc
index f74814c..3ab3a93 100644
--- a/src/mirror/abstract_method.cc
+++ b/src/mirror/abstract_method.cc
@@ -151,23 +151,9 @@
   return result;
 }
 
-static const void* GetOatCode(const AbstractMethod* m)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  Runtime* runtime = Runtime::Current();
-  const void* code = m->GetCode();
-  // Peel off any method tracing trampoline.
-  if (runtime->IsMethodTracingActive() && runtime->GetInstrumentation()->GetSavedCodeFromMap(m) != NULL) {
-    code = runtime->GetInstrumentation()->GetSavedCodeFromMap(m);
-  }
-  // Peel off any resolution stub.
-  if (code == runtime->GetResolutionStubArray(Runtime::kStaticMethod)->GetData()) {
-    code = runtime->GetClassLinker()->GetOatCodeFor(m);
-  }
-  return code;
-}
-
 uintptr_t AbstractMethod::NativePcOffset(const uintptr_t pc) const {
-  return pc - reinterpret_cast<uintptr_t>(GetOatCode(this));
+  const void* code = Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(this);
+  return pc - reinterpret_cast<uintptr_t>(code);
 }
 
 // Find the lowest-address native safepoint pc for a given dex pc
@@ -181,7 +167,8 @@
   size_t mapping_table_length = GetPcToDexMappingTableLength();
   for (size_t i = 0; i < mapping_table_length; i += 2) {
     if (mapping_table[i + 1] == dex_pc) {
-      return mapping_table[i] + reinterpret_cast<uintptr_t>(GetOatCode(this));
+      const void* code = Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(this);
+      return mapping_table[i] + reinterpret_cast<uintptr_t>(code);
     }
   }
   LOG(FATAL) << "Failed to find native offset for dex pc 0x" << std::hex << dex_pc
@@ -201,14 +188,16 @@
     return DexFile::kDexNoIndex;   // Special no mapping case
   }
   size_t mapping_table_length = GetPcToDexMappingTableLength();
-  uint32_t sought_offset = pc - reinterpret_cast<uintptr_t>(GetOatCode(this));
+  const void* code = Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(this);
+  uint32_t sought_offset = pc - reinterpret_cast<uintptr_t>(code);
   for (size_t i = 0; i < mapping_table_length; i += 2) {
     if (mapping_table[i] == sought_offset) {
       return mapping_table[i + 1];
     }
   }
-  LOG(ERROR) << "Failed to find Dex offset for PC offset " << reinterpret_cast<void*>(sought_offset)
-             << "(PC " << reinterpret_cast<void*>(pc) << ") in " << PrettyMethod(this);
+  LOG(FATAL) << "Failed to find Dex offset for PC offset " << reinterpret_cast<void*>(sought_offset)
+             << "(PC " << reinterpret_cast<void*>(pc) << ", code=" << code
+             << ") in " << PrettyMethod(this);
   return DexFile::kDexNoIndex;
 #else
   // Compiler LLVM doesn't use the machine pc, we just use dex pc instead.
@@ -227,7 +216,8 @@
     uint32_t map_offset = mapping_table[i];
     uint32_t map_dex_offset = mapping_table[i + 1];
     if (map_dex_offset == dex_pc) {
-      return reinterpret_cast<uintptr_t>(GetOatCode(this)) + map_offset;
+      const void* code = Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(this);
+      return reinterpret_cast<uintptr_t>(code) + map_offset;
     }
   }
   LOG(FATAL) << "Looking up Dex PC not contained in method, 0x" << std::hex << dex_pc
@@ -270,14 +260,16 @@
   ManagedStack fragment;
   self->PushManagedStackFragment(&fragment);
 
+  Runtime* runtime = Runtime::Current();
   // Call the invoke stub, passing everything as arguments.
-  if (UNLIKELY(!Runtime::Current()->IsStarted())){
+  if (UNLIKELY(!runtime->IsStarted())){
     LOG(INFO) << "Not invoking " << PrettyMethod(this) << " for a runtime that isn't started";
     if (result != NULL) {
       result->SetJ(0);
     }
   } else {
-    bool interpret = self->ReadFlag(kEnterInterpreter) && !IsNative() && !IsProxyMethod();
+    bool interpret = runtime->GetInstrumentation()->InterpretOnly() && !IsNative() &&
+        !IsProxyMethod();
     const bool kLogInvocationStartAndReturn = false;
     if (GetCode() != NULL) {
       if (!interpret) {
@@ -289,15 +281,15 @@
 #else
         (*art_quick_invoke_stub)(this, args, args_size, self, result, result_type);
 #endif
-        if (UNLIKELY(reinterpret_cast<int32_t>(self->GetException()) == -1)) {
+        if (UNLIKELY(reinterpret_cast<int32_t>(self->GetException(NULL)) == -1)) {
           // Unusual case where we were running LLVM generated code and an
           // exception was thrown to force the activations to be removed from the
           // stack. Continue execution in the interpreter.
-          JValue value;
           self->ClearException();
-          ShadowFrame* shadow_frame = self->GetAndClearDeoptimizationShadowFrame(&value);
+          ShadowFrame* shadow_frame = self->GetAndClearDeoptimizationShadowFrame(result);
+          self->SetTopOfStack(NULL, 0);
           self->SetTopOfShadowStack(shadow_frame);
-          interpreter::EnterInterpreterFromLLVM(self, shadow_frame, result);
+          interpreter::EnterInterpreterFromDeoptimize(self, shadow_frame, result);
         }
         if (kLogInvocationStartAndReturn) {
           LOG(INFO) << StringPrintf("Returned '%s' code=%p", PrettyMethod(this).c_str(), GetCode());
diff --git a/src/mirror/abstract_method.h b/src/mirror/abstract_method.h
index d10031a..9440915 100644
--- a/src/mirror/abstract_method.h
+++ b/src/mirror/abstract_method.h
@@ -321,6 +321,11 @@
     return GetFrameSizeInBytes() - kPointerSize;
   }
 
+  size_t GetSirtOffsetInBytes() const {
+    CHECK(IsNative());
+    return kPointerSize;
+  }
+
   bool IsRegistered() const;
 
   void RegisterNative(Thread* self, const void* native_method)
diff --git a/src/mirror/array.cc b/src/mirror/array.cc
index d0b3838..84c2dc6 100644
--- a/src/mirror/array.cc
+++ b/src/mirror/array.cc
@@ -18,6 +18,7 @@
 
 #include "class.h"
 #include "class-inl.h"
+#include "common_throws.h"
 #include "dex_file-inl.h"
 #include "gc/card_table-inl.h"
 #include "object-inl.h"
@@ -43,10 +44,10 @@
 
   // Check for overflow and throw OutOfMemoryError if this was an unreasonable request.
   size_t component_shift = sizeof(size_t) * 8 - 1 - CLZ(component_size);
-  if (data_size >> component_shift != size_t(component_count) || size < data_size) {
-    self->ThrowNewExceptionF("Ljava/lang/OutOfMemoryError;",
-        "%s of length %d would overflow",
-        PrettyDescriptor(array_class).c_str(), component_count);
+  if (UNLIKELY(data_size >> component_shift != size_t(component_count) || size < data_size)) {
+    self->ThrowOutOfMemoryError(StringPrintf("%s of length %d would overflow",
+                                             PrettyDescriptor(array_class).c_str(),
+                                             component_count).c_str());
     return NULL;
   }
 
@@ -108,8 +109,7 @@
   for (int i = 0; i < num_dimensions; i++) {
     int dimension = dimensions->Get(i);
     if (UNLIKELY(dimension < 0)) {
-      self->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;",
-                               "Dimension %d: %d", i, dimension);
+      ThrowNegativeArraySizeException(StringPrintf("Dimension %d: %d", i, dimension).c_str());
       return NULL;
     }
   }
@@ -135,15 +135,12 @@
 }
 
 bool Array::ThrowArrayIndexOutOfBoundsException(int32_t index) const {
-  Thread::Current()->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;",
-      "length=%i; index=%i", length_, index);
+  art::ThrowArrayIndexOutOfBoundsException(index, GetLength());
   return false;
 }
 
 bool Array::ThrowArrayStoreException(Object* object) const {
-  Thread::Current()->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
-      "%s cannot be stored in an array of type %s",
-      PrettyTypeOf(object).c_str(), PrettyTypeOf(this).c_str());
+  art::ThrowArrayStoreException(object->GetClass(), this->GetClass());
   return false;
 }
 
diff --git a/src/mirror/class-inl.h b/src/mirror/class-inl.h
index ec92c19..d7afed6 100644
--- a/src/mirror/class-inl.h
+++ b/src/mirror/class-inl.h
@@ -24,7 +24,7 @@
 #include "dex_cache.h"
 #include "field.h"
 #include "iftable.h"
-#include "object_array.h"
+#include "object_array-inl.h"
 #include "runtime.h"
 #include "string.h"
 
diff --git a/src/mirror/class.cc b/src/mirror/class.cc
index ba3556e..15129ab 100644
--- a/src/mirror/class.cc
+++ b/src/mirror/class.cc
@@ -60,10 +60,22 @@
   if (new_status == kStatusError) {
     CHECK_NE(GetStatus(), kStatusError) << PrettyClass(this);
 
-    // stash current exception
+    // Stash current exception.
     Thread* self = Thread::Current();
-    SirtRef<Throwable> exception(self, self->GetException());
-    CHECK(exception.get() != NULL);
+    SirtRef<mirror::Object> old_throw_this_object(self, NULL);
+    SirtRef<mirror::AbstractMethod> old_throw_method(self, NULL);
+    SirtRef<mirror::Throwable> old_exception(self, NULL);
+    uint32_t old_throw_dex_pc;
+    {
+      ThrowLocation old_throw_location;
+      mirror::Throwable* old_exception_obj = self->GetException(&old_throw_location);
+      old_throw_this_object.reset(old_throw_location.GetThis());
+      old_throw_method.reset(old_throw_location.GetMethod());
+      old_exception.reset(old_exception_obj);
+      old_throw_dex_pc = old_throw_location.GetDexPc();
+      self->ClearException();
+    }
+    CHECK(old_exception.get() != NULL);
 
     // clear exception to call FindSystemClass
     self->ClearException();
@@ -71,15 +83,18 @@
     Class* eiie_class = class_linker->FindSystemClass("Ljava/lang/ExceptionInInitializerError;");
     CHECK(!self->IsExceptionPending());
 
-    // only verification errors, not initialization problems, should set a verify error.
-    // this is to ensure that ThrowEarlierClassFailure will throw NoClassDefFoundError in that case.
-    Class* exception_class = exception->GetClass();
+    // Only verification errors, not initialization problems, should set a verify error.
+    // This is to ensure that ThrowEarlierClassFailure will throw NoClassDefFoundError in that case.
+    Class* exception_class = old_exception->GetClass();
     if (!eiie_class->IsAssignableFrom(exception_class)) {
       SetVerifyErrorClass(exception_class);
     }
 
-    // restore exception
-    self->SetException(exception.get());
+    // Restore exception.
+    ThrowLocation gc_safe_throw_location(old_throw_this_object.get(), old_throw_method.get(),
+                                         old_throw_dex_pc);
+
+    self->SetException(gc_safe_throw_location, old_exception.get());
   }
   return SetField32(OFFSET_OF_OBJECT_MEMBER(Class, status_), new_status, false);
 }
diff --git a/src/mirror/object_test.cc b/src/mirror/object_test.cc
index eed96bd..5c7ec11 100644
--- a/src/mirror/object_test.cc
+++ b/src/mirror/object_test.cc
@@ -116,12 +116,12 @@
 
   EXPECT_TRUE(oa->Get(-1) == NULL);
   EXPECT_TRUE(soa.Self()->IsExceptionPending());
-  EXPECT_EQ(aioobe, soa.Self()->GetException()->GetClass());
+  EXPECT_EQ(aioobe, soa.Self()->GetException(NULL)->GetClass());
   soa.Self()->ClearException();
 
   EXPECT_TRUE(oa->Get(2) == NULL);
   EXPECT_TRUE(soa.Self()->IsExceptionPending());
-  EXPECT_EQ(aioobe, soa.Self()->GetException()->GetClass());
+  EXPECT_EQ(aioobe, soa.Self()->GetException(NULL)->GetClass());
   soa.Self()->ClearException();
 
   ASSERT_TRUE(oa->GetClass() != NULL);
@@ -166,12 +166,12 @@
 
   EXPECT_EQ(0, a->Get(-1));
   EXPECT_TRUE(soa.Self()->IsExceptionPending());
-  EXPECT_EQ(aioobe, soa.Self()->GetException()->GetClass());
+  EXPECT_EQ(aioobe, soa.Self()->GetException(NULL)->GetClass());
   soa.Self()->ClearException();
 
   EXPECT_EQ(0, a->Get(2));
   EXPECT_TRUE(soa.Self()->IsExceptionPending());
-  EXPECT_EQ(aioobe, soa.Self()->GetException()->GetClass());
+  EXPECT_EQ(aioobe, soa.Self()->GetException(NULL)->GetClass());
   soa.Self()->ClearException();
 }
 
@@ -231,7 +231,7 @@
   dims->Set(0, -1);
   multi = Array::CreateMultiArray(soa.Self(), c.get(), dims.get());
   EXPECT_TRUE(soa.Self()->IsExceptionPending());
-  EXPECT_EQ(PrettyDescriptor(soa.Self()->GetException()->GetClass()),
+  EXPECT_EQ(PrettyDescriptor(soa.Self()->GetException(NULL)->GetClass()),
             "java.lang.NegativeArraySizeException");
   soa.Self()->ClearException();
 
diff --git a/src/mirror/string.cc b/src/mirror/string.cc
index f571fb8..45a6779 100644
--- a/src/mirror/string.cc
+++ b/src/mirror/string.cc
@@ -103,8 +103,9 @@
   // bounds check itself.
   if (index < 0 || index >= count_) {
     Thread* self = Thread::Current();
-    self->ThrowNewExceptionF("Ljava/lang/StringIndexOutOfBoundsException;",
-        "length=%i; index=%i", count_, index);
+    ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+    self->ThrowNewExceptionF(throw_location, "Ljava/lang/StringIndexOutOfBoundsException;",
+                             "length=%i; index=%i", count_, index);
     return 0;
   }
   return GetCharArray()->Get(index + GetOffset());
diff --git a/src/mirror/throwable.cc b/src/mirror/throwable.cc
index d1192b0..bbff9c2 100644
--- a/src/mirror/throwable.cc
+++ b/src/mirror/throwable.cc
@@ -35,7 +35,9 @@
 void Throwable::SetCause(Throwable* cause) {
   CHECK(cause != NULL);
   CHECK(cause != this);
-  CHECK(GetFieldObject<Throwable*>(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_), false) == NULL);
+  Throwable* current_cause = GetFieldObject<Throwable*>(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_),
+                                                        false);
+  CHECK(current_cause == NULL || current_cause == this);
   SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_), cause, false);
 }
 
diff --git a/src/monitor.cc b/src/monitor.cc
index 2377734..11790e5 100644
--- a/src/monitor.cc
+++ b/src/monitor.cc
@@ -254,10 +254,12 @@
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   va_list args;
   va_start(args, fmt);
-  Thread::Current()->ThrowNewExceptionV("Ljava/lang/IllegalMonitorStateException;", fmt, args);
+  Thread* self = Thread::Current();
+  ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+  self->ThrowNewExceptionV(throw_location, "Ljava/lang/IllegalMonitorStateException;", fmt, args);
   if (!Runtime::Current()->IsStarted()) {
     std::ostringstream ss;
-    Thread::Current()->Dump(ss);
+    self->Dump(ss);
     std::string str(ss.str());
     LOG(ERROR) << "IllegalMonitorStateException: " << str;
   }
@@ -411,8 +413,9 @@
                            bool interruptShouldThrow, ThreadState why) {
   // Enforce the timeout range.
   if (ms < 0 || ns < 0 || ns > 999999) {
-    Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;",
-        "timeout arguments out of range: ms=%lld ns=%d", ms, ns);
+    ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+    self->ThrowNewExceptionF(throw_location, "Ljava/lang/IllegalArgumentException;",
+                             "timeout arguments out of range: ms=%lld ns=%d", ms, ns);
     return;
   }
 
@@ -517,7 +520,8 @@
       self->interrupted_ = false;
     }
     if (interruptShouldThrow) {
-      Thread::Current()->ThrowNewException("Ljava/lang/InterruptedException;", NULL);
+      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+      self->ThrowNewException(throw_location, "Ljava/lang/InterruptedException;", NULL);
     }
   }
 }
diff --git a/src/monitor_android.cc b/src/monitor_android.cc
index d3ac143..9265cd6 100644
--- a/src/monitor_android.cc
+++ b/src/monitor_android.cc
@@ -77,7 +77,7 @@
   cp = EventLogWriteInt(cp, wait_ms);
 
   // Emit the source code file name, <= 37 bytes.
-  uintptr_t pc;
+  uint32_t pc;
   mirror::AbstractMethod* m = self->GetCurrentMethod(&pc);
   const char* filename;
   uint32_t line_number;
diff --git a/src/native/dalvik_system_DexFile.cc b/src/native/dalvik_system_DexFile.cc
index 7c6fbd9..d703f83 100644
--- a/src/native/dalvik_system_DexFile.cc
+++ b/src/native/dalvik_system_DexFile.cc
@@ -18,6 +18,7 @@
 
 #include "base/logging.h"
 #include "class_linker.h"
+#include "common_throws.h"
 #include "dex_file-inl.h"
 #include "gc/space.h"
 #include "image.h"
@@ -103,18 +104,18 @@
   }
   if (dex_file == NULL) {
     LOG(WARNING) << "Failed to open dex file: " << source;
-    Thread::Current()->ThrowNewExceptionF("Ljava/io/IOException;", "Unable to open dex file: %s",
-                                          source.c_str());
+    ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
+    soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/io/IOException;",
+                                   "Unable to open dex file: %s", source.c_str());
     return 0;
   }
   return static_cast<jint>(reinterpret_cast<uintptr_t>(dex_file));
 }
 
-static const DexFile* toDexFile(int dex_file_address)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+static const DexFile* toDexFile(int dex_file_address) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   const DexFile* dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(dex_file_address));
   if (dex_file == NULL) {
-    Thread::Current()->ThrowNewExceptionF("Ljava/lang/NullPointerException;", "dex_file == null");
+    ThrowNullPointerException(NULL, "dex_file == null");
   }
   return dex_file;
 }
@@ -188,7 +189,9 @@
   if (!OS::FileExists(filename.c_str())) {
     LOG(ERROR) << "DexFile_isDexOptNeeded file '" << filename.c_str() << "' does not exist";
     ScopedObjectAccess soa(env);
-    Thread::Current()->ThrowNewExceptionF("Ljava/io/FileNotFoundException;", "%s", filename.c_str());
+    ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
+    soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/io/FileNotFoundException;",
+                                   "%s", filename.c_str());
     return JNI_TRUE;
   }
 
diff --git a/src/native/dalvik_system_VMDebug.cc b/src/native/dalvik_system_VMDebug.cc
index dc07a31..992998e 100644
--- a/src/native/dalvik_system_VMDebug.cc
+++ b/src/native/dalvik_system_VMDebug.cc
@@ -18,6 +18,7 @@
 #include <unistd.h>
 
 #include "class_linker.h"
+#include "common_throws.h"
 #include "debugger.h"
 #include "hprof/hprof.h"
 #include "jni_internal.h"
@@ -68,8 +69,9 @@
   int fd = dup(originalFd);
   if (fd < 0) {
     ScopedObjectAccess soa(env);
-    Thread::Current()->ThrowNewExceptionF("Ljava/lang/RuntimeException;",
-                                          "dup(%d) failed: %s", originalFd, strerror(errno));
+    ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
+    soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/RuntimeException;",
+                                   "dup(%d) failed: %s", originalFd, strerror(errno));
     return;
   }
 
@@ -90,7 +92,7 @@
 }
 
 static jboolean VMDebug_isMethodTracingActive(JNIEnv*, jclass) {
-  return Runtime::Current()->IsMethodTracingActive();
+  return Trace::IsMethodTracingActive();
 }
 
 static void VMDebug_stopMethodTracing(JNIEnv*, jclass) {
@@ -119,24 +121,26 @@
   return Dbg::LastDebuggerActivity();
 }
 
-static void VMDebug_startInstructionCounting(JNIEnv* env, jclass) {
+static void ThrowUnsupportedOperationException(JNIEnv* env) {
   ScopedObjectAccess soa(env);
-  Thread::Current()->ThrowNewException("Ljava/lang/UnsupportedOperationException;", "");
+  ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
+  soa.Self()->ThrowNewException(throw_location, "Ljava/lang/UnsupportedOperationException;", NULL);
+}
+
+static void VMDebug_startInstructionCounting(JNIEnv* env, jclass) {
+  ThrowUnsupportedOperationException(env);
 }
 
 static void VMDebug_stopInstructionCounting(JNIEnv* env, jclass) {
-  ScopedObjectAccess soa(env);
-  Thread::Current()->ThrowNewException("Ljava/lang/UnsupportedOperationException;", "");
+  ThrowUnsupportedOperationException(env);
 }
 
 static void VMDebug_getInstructionCount(JNIEnv* env, jclass, jintArray /*javaCounts*/) {
-  ScopedObjectAccess soa(env);
-  Thread::Current()->ThrowNewException("Ljava/lang/UnsupportedOperationException;", "");
+  ThrowUnsupportedOperationException(env);
 }
 
 static void VMDebug_resetInstructionCount(JNIEnv* env, jclass) {
-  ScopedObjectAccess soa(env);
-  Thread::Current()->ThrowNewException("Ljava/lang/UnsupportedOperationException;", "");
+  ThrowUnsupportedOperationException(env);
 }
 
 static void VMDebug_printLoadedClasses(JNIEnv* env, jclass, jint flags) {
@@ -166,8 +170,7 @@
   // Only one of these may be NULL.
   if (javaFilename == NULL && javaFd == NULL) {
     ScopedObjectAccess soa(env);
-    Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;",
-                                         "fileName == null && fd == null");
+    ThrowNullPointerException(NULL, "fileName == null && fd == null");
     return;
   }
 
@@ -187,8 +190,7 @@
     fd = jniGetFDFromFileDescriptor(env, javaFd);
     if (fd < 0) {
       ScopedObjectAccess soa(env);
-      Thread::Current()->ThrowNewException("Ljava/lang/RuntimeException;",
-                                           "Invalid file descriptor");
+      ThrowRuntimeException("Invalid file descriptor");
       return;
     }
   }
diff --git a/src/native/dalvik_system_VMRuntime.cc b/src/native/dalvik_system_VMRuntime.cc
index a13d07a..d2ef43c 100644
--- a/src/native/dalvik_system_VMRuntime.cc
+++ b/src/native/dalvik_system_VMRuntime.cc
@@ -17,6 +17,7 @@
 #include <limits.h>
 
 #include "class_linker.h"
+#include "common_throws.h"
 #include "debugger.h"
 #include "dex_file-inl.h"
 #include "jni_internal.h"
@@ -57,11 +58,11 @@
 
   mirror::Class* element_class = soa.Decode<mirror::Class*>(javaElementClass);
   if (element_class == NULL) {
-    soa.Self()->ThrowNewException("Ljava/lang/NullPointerException;", "element class == null");
+    ThrowNullPointerException(NULL, "element class == null");
     return NULL;
   }
   if (length < 0) {
-    soa.Self()->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d", length);
+    ThrowNegativeArraySizeException(length);
     return NULL;
   }
 
@@ -84,7 +85,7 @@
   ScopedObjectAccess soa(env);
   mirror::Array* array = soa.Decode<mirror::Array*>(javaArray);
   if (!array->IsArrayInstance()) {
-    soa.Self()->ThrowNewException("Ljava/lang/IllegalArgumentException;", "not an array");
+    ThrowIllegalArgumentException(NULL, "not an array");
     return 0;
   }
   // TODO: we should also check that this is a non-movable array.
diff --git a/src/native/java_lang_Class.cc b/src/native/java_lang_Class.cc
index 72f4c18..a729699 100644
--- a/src/native/java_lang_Class.cc
+++ b/src/native/java_lang_Class.cc
@@ -53,8 +53,9 @@
   // is especially handy for array types, since we want to avoid
   // auto-generating bogus array classes.
   if (!IsValidBinaryClassName(name.c_str())) {
-    Thread::Current()->ThrowNewExceptionF("Ljava/lang/ClassNotFoundException;",
-        "Invalid name: %s", name.c_str());
+    ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
+    soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/ClassNotFoundException;",
+                                   "Invalid name: %s", name.c_str());
     return NULL;
   }
 
diff --git a/src/native/java_lang_String.cc b/src/native/java_lang_String.cc
index 44ab1ca..3e9c3f3 100644
--- a/src/native/java_lang_String.cc
+++ b/src/native/java_lang_String.cc
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "common_throws.h"
 #include "jni_internal.h"
 #include "mirror/string.h"
 #include "scoped_thread_state_change.h"
@@ -22,12 +23,11 @@
 namespace art {
 
 static jint String_compareTo(JNIEnv* env, jobject javaThis, jobject javaRhs) {
+  ScopedObjectAccess soa(env);
   if (UNLIKELY(javaRhs == NULL)) {
-    ScopedLocalRef<jclass> npe(env, env->FindClass("java/lang/NullPointerException"));
-    env->ThrowNew(npe.get(), "rhs == null");
+    ThrowNullPointerException(NULL, "rhs == null");
     return -1;
   } else {
-    ScopedObjectAccess soa(env);
     return soa.Decode<mirror::String*>(javaThis)->CompareTo(soa.Decode<mirror::String*>(javaRhs));
   }
 }
diff --git a/src/native/java_lang_System.cc b/src/native/java_lang_System.cc
index 5572623..d8df9d9 100644
--- a/src/native/java_lang_System.cc
+++ b/src/native/java_lang_System.cc
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "common_throws.h"
 #include "gc/card_table-inl.h"
 #include "jni_internal.h"
 #include "mirror/array.h"
@@ -171,31 +172,33 @@
 static void ThrowArrayStoreException_NotAnArray(const char* identifier, mirror::Object* array)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   std::string actualType(PrettyTypeOf(array));
-  Thread::Current()->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
-      "%s of type %s is not an array", identifier, actualType.c_str());
+  Thread* self = Thread::Current();
+  ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+  self->ThrowNewExceptionF(throw_location, "Ljava/lang/ArrayStoreException;",
+                           "%s of type %s is not an array", identifier, actualType.c_str());
 }
 
 static void System_arraycopy(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, jobject javaDst, jint dstPos, jint length) {
   ScopedObjectAccess soa(env);
 
   // Null pointer checks.
-  if (javaSrc == NULL) {
-    soa.Self()->ThrowNewException("Ljava/lang/NullPointerException;", "src == null");
+  if (UNLIKELY(javaSrc == NULL)) {
+    ThrowNullPointerException(NULL, "src == null");
     return;
   }
-  if (javaDst == NULL) {
-    soa.Self()->ThrowNewException("Ljava/lang/NullPointerException;", "dst == null");
+  if (UNLIKELY(javaDst == NULL)) {
+    ThrowNullPointerException(NULL, "dst == null");
     return;
   }
 
   // Make sure source and destination are both arrays.
   mirror::Object* srcObject = soa.Decode<mirror::Object*>(javaSrc);
   mirror::Object* dstObject = soa.Decode<mirror::Object*>(javaDst);
-  if (!srcObject->IsArrayInstance()) {
+  if (UNLIKELY(!srcObject->IsArrayInstance())) {
     ThrowArrayStoreException_NotAnArray("source", srcObject);
     return;
   }
-  if (!dstObject->IsArrayInstance()) {
+  if (UNLIKELY(!dstObject->IsArrayInstance())) {
     ThrowArrayStoreException_NotAnArray("destination", dstObject);
     return;
   }
@@ -205,21 +208,24 @@
   mirror::Class* dstComponentType = dstArray->GetClass()->GetComponentType();
 
   // Bounds checking.
-  if (srcPos < 0 || dstPos < 0 || length < 0 || srcPos > srcArray->GetLength() - length || dstPos > dstArray->GetLength() - length) {
-    soa.Self()->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;",
-        "src.length=%d srcPos=%d dst.length=%d dstPos=%d length=%d",
-        srcArray->GetLength(), srcPos, dstArray->GetLength(), dstPos, length);
+  if (UNLIKELY(srcPos < 0 || dstPos < 0 || length < 0 || srcPos > srcArray->GetLength() - length || dstPos > dstArray->GetLength() - length)) {
+    ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
+    soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/ArrayIndexOutOfBoundsException;",
+                                   "src.length=%d srcPos=%d dst.length=%d dstPos=%d length=%d",
+                                   srcArray->GetLength(), srcPos, dstArray->GetLength(), dstPos, length);
     return;
   }
 
   // Handle primitive arrays.
   if (srcComponentType->IsPrimitive() || dstComponentType->IsPrimitive()) {
     // If one of the arrays holds a primitive type the other array must hold the exact same type.
-    if (srcComponentType->IsPrimitive() != dstComponentType->IsPrimitive() || srcComponentType != dstComponentType) {
+    if (UNLIKELY(srcComponentType != dstComponentType)) {
       std::string srcType(PrettyTypeOf(srcArray));
       std::string dstType(PrettyTypeOf(dstArray));
-      soa.Self()->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
-          "Incompatible types: src=%s, dst=%s", srcType.c_str(), dstType.c_str());
+      ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
+      soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/ArrayStoreException;",
+                                     "Incompatible types: src=%s, dst=%s",
+                                     srcType.c_str(), dstType.c_str());
       return;
     }
 
@@ -299,12 +305,13 @@
   }
 
   Runtime::Current()->GetHeap()->WriteBarrierArray(dstArray, dstPos, length);
-  if (i != length) {
+  if (UNLIKELY(i != length)) {
     std::string actualSrcType(PrettyTypeOf(o));
     std::string dstType(PrettyTypeOf(dstArray));
-    soa.Self()->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
-        "source[%d] of type %s cannot be stored in destination array of type %s",
-        srcPos + i, actualSrcType.c_str(), dstType.c_str());
+    ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
+    soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/ArrayStoreException;",
+                                   "source[%d] of type %s cannot be stored in destination array of type %s",
+                                   srcPos + i, actualSrcType.c_str(), dstType.c_str());
     return;
   }
 }
diff --git a/src/native/java_lang_Thread.cc b/src/native/java_lang_Thread.cc
index ca4be9d..7ccfaaa 100644
--- a/src/native/java_lang_Thread.cc
+++ b/src/native/java_lang_Thread.cc
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "common_throws.h"
 #include "debugger.h"
 #include "jni_internal.h"
 #include "monitor.h"
@@ -90,7 +91,7 @@
   ScopedObjectAccess soa(env);
   mirror::Object* object = soa.Decode<mirror::Object*>(java_object);
   if (object == NULL) {
-    Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", "object == null");
+    ThrowNullPointerException(NULL, "object == null");
     return JNI_FALSE;
   }
   MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
diff --git a/src/native/java_lang_reflect_Array.cc b/src/native/java_lang_reflect_Array.cc
index af7a77a..45ec0ad 100644
--- a/src/native/java_lang_reflect_Array.cc
+++ b/src/native/java_lang_reflect_Array.cc
@@ -15,6 +15,7 @@
  */
 
 #include "class_linker.h"
+#include "common_throws.h"
 #include "dex_file-inl.h"
 #include "jni_internal.h"
 #include "mirror/class-inl.h"
@@ -44,7 +45,7 @@
   DCHECK(javaElementClass != NULL);
   mirror::Class* element_class = soa.Decode<mirror::Class*>(javaElementClass);
   if (UNLIKELY(length < 0)) {
-    soa.Self()->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d", length);
+    ThrowNegativeArraySizeException(length);
     return NULL;
   }
   std::string descriptor("[");
diff --git a/src/native/java_lang_reflect_Constructor.cc b/src/native/java_lang_reflect_Constructor.cc
index fb84dfd..9180217 100644
--- a/src/native/java_lang_reflect_Constructor.cc
+++ b/src/native/java_lang_reflect_Constructor.cc
@@ -37,9 +37,12 @@
   ScopedObjectAccess soa(env);
   mirror::AbstractMethod* m = soa.Decode<mirror::Object*>(javaMethod)->AsMethod();
   mirror::Class* c = m->GetDeclaringClass();
-  if (c->IsAbstract()) {
-    soa.Self()->ThrowNewExceptionF("Ljava/lang/InstantiationException;",
-        "Can't instantiate abstract class %s", PrettyDescriptor(c).c_str());
+  if (UNLIKELY(c->IsAbstract())) {
+    ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
+    soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/InstantiationException;",
+                                   "Can't instantiate %s %s",
+                                   c->IsInterface() ? "interface" : "abstract class",
+                                   PrettyDescriptor(c).c_str());
     return NULL;
   }
 
diff --git a/src/native/java_lang_reflect_Field.cc b/src/native/java_lang_reflect_Field.cc
index 922fe00..b0daa91 100644
--- a/src/native/java_lang_reflect_Field.cc
+++ b/src/native/java_lang_reflect_Field.cc
@@ -16,6 +16,7 @@
 
 #include "class_linker.h"
 #include "class_linker-inl.h"
+#include "common_throws.h"
 #include "dex_file-inl.h"
 #include "jni_internal.h"
 #include "mirror/class-inl.h"
@@ -71,22 +72,23 @@
     // Never okay.
     break;
   }
-  soa.Self()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;",
-      "Not a primitive field: %s", PrettyField(f).c_str());
+  ThrowIllegalArgumentException(NULL,
+                                StringPrintf("Not a primitive field: %s",
+                                             PrettyField(f).c_str()).c_str());
   return false;
 }
 
-static bool CheckReceiver(const ScopedObjectAccess& soa, jobject javaObj, mirror::Field* f,
-                          mirror::Object*& o)
+static bool CheckReceiver(const ScopedObjectAccess& soa, jobject j_rcvr, mirror::Field* f,
+                          mirror::Object*& class_or_rcvr)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   if (f->IsStatic()) {
-    o = f->GetDeclaringClass();
+    class_or_rcvr = f->GetDeclaringClass();
     return true;
   }
 
-  o = soa.Decode<mirror::Object*>(javaObj);
+  class_or_rcvr = soa.Decode<mirror::Object*>(j_rcvr);
   mirror::Class* declaringClass = f->GetDeclaringClass();
-  if (!VerifyObjectInClass(o, declaringClass)) {
+  if (!VerifyObjectInClass(class_or_rcvr, declaringClass)) {
     return false;
   }
   return true;
@@ -126,8 +128,8 @@
   // Widen it if necessary (and possible).
   JValue wide_value;
   mirror::Class* dst_type = Runtime::Current()->GetClassLinker()->FindPrimitiveClass(dst_descriptor);
-  if (!ConvertPrimitiveValue(FieldHelper(f).GetTypeAsPrimitiveType(), dst_type->GetPrimitiveType(),
-                             field_value, wide_value)) {
+  if (!ConvertPrimitiveValue(NULL, false, FieldHelper(f).GetTypeAsPrimitiveType(),
+                             dst_type->GetPrimitiveType(), field_value, wide_value)) {
     return JValue();
   }
   return wide_value;
@@ -205,8 +207,8 @@
     // Else fall through to report an error.
   case Primitive::kPrimVoid:
     // Never okay.
-    Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;",
-        "Not a primitive field: %s", PrettyField(f).c_str());
+    ThrowIllegalArgumentException(NULL, StringPrintf("Not a primitive field: %s",
+                                                     PrettyField(f).c_str()).c_str());
     return;
   }
 
@@ -247,15 +249,15 @@
   }
   FieldHelper fh(f);
   if (!fh.IsPrimitiveType()) {
-    soa.Self()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;",
-        "Not a primitive field: %s", PrettyField(f).c_str());
+    ThrowIllegalArgumentException(NULL, StringPrintf("Not a primitive field: %s",
+                                                     PrettyField(f).c_str()).c_str());
     return;
   }
 
   // Widen the value if necessary (and possible).
   JValue wide_value;
   mirror::Class* src_type = Runtime::Current()->GetClassLinker()->FindPrimitiveClass(src_descriptor);
-  if (!ConvertPrimitiveValue(src_type->GetPrimitiveType(), fh.GetTypeAsPrimitiveType(),
+  if (!ConvertPrimitiveValue(NULL, false, src_type->GetPrimitiveType(), fh.GetTypeAsPrimitiveType(),
                              new_value, wide_value)) {
     return;
   }
diff --git a/src/nth_caller_visitor.h b/src/nth_caller_visitor.h
index 7d9feb6..c32a46a 100644
--- a/src/nth_caller_visitor.h
+++ b/src/nth_caller_visitor.h
@@ -18,6 +18,7 @@
 #define ART_SRC_NTH_CALLER_VISITOR_H_
 
 #include "mirror/abstract_method.h"
+#include "locks.h"
 #include "stack.h"
 
 namespace art {
@@ -25,19 +26,32 @@
 
 // Walks up the stack 'n' callers, when used with Thread::WalkStack.
 struct NthCallerVisitor : public StackVisitor {
-  NthCallerVisitor(Thread* thread, size_t n)
-      : StackVisitor(thread, NULL), n(n), count(0), caller(NULL) {}
+  NthCallerVisitor(Thread* thread, size_t n, bool include_runtime_and_upcalls = false)
+      : StackVisitor(thread, NULL), n(n), include_runtime_and_upcalls_(include_runtime_and_upcalls),
+        count(0), caller(NULL) {}
 
-  bool VisitFrame() {
-    DCHECK(caller == NULL);
-    if (count++ == n) {
-      caller = GetMethod();
-      return false;
+  bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    mirror::AbstractMethod* m = GetMethod();
+    bool do_count = false;
+    if (m == NULL || m->IsRuntimeMethod()) {
+      // Upcall.
+      do_count = include_runtime_and_upcalls_;
+    } else {
+      do_count = true;
+    }
+    if (do_count) {
+      DCHECK(caller == NULL);
+      if (count == n) {
+        caller = m;
+        return false;
+      }
+      count++;
     }
     return true;
   }
 
-  size_t n;
+  const size_t n;
+  const bool include_runtime_and_upcalls_;
   size_t count;
   mirror::AbstractMethod* caller;
 };
diff --git a/src/oat/runtime/arm/oat_support_entrypoints_arm.cc b/src/oat/runtime/arm/oat_support_entrypoints_arm.cc
index 58341bf..1276f78 100644
--- a/src/oat/runtime/arm/oat_support_entrypoints_arm.cc
+++ b/src/oat/runtime/arm/oat_support_entrypoints_arm.cc
@@ -33,10 +33,6 @@
 extern "C" void art_quick_can_put_array_element_from_code(void*, void*);
 extern "C" void art_quick_check_cast_from_code(void*, void*);
 
-// Debug entrypoints.
-extern void DebugMe(mirror::AbstractMethod* method, uint32_t info);
-extern "C" void art_quick_update_debugger(void*, void*, int32_t, void*);
-
 // DexCache entrypoints.
 extern "C" void* art_quick_initialize_static_storage_from_code(uint32_t, void*);
 extern "C" void* art_quick_initialize_type_from_code(uint32_t, void*);
@@ -145,10 +141,6 @@
   points->pCanPutArrayElementFromCode = art_quick_can_put_array_element_from_code;
   points->pCheckCastFromCode = art_quick_check_cast_from_code;
 
-  // Debug
-  points->pDebugMe = DebugMe;
-  points->pUpdateDebuggerFromCode = NULL; // Controlled by SetDebuggerUpdatesEnabled.
-
   // DexCache
   points->pInitializeStaticStorage = art_quick_initialize_static_storage_from_code;
   points->pInitializeTypeAndVerifyAccessFromCode = art_quick_initialize_type_and_verify_access_from_code;
@@ -236,10 +228,6 @@
   points->pThrowStackOverflowFromCode = art_quick_throw_stack_overflow_from_code;
 };
 
-void ChangeDebuggerEntryPoint(EntryPoints* points, bool enabled) {
-  points->pUpdateDebuggerFromCode = (enabled ? art_quick_update_debugger : NULL);
-}
-
 uintptr_t GetInstrumentationExitPc() {
   return reinterpret_cast<uintptr_t>(art_quick_instrumentation_exit_from_code);
 }
diff --git a/src/oat/runtime/arm/runtime_support_arm.S b/src/oat/runtime/arm/runtime_support_arm.S
index fe7d69f..96b3980 100644
--- a/src/oat/runtime/arm/runtime_support_arm.S
+++ b/src/oat/runtime/arm/runtime_support_arm.S
@@ -340,22 +340,6 @@
 END art_quick_invoke_stub
 
     /*
-     * On entry, r0 and r1 must be preserved, r2 is dex PC
-     */
-    .extern artUpdateDebuggerFromCode
-ENTRY art_quick_update_debugger
-    mov    r3, r0         @ stash away r0 so that it's saved as if it were an argument
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
-    mov    r0, r2         @ arg0 is dex PC
-    mov    r1, rSELF      @ arg1 is Thread*
-    mov    r2, sp         @ arg2 is sp
-    bl     artUpdateDebuggerFromCode      @ artUpdateDebuggerFromCode(int32_t, Thread*, Method**)
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
-    mov    r0, r3         @ restore original r0
-    bx     lr
-END art_quick_update_debugger
-
-    /*
      * On entry r0 is uint32_t* gprs_ and r1 is uint32_t* fprs_
      */
 ENTRY art_quick_do_long_jump
@@ -979,21 +963,21 @@
     .extern artInstrumentationMethodEntryFromCode
     .extern artInstrumentationMethodExitFromCode
 ENTRY art_quick_instrumentation_entry_from_code
-    mov   r12, sp        @ remember bottom of caller's frame
-    push  {r0-r3}        @ save arguments (4 words)
-    .save {r0-r3}
+    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    str   r0, [sp, #4]     @ preserve r0
+    mov   r12, sp          @ remember sp
+    str   lr, [sp, #-16]!  @ expand the frame and pass LR
+    .pad #16
     .cfi_adjust_cfa_offset 16
-    .cfi_rel_offset r0, 0
-    .cfi_rel_offset r1, 4
-    .cfi_rel_offset r2, 8
-    .cfi_rel_offset r3, 12
-    mov   r1, r9         @ pass Thread::Current
-    mov   r2, r12        @ pass SP
-    mov   r3, lr         @ pass LR
-    blx   artInstrumentationMethodEntryFromCode  @ (Method*, Thread*, SP, LR)
-    mov   r12, r0        @ r12 holds reference to code
-    pop   {r0-r3}        @ restore arguments
+    .cfi_rel_offset lr, 0
+    mov   r2, r9         @ pass Thread::Current
+    mov   r3, r12        @ pass SP
+    blx   artInstrumentationMethodEntryFromCode  @ (Method*, Object*, Thread*, SP, LR)
+    add   sp, #16        @ remove out argument and padding from stack
     .cfi_adjust_cfa_offset -16
+    mov   r12, r0        @ r12 holds reference to code
+    ldr   r0, [sp, #4]   @ restore r0
+    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
     blx   r12            @ call method with lr set to art_quick_instrumentation_exit_from_code
 END art_quick_instrumentation_entry_from_code
     .type art_quick_instrumentation_exit_from_code, #function
@@ -1001,51 +985,44 @@
 art_quick_instrumentation_exit_from_code:
     .cfi_startproc
     .fnstart
+    mov   lr, #0         @ link register is to here, so clobber with 0 for later checks
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME
     mov   r12, sp        @ remember bottom of caller's frame
     push  {r0-r1}        @ save return value
     .save {r0-r1}
     .cfi_adjust_cfa_offset 8
     .cfi_rel_offset r0, 0
     .cfi_rel_offset r1, 4
-    sub   sp, #8         @ align stack
+    sub   sp, #8         @ space for return value argument
     .pad #8
     .cfi_adjust_cfa_offset 8
+    strd r0, [sp]        @ r0/r1 -> [sp] for fpr_res
+    mov   r2, r0         @ pass return value as gpr_res
+    mov   r3, r1
     mov   r0, r9         @ pass Thread::Current
     mov   r1, r12        @ pass SP
-    blx   artInstrumentationMethodExitFromCode  @ (Thread*, SP)
+    blx   artInstrumentationMethodExitFromCode  @ (Thread*, SP, gpr_res, fpr_res)
     add   sp, #8
     .cfi_adjust_cfa_offset -8
+
     mov   r2, r0         @ link register saved by instrumentation
     mov   lr, r1         @ r1 is holding link register if we're to bounce to deoptimize
     pop   {r0, r1}       @ restore return value
-    .cfi_adjust_cfa_offset -8
+    add sp, #32          @ remove callee save frame
+    .cfi_adjust_cfa_offset -32
     bx    r2             @ return
 END art_quick_instrumentation_exit_from_code
 
     /*
-     * The thread's enter interpreter flag is set and so we should transition to the interpreter
-     * rather than allow execution to continue in the frame below. There may be live results in
-     * registers depending on how complete the operation is when we safepoint - for example, a
-     * set operation may have completed while a get operation needs writing back into the vregs.
+     * Instrumentation has requested that we deoptimize into the interpreter. The deoptimization
+     * will long jump to the upcall with a special exception of -1.
      */
     .extern artDeoptimize
-    .extern artEnterInterpreterFromDeoptimize
 ENTRY art_quick_deoptimize
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME
-    mov    r2, r9         @ Set up args.
-    mov    r3, sp
-    blx    artDeoptimize  @ artDeoptimize(return value, Thread*, SP)
-                          @ Returns caller method's frame size.
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
-    cmp    r0, #0         @ Was the caller an upcall?
-    bxeq   lr             @ Return if caller was upcall.
-    add    r12, sp, r0    @ r12 == bottom of caller's frame.
-    ldr    lr, [r12, #-4] @ Restore lr.
-    mov    sp, r12        @ Remove frame.
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME
-    blx     artEnterInterpreterFromDeoptimize  @ Enter interpreter, callee-save ends stack fragment.
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
-    bx lr                 @ Return to caller.
+    mov    r0, r9         @ Set up args.
+    mov    r1, sp
+    blx    artDeoptimize  @ artDeoptimize(Thread*, SP)
 END art_quick_deoptimize
 
     /*
diff --git a/src/oat/runtime/mips/oat_support_entrypoints_mips.cc b/src/oat/runtime/mips/oat_support_entrypoints_mips.cc
index ea861e8..599b14c 100644
--- a/src/oat/runtime/mips/oat_support_entrypoints_mips.cc
+++ b/src/oat/runtime/mips/oat_support_entrypoints_mips.cc
@@ -33,10 +33,6 @@
 extern "C" void art_quick_can_put_array_element_from_code(void*, void*);
 extern "C" void art_quick_check_cast_from_code(void*, void*);
 
-// Debug entrypoints.
-extern void DebugMe(mirror::AbstractMethod* method, uint32_t info);
-extern "C" void art_quick_update_debugger(void*, void*, int32_t, void*);
-
 // DexCache entrypoints.
 extern "C" void* art_quick_initialize_static_storage_from_code(uint32_t, void*);
 extern "C" void* art_quick_initialize_type_from_code(uint32_t, void*);
@@ -147,10 +143,6 @@
   points->pCanPutArrayElementFromCode = art_quick_can_put_array_element_from_code;
   points->pCheckCastFromCode = art_quick_check_cast_from_code;
 
-  // Debug
-  points->pDebugMe = DebugMe;
-  points->pUpdateDebuggerFromCode = NULL; // Controlled by SetDebuggerUpdatesEnabled.
-
   // DexCache
   points->pInitializeStaticStorage = art_quick_initialize_static_storage_from_code;
   points->pInitializeTypeAndVerifyAccessFromCode = art_quick_initialize_type_and_verify_access_from_code;
@@ -237,10 +229,6 @@
   points->pThrowStackOverflowFromCode = art_quick_throw_stack_overflow_from_code;
 };
 
-void ChangeDebuggerEntryPoint(EntryPoints* points, bool enabled) {
-  points->pUpdateDebuggerFromCode = (enabled ? art_quick_update_debugger : NULL);
-}
-
 uintptr_t GetInstrumentationExitPc() {
   return reinterpret_cast<uintptr_t>(art_quick_instrumentation_exit_from_code);
 }
diff --git a/src/oat/runtime/mips/runtime_support_mips.S b/src/oat/runtime/mips/runtime_support_mips.S
index 0fc2437..529fd0d 100644
--- a/src/oat/runtime/mips/runtime_support_mips.S
+++ b/src/oat/runtime/mips/runtime_support_mips.S
@@ -204,24 +204,6 @@
 .endm
 
     /*
-     * On entry, $a0 and $a1 must be preserved, $a2 is dex PC
-     */
-    .extern artUpdateDebuggerFromCode
-ENTRY art_quick_update_debugger
-    GENERATE_GLOBAL_POINTER
-    move    $a3, $a0        # stash away $a0 so that it's saved as if it were an argument
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
-    move    $a0, $a2        # arg0 is dex PC
-    move    $a1, rSELF      # arg1 is Thread*
-    move    $a2, $sp        # arg2 is $sp
-    jal     artUpdateDebuggerFromCode      # artUpdateDebuggerFromCode(int32_t, Thread*, Method**)
-    nop
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
-    jr      $ra
-    move    $a0, $a3        # restore original $a0
-END art_quick_update_debugger
-
-    /*
      * On entry $a0 is uint32_t* gprs_ and $a1 is uint32_t* fprs_
      * FIXME: just guessing about the shape of the jmpbuf.  Where will pc be?
      */
@@ -989,29 +971,22 @@
     .extern artInstrumentationMethodExitFromCode
 ENTRY art_quick_instrumentation_entry_from_code
     GENERATE_GLOBAL_POINTER
+    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
     move     $t0, $sp       # remember bottom of caller's frame
-    addiu    $sp, $sp, -16  # save arguments (4 words)
-    .cfi_adjust_cfa_offset 16
-    sw       $a0, 0($sp)
-    .cfi_rel_offset 4, 0
-    sw       $a1, 4($sp)
-    .cfi_rel_offset 5, 4
-    sw       $a2, 8($sp)
-    .cfi_rel_offset 6, 8
-    sw       $a3, 12($sp)
-    .cfi_rel_offset 7, 12
-    move     $a3, $ra       # pass $ra
-    move     $a2, $t0       # pass $sp
-    jal      artInstrumentationMethodEntryFromCode  # (Method*, Thread*, SP, LR)
-    move     $a1, rSELF     # pass Thread::Current
+    addiu    $sp, $sp, -32  # space for args, pad (3 words), arguments (5 words)
+    .cfi_adjust_cfa_offset 32
+    sw       $a0, 28($sp)   # save arg0
+    sw       $ra, 16($sp)   # pass $ra
+    move     $a3, $t0       # pass $sp
+    jal      artInstrumentationMethodEntryFromCode  # (Method*, Object*, Thread*, SP, LR)
+    move     $a2, rSELF     # pass Thread::Current
     move     $t9, $v0       # $t9 holds reference to code
-    lw       $a0, 0($sp)
-    lw       $a1, 4($sp)
-    lw       $a2, 8($sp)
-    lw       $a3, 12($sp)
+    lw       $a0, 28($sp)   # restore arg0
+    addiu    $sp, $sp, 32   # remove args
+    .cfi_adjust_cfa_offset -32
+    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
     jalr     $t9            # call method
-    addiu    $sp, $sp, 16
-    .cfi_adjust_cfa_offset -16
+    nop
 END art_quick_instrumentation_entry_from_code
     /* intentional fallthrough */
     .global art_quick_instrumentation_exit_from_code
@@ -1020,53 +995,46 @@
     addiu    $t9, $ra, 4    # put current address into $t9 to rebuild $gp
     GENERATE_GLOBAL_POINTER
     move     $t0, $sp       # remember bottom of caller's frame
-    addiu    $sp, $sp, -16  # save return values
-    .cfi_adjust_cfa_offset 16
-    sw       $v0, 0($sp)
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME
+    addiu    $sp, $sp, -48  # save return values and set up args
+    .cfi_adjust_cfa_offset 48
+    sw       $v0, 32($sp)
     .cfi_rel_offset 2, 0
-    sw       $v1, 4($sp)
+    sw       $v1, 36($sp)
     .cfi_rel_offset 3, 4
+    s.s      $f0, 40($sp)
+    s.s      $f1, 44($sp)
+    s.s      $f0, 16($sp)   # pass fpr result
+    s.s      $f1, 20($sp)
+    move     $a2, $v0       # pass gpr result
+    move     $a3, $v1
     move     $a1, $t0       # pass $sp
-    jal      artInstrumentationMethodExitFromCode  # (Thread*, SP)
+    jal      artInstrumentationMethodExitFromCode  # (Thread*, SP, gpr_res, fpr_res)
     move     $a0, rSELF     # pass Thread::Current
     move     $t0, $v0       # set aside returned link register
     move     $ra, $v1       # set link register for deoptimization
-    lw       $v0, 0($sp)
-    lw       $v1, 4($sp)
+    lw       $v0, 32($sp)   # restore return values
+    lw       $v1, 36($sp)
+    l.s      $f0, 40($sp)
+    l.s      $f1, 44($sp)
     jr       $t0            # return
-    addiu    $sp, $sp, 16
-    .cfi_adjust_cfa_offset -16
+    addiu    $sp, $sp, 112  # 48 bytes of args + 64 bytes of callee save frame
+    .cfi_adjust_cfa_offset -112
 END art_quick_instrumentation_exit_from_code
 
     /*
-     * The thread's enter interpreter flag is set and so we should transition to the interpreter
-     * rather than allow execution to continue in the frame below. There may be live results in
-     * registers depending on how complete the operation is when we safepoint - for example, a
-     * set operation may have completed while a get operation needs writing back into the vregs.
+     * Instrumentation has requested that we deoptimize into the interpreter. The deoptimization
+     * will long jump to the upcall with a special exception of -1.
      */
     .extern artDeoptimize
     .extern artEnterInterpreterFromDeoptimize
 ENTRY art_quick_deoptimize
     GENERATE_GLOBAL_POINTER
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME
-    move     $a0, $v0       # pass first half of return value
-    move     $a1, $v1       # pass second half of return value
-    move     $a2, rSELF     # pass Thread::current
-    jal      artDeoptimize  # artDeoptimize(return value, Thread*, SP)
+    move     $a0, rSELF     # pass Thread::current
+    jal      artDeoptimize  # artDeoptimize(Thread*, SP)
                             # Returns caller method's frame size.
-    move     $a3, $sp       # pass $sp
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
-    beqz     $v0, 1f        # Return if caller was upcall.
-    add      $t9, $sp, $v0  # $t9 == bottom of caller's frame.
-    lw       $ra, -4($t9)   # Restore $ra.
-    move     $sp, $t9       # Remove frame.
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME
-    jal      artEnterInterpreterFromDeoptimize  # Enter interpreter, callee-save ends stack fragment.
-    nop
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
-1:
-    jr       $ra            # Return to caller.
-    nop
+    move     $a1, $sp       # pass $sp
 END art_quick_deoptimize
 
     /*
diff --git a/src/oat/runtime/oat_support_entrypoints.h b/src/oat/runtime/oat_support_entrypoints.h
index ee59df4..9cd9cc7 100644
--- a/src/oat/runtime/oat_support_entrypoints.h
+++ b/src/oat/runtime/oat_support_entrypoints.h
@@ -46,10 +46,6 @@
   void (*pCanPutArrayElementFromCode)(void*, void*);
   void (*pCheckCastFromCode)(void*, void*);
 
-  // Debug
-  void (*pDebugMe)(mirror::AbstractMethod*, uint32_t);
-  void (*pUpdateDebuggerFromCode)(void*, void*, int32_t, void*);
-
   // DexCache
   void* (*pInitializeStaticStorage)(uint32_t, void*);
   void* (*pInitializeTypeAndVerifyAccessFromCode)(uint32_t, void*);
diff --git a/src/oat/runtime/support_cast.cc b/src/oat/runtime/support_cast.cc
index 0b1fb74..fe91e61 100644
--- a/src/oat/runtime/support_cast.cc
+++ b/src/oat/runtime/support_cast.cc
@@ -32,19 +32,16 @@
 }
 
 // Check whether it is safe to cast one class to the other, throw exception and return -1 on failure
-extern "C" int artCheckCastFromCode(const mirror::Class* a, const mirror::Class* b, Thread* self,
-                                    mirror::AbstractMethod** sp)
+extern "C" int artCheckCastFromCode(mirror::Class* src_type, mirror::Class* dest_type,
+                                    Thread* self, mirror::AbstractMethod** sp)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  DCHECK(a->IsClass()) << PrettyClass(a);
-  DCHECK(b->IsClass()) << PrettyClass(b);
-  if (LIKELY(b->IsAssignableFrom(a))) {
+  DCHECK(src_type->IsClass()) << PrettyClass(src_type);
+  DCHECK(dest_type->IsClass()) << PrettyClass(dest_type);
+  if (LIKELY(dest_type->IsAssignableFrom(src_type))) {
     return 0;  // Success
   } else {
     FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
-    self->ThrowNewExceptionF("Ljava/lang/ClassCastException;",
-        "%s cannot be cast to %s",
-        PrettyDescriptor(a).c_str(),
-        PrettyDescriptor(b).c_str());
+    ThrowClassCastException(dest_type, src_type);
     return -1;  // Failure
   }
 }
@@ -63,10 +60,7 @@
     return 0;  // Success
   } else {
     FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
-    self->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
-        "%s cannot be stored in an array of type %s",
-        PrettyDescriptor(element_class).c_str(),
-        PrettyDescriptor(array_class).c_str());
+    ThrowArrayStoreException(element_class, array_class);
     return -1;  // Failure
   }
 }
diff --git a/src/oat/runtime/support_debug.cc b/src/oat/runtime/support_debug.cc
deleted file mode 100644
index 0d67dd9..0000000
--- a/src/oat/runtime/support_debug.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2012 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 "callee_save_frame.h"
-#include "debugger.h"
-
-namespace art {
-
-/*
- * Report location to debugger.  Note: dex_pc is the current offset within
- * the method.  However, because the offset alone cannot distinguish between
- * method entry and offset 0 within the method, we'll use an offset of -1
- * to denote method entry.
- */
-extern "C" void artUpdateDebuggerFromCode(int32_t dex_pc, Thread* self, mirror::AbstractMethod** sp)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  FinishCalleeSaveFrameSetup(self, sp,  Runtime::kRefsAndArgs);
-  Dbg::UpdateDebugger(dex_pc, self);
-}
-
-// Temporary debugging hook for compiler.
-extern void DebugMe(mirror::AbstractMethod* method, uint32_t info)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  LOG(INFO) << "DebugMe";
-  if (method != NULL) {
-    LOG(INFO) << PrettyMethod(method);
-  }
-  LOG(INFO) << "Info: " << info;
-}
-
-}  // namespace art
diff --git a/src/oat/runtime/support_deoptimize.cc b/src/oat/runtime/support_deoptimize.cc
index 2cc5dd3..0b0a7c3 100644
--- a/src/oat/runtime/support_deoptimize.cc
+++ b/src/oat/runtime/support_deoptimize.cc
@@ -28,88 +28,11 @@
 
 namespace art {
 
-extern "C" uint64_t artDeoptimize(JValue ret_val, Thread* self, mirror::AbstractMethod** sp)
+extern "C" void artDeoptimize(Thread* self, mirror::AbstractMethod** sp)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
-  // Return value may hold Object* so avoid suspension.
-  const char* old_cause = self->StartAssertNoThreadSuspension("Deoptimizing stack frame");
-  CHECK(old_cause == NULL);
-  class DeoptimizationVisitor : public StackVisitor {
-   public:
-    DeoptimizationVisitor(Thread* thread, Context* context)
-        : StackVisitor(thread, context), shadow_frame_(NULL), runtime_frames_(0) { }
-
-    virtual bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-      mirror::AbstractMethod* m = GetMethod();
-      if (m->IsRuntimeMethod()) {
-        if (runtime_frames_ == 0) {
-          runtime_frames_++;
-          return true;  // Skip the callee save frame.
-        } else {
-          return false;  // Caller was an upcall.
-        }
-      }
-      MethodHelper mh(m);
-      const DexFile::CodeItem* code_item = mh.GetCodeItem();
-      CHECK(code_item != NULL);
-      uint16_t num_regs =  code_item->registers_size_;
-      shadow_frame_ = ShadowFrame::Create(num_regs, NULL, m, GetDexPc());
-      std::vector<int32_t> kinds = DescribeVRegs(m->GetDexMethodIndex(), &mh.GetDexFile(),
-                                                 mh.GetDexCache(), mh.GetClassLoader(),
-                                                 mh.GetClassDefIndex(), code_item, m,
-                                                 m->GetAccessFlags(), GetDexPc());
-      for(uint16_t reg = 0; reg < num_regs; reg++) {
-        VRegKind kind = static_cast<VRegKind>(kinds.at(reg * 2));
-        switch (kind) {
-          case kUndefined:
-            shadow_frame_->SetVReg(reg, 0xEBADDE09);
-            break;
-          case kConstant:
-            shadow_frame_->SetVReg(reg, kinds.at((reg * 2) + 1));
-            break;
-          default:
-            shadow_frame_->SetVReg(reg, GetVReg(m, reg, kind));
-            break;
-        }
-      }
-      return false;  // Stop now we have built the shadow frame.
-    }
-
-    std::vector<int32_t> DescribeVRegs(uint32_t dex_method_idx,
-                                       const DexFile* dex_file,
-                                       mirror::DexCache* dex_cache,
-                                       mirror::ClassLoader* class_loader,
-                                       uint32_t class_def_idx,
-                                       const DexFile::CodeItem* code_item,
-                                       mirror::AbstractMethod* method,
-                                       uint32_t method_access_flags, uint32_t dex_pc)
-        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-      verifier::MethodVerifier verifier(dex_file, dex_cache, class_loader, class_def_idx, code_item,
-                                        dex_method_idx, method, method_access_flags, true);
-      verifier.Verify();
-      return verifier.DescribeVRegs(dex_pc);
-    }
-
-    ShadowFrame* shadow_frame_;
-    uint32_t runtime_frames_;
-  } visitor(self, self->GetLongJumpContext());
-  visitor.WalkStack(false);
-  if (visitor.shadow_frame_ != NULL) {
-    self->SetDeoptimizationShadowFrame(visitor.shadow_frame_, ret_val);
-    return (*sp)->GetFrameSizeInBytes();
-  } else {
-    return 0;  // Caller was an upcall.
-  }
-}
-
-
-extern "C" JValue artEnterInterpreterFromDeoptimize(Thread* self, mirror::AbstractMethod** sp)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
-  JValue return_value;
-  UniquePtr<ShadowFrame> shadow_frame(self->GetAndClearDeoptimizationShadowFrame(&return_value));
-  self->EndAssertNoThreadSuspension(NULL);
-  return interpreter::EnterInterpreterFromDeoptimize(self, *shadow_frame.get(), return_value);
+  self->SetException(ThrowLocation(), reinterpret_cast<mirror::Throwable*>(-1));
+  self->QuickDeliverException();
 }
 
 }  // namespace art
diff --git a/src/oat/runtime/support_field.cc b/src/oat/runtime/support_field.cc
index 43d5c9b..5821063 100644
--- a/src/oat/runtime/support_field.cc
+++ b/src/oat/runtime/support_field.cc
@@ -86,7 +86,8 @@
   field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveRead, sizeof(int32_t));
   if (LIKELY(field != NULL)) {
     if (UNLIKELY(obj == NULL)) {
-      ThrowNullPointerExceptionForFieldAccess(field, true);
+      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+      ThrowNullPointerExceptionForFieldAccess(throw_location, field, true);
     } else {
       return field->Get32(obj);
     }
@@ -106,7 +107,8 @@
   field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveRead, sizeof(int64_t));
   if (LIKELY(field != NULL)) {
     if (UNLIKELY(obj == NULL)) {
-      ThrowNullPointerExceptionForFieldAccess(field, true);
+      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+      ThrowNullPointerExceptionForFieldAccess(throw_location, field, true);
     } else {
       return field->Get64(obj);
     }
@@ -127,7 +129,8 @@
   field = FindFieldFromCode(field_idx, referrer, self, InstanceObjectRead, sizeof(mirror::Object*));
   if (LIKELY(field != NULL)) {
     if (UNLIKELY(obj == NULL)) {
-      ThrowNullPointerExceptionForFieldAccess(field, true);
+      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+      ThrowNullPointerExceptionForFieldAccess(throw_location, field, true);
     } else {
       return field->GetObj(obj);
     }
@@ -204,7 +207,8 @@
   field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveWrite, sizeof(int32_t));
   if (LIKELY(field != NULL)) {
     if (UNLIKELY(obj == NULL)) {
-      ThrowNullPointerExceptionForFieldAccess(field, false);
+      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+      ThrowNullPointerExceptionForFieldAccess(throw_location, field, false);
     } else {
       field->Set32(obj, new_value);
       return 0;  // success
@@ -230,7 +234,8 @@
   field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveWrite, sizeof(int64_t));
   if (LIKELY(field != NULL)) {
     if (UNLIKELY(obj == NULL)) {
-      ThrowNullPointerExceptionForFieldAccess(field, false);
+      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+      ThrowNullPointerExceptionForFieldAccess(throw_location, field, false);
     } else {
       field->Set64(obj, new_value);
       return 0;  // success
@@ -255,7 +260,8 @@
                             sizeof(mirror::Object*));
   if (LIKELY(field != NULL)) {
     if (UNLIKELY(obj == NULL)) {
-      ThrowNullPointerExceptionForFieldAccess(field, false);
+      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+      ThrowNullPointerExceptionForFieldAccess(throw_location, field, false);
     } else {
       field->SetObj(obj, new_value);
       return 0;  // success
diff --git a/src/oat/runtime/support_fillarray.cc b/src/oat/runtime/support_fillarray.cc
index 73f832a..a0b06fb 100644
--- a/src/oat/runtime/support_fillarray.cc
+++ b/src/oat/runtime/support_fillarray.cc
@@ -15,6 +15,7 @@
  */
 
 #include "callee_save_frame.h"
+#include "common_throws.h"
 #include "dex_instruction.h"
 #include "mirror/array.h"
 #include "mirror/object-inl.h"
@@ -43,15 +44,15 @@
   FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
   DCHECK_EQ(payload->ident, static_cast<uint16_t>(Instruction::kArrayDataSignature));
   if (UNLIKELY(array == NULL)) {
-    Thread::Current()->ThrowNewExceptionF("Ljava/lang/NullPointerException;",
-        "null array in FILL_ARRAY_DATA");
+    ThrowNullPointerException(NULL, "null array in FILL_ARRAY_DATA");
     return -1;  // Error
   }
   DCHECK(array->IsArrayInstance() && !array->IsObjectArray());
   if (UNLIKELY(static_cast<int32_t>(payload->element_count) > array->GetLength())) {
-    Thread::Current()->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;",
-                                          "failed FILL_ARRAY_DATA; length=%d, index=%d",
-                                          array->GetLength(), payload->element_count);
+    ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+    self->ThrowNewExceptionF(throw_location, "Ljava/lang/ArrayIndexOutOfBoundsException;",
+                             "failed FILL_ARRAY_DATA; length=%d, index=%d",
+                             array->GetLength(), payload->element_count);
     return -1;  // Error
   }
   uint32_t size_in_bytes = payload->element_count * payload->element_width;
diff --git a/src/oat/runtime/support_instrumentation.cc b/src/oat/runtime/support_instrumentation.cc
index 6598f19..8f56ce3 100644
--- a/src/oat/runtime/support_instrumentation.cc
+++ b/src/oat/runtime/support_instrumentation.cc
@@ -14,58 +14,51 @@
  * limitations under the License.
  */
 
-#include "base/logging.h"
+#include "callee_save_frame.h"
 #include "instrumentation.h"
+#include "mirror/abstract_method-inl.h"
+#include "mirror/object-inl.h"
 #include "runtime.h"
 #include "thread-inl.h"
-#include "trace.h"
 
 namespace art {
 
 extern "C" const void* artInstrumentationMethodEntryFromCode(mirror::AbstractMethod* method,
+                                                             mirror::Object* this_object,
                                                              Thread* self,
                                                              mirror::AbstractMethod** sp,
                                                              uintptr_t lr)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  self->SetTopOfStack(sp, lr);
-  self->VerifyStack();
-  Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
-  // +1 as frame id's start at 1, +1 as we haven't yet built this method's frame.
-  size_t frame_id = StackVisitor::ComputeNumFrames(self) + 2;
-  InstrumentationStackFrame instrumentation_frame(method, lr, frame_id);
-  self->PushInstrumentationStackFrame(instrumentation_frame);
-
-  Trace* trace = instrumentation->GetTrace();
-  if (trace != NULL) {
-    trace->LogMethodTraceEvent(self, method, Trace::kMethodTraceEnter);
-  }
-
-  return instrumentation->GetSavedCodeFromMap(method);
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs);
+  instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+  instrumentation->PushInstrumentationStackFrame(self, method->IsStatic() ? NULL : this_object,
+                                                 method, lr);
+  const void* result = instrumentation->GetQuickCodeFor(method);
+  CHECK(result != NULL) << PrettyMethod(method);
+  return result;
 }
 
-extern "C" uint64_t artInstrumentationMethodExitFromCode(Thread* self, mirror::AbstractMethod** sp)
+extern "C" uint64_t artInstrumentationMethodExitFromCode(Thread* self, mirror::AbstractMethod** sp,
+                                                         uint64_t gpr_result, uint64_t fpr_result)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  // TODO: use FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly) not the hand inlined below.
+  //       We use the hand inline version to ensure the return_pc is assigned before verifying the
+  //       stack.
+  // Be aware the store below may well stomp on an incoming argument.
+  Locks::mutator_lock_->AssertSharedHeld(self);
+  mirror::AbstractMethod* callee_save = Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsOnly);
+  *sp = callee_save;
+  uintptr_t* return_pc = reinterpret_cast<uintptr_t*>(reinterpret_cast<byte*>(sp) +
+                                                      callee_save->GetReturnPcOffsetInBytes());
+  CHECK(*return_pc == 0);
   self->SetTopOfStack(sp, 0);
   self->VerifyStack();
-  // +1 as frame id's start at 1, +1 as we want the called frame not the frame being returned into.
-  size_t frame_id = StackVisitor::ComputeNumFrames(self) + 2;
-  InstrumentationStackFrame instrumentation_frame;
-  instrumentation_frame = self->PopInstrumentationStackFrame();
-  if (frame_id != instrumentation_frame.frame_id_) {
-    LOG(ERROR) << "Expected frame_id=" << frame_id << " but found " << instrumentation_frame.frame_id_;
-    StackVisitor::DescribeStack(self);
-  }
-  Runtime* runtime = Runtime::Current();
-  if (runtime->IsMethodTracingActive()) {
-    Trace* trace = runtime->GetInstrumentation()->GetTrace();
-    trace->LogMethodTraceEvent(self, instrumentation_frame.method_, Trace::kMethodTraceExit);
-  }
-  if (self->ReadFlag(kEnterInterpreter)) {
-    return static_cast<uint64_t>(GetDeoptimizationEntryPoint()) |
-        (static_cast<uint64_t>(instrumentation_frame.return_pc_) << 32);
-  } else {
-    return instrumentation_frame.return_pc_;
-  }
+  instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+  uint64_t return_or_deoptimize_pc = instrumentation->PopInstrumentationStackFrame(self, return_pc,
+                                                                                   gpr_result,
+                                                                                   fpr_result);
+  self->VerifyStack();
+  return return_or_deoptimize_pc;
 }
 
 }  // namespace art
diff --git a/src/oat/runtime/support_jni.cc b/src/oat/runtime/support_jni.cc
index ee19d4e..bc9cc45 100644
--- a/src/oat/runtime/support_jni.cc
+++ b/src/oat/runtime/support_jni.cc
@@ -33,7 +33,7 @@
   DCHECK(Thread::Current() == self);
   ScopedObjectAccess soa(self);
 
-  mirror::AbstractMethod* method = self->GetCurrentMethod();
+  mirror::AbstractMethod* method = self->GetCurrentMethod(NULL);
   DCHECK(method != NULL);
 
   // Lookup symbol address for method, on failure we'll return NULL with an
@@ -139,7 +139,7 @@
   // | unused |
   // | unused |
   // | unused | <- sp
-  mirror::AbstractMethod* jni_method = self->GetCurrentMethod();
+  mirror::AbstractMethod* jni_method = self->GetCurrentMethod(NULL);
   DCHECK(jni_method->IsNative()) << PrettyMethod(jni_method);
   intptr_t* arg_ptr = sp + 4;  // pointer to r1 on stack
   // Fix up this/jclass argument
diff --git a/src/oat/runtime/support_stubs.cc b/src/oat/runtime/support_stubs.cc
index 0cb3fe4..0e00dfd 100644
--- a/src/oat/runtime/support_stubs.cc
+++ b/src/oat/runtime/support_stubs.cc
@@ -242,7 +242,7 @@
     // go into deliver exception with the pending exception in r0
     CHECK(thread->IsExceptionPending());
     code = reinterpret_cast<void*>(art_quick_deliver_exception_from_code);
-    regs[0] = reinterpret_cast<uintptr_t>(thread->GetException());
+    regs[0] = reinterpret_cast<uintptr_t>(thread->GetException(NULL));
     thread->ClearException();
   } else {
     // Expect class to at least be initializing.
@@ -350,13 +350,14 @@
 
 #if !defined(ART_USE_PORTABLE_COMPILER)
 // Called by the AbstractMethodError. Called by stub code.
-extern void ThrowAbstractMethodErrorFromCode(mirror::AbstractMethod* method, Thread* thread,
+extern void ThrowAbstractMethodErrorFromCode(mirror::AbstractMethod* method, Thread* self,
                                              mirror::AbstractMethod** sp)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll);
-  thread->ThrowNewExceptionF("Ljava/lang/AbstractMethodError;",
-                             "abstract method \"%s\"", PrettyMethod(method).c_str());
-  thread->QuickDeliverException();
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
+  ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+  self->ThrowNewExceptionF(throw_location, "Ljava/lang/AbstractMethodError;",
+                           "abstract method \"%s\"", PrettyMethod(method).c_str());
+  self->QuickDeliverException();
 }
 #else // ART_USE_PORTABLE_COMPILER
 extern void ThrowAbstractMethodErrorFromCode(mirror::AbstractMethod* method, Thread* thread,
diff --git a/src/oat/runtime/support_throw.cc b/src/oat/runtime/support_throw.cc
index 80ba118..b8c68a5 100644
--- a/src/oat/runtime/support_throw.cc
+++ b/src/oat/runtime/support_throw.cc
@@ -23,14 +23,6 @@
 
 namespace art {
 
-// Used to implement MOVE_EXCEPTION.
-extern "C" void* GetAndClearException(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  DCHECK(self->IsExceptionPending());
-  mirror::Throwable* exception = self->GetException();
-  self->ClearException();
-  return exception;
-}
-
 // Deliver an exception that's pending on thread helping set up a callee save frame on the way.
 extern "C" void artDeliverPendingExceptionFromCode(Thread* thread, mirror::AbstractMethod** sp)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -39,7 +31,7 @@
 }
 
 // Called by generated call to throw an exception.
-extern "C" void artDeliverExceptionFromCode(mirror::Throwable* exception, Thread* thread,
+extern "C" void artDeliverExceptionFromCode(mirror::Throwable* exception, Thread* self,
                                             mirror::AbstractMethod** sp)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   /*
@@ -49,9 +41,15 @@
    * and threw a NPE if NULL.  This routine responsible for setting
    * exception_ in thread and delivering the exception.
    */
-  FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll);
-  thread->DeliverException(exception);
-  thread->QuickDeliverException();
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
+  ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+  if (exception == NULL) {
+    self->ThrowNewException(throw_location, "Ljava/lang/NullPointerException;",
+                            "throw with null exception");
+  } else {
+    self->SetException(throw_location, exception);
+  }
+  self->QuickDeliverException();
 }
 
 // Called by generated call to throw a NPE exception.
@@ -59,29 +57,27 @@
                                                      mirror::AbstractMethod** sp)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
-  uint32_t dex_pc;
-  mirror::AbstractMethod* throw_method = self->GetCurrentMethod(&dex_pc);
-  ThrowNullPointerExceptionFromDexPC(throw_method, dex_pc);
+  ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+  ThrowNullPointerExceptionFromDexPC(throw_location);
   self->QuickDeliverException();
 }
 
 // Called by generated call to throw an arithmetic divide by zero exception.
-extern "C" void artThrowDivZeroFromCode(Thread* thread,
+extern "C" void artThrowDivZeroFromCode(Thread* self,
                                         mirror::AbstractMethod** sp)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll);
-  thread->ThrowNewException("Ljava/lang/ArithmeticException;", "divide by zero");
-  thread->QuickDeliverException();
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
+  ThrowArithmeticExceptionDivideByZero(self);
+  self->QuickDeliverException();
 }
 
 // Called by generated call to throw an array index out of bounds exception.
-extern "C" void artThrowArrayBoundsFromCode(int index, int limit, Thread* thread,
+extern "C" void artThrowArrayBoundsFromCode(int index, int length, Thread* self,
                                             mirror::AbstractMethod** sp)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll);
-  thread->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;",
-                             "length=%d; index=%d", limit, index);
-  thread->QuickDeliverException();
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
+  ThrowArrayIndexOutOfBoundsException(index, length);
+  self->QuickDeliverException();
 }
 
 extern "C" void artThrowStackOverflowFromCode(Thread* self, mirror::AbstractMethod** sp)
@@ -95,8 +91,7 @@
                                              mirror::AbstractMethod** sp)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
-  mirror::AbstractMethod* method = self->GetCurrentMethod();
-  ThrowNoSuchMethodError(method_idx, method);
+  ThrowNoSuchMethodError(method_idx);
   self->QuickDeliverException();
 }
 
diff --git a/src/oat/runtime/x86/oat_support_entrypoints_x86.cc b/src/oat/runtime/x86/oat_support_entrypoints_x86.cc
index a7c518a..708e04e 100644
--- a/src/oat/runtime/x86/oat_support_entrypoints_x86.cc
+++ b/src/oat/runtime/x86/oat_support_entrypoints_x86.cc
@@ -33,10 +33,6 @@
 extern "C" void art_quick_can_put_array_element_from_code(void*, void*);
 extern "C" void art_quick_check_cast_from_code(void*, void*);
 
-// Debug entrypoints.
-extern void DebugMe(mirror::AbstractMethod* method, uint32_t info);
-extern "C" void art_quick_update_debugger(void*, void*, int32_t, void*);
-
 // DexCache entrypoints.
 extern "C" void* art_quick_initialize_static_storage_from_code(uint32_t, void*);
 extern "C" void* art_quick_initialize_type_from_code(uint32_t, void*);
@@ -130,10 +126,6 @@
   points->pCanPutArrayElementFromCode = art_quick_can_put_array_element_from_code;
   points->pCheckCastFromCode = art_quick_check_cast_from_code;
 
-  // Debug
-  points->pDebugMe = DebugMe;
-  points->pUpdateDebuggerFromCode = NULL; // Controlled by SetDebuggerUpdatesEnabled.
-
   // DexCache
   points->pInitializeStaticStorage = art_quick_initialize_static_storage_from_code;
   points->pInitializeTypeAndVerifyAccessFromCode = art_quick_initialize_type_and_verify_access_from_code;
@@ -220,10 +212,6 @@
   points->pThrowStackOverflowFromCode = art_quick_throw_stack_overflow_from_code;
 };
 
-void ChangeDebuggerEntryPoint(EntryPoints* points, bool enabled) {
-  points->pUpdateDebuggerFromCode = (enabled ? art_quick_update_debugger : NULL);
-}
-
 uintptr_t GetInstrumentationExitPc() {
   return reinterpret_cast<uintptr_t>(art_quick_instrumentation_exit_from_code);
 }
diff --git a/src/oat/runtime/x86/runtime_support_x86.S b/src/oat/runtime/x86/runtime_support_x86.S
index 4b4689f..4900b84 100644
--- a/src/oat/runtime/x86/runtime_support_x86.S
+++ b/src/oat/runtime/x86/runtime_support_x86.S
@@ -512,26 +512,6 @@
 TWO_ARG_DOWNCALL art_quick_initialize_type_from_code, artInitializeTypeFromCode, RETURN_IF_EAX_NOT_ZERO
 TWO_ARG_DOWNCALL art_quick_initialize_type_and_verify_access_from_code, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_EAX_NOT_ZERO
 
-    /*
-     * On entry, eax and ecx must be preserved, edx is dex PC
-     */
-DEFINE_FUNCTION art_quick_update_debugger
-    mov %eax, %ebx                // stash away eax so that it's saved as if it were an argument
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
-    subl LITERAL(4), %esp         // alignment padding
-    .cfi_adjust_cfa_offset 4
-    PUSH esp                      // pass arg2 (sp)
-    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
-    .cfi_adjust_cfa_offset 4
-    PUSH edx                      // pass arg0 (dex pc)
-    call SYMBOL(artUpdateDebuggerFromCode) // artUpdateDebuggerFromCode(int32_t, Thread*, Method**)
-    addl LITERAL(16), %esp        // pop arguments
-    .cfi_adjust_cfa_offset -16
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
-    mov %ebx, %eax                // restore original eax
-    ret
-END_FUNCTION art_quick_update_debugger
-
 ONE_ARG_DOWNCALL art_quick_lock_object_from_code, artLockObjectFromCode, ret
 ONE_ARG_DOWNCALL art_quick_unlock_object_from_code, artUnlockObjectFromCode, RETURN_IF_EAX_ZERO
 
@@ -879,6 +859,7 @@
     mov %esp, %ebx                // remember SP
     mov 32(%esp), %edx            // get referrer
     subl LITERAL(12), %esp        // alignment padding
+    .cfi_adjust_cfa_offset 12
     PUSH ebx                      // pass SP
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     .cfi_adjust_cfa_offset 4
@@ -972,7 +953,7 @@
 END_FUNCTION art_quick_proxy_invoke_handler
 
 DEFINE_FUNCTION art_quick_interpreter_entry
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME   // save frame and Method*
+    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME   // save frame
     mov %esp, %edx                // remember SP
     PUSH eax                      // alignment padding
     PUSH edx                      // pass SP
@@ -992,71 +973,84 @@
      * Routine that intercepts method calls and returns.
      */
 DEFINE_FUNCTION art_quick_instrumentation_entry_from_code
-    xchgl %eax, (%esp)            // place LR in eax, save eax
-    PUSH ecx                      // save ecx
-    PUSH edx                      // save edx
-    PUSH ebx                      // save ebx
-    lea   16(%esp), %edx          // remember bottom of caller's frame
-    PUSH eax                      // pass LR
-    PUSH edx                      // pass SP
-    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
+    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    movl  %esp, %edx              // Save SP.
+    PUSH eax                      // Save eax which will be clobbered by the callee-save method.
+    subl LITERAL(8), %esp         // Align stack.
+    .cfi_adjust_cfa_offset 8
+    pushl 40(%esp)                // Pass LR.
     .cfi_adjust_cfa_offset 4
-    pushl 24(%esp)                // pass Method*
+    PUSH edx                      // Pass SP.
+    pushl %fs:THREAD_SELF_OFFSET  // Pass Thread::Current().
     .cfi_adjust_cfa_offset 4
-    call  SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Thread*, SP, LR)
-    addl  LITERAL(16), %esp       // pop arguments
-    POP ebx                       // restore ebx
-    POP edx                       // restore edx
-    movl  (%esp), %ecx            // restore ecx (without popping)
-    movl  %eax, (%esp)            // place method's code pointer on stack
-    movl  4(%esp), %eax           // restore eax (without popping)
-    movl  LITERAL(SYMBOL(art_quick_instrumentation_exit_from_code)), 4(%esp)
-                                  // place instrumentation exit as return pc
-    ret                           // call method (and pop)
+    PUSH ecx                      // Pass receiver.
+    PUSH eax                      // Pass Method*.
+    call  SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Object*, Thread*, SP, LR)
+    addl  LITERAL(28), %esp       // Pop arguments upto saved Method*.
+    movl 28(%esp), %edi           // Restore edi.
+    movl %eax, 28(%esp)           // Place code* over edi, just under return pc.
+    movl LITERAL(SYMBOL(art_quick_instrumentation_exit_from_code)), 32(%esp)
+                                  // Place instrumentation exit as return pc.
+    movl (%esp), %eax             // Restore eax.
+    movl 8(%esp), %ecx            // Restore ecx.
+    movl 12(%esp), %edx           // Restore edx.
+    movl 16(%esp), %ebx           // Restore ebx.
+    movl 20(%esp), %ebp           // Restore ebp.
+    movl 24(%esp), %esi           // Restore esi.
+    addl LITERAL(28), %esp        // Wind stack back upto code*.
+    ret                           // Call method (and pop).
 END_FUNCTION art_quick_instrumentation_entry_from_code
+
 DEFINE_FUNCTION art_quick_instrumentation_exit_from_code
-    mov   %esp, %ecx              // remember bottom of caller's frame
-    PUSH edx                      // save return value
-    PUSH eax                      // save other half of return value
-    PUSH ecx                      // pass SP
-    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current
+    pushl LITERAL(0)              // Push a fake return PC as there will be none on the stack.
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME
+    mov  %esp, %ecx               // Remember SP
+    subl LITERAL(8), %esp         // Save float return value.
+    .cfi_adjust_cfa_offset 8
+    movd %xmm0, (%esp)
+    PUSH edx                      // Save gpr return value.
+    PUSH eax
+    subl LITERAL(8), %esp         // Align stack
+    movd %xmm0, (%esp)
+    subl LITERAL(8), %esp         // Pass float return value.
+    .cfi_adjust_cfa_offset 8
+    movd %xmm0, (%esp)
+    PUSH edx                      // Pass gpr return value.
+    PUSH eax
+    PUSH ecx                      // Pass SP.
+    pushl %fs:THREAD_SELF_OFFSET  // Pass Thread::Current.
     .cfi_adjust_cfa_offset 4
-    call  SYMBOL(artInstrumentationMethodExitFromCode)  // (Thread*, SP)
-    mov   %eax, %ecx              // move returned link register
-    // TODO: Set link register for deopt
-    addl LITERAL(8), %esp         // pop arguments
+    call  SYMBOL(artInstrumentationMethodExitFromCode)  // (Thread*, SP, gpr_result, fpr_result)
+    mov   %eax, %ecx              // Move returned link register.
+    addl LITERAL(32), %esp        // Pop arguments.
+    .cfi_adjust_cfa_offset -32
+    movl %edx, %ebx               // Move returned link register for deopt
+                                  // (ebx is pretending to be our LR).
+    POP eax                       // Restore gpr return value.
+    POP edx
+    movd (%esp), %xmm0            // Restore fpr return value.
+    addl LITERAL(8), %esp
     .cfi_adjust_cfa_offset -8
-    POP eax                       // restore return value
-    POP edx                       // restore other half of return value
-    jmp   *%ecx                   // return
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    addl LITERAL(4), %esp         // Remove fake return pc.
+    jmp   *%ecx                   // Return.
 END_FUNCTION art_quick_instrumentation_exit_from_code
 
     /*
-     * The thread's enter interpreter flag is set and so we should transition to the interpreter
-     * rather than allow execution to continue in the frame below. There may be live results in
-     * registers depending on how complete the operation is when we safepoint - for example, a
-     * set operation may have completed while a get operation needs writing back into the vregs.
+     * Instrumentation has requested that we deoptimize into the interpreter. The deoptimization
+     * will long jump to the upcall with a special exception of -1.
      */
 DEFINE_FUNCTION art_quick_deoptimize
+    pushl %ebx                    // Fake that we were called.
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME
-    PUSH esp                      // pass SP
-    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
+    mov  %esp, %ecx               // Remember SP.
+    subl LITERAL(8), %esp         // Align stack.
+    .cfi_adjust_cfa_offset 8
+    PUSH ecx                      // Pass SP.
+    pushl %fs:THREAD_SELF_OFFSET  // Pass Thread::Current().
     .cfi_adjust_cfa_offset 4
-    PUSH edx                      // push half of return value
-    PUSH eax                      // push other half of return value
-    call SYMBOL(artDeoptimize)    // artDeoptimize(return value, Thread*, SP)
-                                  // Returns caller method's frame size.
-    addl LITERAL(16), %esp        // pop arguments
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
-    testl %eax, %eax              // Was the caller an upcall?
-    jz    1f                      // Return if caller was upcall.
-    lea   (%esp, %eax), %edx      // edx == bottom of caller's frame.
-    mov   %edx, %esp              // Remove frame.
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME
-    call SYMBOL(artEnterInterpreterFromDeoptimize) // Enter interpreter, callee-save ends stack fragment.
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
-1:
-    ret                           // Return to caller.
+    call SYMBOL(artDeoptimize)    // artDeoptimize(Thread*, SP)
+    int3                          // Unreachable.
 END_FUNCTION art_quick_deoptimize
 
     /*
diff --git a/src/reflection.cc b/src/reflection.cc
index 73a8a53..467575c 100644
--- a/src/reflection.cc
+++ b/src/reflection.cc
@@ -17,6 +17,7 @@
 #include "reflection.h"
 
 #include "class_linker.h"
+#include "common_throws.h"
 #include "dex_file-inl.h"
 #include "invoke_arg_array_builder.h"
 #include "jni_internal.h"
@@ -64,9 +65,9 @@
   uint32_t classes_size = classes == NULL ? 0 : classes->Size();
   uint32_t arg_count = (objects != NULL) ? objects->GetLength() : 0;
   if (arg_count != classes_size) {
-    soa.Self()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;",
-        "wrong number of arguments; expected %d, got %d",
-        classes_size, arg_count);
+    ThrowIllegalArgumentException(NULL,
+                                  StringPrintf("Wrong number of arguments; expected %d, got %d",
+                                               classes_size, arg_count).c_str());
     return NULL;
   }
 
@@ -103,23 +104,23 @@
 }
 
 bool VerifyObjectInClass(mirror::Object* o, mirror::Class* c) {
-  const char* exception = NULL;
   if (o == NULL) {
-    exception = "Ljava/lang/NullPointerException;";
+    ThrowNullPointerException(NULL, "null receiver");
+    return false;
   } else if (!o->InstanceOf(c)) {
-    exception = "Ljava/lang/IllegalArgumentException;";
-  }
-  if (exception != NULL) {
     std::string expected_class_name(PrettyDescriptor(c));
     std::string actual_class_name(PrettyTypeOf(o));
-    Thread::Current()->ThrowNewExceptionF(exception, "expected receiver of type %s, but got %s",
-                                          expected_class_name.c_str(), actual_class_name.c_str());
+    ThrowIllegalArgumentException(NULL,
+                                  StringPrintf("Expected receiver of type %s, but got %s",
+                                               expected_class_name.c_str(),
+                                               actual_class_name.c_str()).c_str());
     return false;
   }
   return true;
 }
 
-bool ConvertPrimitiveValue(Primitive::Type srcType, Primitive::Type dstType,
+bool ConvertPrimitiveValue(const ThrowLocation* throw_location, bool unbox_for_result,
+                           Primitive::Type srcType, Primitive::Type dstType,
                            const JValue& src, JValue& dst) {
   CHECK(srcType != Primitive::kPrimNot && dstType != Primitive::kPrimNot);
   switch (dstType) {
@@ -196,10 +197,18 @@
   default:
     break;
   }
-  Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;",
-                                        "invalid primitive conversion from %s to %s",
-                                        PrettyDescriptor(srcType).c_str(),
-                                        PrettyDescriptor(dstType).c_str());
+  if (!unbox_for_result) {
+    ThrowIllegalArgumentException(throw_location,
+                                  StringPrintf("Invalid primitive conversion from %s to %s",
+                                               PrettyDescriptor(srcType).c_str(),
+                                               PrettyDescriptor(dstType).c_str()).c_str());
+  } else {
+    ThrowClassCastException(throw_location,
+                            StringPrintf("Couldn't convert result of type %s to %s",
+                                         PrettyDescriptor(srcType).c_str(),
+                                         PrettyDescriptor(dstType).c_str()
+                                         ).c_str());
+  }
   return false;
 }
 
@@ -271,32 +280,48 @@
   return "result";
 }
 
-static bool UnboxPrimitive(mirror::Object* o, mirror::Class* dst_class, JValue& unboxed_value,
+static bool UnboxPrimitive(const ThrowLocation* throw_location, mirror::Object* o,
+                           mirror::Class* dst_class, JValue& unboxed_value,
                            mirror::AbstractMethod* m, int index, mirror::Field* f)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  bool unbox_for_result = (f == NULL) && (index == -1);
   if (!dst_class->IsPrimitive()) {
-    if (o != NULL && !o->InstanceOf(dst_class)) {
-      Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;",
-                                            "%s has type %s, got %s",
-                                            UnboxingFailureKind(m, index, f).c_str(),
-                                            PrettyDescriptor(dst_class).c_str(),
-                                            PrettyTypeOf(o).c_str());
+    if (UNLIKELY(o != NULL && !o->InstanceOf(dst_class))) {
+      if (!unbox_for_result) {
+        ThrowIllegalArgumentException(throw_location,
+                                      StringPrintf("%s has type %s, got %s",
+                                                   UnboxingFailureKind(m, index, f).c_str(),
+                                                   PrettyDescriptor(dst_class).c_str(),
+                                                   PrettyTypeOf(o).c_str()).c_str());
+      } else {
+        ThrowClassCastException(throw_location,
+                                StringPrintf("Couldn't convert result of type %s to %s",
+                                             PrettyTypeOf(o).c_str(),
+                                             PrettyDescriptor(dst_class).c_str()
+                                             ).c_str());
+      }
       return false;
     }
     unboxed_value.SetL(o);
     return true;
-  } else if (dst_class->GetPrimitiveType() == Primitive::kPrimVoid) {
-    Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;",
-                                          "can't unbox %s to void",
-                                          UnboxingFailureKind(m, index, f).c_str());
+  }
+  if (UNLIKELY(dst_class->GetPrimitiveType() == Primitive::kPrimVoid)) {
+    ThrowIllegalArgumentException(throw_location,
+                                  StringPrintf("Can't unbox %s to void",
+                                               UnboxingFailureKind(m, index, f).c_str()).c_str());
     return false;
   }
-
-  if (o == NULL) {
-    Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;",
-                                          "%s has type %s, got null",
-                                          UnboxingFailureKind(m, index, f).c_str(),
-                                          PrettyDescriptor(dst_class).c_str());
+  if (UNLIKELY(o == NULL)) {
+    if (!unbox_for_result) {
+      ThrowIllegalArgumentException(throw_location,
+                                    StringPrintf("%s has type %s, got null",
+                                                 UnboxingFailureKind(m, index, f).c_str(),
+                                                 PrettyDescriptor(dst_class).c_str()).c_str());
+    } else {
+      ThrowNullPointerException(throw_location,
+                                StringPrintf("Expected to unbox a '%s' primitive type but was returned null",
+                                             PrettyDescriptor(dst_class).c_str()).c_str());
+    }
     return false;
   }
 
@@ -330,32 +355,35 @@
     src_class = class_linker->FindPrimitiveClass('S');
     boxed_value.SetS(primitive_field->GetShort(o));
   } else {
-    Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;",
-                                          "%s has type %s, got %s",
-                                          UnboxingFailureKind(m, index, f).c_str(),
-                                          PrettyDescriptor(dst_class).c_str(),
-                                          PrettyDescriptor(src_descriptor.c_str()).c_str());
+    ThrowIllegalArgumentException(throw_location,
+                                  StringPrintf("%s has type %s, got %s",
+                                               UnboxingFailureKind(m, index, f).c_str(),
+                                               PrettyDescriptor(dst_class).c_str(),
+                                               PrettyDescriptor(src_descriptor.c_str()).c_str()
+                                               ).c_str());
     return false;
   }
 
-  return ConvertPrimitiveValue(src_class->GetPrimitiveType(), dst_class->GetPrimitiveType(),
+  return ConvertPrimitiveValue(throw_location, unbox_for_result,
+                               src_class->GetPrimitiveType(), dst_class->GetPrimitiveType(),
                                boxed_value, unboxed_value);
 }
 
 bool UnboxPrimitiveForArgument(mirror::Object* o, mirror::Class* dst_class, JValue& unboxed_value,
                                mirror::AbstractMethod* m, size_t index) {
   CHECK(m != NULL);
-  return UnboxPrimitive(o, dst_class, unboxed_value, m, index, NULL);
+  return UnboxPrimitive(NULL, o, dst_class, unboxed_value, m, index, NULL);
 }
 
 bool UnboxPrimitiveForField(mirror::Object* o, mirror::Class* dst_class, JValue& unboxed_value,
                             mirror::Field* f) {
   CHECK(f != NULL);
-  return UnboxPrimitive(o, dst_class, unboxed_value, NULL, -1, f);
+  return UnboxPrimitive(NULL, o, dst_class, unboxed_value, NULL, -1, f);
 }
 
-bool UnboxPrimitiveForResult(mirror::Object* o, mirror::Class* dst_class, JValue& unboxed_value) {
-  return UnboxPrimitive(o, dst_class, unboxed_value, NULL, -1, NULL);
+bool UnboxPrimitiveForResult(const ThrowLocation& throw_location, mirror::Object* o,
+                             mirror::Class* dst_class, JValue& unboxed_value) {
+  return UnboxPrimitive(&throw_location, o, dst_class, unboxed_value, NULL, -1, NULL);
 }
 
 }  // namespace art
diff --git a/src/reflection.h b/src/reflection.h
index 8f32243..e9f4e08 100644
--- a/src/reflection.h
+++ b/src/reflection.h
@@ -29,6 +29,7 @@
 }  // namespace mirror
 union JValue;
 class ScopedObjectAccess;
+class ThrowLocation;
 
 mirror::Object* BoxPrimitive(Primitive::Type src_class, const JValue& value)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -38,11 +39,13 @@
 bool UnboxPrimitiveForField(mirror::Object* o, mirror::Class* dst_class, JValue& unboxed_value,
                             mirror::Field* f)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-bool UnboxPrimitiveForResult(mirror::Object* o, mirror::Class* dst_class, JValue& unboxed_value)
+bool UnboxPrimitiveForResult(const ThrowLocation& throw_location, mirror::Object* o,
+                             mirror::Class* dst_class, JValue& unboxed_value)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-bool ConvertPrimitiveValue(Primitive::Type src_class, Primitive::Type dst_class, const JValue& src,
-                           JValue& dst)
+bool ConvertPrimitiveValue(const ThrowLocation* throw_location, bool unbox_for_result,
+                           Primitive::Type src_class, Primitive::Type dst_class,
+                           const JValue& src, JValue& dst)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 jobject InvokeMethod(const ScopedObjectAccess& soa, jobject method, jobject receiver, jobject args)
diff --git a/src/runtime.cc b/src/runtime.cc
index 3e9cd8e..23a7309 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -43,6 +43,7 @@
 #include "jni_internal.h"
 #include "mirror/abstract_method-inl.h"
 #include "mirror/array.h"
+#include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
 #include "mirror/field.h"
 #include "mirror/field-inl.h"
@@ -97,7 +98,7 @@
       stats_enabled_(false),
       method_trace_(0),
       method_trace_file_size_(0),
-      instrumentation_(NULL),
+      instrumentation_(),
       use_compile_time_class_path_(false),
       main_thread_group_(NULL),
       system_thread_group_(NULL) {
@@ -119,11 +120,7 @@
     }
     shutting_down_ = true;
   }
-
-  if (IsMethodTracingActive()) {
-    Trace::Shutdown();
-  }
-  delete instrumentation_;
+  Trace::Shutdown();
 
   // Make sure to let the GC complete if it is running.
   heap_->WaitForConcurrentGcToComplete(self);
@@ -171,8 +168,11 @@
       os << "Aborting thread:\n";
       self->Dump(os);
       if (self->IsExceptionPending()) {
-        os << "Pending " << PrettyTypeOf(self->GetException()) << " on thread:\n"
-           << self->GetException()->Dump();
+        ThrowLocation throw_location;
+        mirror::Throwable* exception = self->GetException(&throw_location);
+        os << "Pending exception " << PrettyTypeOf(exception)
+            << " thrown by '" << throw_location.Dump() << "\n"
+            << exception->Dump();
       }
     }
     DumpAllThreads(os, self);
@@ -652,9 +652,9 @@
 
   // Pre-allocate an OutOfMemoryError for the double-OOME case.
   Thread* self = Thread::Current();
-  self->ThrowNewException("Ljava/lang/OutOfMemoryError;",
+  self->ThrowNewException(ThrowLocation(), "Ljava/lang/OutOfMemoryError;",
                           "OutOfMemoryError thrown while trying to throw OutOfMemoryError; no stack available");
-  pre_allocated_OutOfMemoryError_ = self->GetException();
+  pre_allocated_OutOfMemoryError_ = self->GetException(NULL);
   self->ClearException();
 
   // Restore main thread state to kNative as expected by native code.
@@ -794,7 +794,6 @@
 
   is_compiler_ = options->is_compiler_;
   is_zygote_ = options->is_zygote_;
-  interpreter_only_ = options->interpreter_only_;
   is_concurrent_gc_enabled_ = options->is_concurrent_gc_enabled_;
 
   vfprintf_ = options->hook_vfprintf_;
@@ -809,6 +808,10 @@
   intern_table_ = new InternTable;
 
 
+  if (options->interpreter_only_) {
+    GetInstrumentation()->ForceInterpretOnly();
+  }
+
   heap_ = new Heap(options->heap_initial_size_,
                    options->heap_growth_limit_,
                    options->heap_min_free_,
@@ -1196,28 +1199,6 @@
   callee_save_methods_[type] = method;
 }
 
-void Runtime::EnableMethodTracing(Trace* trace) {
-  CHECK(!IsMethodTracingActive());
-  if (instrumentation_ == NULL) {
-    instrumentation_ = new Instrumentation();
-  }
-  instrumentation_->SetTrace(trace);
-}
-
-void Runtime::DisableMethodTracing() {
-  CHECK(IsMethodTracingActive());
-  instrumentation_->RemoveTrace();
-}
-
-bool Runtime::IsMethodTracingActive() const {
-  return instrumentation_ != NULL && instrumentation_->GetTrace() != NULL;
-}
-
-Instrumentation* Runtime::GetInstrumentation() const {
-  CHECK(IsMethodTracingActive());
-  return instrumentation_;
-}
-
 const std::vector<const DexFile*>& Runtime::GetCompileTimeClassPath(jobject class_loader) {
   if (class_loader == NULL) {
     return GetClassLinker()->GetBootClassPath();
diff --git a/src/runtime.h b/src/runtime.h
index f8788ad..67c8e36 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -30,6 +30,7 @@
 #include "globals.h"
 #include "heap.h"
 #include "instruction_set.h"
+#include "instrumentation.h"
 #include "jobject_comparator.h"
 #include "locks.h"
 #include "root_visitor.h"
@@ -49,7 +50,6 @@
 class ClassLinker;
 class DexFile;
 class Heap;
-class Instrumentation;
 class InternTable;
 struct JavaVMExt;
 class MonitorList;
@@ -112,10 +112,6 @@
     return is_zygote_;
   }
 
-  bool InterpreterOnly() const {
-    return interpreter_only_;
-  }
-
   bool IsConcurrentGcEnabled() const {
     return is_concurrent_gc_enabled_;
   }
@@ -234,8 +230,7 @@
   void DirtyRoots();
 
   // Visit all the roots.
-  void VisitRoots(RootVisitor* visitor, void* arg)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void VisitRoots(RootVisitor* visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Visit all of the roots we can do safely do concurrently.
   void VisitConcurrentRoots(RootVisitor* visitor, void* arg);
@@ -350,10 +345,9 @@
   bool InitZygote();
   void DidForkFromZygote();
 
-  void EnableMethodTracing(Trace* trace);
-  void DisableMethodTracing();
-  bool IsMethodTracingActive() const;
-  Instrumentation* GetInstrumentation() const;
+  instrumentation::Instrumentation* GetInstrumentation() {
+    return &instrumentation_;
+  }
 
   bool UseCompileTimeClassPath() const {
     return use_compile_time_class_path_;
@@ -383,7 +377,6 @@
 
   bool is_compiler_;
   bool is_zygote_;
-  bool interpreter_only_;
   bool is_concurrent_gc_enabled_;
 
   // The host prefix is used during cross compilation. It is removed
@@ -466,7 +459,7 @@
   bool method_trace_;
   std::string method_trace_file_;
   size_t method_trace_file_size_;
-  Instrumentation* instrumentation_;
+  instrumentation::Instrumentation instrumentation_;
 
   typedef SafeMap<jobject, std::vector<const DexFile*>, JobjectComparator> CompileTimeClassPaths;
   CompileTimeClassPaths compile_time_class_paths_;
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index 5b2c58c..b601f8c 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -101,16 +101,16 @@
 namespace art {
 
 // Helper function to allocate array for FILLED_NEW_ARRAY.
-mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::AbstractMethod* method,
+mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::AbstractMethod* referrer,
                                           int32_t component_count, Thread* self,
                                           bool access_check) {
   if (UNLIKELY(component_count < 0)) {
-    self->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d", component_count);
+    ThrowNegativeArraySizeException(component_count);
     return NULL;  // Failure
   }
-  mirror::Class* klass = method->GetDexCacheResolvedTypes()->Get(type_idx);
+  mirror::Class* klass = referrer->GetDexCacheResolvedTypes()->Get(type_idx);
   if (UNLIKELY(klass == NULL)) {  // Not in dex cache so try to resolve
-    klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method);
+    klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, referrer);
     if (klass == NULL) {  // Error
       DCHECK(self->IsExceptionPending());
       return NULL;  // Failure
@@ -118,20 +118,21 @@
   }
   if (UNLIKELY(klass->IsPrimitive() && !klass->IsPrimitiveInt())) {
     if (klass->IsPrimitiveLong() || klass->IsPrimitiveDouble()) {
-      self->ThrowNewExceptionF("Ljava/lang/RuntimeException;",
-                               "Bad filled array request for type %s",
-                                PrettyDescriptor(klass).c_str());
+      ThrowRuntimeException("Bad filled array request for type %s",
+                            PrettyDescriptor(klass).c_str());
     } else {
-      self->ThrowNewExceptionF("Ljava/lang/InternalError;",
+      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+      DCHECK(throw_location.GetMethod() == referrer);
+      self->ThrowNewExceptionF(throw_location, "Ljava/lang/InternalError;",
                                "Found type %s; filled-new-array not implemented for anything but \'int\'",
                                PrettyDescriptor(klass).c_str());
     }
     return NULL;  // Failure
   } else {
     if (access_check) {
-      mirror::Class* referrer = method->GetDeclaringClass();
-      if (UNLIKELY(!referrer->CanAccess(klass))) {
-        ThrowIllegalAccessErrorClass(referrer, klass);
+      mirror::Class* referrer_klass = referrer->GetDeclaringClass();
+      if (UNLIKELY(!referrer_klass->CanAccess(klass))) {
+        ThrowIllegalAccessErrorClass(referrer_klass, klass);
         return NULL;  // Failure
       }
     }
@@ -194,7 +195,9 @@
       FieldHelper fh(resolved_field);
       if (UNLIKELY(fh.IsPrimitiveType() != is_primitive ||
                    fh.FieldSize() != expected_size)) {
-        self->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;",
+        ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+        DCHECK(throw_location.GetMethod() == referrer);
+        self->ThrowNewExceptionF(throw_location, "Ljava/lang/NoSuchFieldError;",
                                  "Attempted read of %zd-bit %s on field '%s'",
                                  expected_size * (32 / sizeof(int32_t)),
                                  is_primitive ? "primitive" : "non-primitive",
@@ -232,7 +235,9 @@
   } else if (UNLIKELY(this_object == NULL && type != kStatic)) {
     // Maintain interpreter-like semantics where NullPointerException is thrown
     // after potential NoSuchMethodError from class linker.
-    ThrowNullPointerExceptionForMethodAccess(referrer, method_idx, type);
+    ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+    DCHECK(referrer == throw_location.GetMethod());
+    ThrowNullPointerExceptionForMethodAccess(throw_location, method_idx, type);
     return NULL;  // Failure.
   } else {
     if (!access_check) {
@@ -320,7 +325,7 @@
           // Behavior to agree with that of the verifier.
           MethodHelper mh(resolved_method);
           ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(), mh.GetName(),
-                                 mh.GetSignature(), referrer);
+                                 mh.GetSignature());
           return NULL;  // Failure.
         }
       }
@@ -363,10 +368,12 @@
 
 void ThrowStackOverflowError(Thread* self) {
   CHECK(!self->IsHandlingStackOverflow()) << "Recursive stack overflow.";
-  // Remove extra entry pushed onto second stack during method tracing.
-  if (Runtime::Current()->IsMethodTracingActive()) {
-    InstrumentationMethodUnwindFromCode(self);
+
+  if (Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) {
+    // Remove extra entry pushed onto second stack during method tracing.
+    Runtime::Current()->GetInstrumentation()->PopMethodForUnwind(self, false);
   }
+
   self->SetStackEndForStackOverflow();  // Allow space on the stack for constructor to execute.
   JNIEnvExt* env = self->GetJniEnv();
   std::string msg("stack size ");
@@ -430,33 +437,36 @@
                                    invocation_args);
 
   // Unbox result and handle error conditions.
-  if (!soa.Self()->IsExceptionPending()) {
-    if (shorty[0] == 'V' || result == NULL) {
+  if (LIKELY(!soa.Self()->IsExceptionPending())) {
+    if (shorty[0] == 'V' || (shorty[0] == 'L' && result == NULL)) {
       // Do nothing.
       return zero;
     } else {
-      JValue result_unboxed;
-      MethodHelper mh(soa.Decode<mirror::AbstractMethod*>(interface_method_jobj));
-      mirror::Class* result_type = mh.GetReturnType();
       mirror::Object* result_ref = soa.Decode<mirror::Object*>(result);
-      bool unboxed_okay = UnboxPrimitiveForResult(result_ref, result_type, result_unboxed);
-      if (!unboxed_okay) {
-        // UnboxPrimitiveForResult creates an IllegalArgumentException. Discard and create a
-        // meaningful ClassCastException.
+      mirror::Object* rcvr = soa.Decode<mirror::Object*>(rcvr_jobj);
+      mirror::AbstractMethod* interface_method =
+          soa.Decode<mirror::AbstractMethod*>(interface_method_jobj);
+      mirror::Class* result_type = MethodHelper(interface_method).GetReturnType();
+      mirror::AbstractMethod* proxy_method;
+      if (interface_method->GetDeclaringClass()->IsInterface()) {
+        proxy_method = rcvr->GetClass()->FindVirtualMethodForInterface(interface_method);
+      } else {
+        // Proxy dispatch to a method defined in Object.
+        DCHECK(interface_method->GetDeclaringClass()->IsObjectClass());
+        proxy_method = interface_method;
+      }
+      ThrowLocation throw_location(rcvr, proxy_method, -1);
+      JValue result_unboxed;
+      if (!UnboxPrimitiveForResult(throw_location, result_ref, result_type, result_unboxed)) {
         DCHECK(soa.Self()->IsExceptionPending());
-        soa.Self()->ClearException();
-        soa.Self()->ThrowNewException("Ljava/lang/ClassCastException;",
-                                      StringPrintf("Couldn't convert result of type %s to %s",
-                                                   PrettyTypeOf(result_ref).c_str(),
-                                                   PrettyDescriptor(result_type).c_str()
-                                      ).c_str());
+        return zero;
       }
       return result_unboxed;
     }
   } else {
     // In the case of checked exceptions that aren't declared, the exception must be wrapped by
     // a UndeclaredThrowableException.
-    mirror::Throwable* exception = soa.Self()->GetException();
+    mirror::Throwable* exception = soa.Self()->GetException(NULL);
     if (exception->IsCheckedException()) {
       mirror::Object* rcvr = soa.Decode<mirror::Object*>(rcvr_jobj);
       mirror::SynthesizedProxyClass* proxy_class =
@@ -482,7 +492,9 @@
         declares_exception = declared_exception->IsAssignableFrom(exception_class);
       }
       if (!declares_exception) {
-        soa.Self()->ThrowNewWrappedException("Ljava/lang/reflect/UndeclaredThrowableException;",
+        ThrowLocation throw_location(rcvr, proxy_method, -1);
+        soa.Self()->ThrowNewWrappedException(throw_location,
+                                             "Ljava/lang/reflect/UndeclaredThrowableException;",
                                              NULL);
       }
     }
diff --git a/src/runtime_support.h b/src/runtime_support.h
index 89026c1..c7eb957 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -68,7 +68,8 @@
   }
   if (access_check) {
     if (UNLIKELY(!klass->IsInstantiable())) {
-      self->ThrowNewException("Ljava/lang/InstantiationError;",
+      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+      self->ThrowNewException(throw_location, "Ljava/lang/InstantiationError;",
                               PrettyDescriptor(klass).c_str());
       return NULL;  // Failure
     }
@@ -95,7 +96,7 @@
                                                 Thread* self, bool access_check)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   if (UNLIKELY(component_count < 0)) {
-    self->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d", component_count);
+    ThrowNegativeArraySizeException(component_count);
     return NULL;  // Failure
   }
   mirror::Class* klass = method->GetDexCacheResolvedTypes()->Get(type_idx);
@@ -257,8 +258,9 @@
     UNLOCK_FUNCTION(monitor_lock_) {
   // Save any pending exception over monitor exit call.
   mirror::Throwable* saved_exception = NULL;
+  ThrowLocation saved_throw_location;
   if (UNLIKELY(self->IsExceptionPending())) {
-    saved_exception = self->GetException();
+    saved_exception = self->GetException(&saved_throw_location);
     self->ClearException();
   }
   // Decode locked object and unlock, before popping local references.
@@ -267,11 +269,11 @@
     LOG(FATAL) << "Synchronized JNI code returning with an exception:\n"
         << saved_exception->Dump()
         << "\nEncountered second exception during implicit MonitorExit:\n"
-        << self->GetException()->Dump();
+        << self->GetException(NULL)->Dump();
   }
   // Restore pending exception.
   if (saved_exception != NULL) {
-    self->SetException(saved_exception);
+    self->SetException(saved_throw_location, saved_exception);
   }
 }
 
@@ -280,14 +282,12 @@
   if (o == NULL) {
     return;
   }
+  mirror::AbstractMethod* m = self->GetCurrentMethod(NULL);
   if (o == kInvalidIndirectRefObject) {
-    JniAbortF(NULL, "invalid reference returned from %s",
-              PrettyMethod(self->GetCurrentMethod()).c_str());
+    JniAbortF(NULL, "invalid reference returned from %s", PrettyMethod(m).c_str());
   }
   // Make sure that the result is an instance of the type this method was expected to return.
-  mirror::AbstractMethod* m = self->GetCurrentMethod();
-  MethodHelper mh(m);
-  mirror::Class* return_type = mh.GetReturnType();
+  mirror::Class* return_type = MethodHelper(m).GetReturnType();
 
   if (!o->InstanceOf(return_type)) {
     JniAbortF(NULL, "attempt to return an instance of %s from %s",
diff --git a/src/stack.cc b/src/stack.cc
index 66051f2..8690a36 100644
--- a/src/stack.cc
+++ b/src/stack.cc
@@ -24,9 +24,28 @@
 #include "mirror/object_array-inl.h"
 #include "object_utils.h"
 #include "thread_list.h"
+#include "throw_location.h"
 
 namespace art {
 
+mirror::Object* ShadowFrame::GetThisObject() const {
+  mirror::AbstractMethod* m = GetMethod();
+  if (m->IsStatic()) {
+    return NULL;
+  } else if (m->IsNative()) {
+    return GetVRegReference(0);
+  } else {
+    const DexFile::CodeItem* code_item = MethodHelper(m).GetCodeItem();
+    CHECK(code_item != NULL) << PrettyMethod(m);
+    uint16_t reg = code_item->registers_size_ - code_item->ins_size_;
+    return GetVRegReference(reg);
+  }
+}
+
+ThrowLocation ShadowFrame::GetCurrentLocationForThrow() const {
+  return ThrowLocation(GetThisObject(), GetMethod(), GetDexPC());
+}
+
 size_t ManagedStack::NumJniShadowFrameReferences() const {
   size_t count = 0;
   for (const ManagedStack* current_fragment = this; current_fragment != NULL;
@@ -59,7 +78,7 @@
     : thread_(thread), cur_shadow_frame_(NULL),
       cur_quick_frame_(NULL), cur_quick_frame_pc_(0), num_frames_(0), cur_depth_(0),
       context_(context) {
-  DCHECK(thread == Thread::Current() || thread->IsSuspended());
+  DCHECK(thread == Thread::Current() || thread->IsSuspended()) << *thread;
 }
 
 uint32_t StackVisitor::GetDexPc() const {
@@ -72,6 +91,33 @@
   }
 }
 
+mirror::Object* StackVisitor::GetThisObject() const {
+  mirror::AbstractMethod* m = GetMethod();
+  if (m->IsStatic()) {
+    return NULL;
+  } else if (m->IsNative()) {
+    if (cur_quick_frame_ != NULL) {
+      StackIndirectReferenceTable* sirt =
+          reinterpret_cast<StackIndirectReferenceTable*>(
+              reinterpret_cast<char*>(cur_quick_frame_) +
+              m->GetSirtOffsetInBytes());
+      return sirt->GetReference(0);
+    } else {
+      return cur_shadow_frame_->GetVRegReference(0);
+    }
+  } else {
+    const DexFile::CodeItem* code_item = MethodHelper(m).GetCodeItem();
+    if (code_item == NULL) {
+      UNIMPLEMENTED(ERROR) << "Failed to determine this object of abstract or proxy method"
+          << PrettyMethod(m);
+      return NULL;
+    } else {
+      uint16_t reg = code_item->registers_size_ - code_item->ins_size_;
+      return reinterpret_cast<mirror::Object*>(GetVReg(m, reg, kReferenceVReg));
+    }
+  }
+}
+
 size_t StackVisitor::GetNativePcOffset() const {
   DCHECK(!IsShadowFrame());
   return GetMethod()->NativePcOffset(cur_quick_frame_pc_);
@@ -198,7 +244,7 @@
   return result;
 }
 
-InstrumentationStackFrame StackVisitor::GetInstrumentationStackFrame(uint32_t depth) const {
+instrumentation::InstrumentationStackFrame StackVisitor::GetInstrumentationStackFrame(uint32_t depth) const {
   return thread_->GetInstrumentationStack()->at(depth);
 }
 
@@ -221,9 +267,8 @@
 
 void StackVisitor::WalkStack(bool include_transitions) {
   DCHECK(thread_ == Thread::Current() || thread_->IsSuspended());
-  const std::deque<InstrumentationStackFrame>* instrumentation_stack =
-      thread_->GetInstrumentationStack();
-  bool method_tracing_active = instrumentation_stack != NULL;
+  CHECK_EQ(cur_depth_, 0U);
+  bool exit_stubs_installed = Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled();
   uint32_t instrumentation_stack_depth = 0;
   for (const ManagedStack* current_fragment = thread_->GetManagedStack(); current_fragment != NULL;
        current_fragment = current_fragment->GetLink()) {
@@ -235,6 +280,7 @@
       DCHECK(current_fragment->GetTopShadowFrame() == NULL);
       mirror::AbstractMethod* method = *cur_quick_frame_;
       while (method != NULL) {
+        DCHECK(cur_quick_frame_pc_ != GetInstrumentationExitPc());
         SanityCheckFrame();
         bool should_continue = VisitFrame();
         if (UNLIKELY(!should_continue)) {
@@ -248,16 +294,24 @@
         size_t return_pc_offset = method->GetReturnPcOffsetInBytes();
         byte* return_pc_addr = reinterpret_cast<byte*>(cur_quick_frame_) + return_pc_offset;
         uintptr_t return_pc = *reinterpret_cast<uintptr_t*>(return_pc_addr);
-        if (UNLIKELY(method_tracing_active)) {
+        if (UNLIKELY(exit_stubs_installed)) {
           // While profiling, the return pc is restored from the side stack, except when walking
           // the stack for an exception where the side stack will be unwound in VisitFrame.
-          // TODO: stop using include_transitions as a proxy for is this the catch block visitor.
-          if (GetInstrumentationExitPc() == return_pc && !include_transitions) {
-            // TODO: unify trace and managed stack.
-            InstrumentationStackFrame instrumentation_frame = GetInstrumentationStackFrame(instrumentation_stack_depth);
+          if (GetInstrumentationExitPc() == return_pc) {
+            instrumentation::InstrumentationStackFrame instrumentation_frame =
+                GetInstrumentationStackFrame(instrumentation_stack_depth);
             instrumentation_stack_depth++;
-            CHECK(instrumentation_frame.method_ == GetMethod()) << "Excepted: " << PrettyMethod(method)
+            if (instrumentation_frame.method_ != GetMethod()) {
+              LOG(FATAL)  << "Expected: " << PrettyMethod(instrumentation_frame.method_)
                 << " Found: " << PrettyMethod(GetMethod());
+            }
+            if (num_frames_ != 0) {
+              // Check agreement of frame Ids only if num_frames_ is computed to avoid infinite
+              // recursion.
+              CHECK(instrumentation_frame.frame_id_ == GetFrameId())
+                    << "Expected: " << instrumentation_frame.frame_id_
+                    << " Found: " << GetFrameId();
+            }
             return_pc = instrumentation_frame.return_pc_;
           }
         }
@@ -278,13 +332,16 @@
         cur_shadow_frame_ = cur_shadow_frame_->GetLink();
       } while(cur_shadow_frame_ != NULL);
     }
-    cur_depth_++;
     if (include_transitions) {
       bool should_continue = VisitFrame();
       if (!should_continue) {
         return;
       }
     }
+    cur_depth_++;
+  }
+  if (num_frames_ != 0) {
+    CHECK_EQ(cur_depth_, num_frames_);
   }
 }
 
diff --git a/src/stack.h b/src/stack.h
index e0cb28e..eb187b2 100644
--- a/src/stack.h
+++ b/src/stack.h
@@ -168,6 +168,10 @@
     return method_;
   }
 
+  mirror::Object* GetThisObject() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  ThrowLocation GetCurrentLocationForThrow() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   void SetMethod(mirror::AbstractMethod* method) {
     DCHECK_NE(method, static_cast<void*>(NULL));
     method_ = method;
@@ -368,6 +372,8 @@
 
   uint32_t GetDexPc() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  mirror::Object* GetThisObject() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   size_t GetNativePcOffset() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   uintptr_t* CalleeSaveAddress(int num, size_t frame_size) const {
@@ -383,7 +389,7 @@
 
   // Returns the height of the stack in the managed stack frames, including transitions.
   size_t GetFrameHeight() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetNumFrames() - cur_depth_;
+    return GetNumFrames() - cur_depth_ - 1;
   }
 
   // Returns a frame ID for JDWP use, starting from 1.
@@ -503,7 +509,7 @@
 
  private:
 
-  InstrumentationStackFrame GetInstrumentationStackFrame(uint32_t depth) const;
+  instrumentation::InstrumentationStackFrame GetInstrumentationStackFrame(uint32_t depth) const;
 
   void SanityCheckFrame() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
diff --git a/src/thread.cc b/src/thread.cc
index 9c58b6d..2c955b1 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -61,6 +61,7 @@
 #include "thread_list.h"
 #include "utils.h"
 #include "verifier/dex_gc_map.h"
+#include "verifier/method_verifier.h"
 #include "well_known_classes.h"
 
 namespace art {
@@ -92,24 +93,16 @@
   InitEntryPoints(&entrypoints_);
 }
 
-void Thread::SetDebuggerUpdatesEnabled(bool enabled) {
-#if !defined(ART_USE_PORTABLE_COMPILER)
-  ChangeDebuggerEntryPoint(&entrypoints_, enabled);
-#else
-  UNIMPLEMENTED(FATAL);
-#endif
+void Thread::SetDeoptimizationShadowFrame(ShadowFrame* sf) {
+  deoptimization_shadow_frame_ = sf;
 }
 
-
-void Thread::SetDeoptimizationShadowFrame(ShadowFrame* sf, const JValue& ret_val) {
-  CHECK(sf != NULL);
-  deoptimization_shadow_frame_ = sf;
+void Thread::SetDeoptimizationReturnValue(const JValue& ret_val) {
   deoptimization_return_value_.SetJ(ret_val.GetJ());
 }
 
 ShadowFrame* Thread::GetAndClearDeoptimizationShadowFrame(JValue* ret_val) {
   ShadowFrame* sf = deoptimization_shadow_frame_;
-  DCHECK(sf != NULL);
   deoptimization_shadow_frame_ = NULL;
   ret_val->SetJ(deoptimization_return_value_.GetJ());
   return sf;
@@ -327,9 +320,6 @@
   InitFunctionPointers();
   InitCardTable();
   InitTid();
-  if (Runtime::Current()->InterpreterOnly()) {
-    AtomicSetFlag(kEnterInterpreter);
-  }
   // Set pthread_self_ ahead of pthread_setspecific, that makes Thread::Current function, this
   // avoids pthread_self_ ever being invalid when discovered from Thread::Current().
   pthread_self_ = pthread_self();
@@ -864,7 +854,7 @@
   // We don't just check kNative because native methods will be in state kSuspended if they're
   // calling back into the VM, or kBlocked if they're blocked on a monitor, or one of the
   // thread-startup states if it's early enough in their life cycle (http://b/7432159).
-  mirror::AbstractMethod* current_method = thread->GetCurrentMethod();
+  mirror::AbstractMethod* current_method = thread->GetCurrentMethod(NULL);
   return current_method != NULL && current_method->IsNative();
 }
 
@@ -948,7 +938,8 @@
       throwing_OutOfMemoryError_(false),
       debug_suspend_count_(0),
       debug_invoke_req_(new DebugInvokeReq),
-      instrumentation_stack_(new std::deque<InstrumentationStackFrame>),
+      deoptimization_shadow_frame_(NULL),
+      instrumentation_stack_(new std::deque<instrumentation::InstrumentationStackFrame>),
       name_(new std::string(kThreadNameDuringStartup)),
       daemon_(daemon),
       pthread_self_(0),
@@ -975,7 +966,7 @@
 void Thread::AssertNoPendingException() const {
   if (UNLIKELY(IsExceptionPending())) {
     ScopedObjectAccess soa(Thread::Current());
-    mirror::Throwable* exception = GetException();
+    mirror::Throwable* exception = GetException(NULL);
     LOG(FATAL) << "No pending exception expected: " << exception->Dump();
   }
 }
@@ -1427,82 +1418,131 @@
   return result;
 }
 
-void Thread::ThrowNewExceptionF(const char* exception_class_descriptor, const char* fmt, ...) {
+void Thread::ThrowNewExceptionF(const ThrowLocation& throw_location,
+                                const char* exception_class_descriptor, const char* fmt, ...) {
   va_list args;
   va_start(args, fmt);
-  ThrowNewExceptionV(exception_class_descriptor, fmt, args);
+  ThrowNewExceptionV(throw_location, exception_class_descriptor,
+                     fmt, args);
   va_end(args);
 }
 
-void Thread::ThrowNewExceptionV(const char* exception_class_descriptor, const char* fmt, va_list ap) {
+void Thread::ThrowNewExceptionV(const ThrowLocation& throw_location,
+                                const char* exception_class_descriptor,
+                                const char* fmt, va_list ap) {
   std::string msg;
   StringAppendV(&msg, fmt, ap);
-  ThrowNewException(exception_class_descriptor, msg.c_str());
+  ThrowNewException(throw_location, exception_class_descriptor, msg.c_str());
 }
 
-void Thread::ThrowNewException(const char* exception_class_descriptor, const char* msg) {
+void Thread::ThrowNewException(const ThrowLocation& throw_location, const char* exception_class_descriptor,
+                               const char* msg) {
   AssertNoPendingException(); // Callers should either clear or call ThrowNewWrappedException.
-  ThrowNewWrappedException(exception_class_descriptor, msg);
+  ThrowNewWrappedException(throw_location, exception_class_descriptor, msg);
 }
 
-void Thread::ThrowNewWrappedException(const char* exception_class_descriptor, const char* msg) {
-  // Convert "Ljava/lang/Exception;" into JNI-style "java/lang/Exception".
-  CHECK_EQ('L', exception_class_descriptor[0]);
-  std::string descriptor(exception_class_descriptor + 1);
-  CHECK_EQ(';', descriptor[descriptor.length() - 1]);
-  descriptor.erase(descriptor.length() - 1);
+void Thread::ThrowNewWrappedException(const ThrowLocation& throw_location,
+                                      const char* exception_class_descriptor,
+                                      const char* msg) {
+  DCHECK_EQ(this, Thread::Current());
+  // Ensure we don't forget arguments over object allocation.
+  SirtRef<mirror::Object> saved_throw_this(this, throw_location.GetThis());
+  SirtRef<mirror::AbstractMethod> saved_throw_method(this, throw_location.GetMethod());
+  // Ignore the cause throw location. TODO: should we report this as a re-throw?
+  SirtRef<mirror::Throwable> cause(this, GetException(NULL));
+  ClearException();
+  Runtime* runtime = Runtime::Current();
 
-  JNIEnv* env = GetJniEnv();
-  jobject cause = env->ExceptionOccurred();
-  env->ExceptionClear();
-
-  ScopedLocalRef<jclass> exception_class(env, env->FindClass(descriptor.c_str()));
-  if (exception_class.get() == NULL) {
-    LOG(ERROR) << "Couldn't throw new " << descriptor << " because JNI FindClass failed: "
-               << PrettyTypeOf(GetException());
+  mirror::ClassLoader* cl = NULL;
+  if (throw_location.GetMethod() != NULL) {
+    cl = throw_location.GetMethod()->GetDeclaringClass()->GetClassLoader();
+  }
+  SirtRef<mirror::Class>
+      exception_class(this, runtime->GetClassLinker()->FindClass(exception_class_descriptor, cl));
+  if (UNLIKELY(exception_class.get() == NULL)) {
     CHECK(IsExceptionPending());
+    LOG(ERROR) << "No exception class " << PrettyDescriptor(exception_class_descriptor);
     return;
   }
-  if (!Runtime::Current()->IsStarted()) {
-    // Something is trying to throw an exception without a started
-    // runtime, which is the common case in the compiler. We won't be
-    // able to invoke the constructor of the exception, so use
-    // AllocObject which will not invoke a constructor.
-    ScopedLocalRef<jthrowable> exception(
-        env, reinterpret_cast<jthrowable>(env->AllocObject(exception_class.get())));
-    if (exception.get() != NULL) {
-      ScopedObjectAccessUnchecked soa(env);
-      mirror::Throwable* t =
-          reinterpret_cast<mirror::Throwable*>(soa.Self()->DecodeJObject(exception.get()));
-      t->SetDetailMessage(mirror::String::AllocFromModifiedUtf8(soa.Self(), msg));
-      if (cause != NULL) {
-        t->SetCause(soa.Decode<mirror::Throwable*>(cause));
-      }
-      soa.Self()->SetException(t);
-    } else {
-      LOG(ERROR) << "Couldn't throw new " << descriptor << " because JNI AllocObject failed: "
-                 << PrettyTypeOf(GetException());
-      CHECK(IsExceptionPending());
+
+  if (UNLIKELY(!runtime->GetClassLinker()->EnsureInitialized(exception_class.get(), true, true))) {
+    DCHECK(IsExceptionPending());
+    return;
+  }
+  DCHECK(!runtime->IsStarted() || exception_class->IsThrowableClass());
+  SirtRef<mirror::Throwable> exception(this,
+                                down_cast<mirror::Throwable*>(exception_class->AllocObject(this)));
+
+  // Choose an appropriate constructor and set up the arguments.
+  const char* signature;
+  SirtRef<mirror::String> msg_string(this, NULL);
+  if (msg != NULL) {
+    // Ensure we remember this and the method over the String allocation.
+    msg_string.reset(mirror::String::AllocFromModifiedUtf8(this, msg));
+    if (UNLIKELY(msg_string.get() == NULL)) {
+      CHECK(IsExceptionPending());  // OOME.
+      return;
     }
-    return;
+    if (cause.get() == NULL) {
+      signature = "(Ljava/lang/String;)V";
+    } else {
+      signature = "(Ljava/lang/String;Ljava/lang/Throwable;)V";
+    }
+  } else {
+    if (cause.get() == NULL) {
+      signature = "()V";
+    } else {
+      signature = "(Ljava/lang/Throwable;)V";
+    }
   }
-  int rc = ::art::ThrowNewException(env, exception_class.get(), msg, cause);
-  if (rc != JNI_OK) {
-    LOG(ERROR) << "Couldn't throw new " << descriptor << " because JNI ThrowNew failed: "
-               << PrettyTypeOf(GetException());
-    CHECK(IsExceptionPending());
+  mirror::AbstractMethod* exception_init_method =
+      exception_class->FindDeclaredDirectMethod("<init>", signature);
+
+  CHECK(exception_init_method != NULL) << "No <init>" << signature << " in "
+      << PrettyDescriptor(exception_class_descriptor);
+
+  if (UNLIKELY(!runtime->IsStarted())) {
+    // Something is trying to throw an exception without a started runtime, which is the common
+    // case in the compiler. We won't be able to invoke the constructor of the exception, so set
+    // the exception fields directly.
+    if (msg != NULL) {
+      exception->SetDetailMessage(msg_string.get());
+    }
+    if (cause.get() != NULL) {
+      exception->SetCause(cause.get());
+    }
+    ThrowLocation gc_safe_throw_location(saved_throw_this.get(), saved_throw_method.get(),
+                                         throw_location.GetDexPc());
+    SetException(gc_safe_throw_location, exception.get());
+  } else {
+    ArgArray args("VLL", 3);
+    args.Append(reinterpret_cast<uint32_t>(exception.get()));
+    if (msg != NULL) {
+      args.Append(reinterpret_cast<uint32_t>(msg_string.get()));
+    }
+    if (cause.get() != NULL) {
+      args.Append(reinterpret_cast<uint32_t>(cause.get()));
+    }
+    JValue result;
+    exception_init_method->Invoke(this, args.GetArray(), args.GetNumBytes(), &result, 'V');
+    if (LIKELY(!IsExceptionPending())) {
+      ThrowLocation gc_safe_throw_location(saved_throw_this.get(), saved_throw_method.get(),
+                                           throw_location.GetDexPc());
+      SetException(gc_safe_throw_location, exception.get());
+    }
   }
 }
 
 void Thread::ThrowOutOfMemoryError(const char* msg) {
   LOG(ERROR) << StringPrintf("Throwing OutOfMemoryError \"%s\"%s",
       msg, (throwing_OutOfMemoryError_ ? " (recursive case)" : ""));
+  ThrowLocation throw_location = GetCurrentLocationForThrow();
   if (!throwing_OutOfMemoryError_) {
     throwing_OutOfMemoryError_ = true;
-    ThrowNewException("Ljava/lang/OutOfMemoryError;", msg);
+    ThrowNewException(throw_location, "Ljava/lang/OutOfMemoryError;", msg);
   } else {
     Dump(LOG(ERROR)); // The pre-allocated OOME has no stack, so help out and log one.
-    SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryError());
+    SetException(throw_location, Runtime::Current()->GetPreAllocatedOutOfMemoryError());
   }
   throwing_OutOfMemoryError_ = false;
 }
@@ -1538,8 +1578,6 @@
   ENTRY_POINT_INFO(pInstanceofNonTrivialFromCode),
   ENTRY_POINT_INFO(pCanPutArrayElementFromCode),
   ENTRY_POINT_INFO(pCheckCastFromCode),
-  ENTRY_POINT_INFO(pDebugMe),
-  ENTRY_POINT_INFO(pUpdateDebuggerFromCode),
   ENTRY_POINT_INFO(pInitializeStaticStorage),
   ENTRY_POINT_INFO(pInitializeTypeAndVerifyAccessFromCode),
   ENTRY_POINT_INFO(pInitializeTypeFromCode),
@@ -1644,13 +1682,17 @@
 static const bool kDebugExceptionDelivery = false;
 class CatchBlockStackVisitor : public StackVisitor {
  public:
-  CatchBlockStackVisitor(Thread* self, mirror::Throwable* exception)
+  CatchBlockStackVisitor(Thread* self, const ThrowLocation& throw_location,
+                         mirror::Throwable* exception, bool is_deoptimization)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       : StackVisitor(self, self->GetLongJumpContext()),
-        self_(self), exception_(exception), to_find_(exception->GetClass()), throw_method_(NULL),
-        throw_frame_id_(0), throw_dex_pc_(0), handler_quick_frame_(NULL),
-        handler_quick_frame_pc_(0), handler_dex_pc_(0), native_method_count_(0),
-        method_tracing_active_(Runtime::Current()->IsMethodTracingActive()) {
+        self_(self), exception_(exception), is_deoptimization_(is_deoptimization),
+        to_find_(is_deoptimization ? NULL : exception->GetClass()), throw_location_(throw_location),
+        handler_quick_frame_(NULL), handler_quick_frame_pc_(0), handler_dex_pc_(0),
+        native_method_count_(0),
+        method_tracing_active_(is_deoptimization ||
+                               Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()),
+        instrumentation_frames_to_pop_(0), top_shadow_frame_(NULL), prev_shadow_frame_(NULL) {
     // Exception not in root sets, can't allow GC.
     last_no_assert_suspension_cause_ = self->StartAssertNoThreadSuspension("Finding catch block");
   }
@@ -1659,38 +1701,38 @@
     LOG(FATAL) << "UNREACHABLE";  // Expected to take long jump.
   }
 
-  bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     mirror::AbstractMethod* method = GetMethod();
     if (method == NULL) {
       // This is the upcall, we remember the frame and last pc so that we may long jump to them.
       handler_quick_frame_pc_ = GetCurrentQuickFramePc();
       handler_quick_frame_ = GetCurrentQuickFrame();
       return false;  // End stack walk.
-    }
-    uint32_t dex_pc = DexFile::kDexNoIndex;
-    if (method->IsRuntimeMethod()) {
-      // ignore callee save method
-      DCHECK(method->IsCalleeSaveMethod());
     } else {
-      if (throw_method_ == NULL) {
-        throw_method_ = method;
-        throw_frame_id_ = GetFrameId();
-        throw_dex_pc_ = GetDexPc();
+      if (UNLIKELY(method_tracing_active_ &&
+                   GetInstrumentationExitPc() == GetReturnPc())) {
+        // Keep count of the number of unwinds during instrumentation.
+        instrumentation_frames_to_pop_++;
       }
-      if (method->IsNative()) {
-        native_method_count_++;
+      if (method->IsRuntimeMethod()) {
+        // Ignore callee save method.
+        DCHECK(method->IsCalleeSaveMethod());
+        return true;
+      } else if (is_deoptimization_) {
+        return HandleDeoptimization(method);
       } else {
-        // Unwind stack when an exception occurs during instrumentation
-        if (UNLIKELY(method_tracing_active_ &&
-                     GetInstrumentationExitPc() == GetCurrentQuickFramePc())) {
-          uintptr_t pc = InstrumentationMethodUnwindFromCode(Thread::Current());
-          dex_pc = method->ToDexPc(pc);
-        } else {
-          dex_pc = GetDexPc();
-        }
+        return HandleTryItems(method);
       }
     }
+  }
+
+  bool HandleTryItems(mirror::AbstractMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    uint32_t dex_pc = DexFile::kDexNoIndex;
+    if (method->IsNative()) {
+      native_method_count_++;
+    } else {
+      dex_pc = GetDexPc();
+    }
     if (dex_pc != DexFile::kDexNoIndex) {
       uint32_t found_dex_pc = method->FindCatchBlock(to_find_, dex_pc);
       if (found_dex_pc != DexFile::kDexNoIndex) {
@@ -1703,22 +1745,81 @@
     return true;  // Continue stack walk.
   }
 
+  bool HandleDeoptimization(mirror::AbstractMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    MethodHelper mh(m);
+    const DexFile::CodeItem* code_item = mh.GetCodeItem();
+    CHECK(code_item != NULL);
+    uint16_t num_regs =  code_item->registers_size_;
+    uint32_t dex_pc = GetDexPc();
+    const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc);
+    uint32_t new_dex_pc = dex_pc + inst->SizeInCodeUnits();
+    ShadowFrame* new_frame = ShadowFrame::Create(num_regs, NULL, m, new_dex_pc);
+    verifier::MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(),
+                                      mh.GetClassDefIndex(), code_item,
+                                      m->GetDexMethodIndex(), m, m->GetAccessFlags(), false);
+    verifier.Verify();
+    std::vector<int32_t> kinds = verifier.DescribeVRegs(dex_pc);
+    for(uint16_t reg = 0; reg < num_regs; reg++) {
+      VRegKind kind = static_cast<VRegKind>(kinds.at(reg * 2));
+      switch (kind) {
+        case kUndefined:
+          new_frame->SetVReg(reg, 0xEBADDE09);
+          break;
+        case kConstant:
+          new_frame->SetVReg(reg, kinds.at((reg * 2) + 1));
+          break;
+        case kReferenceVReg:
+          new_frame->SetVRegReference(reg,
+                                      reinterpret_cast<mirror::Object*>(GetVReg(m, reg, kind)));
+          break;
+        default:
+          new_frame->SetVReg(reg, GetVReg(m, reg, kind));
+          break;
+      }
+    }
+    if (prev_shadow_frame_ != NULL) {
+      prev_shadow_frame_->SetLink(new_frame);
+    } else {
+      top_shadow_frame_ = new_frame;
+    }
+    prev_shadow_frame_ = new_frame;
+    return true;
+  }
+
   void DoLongJump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     mirror::AbstractMethod* catch_method = *handler_quick_frame_;
-    if (kDebugExceptionDelivery) {
-      if (catch_method == NULL) {
+    if (catch_method == NULL) {
+      if (kDebugExceptionDelivery) {
         LOG(INFO) << "Handler is upcall";
-      } else {
+      }
+    } else {
+      CHECK(!is_deoptimization_);
+      if (instrumentation_frames_to_pop_ > 0) {
+        // Don't pop the instrumentation frame of the catch handler.
+        instrumentation_frames_to_pop_--;
+      }
+      if (kDebugExceptionDelivery) {
         const DexFile& dex_file = *catch_method->GetDeclaringClass()->GetDexCache()->GetDexFile();
         int line_number = dex_file.GetLineNumFromPC(catch_method, handler_dex_pc_);
         LOG(INFO) << "Handler: " << PrettyMethod(catch_method) << " (line: " << line_number << ")";
       }
     }
-    self_->SetException(exception_);  // Exception back in root set.
+    // Put exception back in root set and clear throw location.
+    self_->SetException(ThrowLocation(), exception_);
     self_->EndAssertNoThreadSuspension(last_no_assert_suspension_cause_);
-    // Do debugger PostException after allowing thread suspension again.
-    Dbg::PostException(self_, throw_frame_id_, throw_method_, throw_dex_pc_,
-                       catch_method, handler_dex_pc_, exception_);
+    // Do instrumentation events after allowing thread suspension again.
+    instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+    for (size_t i = 0; i < instrumentation_frames_to_pop_; ++i) {
+      // We pop the instrumentation stack here so as not to corrupt it during the stack walk.
+      instrumentation->PopMethodForUnwind(self_, is_deoptimization_);
+    }
+    if (!is_deoptimization_) {
+      instrumentation->ExceptionCaughtEvent(self_, throw_location_, catch_method, handler_dex_pc_,
+                                            exception_);
+    } else {
+      // TODO: proper return value.
+      self_->SetDeoptimizationShadowFrame(top_shadow_frame_);
+    }
     // Place context back on thread so it will be available when we continue.
     self_->ReleaseLongJumpContext(context_);
     context_->SetSP(reinterpret_cast<uintptr_t>(handler_quick_frame_));
@@ -1729,13 +1830,13 @@
   }
 
  private:
-  Thread* self_;
-  mirror::Throwable* exception_;
+  Thread* const self_;
+  mirror::Throwable* const exception_;
+  const bool is_deoptimization_;
   // The type of the exception catch block to find.
-  mirror::Class* to_find_;
-  mirror::AbstractMethod* throw_method_;
-  JDWP::FrameId throw_frame_id_;
-  uint32_t throw_dex_pc_;
+  mirror::Class* const to_find_;
+  // Location of the throw.
+  const ThrowLocation& throw_location_;
   // Quick frame with found handler or last frame if no handler found.
   mirror::AbstractMethod** handler_quick_frame_;
   // PC to branch to for the handler.
@@ -1748,21 +1849,32 @@
   const bool method_tracing_active_;
   // Support for nesting no thread suspension checks.
   const char* last_no_assert_suspension_cause_;
+  // Number of frames to pop in long jump.
+  size_t instrumentation_frames_to_pop_;
+  ShadowFrame* top_shadow_frame_;
+  ShadowFrame* prev_shadow_frame_;
 };
 
 void Thread::QuickDeliverException() {
-  mirror::Throwable* exception = GetException();  // Get exception from thread
+  // Get exception from thread.
+  ThrowLocation throw_location;
+  mirror::Throwable* exception = GetException(&throw_location);
   CHECK(exception != NULL);
   // Don't leave exception visible while we try to find the handler, which may cause class
   // resolution.
   ClearException();
+  bool is_deoptimization = (exception == reinterpret_cast<mirror::Throwable*>(-1));
   if (kDebugExceptionDelivery) {
-    mirror::String* msg = exception->GetDetailMessage();
-    std::string str_msg(msg != NULL ? msg->ToModifiedUtf8() : "");
-    DumpStack(LOG(INFO) << "Delivering exception: " << PrettyTypeOf(exception)
-                        << ": " << str_msg << "\n");
+    if (!is_deoptimization) {
+      mirror::String* msg = exception->GetDetailMessage();
+      std::string str_msg(msg != NULL ? msg->ToModifiedUtf8() : "");
+      DumpStack(LOG(INFO) << "Delivering exception: " << PrettyTypeOf(exception)
+                << ": " << str_msg << "\n");
+    } else {
+      DumpStack(LOG(INFO) << "Deoptimizing: ");
+    }
   }
-  CatchBlockStackVisitor catch_finder(this, exception);
+  CatchBlockStackVisitor catch_finder(this, throw_location, exception, is_deoptimization);
   catch_finder.WalkStack(true);
   catch_finder.DoLongJump();
   LOG(FATAL) << "UNREACHABLE";
@@ -1779,39 +1891,45 @@
   return result;
 }
 
-mirror::AbstractMethod* Thread::GetCurrentMethod(uint32_t* dex_pc, size_t* frame_id) const {
-  struct CurrentMethodVisitor : public StackVisitor {
-    CurrentMethodVisitor(Thread* thread)
-        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-        : StackVisitor(thread, NULL), method_(NULL), dex_pc_(0), frame_id_(0) {}
-
-    virtual bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-      mirror::AbstractMethod* m = GetMethod();
-      if (m->IsRuntimeMethod()) {
-        // Continue if this is a runtime method.
-        return true;
-      }
-      method_ = m;
-      dex_pc_ = GetDexPc();
-      frame_id_ = GetFrameId();
-      return false;
+struct CurrentMethodVisitor : public StackVisitor {
+  CurrentMethodVisitor(Thread* thread, Context* context)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : StackVisitor(thread, context), this_object_(NULL), method_(NULL), dex_pc_(0) {}
+  virtual bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    mirror::AbstractMethod* m = GetMethod();
+    if (m->IsRuntimeMethod()) {
+      // Continue if this is a runtime method.
+      return true;
     }
-    mirror::AbstractMethod* method_;
-    uint32_t dex_pc_;
-    size_t frame_id_;
-  };
+    if (context_ != NULL) {
+      this_object_ = GetThisObject();
+    }
+    method_ = m;
+    dex_pc_ = GetDexPc();
+    return false;
+  }
+  mirror::Object* this_object_;
+  mirror::AbstractMethod* method_;
+  uint32_t dex_pc_;
+};
 
-  CurrentMethodVisitor visitor(const_cast<Thread*>(this));
+mirror::AbstractMethod* Thread::GetCurrentMethod(uint32_t* dex_pc) const {
+  CurrentMethodVisitor visitor(const_cast<Thread*>(this), NULL);
   visitor.WalkStack(false);
   if (dex_pc != NULL) {
     *dex_pc = visitor.dex_pc_;
   }
-  if (frame_id != NULL) {
-    *frame_id = visitor.frame_id_;
-  }
   return visitor.method_;
 }
 
+ThrowLocation Thread::GetCurrentLocationForThrow() {
+  Context* context = GetLongJumpContext();
+  CurrentMethodVisitor visitor(this, context);
+  visitor.WalkStack(false);
+  ReleaseLongJumpContext(context);
+  return ThrowLocation(visitor.this_object_, visitor.method_, visitor.dex_pc_);
+}
+
 bool Thread::HoldsLock(mirror::Object* object) {
   if (object == NULL) {
     return false;
@@ -1981,6 +2099,7 @@
   if (exception_ != NULL) {
     VerifyRootWrapperCallback(exception_, &wrapperArg);
   }
+  throw_location_.VisitRoots(VerifyRootWrapperCallback, &wrapperArg);
   if (class_loader_override_ != NULL) {
     VerifyRootWrapperCallback(class_loader_override_, &wrapperArg);
   }
@@ -1995,6 +2114,17 @@
   ReferenceMapVisitor<VerifyCallbackVisitor> mapper(this, context, visitorToCallback);
   mapper.WalkStack();
   ReleaseLongJumpContext(context);
+
+  std::deque<instrumentation::InstrumentationStackFrame>* instrumentation_stack = GetInstrumentationStack();
+  typedef std::deque<instrumentation::InstrumentationStackFrame>::const_iterator It;
+  for (It it = instrumentation_stack->begin(), end = instrumentation_stack->end(); it != end; ++it) {
+    mirror::Object* this_object = (*it).this_object_;
+    if (this_object != NULL) {
+      VerifyRootWrapperCallback(this_object, &wrapperArg);
+    }
+    mirror::AbstractMethod* method = (*it).method_;
+    VerifyRootWrapperCallback(method, &wrapperArg);
+  }
 }
 
 void Thread::VisitRoots(RootVisitor* visitor, void* arg) {
@@ -2004,6 +2134,7 @@
   if (exception_ != NULL) {
     visitor(exception_, arg);
   }
+  throw_location_.VisitRoots(visitor, arg);
   if (class_loader_override_ != NULL) {
     visitor(class_loader_override_, arg);
   }
@@ -2018,6 +2149,17 @@
   ReferenceMapVisitor<RootCallbackVisitor> mapper(this, context, visitorToCallback);
   mapper.WalkStack();
   ReleaseLongJumpContext(context);
+
+  std::deque<instrumentation::InstrumentationStackFrame>* instrumentation_stack = GetInstrumentationStack();
+  typedef std::deque<instrumentation::InstrumentationStackFrame>::const_iterator It;
+  for (It it = instrumentation_stack->begin(), end = instrumentation_stack->end(); it != end; ++it) {
+    mirror::Object* this_object = (*it).this_object_;
+    if (this_object != NULL) {
+      visitor(this_object, arg);
+    }
+    mirror::AbstractMethod* method = (*it).method_;
+    visitor(method, arg);
+  }
 }
 
 static void VerifyObject(const mirror::Object* root, void* arg) {
diff --git a/src/thread.h b/src/thread.h
index dd67a21..37f2721 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -36,6 +36,7 @@
 #include "stack.h"
 #include "stack_indirect_reference_table.h"
 #include "thread_state.h"
+#include "throw_location.h"
 #include "UniquePtr.h"
 
 namespace art {
@@ -80,14 +81,13 @@
 enum ThreadFlag {
   kSuspendRequest   = 1,  // If set implies that suspend_count_ > 0 and the Thread should enter the
                           // safepoint handler.
-  kCheckpointRequest = 2, // Request that the thread do some checkpoint work and then continue.
-  kEnterInterpreter = 4,  // Instruct managed code it should enter the interpreter.
+  kCheckpointRequest = 2  // Request that the thread do some checkpoint work and then continue.
 };
 
 class PACKED(4) Thread {
  public:
   // Space to throw a StackOverflowError in.
-  static const size_t kStackOverflowReservedBytes = 10 * KB;
+  static const size_t kStackOverflowReservedBytes = 16 * KB;
 
   // Creates a new native thread corresponding to the given managed peer.
   // Used to implement Thread.start.
@@ -279,28 +279,27 @@
     return exception_ != NULL;
   }
 
-  mirror::Throwable* GetException() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  mirror::Throwable* GetException(ThrowLocation* throw_location) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    if (throw_location != NULL) {
+      *throw_location = throw_location_;
+    }
     return exception_;
   }
 
   void AssertNoPendingException() const;
 
-  void SetException(mirror::Throwable* new_exception) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  void SetException(const ThrowLocation& throw_location, mirror::Throwable* new_exception)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     CHECK(new_exception != NULL);
     // TODO: DCHECK(!IsExceptionPending());
     exception_ = new_exception;
+    throw_location_ = throw_location;
   }
 
   void ClearException() {
     exception_ = NULL;
-  }
-
-  void DeliverException(mirror::Throwable* exception) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    if (exception == NULL) {
-      ThrowNewException("Ljava/lang/NullPointerException;", "throw with null exception");
-    } else {
-      SetException(exception);
-    }
+    throw_location_.Clear();
   }
 
   // Find catch block and perform long jump to appropriate exception handle
@@ -312,9 +311,11 @@
     long_jump_context_ = context;
   }
 
-  mirror::AbstractMethod* GetCurrentMethod(uint32_t* dex_pc = NULL, size_t* frame_id = NULL) const
+  mirror::AbstractMethod* GetCurrentMethod(uint32_t* dex_pc) const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  ThrowLocation GetCurrentLocationForThrow() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   void SetTopOfStack(void* stack, uintptr_t pc) {
     mirror::AbstractMethod** top_method = reinterpret_cast<mirror::AbstractMethod**>(stack);
     managed_stack_.SetTopQuickFrame(top_method);
@@ -330,32 +331,30 @@
   }
 
   // If 'msg' is NULL, no detail message is set.
-  void ThrowNewException(const char* exception_class_descriptor, const char* msg)
+  void ThrowNewException(const ThrowLocation& throw_location,
+                         const char* exception_class_descriptor, const char* msg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // If 'msg' is NULL, no detail message is set. An exception must be pending, and will be
   // used as the new exception's cause.
-  void ThrowNewWrappedException(const char* exception_class_descriptor, const char* msg)
+  void ThrowNewWrappedException(const ThrowLocation& throw_location,
+                                const char* exception_class_descriptor,
+                                const char* msg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void ThrowNewExceptionF(const char* exception_class_descriptor, const char* fmt, ...)
-      __attribute__((format(printf, 3, 4)))
+  void ThrowNewExceptionF(const ThrowLocation& throw_location,
+                          const char* exception_class_descriptor, const char* fmt, ...)
+      __attribute__((format(printf, 4, 5)))
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void ThrowNewExceptionV(const char* exception_class_descriptor, const char* fmt, va_list ap)
+  void ThrowNewExceptionV(const ThrowLocation& throw_location,
+                          const char* exception_class_descriptor, const char* fmt, va_list ap)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // OutOfMemoryError is special, because we need to pre-allocate an instance.
   // Only the GC should call this.
   void ThrowOutOfMemoryError(const char* msg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  //QuickFrameIterator FindExceptionHandler(void* throw_pc, void** handler_pc);
-
-  void* FindExceptionHandlerInMethod(const mirror::AbstractMethod* method,
-                                     void* throw_pc,
-                                     const DexFile& dex_file,
-                                     ClassLinker* class_linker);
-
   static void Startup();
   static void FinishStartup();
   static void Shutdown();
@@ -395,8 +394,7 @@
   static jobjectArray InternalStackTraceToStackTraceElementArray(JNIEnv* env, jobject internal,
       jobjectArray output_array = NULL, int* stack_depth = NULL);
 
-  void VisitRoots(RootVisitor* visitor, void* arg)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void VisitRoots(RootVisitor* visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void VerifyRoots(VerifyRootVisitor* visitor, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -535,34 +533,15 @@
     return debug_invoke_req_;
   }
 
-  void SetDebuggerUpdatesEnabled(bool enabled);
-
-  void SetDeoptimizationShadowFrame(ShadowFrame* sf, const JValue& ret_val);
+  void SetDeoptimizationShadowFrame(ShadowFrame* sf);
+  void SetDeoptimizationReturnValue(const JValue& ret_val);
 
   ShadowFrame* GetAndClearDeoptimizationShadowFrame(JValue* ret_val);
 
-  const std::deque<InstrumentationStackFrame>* GetInstrumentationStack() const {
+  std::deque<instrumentation::InstrumentationStackFrame>* GetInstrumentationStack() {
     return instrumentation_stack_;
   }
 
-  bool IsInstrumentationStackEmpty() const {
-    return instrumentation_stack_->empty();
-  }
-
-  void PushInstrumentationStackFrame(const InstrumentationStackFrame& frame) {
-    instrumentation_stack_->push_front(frame);
-  }
-
-  void PushBackInstrumentationStackFrame(const InstrumentationStackFrame& frame) {
-    instrumentation_stack_->push_back(frame);
-  }
-
-  InstrumentationStackFrame PopInstrumentationStackFrame() {
-    InstrumentationStackFrame frame = instrumentation_stack_->front();
-    instrumentation_stack_->pop_front();
-    return frame;
-  }
-
   BaseMutex* GetHeldMutex(LockLevel level) const {
     return held_mutexes_[level];
   }
@@ -598,13 +577,15 @@
   void CreatePeer(const char* name, bool as_daemon, jobject thread_group);
   friend class Runtime; // For CreatePeer.
 
-  // Avoid use, callers should use SetState. Used only by SignalCatcher::HandleSigQuit and ~Thread.
+  // Avoid use, callers should use SetState. Used only by SignalCatcher::HandleSigQuit, ~Thread and
+  // Dbg::Disconnected.
   ThreadState SetStateUnsafe(ThreadState new_state) {
     ThreadState old_state = GetState();
     state_and_flags_.as_struct.state = new_state;
     return old_state;
   }
   friend class SignalCatcher;  // For SetStateUnsafe.
+  friend class Dbg;  // For SetStateUnsafe.
 
   void VerifyStackImpl() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -710,6 +691,8 @@
   // System thread id.
   pid_t tid_;
 
+  ThrowLocation throw_location_;
+
   // Guards the 'interrupted_' and 'wait_monitor_' members.
   mutable Mutex* wait_mutex_ DEFAULT_MUTEX_ACQUIRED_AFTER;
   ConditionVariable* wait_cond_ GUARDED_BY(wait_mutex_);
@@ -755,7 +738,7 @@
 
   // Additional stack used by method instrumentation to store method and return pc values.
   // Stored as a pointer since std::deque is not PACKED.
-  std::deque<InstrumentationStackFrame>* instrumentation_stack_;
+  std::deque<instrumentation::InstrumentationStackFrame>* instrumentation_stack_;
 
   // A cached copy of the java.lang.Thread's name.
   std::string* name_;
diff --git a/src/throw_location.cc b/src/throw_location.cc
new file mode 100644
index 0000000..84d2c9b
--- /dev/null
+++ b/src/throw_location.cc
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2013 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 "throw_location.h"
+
+#include "mirror/abstract_method-inl.h"
+#include "mirror/class-inl.h"
+#include "mirror/object-inl.h"
+#include "object_utils.h"
+#include "utils.h"
+
+namespace art {
+
+std::string ThrowLocation::Dump() const {
+  return StringPrintf("%s:%d", PrettyMethod(method_).c_str(),
+                      MethodHelper(method_).GetLineNumFromDexPC(dex_pc_));
+}
+
+void ThrowLocation::VisitRoots(RootVisitor* visitor, void* arg) {
+  if (this_object_ != NULL) {
+    visitor(this_object_, arg);
+  }
+  if (method_ != NULL) {
+    visitor(method_, arg);
+  }
+}
+
+}  // namespace art
diff --git a/src/throw_location.h b/src/throw_location.h
new file mode 100644
index 0000000..8c1b941
--- /dev/null
+++ b/src/throw_location.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef ART_SRC_THROW_LOCATION_H_
+#define ART_SRC_THROW_LOCATION_H_
+
+#include "base/macros.h"
+#include "root_visitor.h"
+
+#include <stdint.h>
+#include <string>
+
+namespace art {
+
+namespace mirror {
+class AbstractMethod;
+class Object;
+}  // mirror
+
+class PACKED(4) ThrowLocation {
+ public:
+  ThrowLocation() {
+    Clear();
+  }
+
+  ThrowLocation(mirror::Object* throw_this_object, mirror::AbstractMethod* throw_method,
+                uint32_t throw_dex_pc) :
+      this_object_(throw_this_object),
+      method_(throw_method),
+      dex_pc_(throw_dex_pc) {}
+
+  mirror::Object* GetThis() const {
+    return this_object_;
+  }
+
+  mirror::AbstractMethod* GetMethod() const {
+    return method_;
+  }
+
+  uint32_t GetDexPc() const {
+    return dex_pc_;
+  }
+
+  void Clear() {
+    this_object_ = NULL;
+    method_ = NULL;
+    dex_pc_ = -1;
+  }
+
+  std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  void VisitRoots(RootVisitor* visitor, void* arg);
+
+ private:
+  // The 'this' reference of the throwing method.
+  mirror::Object* this_object_;
+  // The throwing method.
+  mirror::AbstractMethod* method_;
+  // The instruction within the throwing method.
+  uint32_t dex_pc_;
+};
+
+}  // namespace art
+
+#endif  // ART_SRC_THROW_LOCATION_H_
diff --git a/src/trace.cc b/src/trace.cc
index 859f523..3293290 100644
--- a/src/trace.cc
+++ b/src/trace.cc
@@ -20,6 +20,7 @@
 
 #include "base/unix_file/fd_file.h"
 #include "class_linker.h"
+#include "common_throws.h"
 #include "debugger.h"
 #include "dex_file-inl.h"
 #include "instrumentation.h"
@@ -73,7 +74,14 @@
 //
 // All values are stored in little-endian order.
 
-static const uint32_t kTraceMethodActionMask      = 0x03; // two bits
+enum TraceAction {
+    kTraceMethodEnter = 0x00,      // method entry
+    kTraceMethodExit = 0x01,       // method exit
+    kTraceUnroll = 0x02,     // method exited by exception unrolling
+    // 0x03 currently unused
+    kTraceMethodActionMask = 0x03, // two bits
+};
+
 static const char     kTraceTokenChar             = '*';
 static const uint16_t kTraceHeaderLength          = 32;
 static const uint32_t kTraceMagicValue            = 0x574f4c53;
@@ -82,34 +90,57 @@
 static const uint16_t kTraceRecordSizeSingleClock = 10; // using v2
 static const uint16_t kTraceRecordSizeDualClock   = 14; // using v3 with two timestamps
 
-static ProfilerClockSource gDefaultTraceClockSource = kProfilerClockSourceDual;
+#if defined(HAVE_POSIX_CLOCKS)
+ProfilerClockSource Trace::default_clock_source_ = kProfilerClockSourceDual;
+#else
+ProfilerClockSource Trace::default_clock_source_ = kProfilerClockSourceWall;
+#endif
 
-static inline uint32_t TraceMethodId(uint32_t methodValue) {
-  return (methodValue & ~kTraceMethodActionMask);
+Trace* Trace::the_trace_ = NULL;
+
+static mirror::AbstractMethod* DecodeTraceMethodId(uint32_t tmid) {
+  return reinterpret_cast<mirror::AbstractMethod*>(tmid & ~kTraceMethodActionMask);
 }
 
-static inline uint32_t TraceMethodCombine(uint32_t method, uint8_t traceEvent) {
-  return (method | traceEvent);
+static TraceAction DecodeTraceAction(uint32_t tmid) {
+  return static_cast<TraceAction>(tmid & kTraceMethodActionMask);
+}
+
+static uint32_t EncodeTraceMethodAndAction(const mirror::AbstractMethod* method,
+                                           TraceAction action) {
+  uint32_t tmid = reinterpret_cast<uint32_t>(method) | action;
+  DCHECK_EQ(method, DecodeTraceMethodId(tmid));
+  return tmid;
 }
 
 void Trace::SetDefaultClockSource(ProfilerClockSource clock_source) {
-  gDefaultTraceClockSource = clock_source;
+#if defined(HAVE_POSIX_CLOCKS)
+  default_clock_source_ = clock_source;
+#else
+  if (clock_source != kProfilerClockSourceWall) {
+    LOG(WARNING) << "Ignoring tracing request to use ";
+  }
+#endif
+}
+
+static uint16_t GetTraceVersion(ProfilerClockSource clock_source) {
+  return (clock_source == kProfilerClockSourceDual) ? kTraceVersionDualClock
+                                                    : kTraceVersionSingleClock;
+}
+
+static uint16_t GetRecordSize(ProfilerClockSource clock_source) {
+  return (clock_source == kProfilerClockSourceDual) ? kTraceRecordSizeDualClock
+                                                    : kTraceRecordSizeSingleClock;
 }
 
 bool Trace::UseThreadCpuClock() {
-#if defined(HAVE_POSIX_CLOCKS)
-  return clock_source_ != kProfilerClockSourceWall;
-#else
-  return false;
-#endif
+  return (clock_source_ == kProfilerClockSourceThreadCpu) ||
+      (clock_source_ == kProfilerClockSourceDual);
 }
 
 bool Trace::UseWallClock() {
-#if defined(HAVE_POSIX_CLOCKS)
-  return clock_source_ != kProfilerClockSourceThreadCpu;
-#else
-  return true;
-#endif
+  return (clock_source_ == kProfilerClockSourceWall) ||
+      (clock_source_ == kProfilerClockSourceDual);
 }
 
 static void MeasureClockOverhead(Trace* trace) {
@@ -165,109 +196,129 @@
   *buf++ = (uint8_t) (val >> 56);
 }
 
-Trace::Trace(File* trace_file, int buffer_size, int flags)
-    : trace_file_(trace_file), buf_(new uint8_t[buffer_size]()), flags_(flags),
-      clock_source_(gDefaultTraceClockSource), overflow_(false),
-      buffer_size_(buffer_size), start_time_(0), trace_version_(0), record_size_(0), cur_offset_(0) {
-}
-
-void Trace::Start(const char* trace_filename, int trace_fd, int buffer_size, int flags, bool direct_to_ddms) {
-  if (Runtime::Current()->IsMethodTracingActive()) {
-    LOG(INFO) << "Trace already in progress, ignoring this request";
-    return;
+void Trace::Start(const char* trace_filename, int trace_fd, int buffer_size, int flags,
+                  bool direct_to_ddms) {
+  Thread* self = Thread::Current();
+  {
+    MutexLock mu(self, *Locks::trace_lock_);
+    if (the_trace_ != NULL) {
+      LOG(ERROR) << "Trace already in progress, ignoring this request";
+      return;
+    }
   }
-
-  Runtime::Current()->GetThreadList()->SuspendAll();
+  Runtime* runtime = Runtime::Current();
+  runtime->GetThreadList()->SuspendAll();
 
   // Open trace file if not going directly to ddms.
-  File* trace_file = NULL;
+  UniquePtr<File> trace_file;
   if (!direct_to_ddms) {
     if (trace_fd < 0) {
-      trace_file = OS::OpenFile(trace_filename, true);
+      trace_file.reset(OS::OpenFile(trace_filename, true));
     } else {
-      trace_file = new File(trace_fd, "tracefile");
+      trace_file.reset(new File(trace_fd, "tracefile"));
       trace_file->DisableAutoClose();
     }
-    if (trace_file == NULL) {
+    if (trace_file.get() == NULL) {
       PLOG(ERROR) << "Unable to open trace file '" << trace_filename << "'";
-      Thread::Current()->ThrowNewException("Ljava/lang/RuntimeException;",
-          StringPrintf("Unable to open trace file '%s'", trace_filename).c_str());
-      Runtime::Current()->GetThreadList()->ResumeAll();
+      runtime->GetThreadList()->ResumeAll();
+      ScopedObjectAccess soa(self);
+      ThrowRuntimeException("Unable to open trace file '%s'", trace_filename);
       return;
     }
   }
 
   // Create Trace object.
-  Trace* tracer(new Trace(trace_file, buffer_size, flags));
+  {
+    MutexLock mu(self, *Locks::trace_lock_);
+    if(the_trace_ != NULL) {
+      LOG(ERROR) << "Trace already in progress, ignoring this request";
+    } else {
+      the_trace_ = new Trace(trace_file.release(), buffer_size, flags);
 
-  // Enable count of allocs if specified in the flags.
-  if ((flags && kTraceCountAllocs) != 0) {
-    Runtime::Current()->SetStatsEnabled(true);
+      // Enable count of allocs if specified in the flags.
+      if ((flags && kTraceCountAllocs) != 0) {
+        runtime->SetStatsEnabled(true);
+      }
+
+      runtime->GetInstrumentation()->AddListener(the_trace_,
+                                                 instrumentation::Instrumentation::kMethodEntered |
+                                                 instrumentation::Instrumentation::kMethodExited |
+                                                 instrumentation::Instrumentation::kMethodUnwind);
+    }
   }
-
-  Runtime::Current()->EnableMethodTracing(tracer);
-  tracer->BeginTracing();
-
-  Runtime::Current()->GetThreadList()->ResumeAll();
+  runtime->GetThreadList()->ResumeAll();
 }
 
 void Trace::Stop() {
-  if (!Runtime::Current()->IsMethodTracingActive()) {
-    LOG(INFO) << "Trace stop requested, but no trace currently running";
-    return;
+  Runtime* runtime = Runtime::Current();
+  runtime->GetThreadList()->SuspendAll();
+  Trace* the_trace = NULL;
+  {
+    MutexLock mu(Thread::Current(), *Locks::trace_lock_);
+    if (the_trace_ == NULL) {
+      LOG(ERROR) << "Trace stop requested, but no trace currently running";
+    } else {
+      the_trace = the_trace_;
+      the_trace_ = NULL;
+    }
   }
-
-  Runtime::Current()->GetThreadList()->SuspendAll();
-
-  Runtime::Current()->GetInstrumentation()->GetTrace()->FinishTracing();
-  Runtime::Current()->DisableMethodTracing();
-
-  Runtime::Current()->GetThreadList()->ResumeAll();
+  if (the_trace != NULL) {
+    the_trace->FinishTracing();
+    runtime->GetInstrumentation()->RemoveListener(the_trace,
+                                                  instrumentation::Instrumentation::kMethodEntered |
+                                                  instrumentation::Instrumentation::kMethodExited |
+                                                  instrumentation::Instrumentation::kMethodUnwind);
+    delete the_trace;
+  }
+  runtime->GetThreadList()->ResumeAll();
 }
 
 void Trace::Shutdown() {
-  if (!Runtime::Current()->IsMethodTracingActive()) {
-    LOG(INFO) << "Trace shutdown requested, but no trace currently running";
-    return;
+  if (IsMethodTracingActive()) {
+    Stop();
   }
-  Runtime::Current()->GetInstrumentation()->GetTrace()->FinishTracing();
-  Runtime::Current()->DisableMethodTracing();
 }
 
-void Trace::BeginTracing() {
-  // Set the start time of tracing.
-  start_time_ = MicroTime();
+bool Trace::IsMethodTracingActive() {
+  MutexLock mu(Thread::Current(), *Locks::trace_lock_);
+  return the_trace_ != NULL;
+}
 
-  // Set trace version and record size.
-  if (UseThreadCpuClock() && UseWallClock()) {
-    trace_version_ = kTraceVersionDualClock;
-    record_size_ = kTraceRecordSizeDualClock;
-  } else {
-    trace_version_ = kTraceVersionSingleClock;
-    record_size_ = kTraceRecordSizeSingleClock;
-  }
-
+Trace::Trace(File* trace_file, int buffer_size, int flags)
+    : trace_file_(trace_file), buf_(new uint8_t[buffer_size]()), flags_(flags),
+      clock_source_(default_clock_source_), buffer_size_(buffer_size), start_time_(MicroTime()),
+      cur_offset_(0),  overflow_(false) {
   // Set up the beginning of the trace.
+  uint16_t trace_version = GetTraceVersion(clock_source_);
   memset(buf_.get(), 0, kTraceHeaderLength);
   Append4LE(buf_.get(), kTraceMagicValue);
-  Append2LE(buf_.get() + 4, trace_version_);
+  Append2LE(buf_.get() + 4, trace_version);
   Append2LE(buf_.get() + 6, kTraceHeaderLength);
   Append8LE(buf_.get() + 8, start_time_);
-  if (trace_version_ >= kTraceVersionDualClock) {
-    Append2LE(buf_.get() + 16, record_size_);
+  if (trace_version >= kTraceVersionDualClock) {
+    uint16_t record_size = GetRecordSize(clock_source_);
+    Append2LE(buf_.get() + 16, record_size);
   }
 
   // Update current offset.
   cur_offset_ = kTraceHeaderLength;
+}
 
-  // Install all method tracing stubs.
-  Runtime::Current()->GetInstrumentation()->InstallStubs();
+static void DumpBuf(uint8_t* buf, size_t buf_size, ProfilerClockSource clock_source)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  uint8_t* ptr = buf + kTraceHeaderLength;
+  uint8_t* end = buf + buf_size;
+
+  while (ptr < end) {
+    uint32_t tmid = ptr[2] | (ptr[3] << 8) | (ptr[4] << 16) | (ptr[5] << 24);
+    mirror::AbstractMethod* method = DecodeTraceMethodId(tmid);
+    TraceAction action = DecodeTraceAction(tmid);
+    LOG(INFO) << PrettyMethod(method) << " " << static_cast<int>(action);
+    ptr += GetRecordSize(clock_source);
+  }
 }
 
 void Trace::FinishTracing() {
-  // Uninstall all method tracing stubs.
-  Runtime::Current()->GetInstrumentation()->UninstallStubs();
-
   // Compute elapsed time.
   uint64_t elapsed = MicroTime() - start_time_;
 
@@ -278,12 +329,13 @@
     Runtime::Current()->SetStatsEnabled(false);
   }
 
-  GetVisitedMethods(final_offset);
+  std::set<mirror::AbstractMethod*> visited_methods;
+  GetVisitedMethods(final_offset, &visited_methods);
 
   std::ostringstream os;
 
   os << StringPrintf("%cversion\n", kTraceTokenChar);
-  os << StringPrintf("%d\n", trace_version_);
+  os << StringPrintf("%d\n", GetTraceVersion(clock_source_));
   os << StringPrintf("data-file-overflow=%s\n", overflow_ ? "true" : "false");
   if (UseThreadCpuClock()) {
     if (UseWallClock()) {
@@ -295,7 +347,8 @@
     os << StringPrintf("clock=wall\n");
   }
   os << StringPrintf("elapsed-time-usec=%llu\n", elapsed);
-  os << StringPrintf("num-method-calls=%zd\n", (final_offset - kTraceHeaderLength) / record_size_);
+  size_t num_records = (final_offset - kTraceHeaderLength) / GetRecordSize(clock_source_);
+  os << StringPrintf("num-method-calls=%zd\n", num_records);
   os << StringPrintf("clock-call-overhead-nsec=%d\n", clock_overhead);
   os << StringPrintf("vm=art\n");
   if ((flags_ & kTraceCountAllocs) != 0) {
@@ -306,7 +359,7 @@
   os << StringPrintf("%cthreads\n", kTraceTokenChar);
   DumpThreadList(os);
   os << StringPrintf("%cmethods\n", kTraceTokenChar);
-  DumpMethodList(os);
+  DumpMethodList(os, visited_methods);
   os << StringPrintf("%cend\n", kTraceTokenChar);
 
   std::string header(os.str());
@@ -317,74 +370,128 @@
     iov[1].iov_base = buf_.get();
     iov[1].iov_len = final_offset;
     Dbg::DdmSendChunkV(CHUNK_TYPE("MPSE"), iov, 2);
+    const bool kDumpTraceInfo = false;
+    if (kDumpTraceInfo) {
+      LOG(INFO) << "Trace sent:\n" << header;
+      DumpBuf(buf_.get(), final_offset, clock_source_);
+    }
   } else {
     if (!trace_file_->WriteFully(header.c_str(), header.length()) ||
         !trace_file_->WriteFully(buf_.get(), final_offset)) {
       std::string detail(StringPrintf("Trace data write failed: %s", strerror(errno)));
       PLOG(ERROR) << detail;
-      Thread::Current()->ThrowNewException("Ljava/lang/RuntimeException;", detail.c_str());
+      ThrowRuntimeException("%s", detail.c_str());
     }
   }
 }
 
-void Trace::LogMethodTraceEvent(Thread* self, const mirror::AbstractMethod* method,
-                                Trace::TraceEvent event) {
-  if (thread_clock_base_map_.find(self) == thread_clock_base_map_.end()) {
-    uint64_t time = ThreadCpuMicroTime();
-    thread_clock_base_map_.Put(self, time);
-  }
+void Trace::DexPcMoved(Thread* thread, mirror::Object* this_object,
+                       const mirror::AbstractMethod* method, uint32_t new_dex_pc) {
+  // We're not recorded to listen to this kind of event, so complain.
+  LOG(ERROR) << "Unexpected dex PC event in tracing " << PrettyMethod(method) << " " << new_dex_pc;
+};
 
+void Trace::MethodEntered(Thread* thread, mirror::Object* this_object,
+                          const mirror::AbstractMethod* method, uint32_t dex_pc) {
+  LogMethodTraceEvent(thread, method, instrumentation::Instrumentation::kMethodEntered);
+}
+
+void Trace::MethodExited(Thread* thread, mirror::Object* this_object,
+                         const mirror::AbstractMethod* method, uint32_t dex_pc,
+                         const JValue& return_value) {
+  UNUSED(return_value);
+  LogMethodTraceEvent(thread, method, instrumentation::Instrumentation::kMethodExited);
+}
+
+void Trace::MethodUnwind(Thread* thread, const mirror::AbstractMethod* method, uint32_t dex_pc) {
+  LogMethodTraceEvent(thread, method, instrumentation::Instrumentation::kMethodUnwind);
+}
+
+void Trace::ExceptionCaught(Thread* thread, const ThrowLocation& throw_location,
+                            mirror::AbstractMethod* catch_method, uint32_t catch_dex_pc,
+                            mirror::Throwable* exception_object)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  LOG(ERROR) << "Unexpected exception caught event in tracing";
+}
+
+void Trace::LogMethodTraceEvent(Thread* thread, const mirror::AbstractMethod* method,
+                                instrumentation::Instrumentation::InstrumentationEvent event) {
   // Advance cur_offset_ atomically.
   int32_t new_offset;
   int32_t old_offset;
   do {
     old_offset = cur_offset_;
-    new_offset = old_offset + record_size_;
+    new_offset = old_offset + GetRecordSize(clock_source_);
     if (new_offset > buffer_size_) {
       overflow_ = true;
       return;
     }
   } while (android_atomic_release_cas(old_offset, new_offset, &cur_offset_) != 0);
 
-  uint32_t method_value = TraceMethodCombine(reinterpret_cast<uint32_t>(method), event);
+  TraceAction action = kTraceMethodEnter;
+  switch (event) {
+    case instrumentation::Instrumentation::kMethodEntered:
+      action = kTraceMethodEnter;
+      break;
+    case instrumentation::Instrumentation::kMethodExited:
+      action = kTraceMethodExit;
+      break;
+    case instrumentation::Instrumentation::kMethodUnwind:
+      action = kTraceUnroll;
+      break;
+    default:
+      UNIMPLEMENTED(FATAL) << "Unexpected event: " << event;
+  }
+
+  uint32_t method_value = EncodeTraceMethodAndAction(method, action);
 
   // Write data
   uint8_t* ptr = buf_.get() + old_offset;
-  Append2LE(ptr, self->GetTid());
+  Append2LE(ptr, thread->GetTid());
   Append4LE(ptr + 2, method_value);
   ptr += 6;
 
   if (UseThreadCpuClock()) {
-    uint64_t thread_clock_base = thread_clock_base_map_.find(self)->second;
-    uint32_t thread_clock_diff = ThreadCpuMicroTime() - thread_clock_base;
+    // TODO: this isn't vaguely thread safe.
+    SafeMap<Thread*, uint64_t>::iterator it = thread_clock_base_map_.find(thread);
+    uint32_t thread_clock_diff = 0;
+    if (UNLIKELY(it == thread_clock_base_map_.end())) {
+      // First event, the diff is 0, record the base time in the map.
+      uint64_t time = ThreadCpuMicroTime();
+      thread_clock_base_map_.Put(thread, time);
+    } else {
+      uint64_t thread_clock_base = it->second;
+      thread_clock_diff = ThreadCpuMicroTime() - thread_clock_base;
+    }
     Append4LE(ptr, thread_clock_diff);
     ptr += 4;
   }
-
   if (UseWallClock()) {
     uint32_t wall_clock_diff = MicroTime() - start_time_;
     Append4LE(ptr, wall_clock_diff);
   }
 }
 
-void Trace::GetVisitedMethods(size_t end_offset) {
+void Trace::GetVisitedMethods(size_t buf_size,
+                              std::set<mirror::AbstractMethod*>* visited_methods) {
   uint8_t* ptr = buf_.get() + kTraceHeaderLength;
-  uint8_t* end = buf_.get() + end_offset;
+  uint8_t* end = buf_.get() + buf_size;
 
   while (ptr < end) {
-    uint32_t method_value = ptr[2] | (ptr[3] << 8) | (ptr[4] << 16) | (ptr[5] << 24);
-    mirror::AbstractMethod* method =
-        reinterpret_cast<mirror::AbstractMethod*>(TraceMethodId(method_value));
-    visited_methods_.insert(method);
-    ptr += record_size_;
+    uint32_t tmid = ptr[2] | (ptr[3] << 8) | (ptr[4] << 16) | (ptr[5] << 24);
+    mirror::AbstractMethod* method = DecodeTraceMethodId(tmid);
+    visited_methods->insert(method);
+    ptr += GetRecordSize(clock_source_);
   }
 }
 
-void Trace::DumpMethodList(std::ostream& os) {
-  typedef std::set<const mirror::AbstractMethod*>::const_iterator It; // TODO: C++0x auto
-  for (It it = visited_methods_.begin(); it != visited_methods_.end(); ++it) {
-    const mirror::AbstractMethod* method = *it;
-    MethodHelper mh(method);
+void Trace::DumpMethodList(std::ostream& os,
+                           const std::set<mirror::AbstractMethod*>& visited_methods) {
+  typedef std::set<mirror::AbstractMethod*>::const_iterator It; // TODO: C++0x auto
+  MethodHelper mh;
+  for (It it = visited_methods.begin(); it != visited_methods.end(); ++it) {
+    mirror::AbstractMethod* method = *it;
+    mh.ChangeMethod(method);
     os << StringPrintf("%p\t%s\t%s\t%s\t%s\n", method,
         PrettyDescriptor(mh.GetDeclaringClassDescriptor()).c_str(), mh.GetName(),
         mh.GetSignature().c_str(), mh.GetDeclaringClassSourceFile());
diff --git a/src/trace.h b/src/trace.h
index 1be1cc4..9432e71 100644
--- a/src/trace.h
+++ b/src/trace.h
@@ -23,6 +23,7 @@
 
 #include "base/macros.h"
 #include "globals.h"
+#include "instrumentation.h"
 #include "os.h"
 #include "safe_map.h"
 #include "UniquePtr.h"
@@ -37,45 +38,65 @@
 enum ProfilerClockSource {
   kProfilerClockSourceThreadCpu,
   kProfilerClockSourceWall,
-  kProfilerClockSourceDual,
+  kProfilerClockSourceDual,  // Both wall and thread CPU clocks.
 };
 
-class Trace {
+class Trace : public instrumentation::InstrumentationListener {
  public:
-  enum TraceEvent {
-    kMethodTraceEnter = 0,
-    kMethodTraceExit = 1,
-    kMethodTraceUnwind = 2,
-  };
-
   enum TraceFlag {
     kTraceCountAllocs = 1,
   };
 
   static void SetDefaultClockSource(ProfilerClockSource clock_source);
 
-  static void Start(const char* trace_filename, int trace_fd, int buffer_size, int flags, bool direct_to_ddms);
-  static void Stop();
-  static void Shutdown() NO_THREAD_SAFETY_ANALYSIS;  // TODO: implement appropriate locking.
+  static void Start(const char* trace_filename, int trace_fd, int buffer_size, int flags,
+                    bool direct_to_ddms)
+  LOCKS_EXCLUDED(Locks::mutator_lock_,
+                 Locks::thread_list_lock_,
+                 Locks::thread_suspend_count_lock_,
+                 Locks::trace_lock_);
+  static void Stop() LOCKS_EXCLUDED(Locks::trace_lock_);
+  static void Shutdown() LOCKS_EXCLUDED(Locks::trace_lock_);
+  static bool IsMethodTracingActive() LOCKS_EXCLUDED(Locks::trace_lock_);
 
   bool UseWallClock();
   bool UseThreadCpuClock();
 
-  void LogMethodTraceEvent(Thread* self, const mirror::AbstractMethod* method, TraceEvent event);
-
+  virtual void MethodEntered(Thread* thread, mirror::Object* this_object,
+                             const mirror::AbstractMethod* method, uint32_t dex_pc)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  virtual void MethodExited(Thread* thread, mirror::Object* this_object,
+                            const mirror::AbstractMethod* method, uint32_t dex_pc,
+                            const JValue& return_value)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  virtual void MethodUnwind(Thread* thread, const mirror::AbstractMethod* method, uint32_t dex_pc)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  virtual void DexPcMoved(Thread* thread, mirror::Object* this_object,
+                          const mirror::AbstractMethod* method, uint32_t new_dex_pc)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  virtual void ExceptionCaught(Thread* thread, const ThrowLocation& throw_location,
+                               mirror::AbstractMethod* catch_method, uint32_t catch_dex_pc,
+                               mirror::Throwable* exception_object)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
  private:
   explicit Trace(File* trace_file, int buffer_size, int flags);
 
-  void BeginTracing();
   void FinishTracing() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  void LogMethodTraceEvent(Thread* thread, const mirror::AbstractMethod* method,
+                           instrumentation::Instrumentation::InstrumentationEvent event);
+
   // Methods to output traced methods and threads.
-  void GetVisitedMethods(size_t end_offset);
-  void DumpMethodList(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void GetVisitedMethods(size_t end_offset, std::set<mirror::AbstractMethod*>* visited_methods);
+  void DumpMethodList(std::ostream& os, const std::set<mirror::AbstractMethod*>& visited_methods)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   void DumpThreadList(std::ostream& os) LOCKS_EXCLUDED(Locks::thread_list_lock_);
 
-  // Set of methods visited by the profiler.
-  std::set<const mirror::AbstractMethod*> visited_methods_;
+  // Singleton instance of the Trace or NULL when no method tracing is active.
+  static Trace* the_trace_ GUARDED_BY(Locks::trace_lock_);
+
+  // The default profiler clock source.
+  static ProfilerClockSource default_clock_source_;
 
   // Maps a thread to its clock base.
   SafeMap<Thread*, uint64_t> thread_clock_base_map_;
@@ -87,18 +108,22 @@
   UniquePtr<uint8_t> buf_;
 
   // Flags enabling extra tracing of things such as alloc counts.
-  int flags_;
+  const int flags_;
 
-  ProfilerClockSource clock_source_;
+  const ProfilerClockSource clock_source_;
 
-  bool overflow_;
-  int buffer_size_;
-  uint64_t start_time_;
-  uint16_t trace_version_;
-  uint16_t record_size_;
+  // Size of buf_.
+  const int buffer_size_;
 
+  // Time trace was created.
+  const uint64_t start_time_;
+
+  // Offset into buf_.
   volatile int32_t cur_offset_;
 
+  // Did we overflow the buffer recording traces?
+  bool overflow_;
+
   DISALLOW_COPY_AND_ASSIGN(Trace);
 };
 
diff --git a/src/verifier/reg_type_cache.cc b/src/verifier/reg_type_cache.cc
index c7a1b4f..6228ba5 100644
--- a/src/verifier/reg_type_cache.cc
+++ b/src/verifier/reg_type_cache.cc
@@ -148,6 +148,10 @@
     klass = class_linker->FindClass(descriptor.c_str(), loader);
   } else {
     klass = class_linker->LookupClass(descriptor.c_str(), loader);
+    if (klass != NULL && !klass->IsLoaded()) {
+      // We found the class but without it being loaded its not safe for use.
+      klass = NULL;
+    }
   }
   return klass;
 }
@@ -244,12 +248,12 @@
 RegTypeCache::~RegTypeCache() {
   CHECK_LE(primitive_count_, entries_.size());
   // Delete only the non primitive types.
-  if (primitive_count_  == static_cast<uint16_t>(entries_.size())) {
+  if (entries_.size() == kNumPrimitives) {
     // All entries are primitive, nothing to delete.
     return;
   }
   std::vector<RegType*>::iterator non_primitive_begin = entries_.begin();
-  std::advance(non_primitive_begin, primitive_count_);
+  std::advance(non_primitive_begin, kNumPrimitives);
   STLDeleteContainerPointers(non_primitive_begin, entries_.end());
 }
 
@@ -267,8 +271,8 @@
     FloatType::GetInstance();
     DoubleLoType::Destroy();
     DoubleHiType::Destroy();
-   RegTypeCache::primitive_initialized_ = false;
-   RegTypeCache::primitive_count_ = 0;
+    RegTypeCache::primitive_initialized_ = false;
+    RegTypeCache::primitive_count_ = 0;
   }
 }
 
diff --git a/src/verifier/reg_type_cache.h b/src/verifier/reg_type_cache.h
index cacd517..41d3c69 100644
--- a/src/verifier/reg_type_cache.h
+++ b/src/verifier/reg_type_cache.h
@@ -35,6 +35,7 @@
 
 class RegType;
 
+const size_t kNumPrimitives = 12;
 class RegTypeCache {
  public:
   explicit RegTypeCache(bool can_load_classes) : can_load_classes_(can_load_classes) {
@@ -45,7 +46,7 @@
     if(!RegTypeCache::primitive_initialized_) {
       CHECK_EQ(RegTypeCache::primitive_count_, 0);
       CreatePrimitiveTypes();
-      CHECK_EQ(RegTypeCache::primitive_count_, 12);
+      CHECK_EQ(RegTypeCache::primitive_count_, kNumPrimitives);
       RegTypeCache::primitive_initialized_ = true;
     }
   }
diff --git a/test/003-omnibus-opcodes/expected.txt b/test/003-omnibus-opcodes/expected.txt
index c5e67e5..a62c498 100644
--- a/test/003-omnibus-opcodes/expected.txt
+++ b/test/003-omnibus-opcodes/expected.txt
@@ -70,14 +70,6 @@
 UnresTest1...
 UnresTest1...
 UnresTest2...
-java.lang.NoClassDefFoundError: Failed resolution of: LUnresClass;
-	at UnresTest2.run(UnresTest2.java:33)
-	at Main.run(Main.java:64)
-	at Main.main(Main.java:26)
-java.lang.NoClassDefFoundError: Failed resolution of: LUnresClassSubclass;
-	at UnresTest2.run(UnresTest2.java:41)
-	at Main.run(Main.java:64)
-	at Main.main(Main.java:26)
 UnresTest2 done
 InternedString.run
 Done!
diff --git a/test/003-omnibus-opcodes/src/UnresTest2.java b/test/003-omnibus-opcodes/src/UnresTest2.java
index c94f226..4135d73 100644
--- a/test/003-omnibus-opcodes/src/UnresTest2.java
+++ b/test/003-omnibus-opcodes/src/UnresTest2.java
@@ -33,22 +33,23 @@
             un = new UnresClass();
             Main.assertTrue(false);
         } catch (NoClassDefFoundError ncdfe) {
-          ncdfe.printStackTrace();
+            Main.assertTrue(ncdfe.getCause() instanceof ClassNotFoundException);
             // good
         }
 
         try {
-          new UnresClassSubclass();
-          Main.assertTrue(false);
+            new UnresClassSubclass();
+            Main.assertTrue(false);
         } catch (NoClassDefFoundError ncdfe) {
-          ncdfe.printStackTrace();
-          // good
+            Main.assertTrue(ncdfe.getCause() instanceof ClassNotFoundException);
+            // good
         }
 
         try {
             UnresClass[] uar = new UnresClass[3];
             Main.assertTrue(false);
         } catch (NoClassDefFoundError ncdfe) {
+            Main.assertTrue(ncdfe.getCause() instanceof ClassNotFoundException);
             // good
         }
 
diff --git a/test/044-proxy/src/ReturnsAndArgPassing.java b/test/044-proxy/src/ReturnsAndArgPassing.java
index 50eff77..a173410 100644
--- a/test/044-proxy/src/ReturnsAndArgPassing.java
+++ b/test/044-proxy/src/ReturnsAndArgPassing.java
@@ -51,6 +51,8 @@
   static int barInvocations = 0;
 
   static class MyInvocationHandler implements InvocationHandler {
+    boolean causeNpeOnReturn = false;
+    Class<?> returnType = null;
     public Object invoke(Object proxy, Method method, Object[] args) {
       check(proxy instanceof Proxy);
       check(method.getDeclaringClass() == MyInterface.class);
@@ -62,30 +64,29 @@
         check(args == null);
         barInvocations++;
       }
-      if      (name.equals("voidFoo"))    { return null; }
-      else if (name.equals("voidBar"))    { return null; }
-      else if (name.equals("booleanFoo")) { return true; }
-      else if (name.equals("booleanBar")) { return false; }
-      else if (name.equals("byteFoo"))    { return Byte.MAX_VALUE; }
-      else if (name.equals("byteBar"))    { return Byte.MIN_VALUE; }
-      else if (name.equals("charFoo"))    { return Character.MAX_VALUE; }
-      else if (name.equals("charBar"))    { return Character.MIN_VALUE; }
-      else if (name.equals("shortFoo"))   { return Short.MAX_VALUE; }
-      else if (name.equals("shortBar"))   { return Short.MIN_VALUE; }
-      else if (name.equals("intFoo"))     { return Integer.MAX_VALUE; }
-      else if (name.equals("intBar"))     { return Integer.MIN_VALUE; }
-      else if (name.equals("longFoo"))    { return Long.MAX_VALUE; }
-      else if (name.equals("longBar"))    { return Long.MIN_VALUE; }
-      else if (name.equals("floatFoo"))   { return Float.MAX_VALUE; }
-      else if (name.equals("floatBar"))   { return Float.MIN_VALUE; }
-      else if (name.equals("doubleFoo"))  { return Double.MAX_VALUE; }
-      else if (name.equals("doubleBar"))  { return Double.MIN_VALUE; }
-      else if (name.equals("selectArg")) {
+      if (causeNpeOnReturn) {
+        return null;
+      } else if (name.equals("voidFoo") || name.equals("voidBar")) {
+        return null;
+      } else if (name.equals("booleanFoo")) {
+        return true;
+      } else if (name.equals("booleanBar")) {
+        return false;
+      } else if (name.equals("selectArg")) {
         check(args.length == 6);
         int select = (Integer)args[0];
         return args[select];
       } else {
-        throw new AssertionError("Unexpect method " + method);
+        try {
+          if (name.endsWith("Foo")) {
+            return returnType.getField("MAX_VALUE").get(null);
+          } else {
+            check(name.endsWith("Bar"));
+            return returnType.getField("MIN_VALUE").get(null);
+          }
+        } catch (Exception e) {
+          throw new Error("return type = " + returnType, e);
+        }
       }
     }
   }
@@ -106,6 +107,7 @@
     check(barInvocations == 1);
 
     check(fooInvocations == 1);
+    myHandler.returnType = Boolean.class;
     check(proxyMyInterface.booleanFoo() == true);
     check(fooInvocations == 2);
 
@@ -114,6 +116,7 @@
     check(barInvocations == 2);
 
     check(fooInvocations == 2);
+    myHandler.returnType = Byte.class;
     check(proxyMyInterface.byteFoo() == Byte.MAX_VALUE);
     check(fooInvocations == 3);
 
@@ -122,6 +125,7 @@
     check(barInvocations == 3);
 
     check(fooInvocations == 3);
+    myHandler.returnType = Character.class;
     check(proxyMyInterface.charFoo() == Character.MAX_VALUE);
     check(fooInvocations == 4);
 
@@ -130,6 +134,7 @@
     check(barInvocations == 4);
 
     check(fooInvocations == 4);
+    myHandler.returnType = Short.class;
     check(proxyMyInterface.shortFoo() == Short.MAX_VALUE);
     check(fooInvocations == 5);
 
@@ -138,6 +143,7 @@
     check(barInvocations == 5);
 
     check(fooInvocations == 5);
+    myHandler.returnType = Integer.class;
     check(proxyMyInterface.intFoo() == Integer.MAX_VALUE);
     check(fooInvocations == 6);
 
@@ -146,6 +152,7 @@
     check(barInvocations == 6);
 
     check(fooInvocations == 6);
+    myHandler.returnType = Long.class;
     check(proxyMyInterface.longFoo() == Long.MAX_VALUE);
     check(fooInvocations == 7);
 
@@ -154,6 +161,7 @@
     check(barInvocations == 7);
 
     check(fooInvocations == 7);
+    myHandler.returnType = Float.class;
     check(proxyMyInterface.floatFoo() == Float.MAX_VALUE);
     check(fooInvocations == 8);
 
@@ -162,6 +170,7 @@
     check(barInvocations == 8);
 
     check(fooInvocations == 8);
+    myHandler.returnType = Double.class;
     check(proxyMyInterface.doubleFoo() == Double.MAX_VALUE);
     check(fooInvocations == 9);
 
@@ -169,6 +178,259 @@
     check(proxyMyInterface.doubleBar() == Double.MIN_VALUE);
     check(barInvocations == 9);
 
+    // Toggle flag to get return values to cause NPEs
+    myHandler.causeNpeOnReturn = true;
+
+    check(fooInvocations == 9);
+    try {
+        proxyMyInterface.booleanFoo();
+        throw new AssertionError("Expected NPE");
+    } catch (NullPointerException e) {
+    }
+    check(fooInvocations == 10);
+
+    check(barInvocations == 9);
+    try {
+        proxyMyInterface.booleanBar();
+        throw new AssertionError("Expected NPE");
+    } catch (NullPointerException e) {
+    }
+    check(barInvocations == 10);
+
+    check(fooInvocations == 10);
+    try {
+        proxyMyInterface.byteFoo();
+        throw new AssertionError("Expected NPE");
+    } catch (NullPointerException e) {
+    }
+    check(fooInvocations == 11);
+
+    check(barInvocations == 10);
+    try {
+        proxyMyInterface.byteBar();
+        throw new AssertionError("Expected NPE");
+    } catch (NullPointerException e) {
+    }
+    check(barInvocations == 11);
+
+    check(fooInvocations == 11);
+    try {
+        proxyMyInterface.charFoo();
+        throw new AssertionError("Expected NPE");
+    } catch (NullPointerException e) {
+    }
+    check(fooInvocations == 12);
+
+    check(barInvocations == 11);
+    try {
+        proxyMyInterface.charBar();
+        throw new AssertionError("Expected NPE");
+    } catch (NullPointerException e) {
+    }
+    check(barInvocations == 12);
+
+    check(fooInvocations == 12);
+    try {
+        proxyMyInterface.shortFoo();
+        throw new AssertionError("Expected NPE");
+    } catch (NullPointerException e) {
+    }
+    check(fooInvocations == 13);
+
+    check(barInvocations == 12);
+    try {
+        proxyMyInterface.shortBar();
+        throw new AssertionError("Expected NPE");
+    } catch (NullPointerException e) {
+    }
+    check(barInvocations == 13);
+
+    check(fooInvocations == 13);
+    try {
+        proxyMyInterface.intFoo();
+        throw new AssertionError("Expected NPE");
+    } catch (NullPointerException e) {
+    }
+    check(fooInvocations == 14);
+
+    check(barInvocations == 13);
+    try {
+        proxyMyInterface.intBar();
+        throw new AssertionError("Expected NPE");
+    } catch (NullPointerException e) {
+    }
+    check(barInvocations == 14);
+
+    check(fooInvocations == 14);
+    try {
+        proxyMyInterface.longFoo();
+        throw new AssertionError("Expected NPE");
+    } catch (NullPointerException e) {
+    }
+    check(fooInvocations == 15);
+
+    check(barInvocations == 14);
+    try {
+        proxyMyInterface.longBar();
+        throw new AssertionError("Expected NPE");
+    } catch (NullPointerException e) {
+    }
+    check(barInvocations == 15);
+
+    check(fooInvocations == 15);
+    try {
+        proxyMyInterface.floatFoo();
+        throw new AssertionError("Expected NPE");
+    } catch (NullPointerException e) {
+    }
+    check(fooInvocations == 16);
+
+    check(barInvocations == 15);
+    try {
+        proxyMyInterface.floatBar();
+        throw new AssertionError("Expected NPE");
+    } catch (NullPointerException e) {
+    }
+    check(barInvocations == 16);
+
+    check(fooInvocations == 16);
+    try {
+        proxyMyInterface.doubleFoo();
+        throw new AssertionError("Expected NPE");
+    } catch (NullPointerException e) {
+    }
+    check(fooInvocations == 17);
+
+    check(barInvocations == 16);
+    try {
+        proxyMyInterface.doubleBar();
+        throw new AssertionError("Expected NPE");
+    } catch (NullPointerException e) {
+    }
+    check(barInvocations == 17);
+
+    // Toggle flag to stop NPEs
+    myHandler.causeNpeOnReturn = false;
+
+    check(fooInvocations == 17);
+    myHandler.returnType = Double.class;  // Double -> byte == fail
+    try {
+        proxyMyInterface.byteFoo();
+        throw new AssertionError("Expected ClassCastException");
+    } catch (ClassCastException e) {
+    }
+    check(fooInvocations == 18);
+
+    check(barInvocations == 17);
+    try {
+        proxyMyInterface.byteBar();
+        throw new AssertionError("Expected NPE");
+    } catch (ClassCastException e) {
+    }
+    check(barInvocations == 18);
+
+    check(fooInvocations == 18);
+    myHandler.returnType = Float.class;  // Float -> byte == fail
+    try {
+        proxyMyInterface.byteFoo();
+        throw new AssertionError("Expected ClassCastException");
+    } catch (ClassCastException e) {
+    }
+    check(fooInvocations == 19);
+
+    check(barInvocations == 18);
+    try {
+        proxyMyInterface.byteBar();
+        throw new AssertionError("Expected NPE");
+    } catch (ClassCastException e) {
+    }
+    check(barInvocations == 19);
+
+    check(fooInvocations == 19);
+    myHandler.returnType = Long.class;  // Long -> byte == fail
+    try {
+        proxyMyInterface.byteFoo();
+        throw new AssertionError("Expected ClassCastException");
+    } catch (ClassCastException e) {
+    }
+    check(fooInvocations == 20);
+
+    check(barInvocations == 19);
+    try {
+        proxyMyInterface.byteBar();
+        throw new AssertionError("Expected NPE");
+    } catch (ClassCastException e) {
+    }
+    check(barInvocations == 20);
+
+    check(fooInvocations == 20);
+    myHandler.returnType = Integer.class;  // Int -> byte == fail
+    try {
+        proxyMyInterface.byteFoo();
+        throw new AssertionError("Expected ClassCastException");
+    } catch (ClassCastException e) {
+    }
+    check(fooInvocations == 21);
+
+    check(barInvocations == 20);
+    try {
+        proxyMyInterface.byteBar();
+        throw new AssertionError("Expected NPE");
+    } catch (ClassCastException e) {
+    }
+    check(barInvocations == 21);
+
+    check(fooInvocations == 21);
+    myHandler.returnType = Short.class;  // Short -> byte == fail
+    try {
+        proxyMyInterface.byteFoo();
+        throw new AssertionError("Expected ClassCastException");
+    } catch (ClassCastException e) {
+    }
+    check(fooInvocations == 22);
+
+    check(barInvocations == 21);
+    try {
+        proxyMyInterface.byteBar();
+        throw new AssertionError("Expected NPE");
+    } catch (ClassCastException e) {
+    }
+    check(barInvocations == 22);
+
+    check(fooInvocations == 22);
+    myHandler.returnType = Character.class;  // Char -> byte == fail
+    try {
+        proxyMyInterface.byteFoo();
+        throw new AssertionError("Expected ClassCastException");
+    } catch (ClassCastException e) {
+    }
+    check(fooInvocations == 23);
+
+    check(barInvocations == 22);
+    try {
+        proxyMyInterface.byteBar();
+        throw new AssertionError("Expected NPE");
+    } catch (ClassCastException e) {
+    }
+    check(barInvocations == 23);
+
+    check(fooInvocations == 23);
+    myHandler.returnType = Character.class;  // Char -> short == fail
+    try {
+        proxyMyInterface.shortFoo();
+        throw new AssertionError("Expected ClassCastException");
+    } catch (ClassCastException e) {
+    }
+    check(fooInvocations == 24);
+
+    check(barInvocations == 23);
+    try {
+        proxyMyInterface.shortBar();
+        throw new AssertionError("Expected NPE");
+    } catch (ClassCastException e) {
+    }
+    check(barInvocations == 24);
+
     System.out.println(testName + ".testProxyReturns PASSED");
   }
 
diff --git a/test/100-reflect2/expected.txt b/test/100-reflect2/expected.txt
index 9417174..0c567d4 100644
--- a/test/100-reflect2/expected.txt
+++ b/test/100-reflect2/expected.txt
@@ -22,7 +22,7 @@
 30
 62
 14
-java.lang.IllegalArgumentException: invalid primitive conversion from int to short
+java.lang.IllegalArgumentException: Invalid primitive conversion from int to short
 	at java.lang.reflect.Field.set(Native Method)
 	at Main.testFieldReflection(Main.java:121)
 	at Main.main(Main.java:269)
diff --git a/test/201-built-in-exception-detail-messages/src/Main.java b/test/201-built-in-exception-detail-messages/src/Main.java
index 9b67db6..f8da644 100644
--- a/test/201-built-in-exception-detail-messages/src/Main.java
+++ b/test/201-built-in-exception-detail-messages/src/Main.java
@@ -393,7 +393,7 @@
       m.invoke("hello"); // Wrong number of arguments.
       fail();
     } catch (IllegalArgumentException iae) {
-      assertEquals("wrong number of arguments; expected 1, got 0", iae.getMessage());
+      assertEquals("Wrong number of arguments; expected 1, got 0", iae.getMessage());
     }
     try {
       Method m = String.class.getMethod("charAt", int.class);
@@ -414,14 +414,14 @@
       m.invoke(new Integer(5)); // Wrong type for 'this'.
       fail();
     } catch (IllegalArgumentException iae) {
-      assertEquals("expected receiver of type java.lang.String, but got java.lang.Integer", iae.getMessage());
+      assertEquals("Expected receiver of type java.lang.String, but got java.lang.Integer", iae.getMessage());
     }
     try {
       Method m = String.class.getMethod("charAt", int.class);
       m.invoke(null); // Null for 'this'.
       fail();
     } catch (NullPointerException npe) {
-      assertEquals("expected receiver of type java.lang.String, but got null", npe.getMessage());
+      assertEquals("null receiver", npe.getMessage());
     }
   }
 
diff --git a/test/StackWalk/stack_walk_jni.cc b/test/StackWalk/stack_walk_jni.cc
index 92cfa99..4b472da 100644
--- a/test/StackWalk/stack_walk_jni.cc
+++ b/test/StackWalk/stack_walk_jni.cc
@@ -21,6 +21,7 @@
 #include "gc_map.h"
 #include "mirror/abstract_method.h"
 #include "mirror/abstract_method-inl.h"
+#include "mirror/class-inl.h"
 #include "mirror/object_array-inl.h"
 #include "mirror/object-inl.h"
 #include "object_utils.h"