Merge changes from topic "ti-stress slicer"

* changes:
  Undefine CHECK while including slicer headers.
  Suppress warnings caused by including slicer headers in ti-stress
  Move ti-stress to use slicer directly
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 6fbb2bd..58b6137 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -1943,20 +1943,15 @@
   DCHECK_EQ(bss_size_, 0u);
   if (HasBootImage()) {
     DCHECK(bss_string_entries_.empty());
-    if (bss_method_entries_.empty() && bss_type_entries_.empty()) {
-      // Nothing to put to the .bss section.
-      return;
-    }
+  }
+  if (bss_method_entries_.empty() &&
+      bss_type_entries_.empty() &&
+      bss_string_entries_.empty()) {
+    // Nothing to put to the .bss section.
+    return;
   }
 
-  // Allocate space for app dex cache arrays in the .bss section.
   PointerSize pointer_size = GetInstructionSetPointerSize(instruction_set);
-  if (!HasBootImage()) {
-    for (const DexFile* dex_file : *dex_files_) {
-      DexCacheArraysLayout layout(pointer_size, dex_file);
-      bss_size_ += layout.Size();
-    }
-  }
 
   bss_methods_offset_ = bss_size_;
 
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index f8f4eb2..a249cac 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -331,8 +331,9 @@
 
 HLoopOptimization::HLoopOptimization(HGraph* graph,
                                      CompilerDriver* compiler_driver,
-                                     HInductionVarAnalysis* induction_analysis)
-    : HOptimization(graph, kLoopOptimizationPassName),
+                                     HInductionVarAnalysis* induction_analysis,
+                                     OptimizingCompilerStats* stats)
+    : HOptimization(graph, kLoopOptimizationPassName, stats),
       compiler_driver_(compiler_driver),
       induction_range_(induction_analysis),
       loop_allocator_(nullptr),
@@ -625,6 +626,7 @@
       TryAssignLastValue(node->loop_info, main_phi, preheader, /*collect_loop_uses*/ true)) {
     Vectorize(node, body, exit, trip_count);
     graph_->SetHasSIMD(true);  // flag SIMD usage
+    MaybeRecordStat(stats_, MethodCompilationStat::kLoopVectorized);
     return true;
   }
   return false;
@@ -1724,6 +1726,7 @@
                 vector_length_,
                 is_unsigned,
                 is_rounded));
+            MaybeRecordStat(stats_, MethodCompilationStat::kLoopVectorizedIdiom);
           } else {
             GenerateVecOp(instruction, vector_map_->Get(r), vector_map_->Get(s), type);
           }
diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h
index ba9126c..f347518 100644
--- a/compiler/optimizing/loop_optimization.h
+++ b/compiler/optimizing/loop_optimization.h
@@ -34,7 +34,8 @@
  public:
   HLoopOptimization(HGraph* graph,
                     CompilerDriver* compiler_driver,
-                    HInductionVarAnalysis* induction_analysis);
+                    HInductionVarAnalysis* induction_analysis,
+                    OptimizingCompilerStats* stats);
 
   void Run() OVERRIDE;
 
diff --git a/compiler/optimizing/loop_optimization_test.cc b/compiler/optimizing/loop_optimization_test.cc
index b5b03d8..1c5603d 100644
--- a/compiler/optimizing/loop_optimization_test.cc
+++ b/compiler/optimizing/loop_optimization_test.cc
@@ -31,7 +31,7 @@
         allocator_(&pool_),
         graph_(CreateGraph(&allocator_)),
         iva_(new (&allocator_) HInductionVarAnalysis(graph_)),
