Merge "ART: Add GetClassVersionNumbers."
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index f708e26..d8b780a 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -121,14 +121,14 @@
 ART_GTEST_dex2oat_environment_tests_HOST_DEPS := \
   $(HOST_CORE_IMAGE_optimizing_pic_64) \
   $(HOST_CORE_IMAGE_optimizing_pic_32) \
-  $(HOST_CORE_IMAGE_optimizing_no-pic_64) \
-  $(HOST_CORE_IMAGE_optimizing_no-pic_32) \
+  $(HOST_CORE_IMAGE_interpreter_pic_64) \
+  $(HOST_CORE_IMAGE_interpreter_pic_32) \
   $(HOST_OUT_EXECUTABLES)/patchoatd
 ART_GTEST_dex2oat_environment_tests_TARGET_DEPS := \
   $(TARGET_CORE_IMAGE_optimizing_pic_64) \
   $(TARGET_CORE_IMAGE_optimizing_pic_32) \
-  $(TARGET_CORE_IMAGE_optimizing_no-pic_64) \
-  $(TARGET_CORE_IMAGE_optimizing_no-pic_32) \
+  $(TARGET_CORE_IMAGE_interpreter_pic_64) \
+  $(TARGET_CORE_IMAGE_interpreter_pic_32) \
   $(TARGET_OUT_EXECUTABLES)/patchoatd
 
 ART_GTEST_oat_file_assistant_test_HOST_DEPS := \
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 4109345..b3b1903 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -2341,6 +2341,16 @@
 void ImageWriter::CopyAndFixupMethod(ArtMethod* orig,
                                      ArtMethod* copy,
                                      const ImageInfo& image_info) {
+  if (orig->IsAbstract()) {
+    // Ignore the single-implementation info for abstract method.
+    // Do this on orig instead of copy, otherwise there is a crash due to methods
+    // are copied before classes.
+    // TODO: handle fixup of single-implementation method for abstract method.
+    orig->SetHasSingleImplementation(false);
+    orig->SetSingleImplementation(
+        nullptr, Runtime::Current()->GetClassLinker()->GetImagePointerSize());
+  }
+
   memcpy(copy, orig, ArtMethod::Size(target_ptr_size_));
 
   copy->SetDeclaringClass(GetImageAddress(orig->GetDeclaringClassUnchecked()));
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 5d40f75..7772e8f 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -304,7 +304,8 @@
     // We do not support HDeoptimize in OSR methods.
     return nullptr;
   }
-  return resolved_method->GetSingleImplementation();
+  PointerSize pointer_size = caller_compilation_unit_.GetClassLinker()->GetImagePointerSize();
+  return resolved_method->GetSingleImplementation(pointer_size);
 }
 
 bool HInliner::TryInline(HInvoke* invoke_instruction) {
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 4f06a91..5fc9972 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -1414,7 +1414,14 @@
       ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
     }
 