-        loop_opt_(new (&allocator_) HLoopOptimization(graph_, nullptr, iva_)) {
+        loop_opt_(new (&allocator_) HLoopOptimization(graph_, nullptr, iva_, nullptr)) {
     BuildGraph();
   }
 
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index e98c97c..71d91ae 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -509,7 +509,7 @@
   } else if (opt_name == SideEffectsAnalysis::kSideEffectsAnalysisPassName) {
     return new (arena) SideEffectsAnalysis(graph);
   } else if (opt_name == HLoopOptimization::kLoopOptimizationPassName) {
-    return new (arena) HLoopOptimization(graph, driver, most_recent_induction);
+    return new (arena) HLoopOptimization(graph, driver, most_recent_induction, stats);
   } else if (opt_name == CHAGuardOptimization::kCHAGuardOptimizationPassName) {
     return new (arena) CHAGuardOptimization(graph);
   } else if (opt_name == CodeSinking::kCodeSinkingPassName) {
@@ -770,7 +770,7 @@
   LICM* licm = new (arena) LICM(graph, *side_effects1, stats);
   HInductionVarAnalysis* induction = new (arena) HInductionVarAnalysis(graph);
   BoundsCheckElimination* bce = new (arena) BoundsCheckElimination(graph, *side_effects1, induction);
-  HLoopOptimization* loop = new (arena) HLoopOptimization(graph, driver, induction);
+  HLoopOptimization* loop = new (arena) HLoopOptimization(graph, driver, induction, stats);
   LoadStoreAnalysis* lsa = new (arena) LoadStoreAnalysis(graph);
   LoadStoreElimination* lse = new (arena) LoadStoreElimination(graph, *side_effects2, *lsa, stats);
   HSharpening* sharpening = new (arena) HSharpening(
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index d6da73c..ff49056 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -63,6 +63,8 @@
   kBooleanSimplified,
   kIntrinsicRecognized,
   kLoopInvariantMoved,
+  kLoopVectorized,
+  kLoopVectorizedIdiom,
   kSelectGenerated,
   kRemovedInstanceOf,
   kInlinedInvokeVirtualOrInterface,
@@ -183,6 +185,8 @@
       case kBooleanSimplified : name = "BooleanSimplified"; break;
       case kIntrinsicRecognized : name = "IntrinsicRecognized"; break;
       case kLoopInvariantMoved : name = "LoopInvariantMoved"; break;
+      case kLoopVectorized : name = "LoopVectorized"; break;
+      case kLoopVectorizedIdiom : name = "LoopVectorizedIdiom"; break;
       case kSelectGenerated : name = "SelectGenerated"; break;
       case kRemovedInstanceOf: name = "RemovedInstanceOf"; break;
       case kInlinedInvokeVirtualOrInterface: name = "InlinedInvokeVirtualOrInterface"; break;
diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc
index 6c0d492..277f611 100644
--- a/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/openjdkjvmti/OpenjdkJvmTi.cc
@@ -810,11 +810,11 @@
   }
 
   static jvmtiError GetObjectMonitorUsage(jvmtiEnv* env,
-                                          jobject object ATTRIBUTE_UNUSED,
-                                          jvmtiMonitorUsage* info_ptr ATTRIBUTE_UNUSED) {
+                                          jobject object,
+                                          jvmtiMonitorUsage* info_ptr) {
     ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_get_monitor_info);
-    return ERR(NOT_IMPLEMENTED);
+    return ObjectUtil::GetObjectMonitorUsage(env, object, info_ptr);
   }
 
   static jvmtiError GetFieldName(jvmtiEnv* env,
diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h
index 93eee28..d3f52f6 100644
--- a/openjdkjvmti/art_jvmti.h
+++ b/openjdkjvmti/art_jvmti.h
@@ -226,7 +226,7 @@
     .can_get_synthetic_attribute                     = 1,
     .can_get_owned_monitor_info                      = 1,
     .can_get_current_contended_monitor               = 0,
-    .can_get_monitor_info                            = 0,
+    .can_get_monitor_info                            = 1,
     .can_pop_frame                                   = 0,
     .can_redefine_classes                            = 1,
     .can_signal_thread                               = 0,
diff --git a/openjdkjvmti/ti_object.cc b/openjdkjvmti/ti_object.cc
index 2506aca..89ce352 100644
--- a/openjdkjvmti/ti_object.cc
+++ b/openjdkjvmti/ti_object.cc
@@ -35,6 +35,8 @@
 #include "mirror/object-inl.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread-current-inl.h"
+#include "thread_list.h"
+#include "ti_thread.h"
 
 namespace openjdkjvmti {
 
@@ -73,4 +75,59 @@
   return ERR(NONE);
 }
 
+jvmtiError ObjectUtil::GetObjectMonitorUsage(
+    jvmtiEnv* baseenv, jobject obj, jvmtiMonitorUsage* usage) {
+  ArtJvmTiEnv* env = ArtJvmTiEnv::AsArtJvmTiEnv(baseenv);
+  if (obj == nullptr) {
+    return ERR(INVALID_OBJECT);
+  }
+  if (usage == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+  art::Thread* self = art::Thread::Current();
+  ThreadUtil::SuspendCheck(self);
+  art::JNIEnvExt* jni = self->GetJniEnv();
+  std::vector<jthread> wait;
+  std::vector<jthread> notify_wait;
+  {
+    art::ScopedObjectAccess soa(self);      // Now we know we have the shared lock.
+    art::ScopedThreadSuspension sts(self, art::kNative);
+    art::ScopedSuspendAll ssa("GetObjectMonitorUsage", /*long_suspend*/false);
+    art::ObjPtr<art::mirror::Object> target(self->DecodeJObject(obj));
+    // This gets the list of threads trying to lock or wait on the monitor.
+    art::MonitorInfo info(target.Ptr());
+    usage->owner = info.owner_ != nullptr ?
+        jni->AddLocalReference<jthread>(info.owner_->GetPeerFromOtherThread()) : nullptr;
+    usage->entry_count = info.entry_count_;
+    for (art::Thread* thd : info.waiters_) {
+      // RI seems to consider waiting for notify to be included in those waiting to acquire the
+      // monitor. We will match this behavior.
+      notify_wait.push_back(jni->AddLocalReference<jthread>(thd->GetPeerFromOtherThread()));
+      wait.push_back(jni->AddLocalReference<jthread>(thd->GetPeerFromOtherThread()));
+    }
+    {
+      // Scan all threads to see which are waiting on this particular monitor.
+      art::MutexLock tll(self, *art::Locks::thread_list_lock_);
+      for (art::Thread* thd : art::Runtime::Current()->GetThreadList()->GetList()) {
+        if (thd != info.owner_ && target.Ptr() == thd->GetMonitorEnterObject()) {
+          wait.push_back(jni->AddLocalReference<jthread>(thd->GetPeerFromOtherThread()));
+        }
+      }
+    }
+  }
+  usage->waiter_count = wait.size();
+  usage->notify_waiter_count = notify_wait.size();
+  jvmtiError ret = CopyDataIntoJvmtiBuffer(env,
+                                           reinterpret_cast<const unsigned char*>(wait.data()),
+                                           wait.size() * sizeof(jthread),
+                                           reinterpret_cast<unsigned char**>(&usage->waiters));
+  if (ret != OK) {
+    return ret;
+  }
+  return CopyDataIntoJvmtiBuffer(env,
+                                 reinterpret_cast<const unsigned char*>(notify_wait.data()),
+                                 notify_wait.size() * sizeof(jthread),
+                                 reinterpret_cast<unsigned char**>(&usage->notify_waiters));
+}
+
 }  // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_object.h b/openjdkjvmti/ti_object.h
index fa3bd0f..977ec39 100644
--- a/openjdkjvmti/ti_object.h
+++ b/openjdkjvmti/ti_object.h
@@ -42,6 +42,8 @@
   static jvmtiError GetObjectSize(jvmtiEnv* env, jobject object, jlong* size_ptr);
 
   static jvmtiError GetObjectHashCode(jvmtiEnv* env, jobject object, jint* hash_code_ptr);
+
+  static jvmtiError GetObjectMonitorUsage(jvmtiEnv* env, jobject object, jvmtiMonitorUsage* usage);
 };
 
 }  // namespace openjdkjvmti
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index d22482f..efef975 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1189,219 +1189,41 @@
   }
 };
 
-// Copies data from one array to another array at the same position
-// if pred returns false. If there is a page of continuous data in
-// the src array for which pred consistently returns true then
-// corresponding page in the dst array will not be touched.
-// This should reduce number of allocated physical pages.
-template <class T, class NullPred>
-static void CopyNonNull(const T* src, size_t count, T* dst, const NullPred& pred) {
-  for (size_t i = 0; i < count; ++i) {
-    if (!pred(src[i])) {
-      dst[i] = src[i];
-    }
-  }
-}
-
-template <typename T>
-static void CopyDexCachePairs(const std::atomic<mirror::DexCachePair<T>>* src,
-                              size_t count,
-                              std::atomic<mirror::DexCachePair<T>>* dst) {
-  DCHECK_NE(count, 0u);
-  DCHECK(!src[0].load(std::memory_order_relaxed).object.IsNull() ||
-         src[0].load(std::memory_order_relaxed).index != 0u);
-  for (size_t i = 0; i < count; ++i) {
-    DCHECK_EQ(dst[i].load(std::memory_order_relaxed).index, 0u);
-    DCHECK(dst[i].load(std::memory_order_relaxed).object.IsNull());
-    mirror::DexCachePair<T> source = src[i].load(std::memory_order_relaxed);
-    if (source.index != 0u || !source.object.IsNull()) {
-      dst[i].store(source, std::memory_order_relaxed);
-    }
-  }
-}
-
-template <typename T>
-static void CopyNativeDexCachePairs(std::atomic<mirror::NativeDexCachePair<T>>* src,
-                                    size_t count,
-                                    std::atomic<mirror::NativeDexCachePair<T>>* dst,
-                                    PointerSize pointer_size) {
-  DCHECK_NE(count, 0u);
-  DCHECK(mirror::DexCache::GetNativePairPtrSize(src, 0, pointer_size).object != nullptr ||
-         mirror::DexCache::GetNativePairPtrSize(src, 0, pointer_size).index != 0u);
-  for (size_t i = 0; i < count; ++i) {
-    DCHECK_EQ(mirror::DexCache::GetNativePairPtrSize(dst, i, pointer_size).index, 0u);
-    DCHECK(mirror::DexCache::GetNativePairPtrSize(dst, i, pointer_size).object == nullptr);
-    mirror::NativeDexCachePair<T> source =
-        mirror::DexCache::GetNativePairPtrSize(src, i, pointer_size);
-    if (source.index != 0u || source.object != nullptr) {
-      mirror::DexCache::SetNativePairPtrSize(dst, i, source, pointer_size);
-    }
-  }
-}
-
 // new_class_set is the set of classes that were read from the class table section in the image.
 // If there was no class table section, it is null.
 // Note: using a class here to avoid having to make ClassLinker internals public.
 class AppImageClassLoadersAndDexCachesHelper {
  public:
-  static bool Update(
+  static void Update(
       ClassLinker* class_linker,
       gc::space::ImageSpace* space,
       Handle<mirror::ClassLoader> class_loader,
       Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches,
-      ClassTable::ClassSet* new_class_set,
-      bool* out_forward_dex_cache_array,
-      std::string* out_error_msg)
+      ClassTable::ClassSet* new_class_set)
       REQUIRES(!Locks::dex_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 };
 
-bool AppImageClassLoadersAndDexCachesHelper::Update(
+void AppImageClassLoadersAndDexCachesHelper::Update(
     ClassLinker* class_linker,
     gc::space::ImageSpace* space,
     Handle<mirror::ClassLoader> class_loader,
     Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches,
-    ClassTable::ClassSet* new_class_set,
-    bool* out_forward_dex_cache_array,
-    std::string* out_error_msg)
+    ClassTable::ClassSet* new_class_set)
     REQUIRES(!Locks::dex_lock_)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  DCHECK(out_forward_dex_cache_array != nullptr);
-  DCHECK(out_error_msg != nullptr);
-  PointerSize image_pointer_size = class_linker->GetImagePointerSize();
   Thread* const self = Thread::Current();
   gc::Heap* const heap = Runtime::Current()->GetHeap();
   const ImageHeader& header = space->GetImageHeader();
   {
-    // Add image classes into the class table for the class loader, and fixup the dex caches and
-    // class loader fields.
+    // Register dex caches with the class loader.
     WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
-    // Dex cache array fixup is all or nothing, we must reject app images that have mixed since we
-    // rely on clobering the dex cache arrays in the image to forward to bss.
-    size_t num_dex_caches_with_bss_arrays = 0;
     const size_t num_dex_caches = dex_caches->GetLength();
     for (size_t i = 0; i < num_dex_caches; i++) {
-      ObjPtr<mirror::DexCache> const dex_cache = dex_caches->Get(i);
-      const DexFile* const dex_file = dex_cache->GetDexFile();
-      const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile();
-      if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) {
-        ++num_dex_caches_with_bss_arrays;
-      }
-    }
-    *out_forward_dex_cache_array = num_dex_caches_with_bss_arrays != 0;
-    if (*out_forward_dex_cache_array) {
-      if (num_dex_caches_with_bss_arrays != num_dex_caches) {
-        // Reject application image since we cannot forward only some of the dex cache arrays.
-        // TODO: We could get around this by having a dedicated forwarding slot. It should be an
-        // uncommon case.
-        *out_error_msg = StringPrintf("Dex caches in bss does not match total: %zu vs %zu",
-                                      num_dex_caches_with_bss_arrays,
-                                      num_dex_caches);
-        return false;
-      }
-    }
-
-    // Only add the classes to the class loader after the points where we can return false.
-    for (size_t i = 0; i < num_dex_caches; i++) {
       ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i);
       const DexFile* const dex_file = dex_cache->GetDexFile();
-      const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile();
-      if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) {
-        // If the oat file expects the dex cache arrays to be in the BSS, then allocate there and
-        // copy over the arrays.
-        DCHECK(dex_file != nullptr);
-        size_t num_strings = mirror::DexCache::kDexCacheStringCacheSize;
-        if (dex_file->NumStringIds() < num_strings) {
-          num_strings = dex_file->NumStringIds();
-        }
-        size_t num_types = mirror::DexCache::kDexCacheTypeCacheSize;
-        if (dex_file->NumTypeIds() < num_types) {
-          num_types = dex_file->NumTypeIds();
-        }
-        size_t num_methods = mirror::DexCache::kDexCacheMethodCacheSize;
-        if (dex_file->NumMethodIds() < num_methods) {
-          num_methods = dex_file->NumMethodIds();
-        }
-        size_t num_fields = mirror::DexCache::kDexCacheFieldCacheSize;
-        if (dex_file->NumFieldIds() < num_fields) {
-          num_fields = dex_file->NumFieldIds();
-        }
-        size_t num_method_types = mirror::DexCache::kDexCacheMethodTypeCacheSize;
-        if (dex_file->NumProtoIds() < num_method_types) {
-          num_method_types = dex_file->NumProtoIds();
-        }
-        const size_t num_call_sites = dex_file->NumCallSiteIds();
-        CHECK_EQ(num_strings, dex_cache->NumStrings());
-        CHECK_EQ(num_types, dex_cache->NumResolvedTypes());
-        CHECK_EQ(num_methods, dex_cache->NumResolvedMethods());
-        CHECK_EQ(num_fields, dex_cache->NumResolvedFields());
-        CHECK_EQ(num_method_types, dex_cache->NumResolvedMethodTypes());
-        CHECK_EQ(num_call_sites, dex_cache->NumResolvedCallSites());
-        DexCacheArraysLayout layout(image_pointer_size, dex_file);
-        uint8_t* const raw_arrays = oat_dex_file->GetDexCacheArrays();
-        if (num_strings != 0u) {
-          mirror::StringDexCacheType* const image_resolved_strings = dex_cache->GetStrings();
-          mirror::StringDexCacheType* const strings =
-              reinterpret_cast<mirror::StringDexCacheType*>(raw_arrays + layout.StringsOffset());
-          CopyDexCachePairs(image_resolved_strings, num_strings, strings);
-          dex_cache->SetStrings(strings);
-        }
-        if (num_types != 0u) {
-          mirror::TypeDexCacheType* const image_resolved_types = dex_cache->GetResolvedTypes();
-          mirror::TypeDexCacheType* const types =
-              reinterpret_cast<mirror::TypeDexCacheType*>(raw_arrays + layout.TypesOffset());
-          CopyDexCachePairs(image_resolved_types, num_types, types);
-          dex_cache->SetResolvedTypes(types);
-        }
-        if (num_methods != 0u) {
-          mirror::MethodDexCacheType* const image_resolved_methods =
-              dex_cache->GetResolvedMethods();
-          mirror::MethodDexCacheType* const methods =
-              reinterpret_cast<mirror::MethodDexCacheType*>(raw_arrays + layout.MethodsOffset());
-          CopyNativeDexCachePairs(image_resolved_methods, num_methods, methods, image_pointer_size);
-          dex_cache->SetResolvedMethods(methods);
-        }
-        if (num_fields != 0u) {
-          mirror::FieldDexCacheType* const image_resolved_fields = dex_cache->GetResolvedFields();
-          mirror::FieldDexCacheType* const fields =
-              reinterpret_cast<mirror::FieldDexCacheType*>(raw_arrays + layout.FieldsOffset());
-          CopyNativeDexCachePairs(image_resolved_fields, num_fields, fields, image_pointer_size);
-          dex_cache->SetResolvedFields(fields);
-        }
-        if (num_method_types != 0u) {
-          // NOTE: We currently (Sep 2016) do not resolve any method types at
-          // compile time, but plan to in the future. This code exists for the
-          // sake of completeness.
-          mirror::MethodTypeDexCacheType* const image_resolved_method_types =
-              dex_cache->GetResolvedMethodTypes();
-          mirror::MethodTypeDexCacheType* const method_types =
-              reinterpret_cast<mirror::MethodTypeDexCacheType*>(
-                  raw_arrays + layout.MethodTypesOffset());
-          CopyDexCachePairs(image_resolved_method_types, num_method_types, method_types);
-          dex_cache->SetResolvedMethodTypes(method_types);
-        }
-        if (num_call_sites != 0u) {
-          GcRoot<mirror::CallSite>* const image_resolved_call_sites =
-              dex_cache->GetResolvedCallSites();
-          GcRoot<mirror::CallSite>* const call_sites =
-              reinterpret_cast<GcRoot<mirror::CallSite>*>(raw_arrays + layout.CallSitesOffset());
-          for (size_t j = 0; kIsDebugBuild && j < num_call_sites; ++j) {
-            DCHECK(call_sites[j].IsNull());
-          }
-          CopyNonNull(image_resolved_call_sites,
-                      num_call_sites,
-                      call_sites,
-                      [](const GcRoot<mirror::CallSite>& elem) {
-                          return elem.IsNull();
-                      });
-          dex_cache->SetResolvedCallSites(call_sites);
-        }
-      }
       {
         WriterMutexLock mu2(self, *Locks::dex_lock_);
-        // Make sure to do this after we update the arrays since we store the resolved types array
-        // in DexCacheData in RegisterDexFileLocked. We need the array pointer to be the one in the
-        // BSS.
         CHECK(!class_linker->FindDexCacheDataLocked(*dex_file).IsValid());
         class_linker->RegisterDexFileLocked(*dex_file, dex_cache, class_loader.Get());
       }