-    {
+    // The two tests below make sure that fiddling with the method kind
+    // (static, virtual, interface) is detected by `ValidateDependencies`.
+
+    // An interface method lookup can succeed with a virtual method lookup on the same class.
+    // That's OK, as we only want to make sure there is a method being defined with the right
+    // flags. Therefore, polluting the interface methods with virtual methods does not have
+    // to fail verification.
+    if (resolution_kind != kVirtualMethodResolution) {
       VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
       VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
       bool found = false;
@@ -1433,7 +1440,8 @@
       ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
     }
 
-    {
+    // See comment above that applies the same way.
+    if (resolution_kind != kInterfaceMethodResolution) {
       VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
       VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
       bool found = false;
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index d7d39af..92f3727 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -55,15 +55,13 @@
 extern "C" void art_quick_invoke_static_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*,
                                              const char*);
 
-ArtMethod* ArtMethod::GetSingleImplementation() {
+ArtMethod* ArtMethod::GetSingleImplementation(PointerSize pointer_size) {
   DCHECK(!IsNative());
   if (!IsAbstract()) {
     // A non-abstract's single implementation is itself.
     return this;
   }
-  // TODO: add single-implementation logic for abstract method by storing it
-  // in ptr_sized_fields_.
-  return nullptr;
+  return reinterpret_cast<ArtMethod*>(GetDataPtrSize(pointer_size));
 }
 
 ArtMethod* ArtMethod::FromReflectedMethod(const ScopedObjectAccessAlreadyRunnable& soa,
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 17f343d..a33111a 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -456,7 +456,7 @@
     }
   }
 
-  ArtMethod* GetSingleImplementation()
+  ArtMethod* GetSingleImplementation(PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   ALWAYS_INLINE void SetSingleImplementation(ArtMethod* method, PointerSize pointer_size) {
@@ -684,7 +684,8 @@
     ArtMethod** dex_cache_resolved_methods_;
 
     // Pointer to JNI function registered to this method, or a function to resolve the JNI function,
-    // or the profiling data for non-native methods, or an ImtConflictTable.
+    // or the profiling data for non-native methods, or an ImtConflictTable, or the
+    // single-implementation of an abstract method.
     void* data_;
 
     // Method dispatch from quick compiled code invokes this pointer which may cause bridging into
diff --git a/runtime/cha.cc b/runtime/cha.cc
index d94b091..a1ad585 100644
--- a/runtime/cha.cc
+++ b/runtime/cha.cc
@@ -185,7 +185,8 @@
 };
 
 void ClassHierarchyAnalysis::VerifyNonSingleImplementation(mirror::Class* verify_class,
-                                                           uint16_t verify_index) {
+                                                           uint16_t verify_index,
+                                                           ArtMethod* excluded_method) {
   // Grab cha_lock_ to make sure all single-implementation updates are seen.
   PointerSize image_pointer_size =
       Runtime::Current()->GetClassLinker()->GetImagePointerSize();
@@ -195,9 +196,11 @@
       return;
     }
     ArtMethod* verify_method = verify_class->GetVTableEntry(verify_index, image_pointer_size);
-    DCHECK(!verify_method->HasSingleImplementation())
-        << "class: " << verify_class->PrettyClass()
-        << " verify_method: " << verify_method->PrettyMethod(true);
+    if (verify_method != excluded_method) {
+      DCHECK(!verify_method->HasSingleImplementation())
+          << "class: " << verify_class->PrettyClass()
+          << " verify_method: " << verify_method->PrettyMethod(true);
+    }
     verify_class = verify_class->GetSuperClass();
   }
 }
@@ -206,41 +209,152 @@
     Handle<mirror::Class> klass,
     ArtMethod* virtual_method,
     ArtMethod* method_in_super,
-    std::unordered_set<ArtMethod*>& invalidated_single_impl_methods) {
+    std::unordered_set<ArtMethod*>& invalidated_single_impl_methods,
+    PointerSize pointer_size) {
   // TODO: if klass is not instantiable, virtual_method isn't invocable yet so
   // even if it overrides, it doesn't invalidate single-implementation
   // assumption.
 
-  DCHECK_NE(virtual_method, method_in_super);
+  DCHECK((virtual_method != method_in_super) || virtual_method->IsAbstract());
   DCHECK(method_in_super->GetDeclaringClass()->IsResolved()) << "class isn't resolved";
   // If virtual_method doesn't come from a default interface method, it should
   // be supplied by klass.
-  DCHECK(virtual_method->IsCopied() ||
+  DCHECK(virtual_method == method_in_super ||
+         virtual_method->IsCopied() ||
          virtual_method->GetDeclaringClass() == klass.Get());
 
-  // A new virtual_method should set method_in_super to
-  // non-single-implementation (if not set already).
-  // We don't grab cha_lock_. Single-implementation flag won't be set to true
-  // again once it's set to false.
+  // To make updating single-implementation flags simple, we always maintain the following
+  // invariant:
+  // Say all virtual methods in the same vtable slot, starting from the bottom child class
+  // to super classes, is a sequence of unique methods m3, m2, m1, ... (after removing duplicate
+  // methods for inherited methods).
+  // For example for the following class hierarchy,
+  //   class A { void m() { ... } }
+  //   class B extends A { void m() { ... } }
+  //   class C extends B {}
+  //   class D extends C { void m() { ... } }
+  // the sequence is D.m(), B.m(), A.m().
+  // The single-implementation status for that sequence of methods begin with one or two true's,
+  // then become all falses. The only case where two true's are possible is for one abstract
+  // method m and one non-abstract method mImpl that overrides method m.
+  // With the invariant, when linking in a new class, we only need to at most update one or
+  // two methods in the sequence for their single-implementation status, in order to maintain
+  // the invariant.
+
   if (!method_in_super->HasSingleImplementation()) {
     // method_in_super already has multiple implementations. All methods in the
     // same vtable slots in its super classes should have
     // non-single-implementation already.
     if (kIsDebugBuild) {
       VerifyNonSingleImplementation(klass->GetSuperClass()->GetSuperClass(),
-                                    method_in_super->GetMethodIndex());
+                                    method_in_super->GetMethodIndex(),
+                                    nullptr /* excluded_method */);
     }
     return;
   }
 
   // Native methods don't have single-implementation flag set.
   DCHECK(!method_in_super->IsNative());
-  // Invalidate method_in_super's single-implementation status.
-  invalidated_single_impl_methods.insert(method_in_super);
+
+  uint16_t method_index = method_in_super->GetMethodIndex();
+  if (method_in_super->IsAbstract()) {
+    if (kIsDebugBuild) {
+      // An abstract method should have made all methods in the same vtable
+      // slot above it in the class hierarchy having non-single-implementation.
+      mirror::Class* super_super = klass->GetSuperClass()->GetSuperClass();
+      VerifyNonSingleImplementation(super_super,
+                                    method_index,
+                                    method_in_super);
+    }
+
+    if (virtual_method->IsAbstract()) {
+      // SUPER: abstract, VIRTUAL: abstract.
+      if (method_in_super == virtual_method) {
+        DCHECK(klass->IsInstantiable());
+        // An instantiable subclass hasn't provided a concrete implementation of
+        // the abstract method. Invoking method_in_super may throw AbstractMethodError.
+        // This is an uncommon case, so we simply treat method_in_super as not
+        // having single-implementation.
+        invalidated_single_impl_methods.insert(method_in_super);
+        return;
+      } else {
+        // One abstract method overrides another abstract method. This is an uncommon
+        // case. We simply treat method_in_super as not having single-implementation.
+        invalidated_single_impl_methods.insert(method_in_super);
+        return;
+      }
+    } else {
+      // SUPER: abstract, VIRTUAL: non-abstract.
+      // A non-abstract method overrides an abstract method.
+      if (method_in_super->GetSingleImplementation(pointer_size) == nullptr) {
+        // Abstract method_in_super gets its first implementation. Keep it as having
+        // single-implementation and record that single implementation.
+        DCHECK(method_in_super->HasSingleImplementation());
+        method_in_super->SetSingleImplementation(virtual_method, pointer_size);
+        return;
+      } else {
+        // Abstract method_in_super already got one implementation.
+        // Invalidate method_in_super's single-implementation status.
+        invalidated_single_impl_methods.insert(method_in_super);
+        return;
+      }
+    }
+  } else {
+    if (virtual_method->IsAbstract()) {
+      // SUPER: non-abstract, VIRTUAL: abstract.
+      // An abstract method overrides a non-abstract method. This is an uncommon
+      // case, we simply treat both methods as not having single-implementation.
+      invalidated_single_impl_methods.insert(virtual_method);
+      // Fall-through to handle invalidating method_in_super of its
+      // single-implementation status.
+    }
+
+    // SUPER: non-abstract, VIRTUAL: non-abstract/abstract(fall-through from previous if).
+    // Invalidate method_in_super's single-implementation status.
+    invalidated_single_impl_methods.insert(method_in_super);
+
+    // method_in_super might be the single-implementation of another abstract method,
+    // which should be also invalidated of its single-implementation status.
+    mirror::Class* super_super = klass->GetSuperClass()->GetSuperClass();
+    while (super_super != nullptr &&
+           method_index < super_super->GetVTableLength()) {
+      ArtMethod* method_in_super_super = super_super->GetVTableEntry(method_index, pointer_size);
+      if (method_in_super_super != method_in_super) {
+        if (method_in_super_super->IsAbstract()) {
+          if (method_in_super_super->HasSingleImplementation()) {
+            // Invalidate method_in_super's single-implementation status.
+            invalidated_single_impl_methods.insert(method_in_super_super);
+            // No need to further traverse up the class hierarchy since if there
+            // are cases that one abstract method overrides another method, we
+            // should have made that method having non-single-implementation already.
+          } else {
+            // method_in_super_super is already non-single-implementation.
+            // No need to further traverse up the class hierarchy.
+          }
+        } else {
+          DCHECK(!method_in_super_super->HasSingleImplementation());
+          // No need to further traverse up the class hierarchy since two non-abstract
+          // methods (method_in_super and method_in_super_super) should have set all
+          // other methods (abstract or not) in the vtable slot to be non-single-implementation.
+        }
+
+        if (kIsDebugBuild) {
+          VerifyNonSingleImplementation(super_super->GetSuperClass(),
+                                        method_index,
+                                        method_in_super_super);
+        }
+        // No need to go any further.
+        return;
+      } else {
+        super_super = super_super->GetSuperClass();
+      }
+    }
+  }
 }
 
 void ClassHierarchyAnalysis::InitSingleImplementationFlag(Handle<mirror::Class> klass,
-                                                          ArtMethod* method) {
+                                                          ArtMethod* method,
+                                                          PointerSize pointer_size) {
   DCHECK(method->IsCopied() || method->GetDeclaringClass() == klass.Get());
   if (klass->IsFinal() || method->IsFinal()) {
     // Final classes or methods do not need CHA for devirtualization.
@@ -253,16 +367,21 @@
     // cannot be inlined. It's not worthwhile to devirtualize the
     // call which can add a deoptimization point.
     DCHECK(!method->HasSingleImplementation());
+  } else if (method->IsAbstract()) {
+    if (method->GetDeclaringClass()->IsInstantiable()) {
+      // Rare case, but we do accept it (such as 800-smali/smali/b_26143249.smali).
+      // Do not attempt to devirtualize it.
+      method->SetHasSingleImplementation(false);
+    } else {
+      // Abstract method starts with single-implementation flag set and null
+      // implementation method.
+      method->SetHasSingleImplementation(true);
+      DCHECK(method->GetSingleImplementation(pointer_size) == nullptr);
+    }
   } else {
     method->SetHasSingleImplementation(true);
-    if (method->IsAbstract()) {
-      // There is no real implementation yet.
-      // TODO: implement single-implementation logic for abstract methods.
-      DCHECK(method->GetSingleImplementation() == nullptr);
-    } else {
-      // Single implementation of non-abstract method is itself.
-      DCHECK_EQ(method->GetSingleImplementation(), method);
-    }
+    // Single implementation of non-abstract method is itself.
+    DCHECK_EQ(method->GetSingleImplementation(pointer_size), method);
   }
 }
 