@@ -1468,7 +1290,6 @@
     VerifyDeclaringClassVisitor visitor;
     header.VisitPackedArtMethods(&visitor, space->Begin(), kRuntimePointerSize);
   }
-  return true;
 }
 
 // Update the class loader. Should only be used on classes in the image space.
@@ -1947,7 +1768,7 @@
   // If we have a class table section, read it and use it for verification in
   // UpdateAppImageClassLoadersAndDexCaches.
   ClassTable::ClassSet temp_set;
-  const ImageSection& class_table_section = header.GetImageSection(ImageHeader::kSectionClassTable);
+  const ImageSection& class_table_section = header.GetClassTableSection();
   const bool added_class_table = class_table_section.Size() > 0u;
   if (added_class_table) {
     const uint64_t start_time2 = NanoTime();
@@ -1958,37 +1779,17 @@
     VLOG(image) << "Adding class table classes took " << PrettyDuration(NanoTime() - start_time2);
   }
   if (app_image) {
-    bool forward_dex_cache_arrays = false;
-    if (!AppImageClassLoadersAndDexCachesHelper::Update(this,
-                                                        space,
-                                                        class_loader,
-                                                        dex_caches,
-                                                        &temp_set,
-                                                        /*out*/&forward_dex_cache_arrays,
-                                                        /*out*/error_msg)) {
-      return false;
-    }
+    AppImageClassLoadersAndDexCachesHelper::Update(this,
+                                                   space,
+                                                   class_loader,
+                                                   dex_caches,
+                                                   &temp_set);
     // Update class loader and resolved strings. If added_class_table is false, the resolved
     // strings were forwarded UpdateAppImageClassLoadersAndDexCaches.
     UpdateClassLoaderVisitor visitor(space, class_loader.Get());
     for (const ClassTable::TableSlot& root : temp_set) {
       visitor(root.Read());
     }
-    // forward_dex_cache_arrays is true iff we copied all of the dex cache arrays into the .bss.
-    // In this case, madvise away the dex cache arrays section of the image to reduce RAM usage and
-    // mark as PROT_NONE to catch any invalid accesses.
-    if (forward_dex_cache_arrays) {
-      const ImageSection& dex_cache_section = header.GetDexCacheArraysSection();
-      uint8_t* section_begin = AlignUp(space->Begin() + dex_cache_section.Offset(), kPageSize);
-      uint8_t* section_end = AlignDown(space->Begin() + dex_cache_section.End(), kPageSize);
-      if (section_begin < section_end) {
-        madvise(section_begin, section_end - section_begin, MADV_DONTNEED);
-        mprotect(section_begin, section_end - section_begin, PROT_NONE);
-        VLOG(image) << "Released and protected dex cache array image section from "
-                    << reinterpret_cast<const void*>(section_begin) << "-"
-                    << reinterpret_cast<const void*>(section_end);
-      }
-    }
   }
   if (!oat_file->GetBssGcRoots().empty()) {
     // Insert oat file to class table for visiting .bss GC roots.
@@ -7951,7 +7752,8 @@
     // We have a valid method from the DexCache but we need to perform ICCE and IAE checks.
     DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex();
     klass = LookupResolvedType(dex_file, method_id.class_idx_, dex_cache.Get(), class_loader.Get());
-    DCHECK(klass != nullptr);
+    CHECK(klass != nullptr) << resolved->PrettyMethod() << " " << resolved << " "
+                            << resolved->GetAccessFlags();
   } else {
     // The method was not in the DexCache, resolve the declaring class.
     klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader);
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index 07afedf..56573f5 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -632,6 +632,10 @@
   }
 }
 