@@ -286,19 +405,29 @@
     ArtMethod* method_in_super = super_class->GetVTableEntry(i, image_pointer_size);
     if (method == method_in_super) {
       // vtable slot entry is inherited from super class.
+      if (method->IsAbstract() && klass->IsInstantiable()) {
+        // An instantiable class that inherits an abstract method is treated as
+        // supplying an implementation that throws AbstractMethodError.
+        CheckSingleImplementationInfo(klass,
+                                      method,
+                                      method_in_super,
+                                      invalidated_single_impl_methods,
+                                      image_pointer_size);
+      }
       continue;
     }
-    InitSingleImplementationFlag(klass, method);
+    InitSingleImplementationFlag(klass, method, image_pointer_size);
     CheckSingleImplementationInfo(klass,
                                   method,
                                   method_in_super,
-                                  invalidated_single_impl_methods);
+                                  invalidated_single_impl_methods,
+                                  image_pointer_size);
   }
 
   // For new virtual methods that don't override.
   for (int32_t i = super_class->GetVTableLength(); i < klass->GetVTableLength(); ++i) {
     ArtMethod* method = klass->GetVTableEntry(i, image_pointer_size);
-    InitSingleImplementationFlag(klass, method);
+    InitSingleImplementationFlag(klass, method, image_pointer_size);
   }
 
   Runtime* const runtime = Runtime::Current();