+static bool IsAbsoluteLocation(const std::string& location) {
+  return !location.empty() && location[0] == '/';
+}
+
 bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec) const {
   ClassLoaderContext expected_context;
   if (!expected_context.Parse(context_spec, /*parse_checksums*/ true)) {
@@ -673,18 +677,52 @@
     DCHECK_EQ(expected_info.classpath.size(), expected_info.checksums.size());
 
     for (size_t k = 0; k < info.classpath.size(); k++) {
-      if (info.classpath[k] != expected_info.classpath[k]) {
+      // Compute the dex location that must be compared.
+      // We shouldn't do a naive comparison `info.classpath[k] == expected_info.classpath[k]`
+      // because even if they refer to the same file, one could be encoded as a relative location
+      // and the other as an absolute one.
+      bool is_dex_name_absolute = IsAbsoluteLocation(info.classpath[k]);
+      bool is_expected_dex_name_absolute = IsAbsoluteLocation(expected_info.classpath[k]);
+      std::string dex_name;
+      std::string expected_dex_name;
+
+      if (is_dex_name_absolute == is_expected_dex_name_absolute) {
+        // If both locations are absolute or relative then compare them as they are.
+        // This is usually the case for: shared libraries and secondary dex files.
+        dex_name = info.classpath[k];
+        expected_dex_name = expected_info.classpath[k];
+      } else if (is_dex_name_absolute) {
+        // The runtime name is absolute but the compiled name (the expected one) is relative.
+        // This is the case for split apks which depend on base or on other splits.
+        dex_name = info.classpath[k];
+        expected_dex_name = OatFile::ResolveRelativeEncodedDexLocation(
+            info.classpath[k].c_str(), expected_info.classpath[k]);
+      } else {
+        // The runtime name is relative but the compiled name is absolute.
+        // There is no expected use case that would end up here as dex files are always loaded
+        // with their absolute location. However, be tolerant and do the best effort (in case
+        // there are unexpected new use case...).
+        DCHECK(is_expected_dex_name_absolute);
+        dex_name = OatFile::ResolveRelativeEncodedDexLocation(
+            expected_info.classpath[k].c_str(), info.classpath[k]);
+        expected_dex_name = expected_info.classpath[k];
+      }
+
+      // Compare the locations.
+      if (dex_name != expected_dex_name) {
         LOG(WARNING) << "ClassLoaderContext classpath element mismatch for position " << i
             << ". expected=" << expected_info.classpath[k]
             << ", found=" << info.classpath[k]
             << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
         return false;
       }
+
+      // Compare the checksums.
       if (info.checksums[k] != expected_info.checksums[k]) {
         LOG(WARNING) << "ClassLoaderContext classpath element checksum mismatch for position " << i
-            << ". expected=" << expected_info.checksums[k]
-            << ", found=" << info.checksums[k]
-            << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
+                     << ". expected=" << expected_info.checksums[k]
+                     << ", found=" << info.checksums[k]
+                     << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
         return false;
       }
     }
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index ddbb73b..1847274 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -697,7 +697,17 @@
 
   std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_d);
 
-  ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile("")));
+  std::string context_with_no_base_dir = context->EncodeContextForOatFile("");
+  ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_no_base_dir));
+
+  std::string dex_location = GetTestDexFileName("ForClassLoaderA");
+  size_t pos = dex_location.rfind('/');
+  ASSERT_NE(std::string::npos, pos);
+  std::string parent = dex_location.substr(0, pos);
+
+  std::string context_with_base_dir = context->EncodeContextForOatFile(parent);
+  ASSERT_NE(context_with_base_dir, context_with_no_base_dir);
+  ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_base_dir));
 }
 
 TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingMultidex) {
diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc
index f6f20ba..2f63dff 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -46,13 +46,10 @@
   DexCacheArraysLayout layout(image_pointer_size, dex_file);
   uint8_t* raw_arrays = nullptr;
 
-  const OatDexFile* const oat_dex = dex_file->GetOatDexFile();
-  if (oat_dex != nullptr && oat_dex->GetDexCacheArrays() != nullptr) {
-    raw_arrays = oat_dex->GetDexCacheArrays();
-  } else if (dex_file->NumStringIds() != 0u ||
-             dex_file->NumTypeIds() != 0u ||
-             dex_file->NumMethodIds() != 0u ||
-             dex_file->NumFieldIds() != 0u) {
+  if (dex_file->NumStringIds() != 0u ||
+      dex_file->NumTypeIds() != 0u ||
+      dex_file->NumMethodIds() != 0u ||
+      dex_file->NumFieldIds() != 0u) {
     static_assert(ArenaAllocator::kAlignment == 8, "Expecting arena alignment of 8.");
     DCHECK(layout.Alignment() == 8u || layout.Alignment() == 16u);
     // Zero-initialized.
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 5c63dca..80e6ad3 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -1498,13 +1498,21 @@
       break;
     case LockWord::kThinLocked:
       owner_ = Runtime::Current()->GetThreadList()->FindThreadByThreadId(lock_word.ThinLockOwner());
+      DCHECK(owner_ != nullptr) << "Thin-locked without owner!";
       entry_count_ = 1 + lock_word.ThinLockCount();
       // Thin locks have no waiters.
       break;
     case LockWord::kFatLocked: {
       Monitor* mon = lock_word.FatLockMonitor();
       owner_ = mon->owner_;
-      entry_count_ = 1 + mon->lock_count_;
+      // Here it is okay for the owner to be null since we don't reset the LockWord back to
+      // kUnlocked until we get a GC. In cases where this hasn't happened yet we will have a fat
+      // lock without an owner.
+      if (owner_ != nullptr) {
+        entry_count_ = 1 + mon->lock_count_;
+      } else {
+        DCHECK_EQ(mon->lock_count_, 0) << "Monitor is fat-locked without any owner!";
+      }
       for (Thread* waiter = mon->wait_set_; waiter != nullptr; waiter = waiter->GetWaitNext()) {
         waiters_.push_back(waiter);
       }
diff --git a/runtime/oat.h b/runtime/oat.h
index 1d79ed6..ab7c42e 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,8 +32,8 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
-  // Last oat version changed reason: Add dex section layout info to header.
-  static constexpr uint8_t kOatVersion[] = { '1', '3', '2', '\0' };
+  // Last oat version changed reason: Remove DexCache arrays from .bss.
+  static constexpr uint8_t kOatVersion[] = { '1', '3', '3', '\0' };
 
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index d9cfa53..200681e 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -55,7 +55,6 @@
 #include "type_lookup_table.h"
 #include "utf-inl.h"
 #include "utils.h"
-#include "utils/dex_cache_arrays_layout-inl.h"
 #include "vdex_file.h"
 
 namespace art {
@@ -279,36 +278,6 @@
   return true;
 }
 
-static bool FindDexFileMapItem(const uint8_t* dex_begin,
-                               const uint8_t* dex_end,
-                               DexFile::MapItemType map_item_type,
-                               const DexFile::MapItem** result_item) {
-  *result_item = nullptr;
-
-  const DexFile::Header* header =
-      BoundsCheckedCast<const DexFile::Header*>(dex_begin, dex_begin, dex_end);
-  if (nullptr == header) return false;
-
-  if (!DexFile::IsMagicValid(header->magic_)) return true;  // Not a dex file, not an error.
-
-  const DexFile::MapList* map_list =
-      BoundsCheckedCast<const DexFile::MapList*>(dex_begin + header->map_off_, dex_begin, dex_end);
-  if (nullptr == map_list) return false;
-
-  const DexFile::MapItem* map_item = map_list->list_;
-  size_t count = map_list->size_;
-  while (count--) {
-    if (map_item->type_ == static_cast<uint16_t>(map_item_type)) {
-      *result_item = map_item;
-      break;
-    }
-    map_item = BoundsCheckedCast<const DexFile::MapItem*>(map_item + 1, dex_begin, dex_end);
-    if (nullptr == map_item) return false;
-  }
-
-  return true;
-}
-
 bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) {
   if (!GetOatHeader().IsValid()) {
     std::string cause = GetOatHeader().GetValidationErrorMessage();
@@ -361,7 +330,7 @@
       (bss_roots_ != nullptr && (bss_roots_ < bss_begin_ || bss_roots_ > bss_end_)) ||
       (bss_methods_ != nullptr && bss_roots_ != nullptr && bss_methods_ > bss_roots_)) {
     *error_msg = StringPrintf("In oat file '%s' found bss symbol(s) outside .bss or unordered: "
-                                  "begin = %p, methods_ = %p, roots = %p, end = %p",
+                                  "begin = %p, methods = %p, roots = %p, end = %p",
                               GetLocation().c_str(),
                               bss_begin_,
                               bss_methods_,
@@ -370,11 +339,15 @@
     return false;
   }
 
-  uint8_t* after_arrays = (bss_methods_ != nullptr) ? bss_methods_ : bss_roots_;  // May be null.
-  uint8_t* dex_cache_arrays = (bss_begin_ == after_arrays) ? nullptr : bss_begin_;
-  uint8_t* dex_cache_arrays_end =
-      (bss_begin_ == after_arrays) ? nullptr : (after_arrays != nullptr) ? after_arrays : bss_end_;
-  DCHECK_EQ(dex_cache_arrays != nullptr, dex_cache_arrays_end != nullptr);
+  if (bss_methods_ != nullptr && bss_methods_ != bss_begin_) {
+    *error_msg = StringPrintf("In oat file '%s' found unexpected .bss gap before 'oatbssmethods': "
+                                  "begin = %p, methods = %p",
+                              GetLocation().c_str(),
+                              bss_begin_,
+                              bss_methods_);
+    return false;
+  }
+
   uint32_t dex_file_count = GetOatHeader().GetDexFileCount();
   oat_dex_files_storage_.reserve(dex_file_count);
   for (size_t i = 0; i < dex_file_count; i++) {
@@ -609,37 +582,6 @@
                reinterpret_cast<const DexFile::Header*>(dex_file_pointer)->method_ids_size_);
     }
 
-    uint8_t* current_dex_cache_arrays = nullptr;
-    if (dex_cache_arrays != nullptr) {
-      // All DexCache types except for CallSite have their instance counts in the
-      // DexFile header. For CallSites, we need to read the info from the MapList.
-      const DexFile::MapItem* call_sites_item = nullptr;
-      if (!FindDexFileMapItem(DexBegin(),
-                              DexEnd(),
-                              DexFile::MapItemType::kDexTypeCallSiteIdItem,
-                              &call_sites_item)) {
-        *error_msg = StringPrintf("In oat file '%s' could not read data from truncated DexFile map",
-                                  GetLocation().c_str());
-        return false;
-      }
-      size_t num_call_sites = call_sites_item == nullptr ? 0 : call_sites_item->size_;
-      DexCacheArraysLayout layout(pointer_size, *header, num_call_sites);
-      if (layout.Size() != 0u) {
-        if (static_cast<size_t>(dex_cache_arrays_end - dex_cache_arrays) < layout.Size()) {
-          *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with "
-                                        "truncated dex cache arrays, %zu < %zu.",
-                                    GetLocation().c_str(),
-                                    i,
-                                    dex_file_location.c_str(),
-                                    static_cast<size_t>(dex_cache_arrays_end - dex_cache_arrays),
-                                    layout.Size());
-          return false;
-        }
-        current_dex_cache_arrays = dex_cache_arrays;
-        dex_cache_arrays += layout.Size();
-      }
-    }
-
     std::string canonical_location = DexFile::GetDexCanonicalLocation(dex_file_location.c_str());
 
     // Create the OatDexFile and add it to the owning container.
@@ -651,7 +593,6 @@
                                               lookup_table_data,
                                               method_bss_mapping,
                                               class_offsets_pointer,
-                                              current_dex_cache_arrays,
                                               dex_layout_sections);
     oat_dex_files_storage_.push_back(oat_dex_file);
 
@@ -664,14 +605,6 @@
     }
   }
 