diff --git a/runtime/cha.h b/runtime/cha.h
index ada5c89..a56a752 100644
--- a/runtime/cha.h
+++ b/runtime/cha.h
@@ -112,7 +112,9 @@
   void UpdateAfterLoadingOf(Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
 
  private:
-  void InitSingleImplementationFlag(Handle<mirror::Class> klass, ArtMethod* method)
+  void InitSingleImplementationFlag(Handle<mirror::Class> klass,
+                                    ArtMethod* method,
+                                    PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // `virtual_method` in `klass` overrides `method_in_super`.
@@ -123,12 +125,16 @@
       Handle<mirror::Class> klass,
       ArtMethod* virtual_method,
       ArtMethod* method_in_super,
-      std::unordered_set<ArtMethod*>& invalidated_single_impl_methods)
+      std::unordered_set<ArtMethod*>& invalidated_single_impl_methods,
+      PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  // Verify all methods in the same vtable slot from verify_class and its supers
-  // don't have single-implementation.
-  void VerifyNonSingleImplementation(mirror::Class* verify_class, uint16_t verify_index)
+  // For all methods in vtable slot at `verify_index` of `verify_class` and its
+  // superclasses, single-implementation status should be false, except if the
+  // method is `excluded_method`.
+  void VerifyNonSingleImplementation(mirror::Class* verify_class,
+                                     uint16_t verify_index,
+                                     ArtMethod* excluded_method)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // A map that maps a method to a set of compiled code that assumes that method has a
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index b0c4597..7ae9f03 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -160,7 +160,7 @@
   // image at GetImageLocation(). This is used for testing mismatched
   // image checksums in the oat_file_assistant_tests.
   std::string GetImageLocation2() const {
-    return GetImageDirectory() + "/core-npic.art";
+    return GetImageDirectory() + "/core-interpreter.art";
   }
 
   std::string GetDexSrc1() const {
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 76777d9..28bcb97 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -596,7 +596,7 @@
     // Get the register arguments for the invoke.
     inst->GetVarArgs(args, inst_data);
     // Drop the first register which is the method handle performing the invoke.
-    memcpy(args, args + 1, sizeof(args[0]) * (Instruction::kMaxVarArgRegs - 1));
+    memmove(args, args + 1, sizeof(args[0]) * (Instruction::kMaxVarArgRegs - 1));
     args[Instruction::kMaxVarArgRegs - 1] = 0;
     return DoInvokePolymorphic<is_range, do_access_check>(self,
                                                           invoke_method,
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index afa804c..84eacde 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -152,15 +152,17 @@
       }
     }
 
-    if (CompilerFilter::IsBytecodeCompilationEnabled(filter)) {
-      if (relocate) {
-        EXPECT_EQ(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
-            oat_header.GetImageFileLocationOatDataBegin());
-        EXPECT_EQ(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
-      } else {
-        EXPECT_NE(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
-            oat_header.GetImageFileLocationOatDataBegin());
-        EXPECT_NE(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
+    if (!with_alternate_image) {
+      if (CompilerFilter::IsBytecodeCompilationEnabled(filter)) {
+        if (relocate) {
+          EXPECT_EQ(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
+              oat_header.GetImageFileLocationOatDataBegin());
+          EXPECT_EQ(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
+        } else {
+          EXPECT_NE(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
+              oat_header.GetImageFileLocationOatDataBegin());
+          EXPECT_NE(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
+        }
       }
     }
   }
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index d5c6520..acdd0d3 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -27,6 +27,7 @@
            "ti_method.cc",
            "ti_monitor.cc",
            "ti_object.cc",
+           "ti_phase.cc",
            "ti_properties.cc",
            "ti_search.cc",
            "ti_stack.cc",
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index a55f662..fcedd4e 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -55,6 +55,7 @@
 #include "ti_method.h"
 #include "ti_monitor.h"
 #include "ti_object.h"
+#include "ti_phase.h"
 #include "ti_properties.h"
 #include "ti_redefine.h"
 #include "ti_search.h"
@@ -198,11 +199,11 @@
   }
 
   static jvmtiError SetThreadLocalStorage(jvmtiEnv* env, jthread thread, const void* data) {
-    return ERR(NOT_IMPLEMENTED);
+    return ThreadUtil::SetThreadLocalStorage(env, thread, data);
   }
 
   static jvmtiError GetThreadLocalStorage(jvmtiEnv* env, jthread thread, void** data_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return ThreadUtil::GetThreadLocalStorage(env, thread, data_ptr);
   }
 
   static jvmtiError GetTopThreadGroups(jvmtiEnv* env,
@@ -1099,11 +1100,12 @@
   }
 
   static jvmtiError GetPhase(jvmtiEnv* env, jvmtiPhase* phase_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return PhaseUtil::GetPhase(env, phase_ptr);
   }
 
   static jvmtiError DisposeEnvironment(jvmtiEnv* env) {
     ENSURE_VALID_ENV(env);
+    gEventHandler.RemoveArtJvmTiEnv(ArtJvmTiEnv::AsArtJvmTiEnv(env));
     delete env;
     return OK;
   }
@@ -1300,8 +1302,17 @@
 // The plugin initialization function. This adds the jvmti environment.
 extern "C" bool ArtPlugin_Initialize() {
   art::Runtime* runtime = art::Runtime::Current();
+
+  if (runtime->IsStarted()) {
+    PhaseUtil::SetToLive();
+  } else {
+    PhaseUtil::SetToOnLoad();
+  }
+  PhaseUtil::Register(&gEventHandler);
+
   runtime->GetJavaVM()->AddEnvironmentHook(GetEnvHandler);
   runtime->AddSystemWeakHolder(&gObjectTagTable);
+
   return true;
 }
 
diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc
index f38aa86..7182055 100644
--- a/runtime/openjdkjvmti/events.cc
+++ b/runtime/openjdkjvmti/events.cc
@@ -144,6 +144,18 @@
   envs.push_back(env);
 }
 
+void EventHandler::RemoveArtJvmTiEnv(ArtJvmTiEnv* env) {
+  auto it = std::find(envs.begin(), envs.end(), env);
+  if (it != envs.end()) {
+    envs.erase(it);
+    for (size_t i = static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal);
+         i <= static_cast<size_t>(ArtJvmtiEvent::kMaxEventTypeVal);
+         ++i) {
+      RecalculateGlobalEventMask(static_cast<ArtJvmtiEvent>(i));
+    }
+  }
+}
+
 static bool IsThreadControllable(ArtJvmtiEvent event) {
   switch (event) {
     case ArtJvmtiEvent::kVmInit:
diff --git a/runtime/openjdkjvmti/events.h b/runtime/openjdkjvmti/events.h
index 08a8765..8e246de 100644
--- a/runtime/openjdkjvmti/events.h
+++ b/runtime/openjdkjvmti/events.h
@@ -141,6 +141,9 @@
   // enabled, yet.
   void RegisterArtJvmTiEnv(ArtJvmTiEnv* env);
 
+  // Remove an env.
+  void RemoveArtJvmTiEnv(ArtJvmTiEnv* env);
+
   bool IsEventEnabledAnywhere(ArtJvmtiEvent event) const {
     if (!EventMask::EventIsInRange(event)) {
       return false;
diff --git a/runtime/openjdkjvmti/ti_phase.cc b/runtime/openjdkjvmti/ti_phase.cc
new file mode 100644
index 0000000..85d6b72
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_phase.cc
@@ -0,0 +1,129 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_phase.h"
+
+#include "art_jvmti.h"
+#include "base/macros.h"
+#include "events-inl.h"
+#include "runtime.h"
+#include "runtime_callbacks.h"
+#include "ScopedLocalRef.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
+#include "thread_list.h"
+
+namespace openjdkjvmti {
+
+jvmtiPhase PhaseUtil::current_phase_ = static_cast<jvmtiPhase>(0);
+
+struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback {
+  inline static JNIEnv* GetJniEnv() {
+    return reinterpret_cast<JNIEnv*>(art::Thread::Current()->GetJniEnv());
+  }
+
+  inline static jthread GetCurrentJThread() {
+    art::ScopedObjectAccess soa(art::Thread::Current());
+    return soa.AddLocalReference<jthread>(soa.Self()->GetPeer());
+  }
+
+  void NextRuntimePhase(RuntimePhase phase) OVERRIDE {
+    // TODO: Events.
+    switch (phase) {
+      case RuntimePhase::kInitialAgents:
+        PhaseUtil::current_phase_ = JVMTI_PHASE_PRIMORDIAL;
+        break;
+      case RuntimePhase::kStart:
+        event_handler->DispatchEvent(nullptr, ArtJvmtiEvent::kVmStart, GetJniEnv());
+        PhaseUtil::current_phase_ = JVMTI_PHASE_START;
+        break;
+      case RuntimePhase::kInit:
+        {
+          ScopedLocalRef<jthread> thread(GetJniEnv(), GetCurrentJThread());
+          event_handler->DispatchEvent(nullptr,
+                                       ArtJvmtiEvent::kVmInit,
+                                       GetJniEnv(),
+                                       thread.get());
+          PhaseUtil::current_phase_ = JVMTI_PHASE_LIVE;
+        }
+        break;
+      case RuntimePhase::kDeath:
+        event_handler->DispatchEvent(nullptr, ArtJvmtiEvent::kVmDeath, GetJniEnv());
+        PhaseUtil::current_phase_ = JVMTI_PHASE_DEAD;
+        // TODO: Block events now.
+        break;
+    }
+  }
+
+  EventHandler* event_handler = nullptr;
+};
+
+PhaseUtil::PhaseCallback gPhaseCallback;
+
+jvmtiError PhaseUtil::GetPhase(jvmtiEnv* env ATTRIBUTE_UNUSED, jvmtiPhase* phase_ptr) {
+  if (phase_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+  jvmtiPhase now = PhaseUtil::current_phase_;
+  DCHECK(now == JVMTI_PHASE_ONLOAD ||
+         now == JVMTI_PHASE_PRIMORDIAL ||
+         now == JVMTI_PHASE_START ||
+         now == JVMTI_PHASE_LIVE ||
+         now == JVMTI_PHASE_DEAD);
+  *phase_ptr = now;
+  return ERR(NONE);
+}
+
+void PhaseUtil::SetToOnLoad() {
+  DCHECK_EQ(0u, static_cast<size_t>(PhaseUtil::current_phase_));
+  PhaseUtil::current_phase_ = JVMTI_PHASE_ONLOAD;
+}
+
+void PhaseUtil::SetToPrimordial() {
+  DCHECK_EQ(static_cast<size_t>(JVMTI_PHASE_ONLOAD), static_cast<size_t>(PhaseUtil::current_phase_));
+  PhaseUtil::current_phase_ = JVMTI_PHASE_ONLOAD;
+}
+
+void PhaseUtil::SetToLive() {
+  DCHECK_EQ(static_cast<size_t>(0), static_cast<size_t>(PhaseUtil::current_phase_));
+  PhaseUtil::current_phase_ = JVMTI_PHASE_LIVE;
+}
+
+void PhaseUtil::Register(EventHandler* handler) {
+  gPhaseCallback.event_handler = handler;
+  art::ScopedThreadStateChange stsc(art::Thread::Current(),
+                                    art::ThreadState::kWaitingForDebuggerToAttach);
+  art::ScopedSuspendAll ssa("Add phase callback");
+  art::Runtime::Current()->GetRuntimeCallbacks()->AddRuntimePhaseCallback(&gPhaseCallback);
+}
+
+
+}  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_phase.h b/runtime/openjdkjvmti/ti_phase.h
new file mode 100644
index 0000000..054652a
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_phase.h
@@ -0,0 +1,65 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_PHASE_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_PHASE_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class EventHandler;
+
+class PhaseUtil {
+ public:
+  static jvmtiError GetPhase(jvmtiEnv* env, jvmtiPhase* phase_ptr);
+
+  static void Register(EventHandler* event_handler);
+
+  // Move the phase from unitialized to LOAD.
+  static void SetToOnLoad();
+
+  // Move the phase from LOAD to PRIMORDIAL.
+  static void SetToPrimordial();
+
+  // Move the phase from unitialized to LIVE.
+  static void SetToLive();
+
+  struct PhaseCallback;
+
+ private:
+  static jvmtiPhase current_phase_;
+};
+
+}  // namespace openjdkjvmti
+
+#endif  // ART_RUNTIME_OPENJDKJVMTI_TI_PHASE_H_
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 8b355c8..4936a2f 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1383,6 +1383,10 @@
       LOG(ERROR) << "Unable to load an agent: " << err;
     }
   }
+  {
+    ScopedObjectAccess soa(self);
+    callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kInitialAgents);
+  }
 
   VLOG(startup) << "Runtime::Init exiting";
 
@@ -1395,7 +1399,7 @@
   constexpr const char* plugin_name = kIsDebugBuild ? "libopenjdkjvmtid.so" : "libopenjdkjvmti.so";
 
   // Is the plugin already loaded?
-  for (Plugin p : *plugins) {
+  for (const Plugin& p : *plugins) {
     if (p.GetLibrary() == plugin_name) {
       return true;
     }
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
index 6344c69..e580e78 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -58,9 +58,10 @@
 class RuntimePhaseCallback {
  public:
   enum RuntimePhase {
-    kStart,  // The runtime is started.
-    kInit,   // The runtime is initialized (and will run user code soon).
-    kDeath,  // The runtime just died.
+    kInitialAgents,   // Initial agent loading is done.
+    kStart,           // The runtime is started.
+    kInit,            // The runtime is initialized (and will run user code soon).
+    kDeath,           // The runtime just died.
   };
 
   virtual ~RuntimePhaseCallback() {}
diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc
index c379b5c..8974b59 100644
--- a/runtime/runtime_callbacks_test.cc
+++ b/runtime/runtime_callbacks_test.cc
@@ -351,8 +351,13 @@
 
   struct Callback : public RuntimePhaseCallback {
     void NextRuntimePhase(RuntimePhaseCallback::RuntimePhase p) OVERRIDE {
-      if (p == RuntimePhaseCallback::RuntimePhase::kStart) {
-        if (init_seen > 0) {
+      if (p == RuntimePhaseCallback::RuntimePhase::kInitialAgents) {
+        if (start_seen > 0 || init_seen > 0 || death_seen > 0) {
+          LOG(FATAL) << "Unexpected order";
+        }
+        ++initial_agents_seen;
+      } else if (p == RuntimePhaseCallback::RuntimePhase::kStart) {
+        if (init_seen > 0 || death_seen > 0) {
           LOG(FATAL) << "Init seen before start.";
         }
         ++start_seen;
@@ -365,6 +370,7 @@
       }
     }
 
+    size_t initial_agents_seen = 0;
     size_t start_seen = 0;
     size_t init_seen = 0;
     size_t death_seen = 0;
@@ -374,6 +380,7 @@
 };
 
 TEST_F(RuntimePhaseCallbackRuntimeCallbacksTest, Phases) {
+  ASSERT_EQ(0u, cb_.initial_agents_seen);
   ASSERT_EQ(0u, cb_.start_seen);
   ASSERT_EQ(0u, cb_.init_seen);
   ASSERT_EQ(0u, cb_.death_seen);
@@ -386,6 +393,7 @@
     ASSERT_TRUE(started);
   }
 
+  ASSERT_EQ(0u, cb_.initial_agents_seen);
   ASSERT_EQ(1u, cb_.start_seen);
   ASSERT_EQ(1u, cb_.init_seen);
   ASSERT_EQ(0u, cb_.death_seen);
@@ -393,6 +401,7 @@
   // Delete the runtime.
   runtime_.reset();
 
+  ASSERT_EQ(0u, cb_.initial_agents_seen);
   ASSERT_EQ(1u, cb_.start_seen);
   ASSERT_EQ(1u, cb_.init_seen);
   ASSERT_EQ(1u, cb_.death_seen);
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index 15cc566..1131607 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -963,20 +963,25 @@
   // Check recorded fields are resolved the same way, have the same recorded class,
   // and have the same recorded flags.
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  StackHandleScope<1> hs(self);
-  Handle<mirror::DexCache> dex_cache(
-      hs.NewHandle(class_linker->FindDexCache(self, dex_file, /* allow_failure */ false)));
   for (const auto& entry : fields) {
-    ArtField* field = class_linker->ResolveFieldJLS(
-        dex_file, entry.GetDexFieldIndex(), dex_cache, class_loader);
-
-    if (field == nullptr) {
-      DCHECK(self->IsExceptionPending());
-      self->ClearException();
+    const DexFile::FieldId& field_id = dex_file.GetFieldId(entry.GetDexFieldIndex());
+    StringPiece name(dex_file.StringDataByIdx(field_id.name_idx_));
+    StringPiece type(dex_file.StringDataByIdx(dex_file.GetTypeId(field_id.type_idx_).descriptor_idx_));
+    // Only use field_id.class_idx_ when the entry is unresolved, which is rare.
+    // Otherwise, we might end up resolving an application class, which is expensive.
+    std::string expected_decl_klass = entry.IsResolved()
+        ? GetStringFromId(dex_file, entry.GetDeclaringClassIndex())
+        : dex_file.StringByTypeIdx(field_id.class_idx_);
+    mirror::Class* cls = FindClassAndClearException(
+        class_linker, self, expected_decl_klass.c_str(), class_loader);
+    if (cls == nullptr) {
+      LOG(INFO) << "VerifierDeps: Could not resolve class " << expected_decl_klass;
+      return false;
     }
+    DCHECK(cls->IsResolved());
 
+    ArtField* field = mirror::Class::FindField(self, cls, name, type);
     if (entry.IsResolved()) {
-      std::string expected_decl_klass = GetStringFromId(dex_file, entry.GetDeclaringClassIndex());
       std::string temp;
       if (field == nullptr) {
         LOG(INFO) << "VerifierDeps: Could not resolve field "
@@ -1025,11 +1030,16 @@
 
     const char* name = dex_file.GetMethodName(method_id);
     const Signature signature = dex_file.GetMethodSignature(method_id);
-    const char* descriptor = dex_file.GetMethodDeclaringClassDescriptor(method_id);
+    // Only use method_id.class_idx_ when the entry is unresolved, which is rare.
+    // Otherwise, we might end up resolving an application class, which is expensive.
+    std::string expected_decl_klass = entry.IsResolved()
+        ? GetStringFromId(dex_file, entry.GetDeclaringClassIndex())
+        : dex_file.StringByTypeIdx(method_id.class_idx_);
 
-    mirror::Class* cls = FindClassAndClearException(class_linker, self, descriptor, class_loader);
+    mirror::Class* cls = FindClassAndClearException(
+        class_linker, self, expected_decl_klass.c_str(), class_loader);
     if (cls == nullptr) {
-      LOG(INFO) << "VerifierDeps: Could not resolve class " << descriptor;
+      LOG(INFO) << "VerifierDeps: Could not resolve class " << expected_decl_klass;
       return false;
     }
     DCHECK(cls->IsResolved());
@@ -1045,7 +1055,6 @@
 
     if (entry.IsResolved()) {
       std::string temp;
-      std::string expected_decl_klass = GetStringFromId(dex_file, entry.GetDeclaringClassIndex());
       if (method == nullptr) {
         LOG(INFO) << "VerifierDeps: Could not resolve "
                   << kind
diff --git a/test/616-cha-abstract/expected.txt b/test/616-cha-abstract/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/616-cha-abstract/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/616-cha-abstract/info.txt b/test/616-cha-abstract/info.txt
new file mode 100644
index 0000000..4f7e013
--- /dev/null
+++ b/test/616-cha-abstract/info.txt
@@ -0,0 +1 @@
+Test for Class Hierarchy Analysis (CHA) on abstract method.
diff --git a/test/616-cha-abstract/run b/test/616-cha-abstract/run
new file mode 100644
index 0000000..d8b4f0d
--- /dev/null
+++ b/test/616-cha-abstract/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 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.
+
+# Run without an app image to prevent the classes to be loaded at startup.
+exec ${RUN} "${@}" --no-app-image
diff --git a/test/616-cha-abstract/src/Main.java b/test/616-cha-abstract/src/Main.java
new file mode 100644
index 0000000..e1d7db1
--- /dev/null
+++ b/test/616-cha-abstract/src/Main.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+abstract class Base {
+  abstract void foo(int i);
+
+  void printError(String msg) {
+    System.out.println(msg);
+  }
+}
+
+class Main1 extends Base {
+  void foo(int i) {
+    if (i != 1) {
+      printError("error1");
+    }
+  }
+}
+
+class Main2 extends Main1 {
+  void foo(int i) {
+    if (i != 2) {
+      printError("error2");
+    }
+  }
+}
+
+public class Main {
+  static Main1 sMain1;
+  static Main1 sMain2;
+
+  static boolean sIsOptimizing = true;
+  static boolean sHasJIT = true;
+  static volatile boolean sOtherThreadStarted;
+
+  private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
+    if (hasSingleImplementation(clazz, method_name) != b) {
+      System.out.println(clazz + "." + method_name +
+          " doesn't have single implementation value of " + b);
+    }
+  }
+
+  // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
+  // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
+  // After Dummy.createMain2() which links in Main2, live testOverride() on stack
+  // should be deoptimized.
+  static void testOverride(boolean createMain2, boolean wait, boolean setHasJIT) {
+    if (setHasJIT) {
+      if (isInterpreted()) {
+        sHasJIT = false;
+      }
+      return;
+    }
+
+    if (createMain2 && (sIsOptimizing || sHasJIT)) {
+      assertIsManaged();
+    }
+
+    sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+
+    if (createMain2) {
+      // Wait for the other thread to start.
+      while (!sOtherThreadStarted);
+      // Create an Main2 instance and assign it to sMain2.
+      // sMain1 is kept the same.
+      sMain2 = Dummy.createMain2();
+      // Wake up the other thread.
+      synchronized(Main.class) {
+        Main.class.notify();
+      }
+    } else if (wait) {
+      // This is the other thread.
+      synchronized(Main.class) {
+        sOtherThreadStarted = true;
+        // Wait for Main2 to be linked and deoptimization is triggered.
+        try {
+          Main.class.wait();
+        } catch (Exception e) {
+        }
+      }
+    }
+
+    // There should be a deoptimization here right after Main2 is linked by
+    // calling Dummy.createMain2(), even though sMain1 didn't change.
+    // The behavior here would be different if inline-cache is used, which
+    // doesn't deoptimize since sMain1 still hits the type cache.
+    sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+    if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) {
+      // This method should be deoptimized right after Main2 is created.
+      assertIsInterpreted();
+    }
+
+    if (sMain2 != null) {
+      sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
+    }
+  }
+
+  // Test scenarios under which CHA-based devirtualization happens,
+  // and class loading that overrides a method can invalidate compiled code.
+  public static void main(String[] args) {
+    System.loadLibrary(args[0]);
+
+    if (isInterpreted()) {
+      sIsOptimizing = false;
+    }
+
+    // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
+    sMain1 = new Main1();
+
+    ensureJitCompiled(Main.class, "testOverride");
+    testOverride(false, false, true);
+
+    if (sHasJIT && !sIsOptimizing) {
+      assertSingleImplementation(Base.class, "foo", true);
+      assertSingleImplementation(Main1.class, "foo", true);
+    } else {
+      // Main2 is verified ahead-of-time so it's linked in already.
+    }
+
+    // Create another thread that also calls sMain1.foo().
+    // Try to test suspend and deopt another thread.
+    new Thread() {
+      public void run() {
+        testOverride(false, true, false);
+      }
+    }.start();
+
+    // This will create Main2 instance in the middle of testOverride().
+    testOverride(true, false, false);
+    assertSingleImplementation(Base.class, "foo", false);
+    assertSingleImplementation(Main1.class, "foo", false);
+  }
+
+  private static native void ensureJitCompiled(Class<?> itf, String method_name);
+  private static native void assertIsInterpreted();
+  private static native void assertIsManaged();
+  private static native boolean isInterpreted();
+  private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
+}
+
+// Put createMain2() in another class to avoid class loading due to verifier.
+class Dummy {
+  static Main1 createMain2() {
+    return new Main2();
+  }
+}
diff --git a/test/901-hello-ti-agent/basics.cc b/test/901-hello-ti-agent/basics.cc
index 052fb9a..0b17656 100644
--- a/test/901-hello-ti-agent/basics.cc
+++ b/test/901-hello-ti-agent/basics.cc
@@ -28,6 +28,46 @@
 namespace art {
 namespace Test901HelloTi {
 
+static void EnableEvent(jvmtiEnv* env, jvmtiEvent evt) {
+  jvmtiError error = env->SetEventNotificationMode(JVMTI_ENABLE, evt, nullptr);
+  if (error != JVMTI_ERROR_NONE) {
+    printf("Failed to enable event");
+  }
+}
+
+static void JNICALL VMStartCallback(jvmtiEnv *jenv ATTRIBUTE_UNUSED,
+                                     JNIEnv* jni_env ATTRIBUTE_UNUSED) {
+  printf("VMStart\n");
+}
+
+static void JNICALL VMInitCallback(jvmtiEnv *jvmti_env ATTRIBUTE_UNUSED,
+                                   JNIEnv* jni_env ATTRIBUTE_UNUSED,
+                                   jthread thread ATTRIBUTE_UNUSED) {
+  printf("VMInit\n");
+}
+
+static void JNICALL VMDeatchCallback(jvmtiEnv *jenv ATTRIBUTE_UNUSED,
+                                     JNIEnv* jni_env ATTRIBUTE_UNUSED) {
+  printf("VMDeath\n");
+}
+
+
+static void InstallVMEvents(jvmtiEnv* env) {
+  jvmtiEventCallbacks callbacks;
+  memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
+  callbacks.VMStart = VMStartCallback;
+  callbacks.VMInit = VMInitCallback;
+  callbacks.VMDeath = VMDeatchCallback;
+  jvmtiError ret = env->SetEventCallbacks(&callbacks, sizeof(callbacks));
+  if (ret != JVMTI_ERROR_NONE) {
+    printf("Failed to install callbacks");
+  }
+
+  EnableEvent(env, JVMTI_EVENT_VM_START);
+  EnableEvent(env, JVMTI_EVENT_VM_INIT);
+  EnableEvent(env, JVMTI_EVENT_VM_DEATH);
+}
+
 jint OnLoad(JavaVM* vm,
             char* options ATTRIBUTE_UNUSED,
             void* reserved ATTRIBUTE_UNUSED) {
@@ -72,6 +112,10 @@
     printf("Unexpected version number!\n");
     return -1;
   }
+
+  InstallVMEvents(env);
+  InstallVMEvents(env2);
+
   CHECK_CALL_SUCCESS(env->DisposeEnvironment());
   CHECK_CALL_SUCCESS(env2->DisposeEnvironment());
 #undef CHECK_CALL_SUCCESS
@@ -82,6 +126,19 @@
   }
   SetAllCapabilities(jvmti_env);
 
+  jvmtiPhase current_phase;
+  jvmtiError phase_result = jvmti_env->GetPhase(&current_phase);
+  if (phase_result != JVMTI_ERROR_NONE) {
+    printf("Could not get phase");
+    return 1;
+  }
+  if (current_phase != JVMTI_PHASE_ONLOAD) {
+    printf("Wrong phase");
+    return 1;
+  }
+
+  InstallVMEvents(jvmti_env);
+
   return JNI_OK;
 }
 
@@ -92,5 +149,15 @@
   JvmtiErrorToException(env, result);
 }
 
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_checkLivePhase(
+    JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
+  jvmtiPhase current_phase;
+  jvmtiError phase_result = jvmti_env->GetPhase(&current_phase);
+  if (JvmtiErrorToException(env, phase_result)) {
+    return JNI_FALSE;
+  }
+  return (current_phase == JVMTI_PHASE_LIVE) ? JNI_TRUE : JNI_FALSE;
+}
+
 }  // namespace Test901HelloTi
 }  // namespace art
diff --git a/test/901-hello-ti-agent/expected.txt b/test/901-hello-ti-agent/expected.txt
index 2aee99b..c4b24cb 100644
--- a/test/901-hello-ti-agent/expected.txt
+++ b/test/901-hello-ti-agent/expected.txt
@@ -1,8 +1,12 @@
 Loaded Agent for test 901-hello-ti-agent
+VMStart
+VMInit
 Hello, world!
+Agent in live phase.
 0
 1
 2
 4
 8
 JVMTI_ERROR_ILLEGAL_ARGUMENT
+VMDeath
diff --git a/test/901-hello-ti-agent/src/Main.java b/test/901-hello-ti-agent/src/Main.java
index 775e5c2..faf2dc2 100644
--- a/test/901-hello-ti-agent/src/Main.java
+++ b/test/901-hello-ti-agent/src/Main.java
@@ -20,6 +20,10 @@
 
     System.out.println("Hello, world!");
 
+    if (checkLivePhase()) {
+      System.out.println("Agent in live phase.");
+    }
+
     set(0);  // OTHER
     set(1);  // GC
     set(2);  // CLASS
@@ -37,5 +41,6 @@
     }
   }
 
+  private static native boolean checkLivePhase();
   private static native void setVerboseFlag(int flag, boolean value);
 }
diff --git a/test/924-threads/expected.txt b/test/924-threads/expected.txt
index 32e3368..3b7fb24 100644
--- a/test/924-threads/expected.txt
+++ b/test/924-threads/expected.txt
@@ -29,3 +29,5 @@
 5 = ALIVE|RUNNABLE
 2 = TERMINATED
 [Thread[FinalizerDaemon,5,system], Thread[FinalizerWatchdogDaemon,5,system], Thread[HeapTaskDaemon,5,system], Thread[ReferenceQueueDaemon,5,system], Thread[Signal Catcher,5,system], Thread[main,5,main]]
+JVMTI_ERROR_THREAD_NOT_ALIVE
+JVMTI_ERROR_THREAD_NOT_ALIVE
diff --git a/test/924-threads/src/Main.java b/test/924-threads/src/Main.java
index 492a7ac..dec49a8 100644
--- a/test/924-threads/src/Main.java
+++ b/test/924-threads/src/Main.java
@@ -56,6 +56,8 @@
     doStateTests();
 
     doAllThreadsTests();
+
+    doTLSTests();
   }
 
   private static class Holder {
@@ -164,6 +166,68 @@
     System.out.println(Arrays.toString(threads));
   }
 
+  private static void doTLSTests() throws Exception {
+    doTLSNonLiveTests();
+    doTLSLiveTests();
+  }
+
+  private static void doTLSNonLiveTests() throws Exception {
+    Thread t = new Thread();
+    try {
+      setTLS(t, 1);
+      System.out.println("Expected failure setting TLS for non-live thread");
+    } catch (Exception e) {
+      System.out.println(e.getMessage());
+    }
+    t.start();
+    t.join();
+    try {
+      setTLS(t, 1);
+      System.out.println("Expected failure setting TLS for non-live thread");
+    } catch (Exception e) {
+      System.out.println(e.getMessage());
+    }
+  }
+
+  private static void doTLSLiveTests() throws Exception {
+    setTLS(Thread.currentThread(), 1);
+
+    long l = getTLS(Thread.currentThread());
+    if (l != 1) {
+      throw new RuntimeException("Unexpected TLS value: " + l);
+    };
+
+    final CountDownLatch cdl1 = new CountDownLatch(1);
+    final CountDownLatch cdl2 = new CountDownLatch(1);
+
+    Runnable r = new Runnable() {
+      @Override
+      public void run() {
+        try {
+          cdl1.countDown();
+          cdl2.await();
+          setTLS(Thread.currentThread(), 2);
+          if (getTLS(Thread.currentThread()) != 2) {
+            throw new RuntimeException("Different thread issue");
+          }
+        } catch (Exception e) {
+          throw new RuntimeException(e);
+        }
+      }
+    };
+
+    Thread t = new Thread(r);
+    t.start();
+    cdl1.await();
+    setTLS(Thread.currentThread(), 1);
+    cdl2.countDown();
+
+    t.join();
+    if (getTLS(Thread.currentThread()) != 1) {
+      throw new RuntimeException("Got clobbered");
+    }
+  }
+
   private final static Comparator<Thread> THREAD_COMP = new Comparator<Thread>() {
     public int compare(Thread o1, Thread o2) {
       return o1.getName().compareTo(o2.getName());
@@ -229,4 +293,6 @@
   private static native Object[] getThreadInfo(Thread t);
   private static native int getThreadState(Thread t);
   private static native Thread[] getAllThreads();
+  private static native void setTLS(Thread t, long l);
+  private static native long getTLS(Thread t);
 }
diff --git a/test/924-threads/threads.cc b/test/924-threads/threads.cc
index 1487b7c..d35eaa8 100644
--- a/test/924-threads/threads.cc
+++ b/test/924-threads/threads.cc
@@ -120,5 +120,22 @@
   return ret;
 }
 
+extern "C" JNIEXPORT jlong JNICALL Java_Main_getTLS(
+    JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread) {
+  void* tls;
+  jvmtiError result = jvmti_env->GetThreadLocalStorage(thread, &tls);
+  if (JvmtiErrorToException(env, result)) {
+    return 0;
+  }
+  return static_cast<jlong>(reinterpret_cast<uintptr_t>(tls));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_setTLS(
+    JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread, jlong val) {
+  const void* tls = reinterpret_cast<void*>(static_cast<uintptr_t>(val));
+  jvmtiError result = jvmti_env->SetThreadLocalStorage(thread, tls);
+  JvmtiErrorToException(env, result);
+}
+
 }  // namespace Test924Threads
 }  // namespace art
diff --git a/test/valgrind-suppressions.txt b/test/valgrind-suppressions.txt
index fd3c331..c148ad0 100644
--- a/test/valgrind-suppressions.txt
+++ b/test/valgrind-suppressions.txt
@@ -22,3 +22,27 @@
    ...
    fun:_ZN3art7Runtime17InitNativeMethodsEv
 }
+
+# SigQuit runs libbacktrace
+{
+   BackTraceReading64
+   Memcheck:Addr8
+   fun:access_mem_unrestricted
+   fun:_Uelf64_memory_read
+   fun:_Uelf64_valid_object_memory
+   fun:map_create_list
+   fun:unw_map_local_create
+   fun:_ZN14UnwindMapLocal5BuildEv
+   fun:_ZN12BacktraceMap6CreateEib
+}
+{
+   BackTraceReading32
+   Memcheck:Addr4
+   fun:access_mem_unrestricted
+   fun:_Uelf32_memory_read
+   fun:_Uelf32_valid_object_memory
+   fun:map_create_list
+   fun:unw_map_local_create
+   fun:_ZN14UnwindMapLocal5BuildEv
+   fun:_ZN12BacktraceMap6CreateEib
+}