-  if (dex_cache_arrays != dex_cache_arrays_end) {
-    // We expect the bss section to be either empty (dex_cache_arrays and bss_end_
-    // both null) or contain just the dex cache arrays and optionally some GC roots.
-    *error_msg = StringPrintf("In oat file '%s' found unexpected bss size bigger by %zu bytes.",
-                              GetLocation().c_str(),
-                              static_cast<size_t>(bss_end_ - dex_cache_arrays));
-    return false;
-  }
   return true;
 }
 
@@ -1379,7 +1312,6 @@
                                 const uint8_t* lookup_table_data,
                                 const MethodBssMapping* method_bss_mapping_data,
                                 const uint32_t* oat_class_offsets_pointer,
-                                uint8_t* dex_cache_arrays,
                                 const DexLayoutSections* dex_layout_sections)
     : oat_file_(oat_file),
       dex_file_location_(dex_file_location),
@@ -1389,7 +1321,6 @@
       lookup_table_data_(lookup_table_data),
       method_bss_mapping_(method_bss_mapping_data),
       oat_class_offsets_pointer_(oat_class_offsets_pointer),
-      dex_cache_arrays_(dex_cache_arrays),
       dex_layout_sections_(dex_layout_sections) {
   // Initialize TypeLookupTable.
   if (lookup_table_data_ != nullptr) {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 9a7fe51..e13126b 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -422,10 +422,6 @@
   // Returns the offset to the OatClass information. Most callers should use GetOatClass.
   uint32_t GetOatClassOffset(uint16_t class_def_index) const;
 
-  uint8_t* GetDexCacheArrays() const {
-    return dex_cache_arrays_;
-  }
-
   const uint8_t* GetLookupTableData() const {
     return lookup_table_data_;
   }
@@ -470,7 +466,6 @@
              const uint8_t* lookup_table_data,
              const MethodBssMapping* method_bss_mapping,
              const uint32_t* oat_class_offsets_pointer,
-             uint8_t* dex_cache_arrays,
              const DexLayoutSections* dex_layout_sections);
 
   static void AssertAotCompiler();
@@ -483,7 +478,6 @@
   const uint8_t* const lookup_table_data_ = nullptr;
   const MethodBssMapping* const method_bss_mapping_ = nullptr;
   const uint32_t* const oat_class_offsets_pointer_ = 0u;
-  uint8_t* const dex_cache_arrays_ = nullptr;
   mutable std::unique_ptr<TypeLookupTable> lookup_table_;
   const DexLayoutSections* const dex_layout_sections_ = nullptr;
 
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index a8ccf89..a67a6aa 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -417,6 +417,12 @@
       return;
     }
     Thread* self = Thread::Current();
+
+    // Dump all threads first and then the aborting thread. While this is counter the logical flow,
+    // it improves the chance of relevant data surviving in the Android logs.
+
+    DumpAllThreads(os, self);
+
     if (self == nullptr) {
       os << "(Aborting thread was not attached to runtime!)\n";
       DumpKernelStack(os, GetTid(), "  kernel: ", false);
@@ -432,7 +438,6 @@
         }
       }
     }
-    DumpAllThreads(os, self);
   }
 
   // No thread-safety analysis as we do explicitly test for holding the mutator lock.
diff --git a/test/1930-monitor-info/expected.txt b/test/1930-monitor-info/expected.txt
new file mode 100644
index 0000000..b43f1b2
--- /dev/null
+++ b/test/1930-monitor-info/expected.txt
@@ -0,0 +1,31 @@
+Running with single thread.
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testSingleThread], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testSingleThread], owner: main, entryCount: 1, waiters: [], notify_waiters: [] }
+Running with single thread in native.
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testSingleThread], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testSingleThread], owner: main, entryCount: 1, waiters: [], notify_waiters: [] }
+Lock twice
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwice], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwice], owner: main, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwice], owner: main, entryCount: 2, waiters: [], notify_waiters: [] }
+Lock twice native
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNative], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNative], owner: main, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNative], owner: main, entryCount: 2, waiters: [], notify_waiters: [] }
+Lock twice Java then native
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceJN], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceJN], owner: main, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceJN], owner: main, entryCount: 2, waiters: [], notify_waiters: [] }
+Lock twice native then Java
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNJ], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNJ], owner: main, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNJ], owner: main, entryCount: 2, waiters: [], notify_waiters: [] }
+lock with wait
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockWait], owner: main, entryCount: 1, waiters: [Test1930 Thread - testLockWait], notify_waiters: [] }
+Thread[Test1930 Thread - testLockWait]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockWait], owner: Test1930 Thread - testLockWait, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockWait], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Wait for notify.
+Thread[Test1930 Thread - testLockWait]: MonitorUsage{ monitor: NamedLock[Test1930 - testNotifyWait], owner: Test1930 Thread - testLockWait, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testNotifyWait], owner: main, entryCount: 1, waiters: [Test1930 Thread - testLockWait], notify_waiters: [Test1930 Thread - testLockWait] }
+Thread[Test1930 Thread - testLockWait]: MonitorUsage{ monitor: NamedLock[Test1930 - testNotifyWait], owner: Test1930 Thread - testLockWait, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testNotifyWait], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
diff --git a/test/1930-monitor-info/info.txt b/test/1930-monitor-info/info.txt
new file mode 100644
index 0000000..8e19edc
--- /dev/null
+++ b/test/1930-monitor-info/info.txt
@@ -0,0 +1,3 @@
+Tests basic functions in the jvmti plugin.
+
+Tests that the GetObjectMonitorUsage function works correctly.
diff --git a/test/1930-monitor-info/monitor.cc b/test/1930-monitor-info/monitor.cc
new file mode 100644
index 0000000..7f97c05
--- /dev/null
+++ b/test/1930-monitor-info/monitor.cc
@@ -0,0 +1,67 @@
+/*
+ * 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 <pthread.h>
+
+#include <cstdio>
+#include <iostream>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "jni.h"
+#include "jvmti.h"
+
+#include "scoped_local_ref.h"
+#include "scoped_primitive_array.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test1930MonitorInfo {
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1930_executeLockedNative(JNIEnv* env,
+                                                                        jclass klass,
+                                                                        jobject run,
+                                                                        jobject l) {
+  ScopedLocalRef<jclass> runnable(env, env->FindClass("java/lang/Runnable"));
+  if (env->ExceptionCheck()) {
+    return;
+  }
+  jmethodID method = env->GetMethodID(runnable.get(), "run", "()V");
+
+  if (env->ExceptionCheck()) {
+    return;
+  }
+  jmethodID printMethod = env->GetStaticMethodID(klass, "printPreLock", "(Ljava/lang/Object;)V");
+  if (env->ExceptionCheck()) {
+    return;
+  }
+
+  env->CallStaticVoidMethod(klass, printMethod, l);
+  if (env->ExceptionCheck()) {
+    return;
+  }
+  if (env->MonitorEnter(l) != 0) {
+    return;
+  }
+  env->CallVoidMethod(run, method);
+  env->MonitorExit(l);
+}
+
+}  // namespace Test1930MonitorInfo
+}  // namespace art
diff --git a/test/1930-monitor-info/run b/test/1930-monitor-info/run
new file mode 100755
index 0000000..e92b873
--- /dev/null
+++ b/test/1930-monitor-info/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+./default-run "$@" --jvmti
diff --git a/test/1930-monitor-info/src/Main.java b/test/1930-monitor-info/src/Main.java
new file mode 100644
index 0000000..3328461
--- /dev/null
+++ b/test/1930-monitor-info/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    art.Test1930.run();
+  }
+}
diff --git a/test/1930-monitor-info/src/art/Monitors.java b/test/1930-monitor-info/src/art/Monitors.java
new file mode 100644
index 0000000..26f7718
--- /dev/null
+++ b/test/1930-monitor-info/src/art/Monitors.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+public class Monitors {
+  public static class NamedLock {
+    public final String name;
+    public NamedLock(String name) {
+      this.name = name;
+    }
+    public String toString() {
+      return String.format("NamedLock[%s]", name);
+    }
+  }
+
+  public static final class MonitorUsage {
+    public final Object monitor;
+    public final Thread owner;
+    public final int entryCount;
+    public final Thread[] waiters;
+    public final Thread[] notifyWaiters;
+
+    public MonitorUsage(
+        Object monitor,
+        Thread owner,
+        int entryCount,
+        Thread[] waiters,
+        Thread[] notifyWaiters) {
+      this.monitor = monitor;
+      this.entryCount = entryCount;
+      this.owner = owner;
+      this.waiters = waiters;
+      this.notifyWaiters = notifyWaiters;
+    }
+
+    private static String toNameList(Thread[] ts) {
+      return Arrays.toString(Arrays.stream(ts).map((Thread t) -> t.getName()).toArray());
+    }
+
+    public String toString() {
+      return String.format(
+          "MonitorUsage{ monitor: %s, owner: %s, entryCount: %d, waiters: %s, notify_waiters: %s }",
+          monitor,
+          (owner != null) ? owner.getName() : "<NULL>",
+          entryCount,
+          toNameList(waiters),
+          toNameList(notifyWaiters));
+    }
+  }
+
+  public static native MonitorUsage getObjectMonitorUsage(Object monitor);
+}
+
diff --git a/test/1930-monitor-info/src/art/Test1930.java b/test/1930-monitor-info/src/art/Test1930.java
new file mode 100644
index 0000000..a7fa1c7
--- /dev/null
+++ b/test/1930-monitor-info/src/art/Test1930.java
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.util.concurrent.Semaphore;
+import java.util.Arrays;
+
+public class Test1930 {
+  public static final int NUM_RETRY = 100;
+  private static void testSingleThread() {
+    Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testSingleThread");
+    executeLocked(() -> { printMonitorUsage(lk); }, lk);
+  }
+  private static void testSingleThreadNative() {
+    Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testSingleThread");
+    executeLockedNative(() -> { printMonitorUsage(lk); }, lk);
+  }
+
+  private static void testLockedTwice() {
+    final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testLockedTwice");
+    executeLocked(() -> { executeLocked(() -> { printMonitorUsage(lk); }, lk); }, lk);
+  }
+
+  private static void testLockedTwiceNJ() {
+    final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testLockedTwiceNJ");
+    executeLockedNative(() -> { executeLockedNative(() -> { printMonitorUsage(lk); }, lk); }, lk);
+  }
+
+  private static void testLockedTwiceJN() {
+    final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testLockedTwiceJN");
+    executeLockedNative(() -> { executeLockedNative(() -> { printMonitorUsage(lk); }, lk); }, lk);
+  }
+
+  private static void testLockedTwiceNative() {
+    final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testLockedTwiceNative");
+    executeLockedNative(() -> { executeLockedNative(() -> { printMonitorUsage(lk); }, lk); }, lk);
+  }
+
+  public final static class ThreadSignaler {
+    public volatile boolean signal = false;
+  }
+
+  private static void testLockWait() throws Exception {
+    final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testLockWait");
+    final Semaphore sem = new Semaphore(0);
+    final Thread t = new Thread(() -> {
+      sem.release();
+      synchronized (lk) {
+        printMonitorUsage(lk);
+      }
+    }, "Test1930 Thread - testLockWait");
+    synchronized (lk) {
+      t.start();
+      // Wait for the other thread to actually start.
+      sem.acquire();
+      // Wait for the other thread to go to sleep trying to get the mutex. This might take a (short)
+      // time since we try spinning first for better performance.
+      boolean found_wait = false;
+      for (long i = 0; i < NUM_RETRY; i++) {
+        if (Arrays.asList(Monitors.getObjectMonitorUsage(lk).waiters).contains(t)) {
+          found_wait = true;
+          break;
+        } else {
+          Thread.sleep(500);
+          Thread.yield();
+        }
+      }
+      if (!found_wait) {
+        System.out.println("other thread doesn't seem to be waiting.");
+      }
+      printMonitorUsage(lk);
+    }
+    t.join();
+    printMonitorUsage(lk);
+  }
+
+  private static void testNotifyWait() throws Exception {
+    final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testNotifyWait");
+    final Semaphore sem = new Semaphore(0);
+    Thread t = new Thread(() -> {
+      synchronized (lk) {
+        printMonitorUsage(lk);
+        sem.release();
+        try {
+          lk.wait();
+        } catch (Exception e) {
+          throw new Error("Error waiting!", e);
+        }
+        printMonitorUsage(lk);
+      }
+    }, "Test1930 Thread - testLockWait");
+    t.start();
+    sem.acquire();
+    synchronized (lk) {
+      printMonitorUsage(lk);
+      lk.notifyAll();
+    }
+    t.join();
+    printMonitorUsage(lk);
+  }
+
+  public static void run() throws Exception {
+    // Single threaded tests.
+    System.out.println("Running with single thread.");
+    testSingleThread();
+    System.out.println("Running with single thread in native.");
+    testSingleThreadNative();
+    System.out.println("Lock twice");
+    testLockedTwice();
+    System.out.println("Lock twice native");
+    testLockedTwiceNative();
+    System.out.println("Lock twice Java then native");
+    testLockedTwiceJN();
+    System.out.println("Lock twice native then Java");
+    testLockedTwiceNJ();
+
+    // Mutli threaded tests.
+    System.out.println("lock with wait");
+    testLockWait();
+    System.out.println("Wait for notify.");
+    testNotifyWait();
+  }
+
+  public static void printPreLock(Object lock) {
+    System.out.println(String.format("Pre-lock[%s]: %s",
+          Thread.currentThread().getName(), Monitors.getObjectMonitorUsage(lock)));
+  }
+
+  public static void executeLocked(Runnable r, Object lock) {
+    printPreLock(lock);
+    synchronized (lock) {
+      r.run();
+    }
+  }
+
+  public native static void executeLockedNative(Runnable r, Object m);
+  public static void printMonitorUsage(Object m) {
+    System.out.println(String.format("Thread[%s]: %s",
+          Thread.currentThread().getName(), Monitors.getObjectMonitorUsage(m)));
+  }
+}
diff --git a/test/Android.bp b/test/Android.bp
index 343431e..2f23056 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -250,6 +250,7 @@
         "ti-agent/common_helper.cc",
         "ti-agent/frame_pop_helper.cc",
         "ti-agent/locals_helper.cc",
+        "ti-agent/monitors_helper.cc",
         "ti-agent/redefinition_helper.cc",
         "ti-agent/suspension_helper.cc",
         "ti-agent/stack_trace_helper.cc",
@@ -299,7 +300,8 @@
         "1922-owned-monitors-info/owned_monitors.cc",
         "1924-frame-pop-toggle/frame_pop_toggle.cc",
         "1926-missed-frame-pop/frame_pop_missed.cc",
-        "1927-exception-event/exception_event.cc"
+        "1927-exception-event/exception_event.cc",
+        "1930-monitor-info/monitor.cc",
     ],
     shared_libs: [
         "libbase",
diff --git a/test/ti-agent/jvmti_helper.cc b/test/ti-agent/jvmti_helper.cc
index 7280102..c290e9b 100644
--- a/test/ti-agent/jvmti_helper.cc
+++ b/test/ti-agent/jvmti_helper.cc
@@ -50,7 +50,7 @@
     .can_get_synthetic_attribute                     = 1,
     .can_get_owned_monitor_info                      = 0,
     .can_get_current_contended_monitor               = 0,
-    .can_get_monitor_info                            = 0,
+    .can_get_monitor_info                            = 1,
     .can_pop_frame                                   = 0,
     .can_redefine_classes                            = 1,
     .can_signal_thread                               = 0,
diff --git a/test/ti-agent/monitors_helper.cc b/test/ti-agent/monitors_helper.cc
new file mode 100644
index 0000000..7c28ede
--- /dev/null
+++ b/test/ti-agent/monitors_helper.cc
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+#include "jni.h"
+#include "jvmti.h"
+#include <vector>
+#include "jvmti_helper.h"
+#include "jni_helper.h"
+#include "test_env.h"
+#include "scoped_local_ref.h"
+namespace art {
+namespace common_monitors {
+
+extern "C" JNIEXPORT jobject JNICALL Java_art_Monitors_getObjectMonitorUsage(
+    JNIEnv* env, jclass, jobject obj) {
+  ScopedLocalRef<jclass> klass(env, env->FindClass("art/Monitors$MonitorUsage"));
+  if (env->ExceptionCheck()) {
+    return nullptr;
+  }
+  jmethodID constructor = env->GetMethodID(
+      klass.get(),
+      "<init>",
+      "(Ljava/lang/Object;Ljava/lang/Thread;I[Ljava/lang/Thread;[Ljava/lang/Thread;)V");
+  if (env->ExceptionCheck()) {
+    return nullptr;
+  }
+  jvmtiMonitorUsage usage;
+  if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetObjectMonitorUsage(obj, &usage))) {
+    return nullptr;
+  }
+  jobjectArray wait = CreateObjectArray(env, usage.waiter_count, "java/lang/Thread",
+                                        [&](jint i) { return usage.waiters[i]; });
+  if (env->ExceptionCheck()) {
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(usage.waiters));
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(usage.notify_waiters));
+    return nullptr;
+  }
+  jobjectArray notify_wait = CreateObjectArray(env, usage.notify_waiter_count, "java/lang/Thread",
+                                               [&](jint i) { return usage.notify_waiters[i]; });
+  if (env->ExceptionCheck()) {
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(usage.waiters));
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(usage.notify_waiters));
+    return nullptr;
+  }
+  return env->NewObject(klass.get(), constructor,
+                        obj, usage.owner, usage.entry_count, wait, notify_wait);
+}
+
+}  // namespace common_monitors
+}  // namespace art
diff --git a/tools/libcore_gcstress_debug_failures.txt b/tools/libcore_gcstress_debug_failures.txt
index 5806b61..d27b8fc 100644
--- a/tools/libcore_gcstress_debug_failures.txt
+++ b/tools/libcore_gcstress_debug_failures.txt
@@ -11,9 +11,11 @@
   names: ["jsr166.CompletableFutureTest#testCompleteOnTimeout_completed",
           "libcore.icu.TransliteratorTest#testAll",
           "libcore.icu.RelativeDateTimeFormatterTest#test_bug25821045",
+          "libcore.icu.RelativeDateTimeFormatterTest#test_bug25883157",
           "libcore.java.lang.ref.ReferenceQueueTest#testRemoveWithDelayedResultAndTimeout",
           "libcore.java.lang.ref.ReferenceQueueTest#testRemoveWithDelayedResultAndNoTimeout",
           "libcore.java.util.TimeZoneTest#testSetDefaultDeadlock",
+          "libcore.javax.crypto.CipherBasicsTest#testBasicEncryption",
           "org.apache.harmony.tests.java.util.TimerTest#testThrowingTaskKillsTimerThread"]
 }
 ]