Merge "ART: Implement try/catch blocks in Builder"
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index c1d5cb7..6f2b234 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -110,8 +110,9 @@
   if (!compiler_options_->IsCompilationEnabled()) {
     return false;
   }
-  // Don't compile class initializers, ever.
-  if (((access_flags & kAccConstructor) != 0) && ((access_flags & kAccStatic) != 0)) {
+  // Don't compile class initializers unless kEverything.
+  if ((compiler_options_->GetCompilerFilter() != CompilerOptions::kEverything) &&
+     ((access_flags & kAccConstructor) != 0) && ((access_flags & kAccStatic) != 0)) {
     return false;
   }
   return true;
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 4cdf75b..7890108 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1971,7 +1971,13 @@
     if (klass->IsResolved()) {
       if (klass->GetStatus() < mirror::Class::kStatusVerified) {
         ObjectLock<mirror::Class> lock(soa.Self(), klass);
+        // Set class status to verified.
         mirror::Class::SetStatus(klass, mirror::Class::kStatusVerified, soa.Self());
+        // Mark methods as pre-verified. If we don't do this, the interpreter will run with
+        // access checks.
+        klass->SetPreverifiedFlagOnAllMethods(
+            GetInstructionSetPointerSize(manager->GetCompiler()->GetInstructionSet()));
+        klass->SetPreverified();
       }
       // Record the final class status if necessary.
       ClassReference ref(manager->GetDexFile(), class_def_index);
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 356663b..fe681e2 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -37,7 +37,7 @@
     kSpace,               // Maximize space savings.
     kBalanced,            // Try to get the best performance return on compilation investment.
     kSpeed,               // Maximize runtime performance.
-    kEverything,          // Force compilation (Note: excludes compilation of class initializers).
+    kEverything,          // Force compilation of everything capable of being compiled.
     kTime,                // Compile methods, but minimize compilation time.
   };
 
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 92ebf06..3efe7c7 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -27,6 +27,7 @@
 #include "mirror/class_loader.h"
 #include "mirror/dex_cache.h"
 #include "nodes.h"
+#include "optimizing_compiler.h"
 #include "reference_type_propagation.h"
 #include "register_allocator.h"
 #include "ssa_phi_elimination.h"
@@ -64,14 +65,14 @@
         // We use the original invoke type to ensure the resolution of the called method
         // works properly.
         if (!TryInline(call, call->GetDexMethodIndex())) {
-          if (kIsDebugBuild) {
+          if (kIsDebugBuild && IsCompilingWithCoreImage()) {
             std::string callee_name =
                 PrettyMethod(call->GetDexMethodIndex(), *outer_compilation_unit_.GetDexFile());
             bool should_inline = callee_name.find("$inline$") != std::string::npos;
             CHECK(!should_inline) << "Could not inline " << callee_name;
           }
         } else {
-          if (kIsDebugBuild) {
+          if (kIsDebugBuild && IsCompilingWithCoreImage()) {
             std::string callee_name =
                 PrettyMethod(call->GetDexMethodIndex(), *outer_compilation_unit_.GetDexFile());
             bool must_not_inline = callee_name.find("$noinline$") != std::string::npos;
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 1944ba6..1e51530 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -614,7 +614,8 @@
   {
     PassScope scope(HGraphBuilder::kBuilderPassName, &pass_observer);
     if (!builder.BuildGraph(*code_item)) {
-      CHECK(!shouldCompile) << "Could not build graph in optimizing compiler";
+      DCHECK(!(IsCompilingWithCoreImage() && shouldCompile))
+          << "Could not build graph in optimizing compiler";
       pass_observer.SetGraphInBadState();
       return nullptr;
     }
@@ -705,4 +706,9 @@
   return new OptimizingCompiler(driver);
 }
 
+bool IsCompilingWithCoreImage() {
+  const std::string& image = Runtime::Current()->GetImageLocation();
+  return EndsWith(image, "core.art") || EndsWith(image, "core-optimizing.art");
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/optimizing_compiler.h b/compiler/optimizing/optimizing_compiler.h
index d076fb5..0c89da1 100644
--- a/compiler/optimizing/optimizing_compiler.h
+++ b/compiler/optimizing/optimizing_compiler.h
@@ -24,6 +24,11 @@
 
 Compiler* CreateOptimizingCompiler(CompilerDriver* driver);
 
+// Returns whether we are compiling against a "core" image, which
+// is an indicative we are running tests. The compiler will use that
+// information for checking invariants.
+bool IsCompilingWithCoreImage();
+
 }  // namespace art
 
 #endif  // ART_COMPILER_OPTIMIZING_OPTIMIZING_COMPILER_H_
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index d45ab1b..7a23746 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -89,6 +89,63 @@
   return Join(command, ' ');
 }
 
+// A stripped version. Remove some less essential parameters. If we see a "--zip-fd=" parameter, be
+// even more aggressive. There won't be much reasonable data here for us in that case anyways (the
+// locations are all staged).
+static std::string StrippedCommandLine() {
+  std::vector<std::string> command;
+
+  // Do a pre-pass to look for zip-fd.
+  bool saw_zip_fd = false;
+  for (int i = 0; i < original_argc; ++i) {
+    if (StartsWith(original_argv[i], "--zip-fd=")) {
+      saw_zip_fd = true;
+      break;
+    }
+  }
+
+  // Now filter out things.
+  for (int i = 0; i < original_argc; ++i) {
+    // All runtime-arg parameters are dropped.
+    if (strcmp(original_argv[i], "--runtime-arg") == 0) {
+      i++;  // Drop the next part, too.
+      continue;
+    }
+
+    // Any instruction-setXXX is dropped.
+    if (StartsWith(original_argv[i], "--instruction-set")) {
+      continue;
+    }
+
+    // The boot image is dropped.
+    if (StartsWith(original_argv[i], "--boot-image=")) {
+      continue;
+    }
+
+    // This should leave any dex-file and oat-file options, describing what we compiled.
+
+    // However, we prefer to drop this when we saw --zip-fd.
+    if (saw_zip_fd) {
+      // Drop anything --zip-X, --dex-X, --oat-X, --swap-X.
+      if (StartsWith(original_argv[i], "--zip-") ||
+          StartsWith(original_argv[i], "--dex-") ||
+          StartsWith(original_argv[i], "--oat-") ||
+          StartsWith(original_argv[i], "--swap-")) {
+        continue;
+      }
+    }
+
+    command.push_back(original_argv[i]);
+  }
+
+  // Construct the final output.
+  if (command.size() <= 1U) {
+    // It seems only "/system/bin/dex2oat" is left, or not even that. Use a pretty line.
+    return "Starting dex2oat.";
+  }
+  return Join(command, ' ');
+}
+
 static void UsageErrorV(const char* fmt, va_list ap) {
   std::string error;
   StringAppendV(&error, fmt, ap);
@@ -1911,7 +1968,17 @@
     return EXIT_FAILURE;
   }
 
-  LOG(INFO) << CommandLine();
+  // Print the complete line when any of the following is true:
+  //   1) Debug build
+  //   2) Compiling an image
+  //   3) Compiling with --host
+  //   4) Compiling on the host (not a target build)
+  // Otherwise, print a stripped command line.
+  if (kIsDebugBuild || dex2oat.IsImage() || dex2oat.IsHost() || !kIsTargetBuild) {
+    LOG(INFO) << CommandLine();
+  } else {
+    LOG(INFO) << StrippedCommandLine();
+  }
 
   if (!dex2oat.Setup()) {
     dex2oat.EraseOatFile();
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 91b5000..7936dd3 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -987,13 +987,18 @@
   // Fall back to running out of the original dex file if we couldn't load any
   // dex_files from the oat file.
   if (dex_files.empty()) {
-    if (Runtime::Current()->IsDexFileFallbackEnabled()) {
-      if (!DexFile::Open(dex_location, dex_location, &error_msg, &dex_files)) {
-        LOG(WARNING) << error_msg;
-        error_msgs->push_back("Failed to open dex files from " + std::string(dex_location));
+    if (oat_file_assistant.HasOriginalDexFiles()) {
+      if (Runtime::Current()->IsDexFileFallbackEnabled()) {
+        if (!DexFile::Open(dex_location, dex_location, &error_msg, &dex_files)) {
+          LOG(WARNING) << error_msg;
+          error_msgs->push_back("Failed to open dex files from " + std::string(dex_location));
+        }
+      } else {
+        error_msgs->push_back("Fallback mode disabled, skipping dex files.");
       }
     } else {
-      error_msgs->push_back("Fallback mode disabled, skipping dex files.");
+      error_msgs->push_back("No original dex files found for dex location "
+          + std::string(dex_location));
     }
   }
   return dex_files;
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index f04f356..de46b35 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -4715,7 +4715,7 @@
     const gc::AllocRecord* record = it->second;
 
     LOG(INFO) << StringPrintf(" Thread %-2d %6zd bytes ", record->GetTid(), record->ByteCount())
-              << PrettyClass(it->first.Read()->GetClass());
+              << PrettyClass(record->GetClass());
 
     for (size_t stack_frame = 0, depth = record->GetDepth(); stack_frame < depth; ++stack_frame) {
       const gc::AllocRecordStackTraceElement& stack_element = record->StackElement(stack_frame);
@@ -4850,7 +4850,7 @@
          count > 0 && it != end; count--, it++) {
       const gc::AllocRecord* record = it->second;
       std::string temp;
-      class_names.Add(it->first.Read()->GetClass()->GetDescriptor(&temp));
+      class_names.Add(record->GetClass()->GetDescriptor(&temp));
       for (size_t i = 0, depth = record->GetDepth(); i < depth; i++) {
         ArtMethod* m = record->StackElement(i).GetMethod();
         class_names.Add(m->GetDeclaringClassDescriptor());
@@ -4902,7 +4902,7 @@
       const gc::AllocRecord* record = it->second;
       size_t stack_depth = record->GetDepth();
       size_t allocated_object_class_name_index =
-          class_names.IndexOf(it->first.Read()->GetClass()->GetDescriptor(&temp));
+          class_names.IndexOf(record->GetClass()->GetDescriptor(&temp));
       JDWP::Append4BE(bytes, record->ByteCount());
       JDWP::Append2BE(bytes, static_cast<uint16_t>(record->GetTid()));
       JDWP::Append2BE(bytes, allocated_object_class_name_index);
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 25d5ef4..c1c7123 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -906,9 +906,9 @@
 
           local_in_reg[reg].name_ = StringDataByIdx(name_idx);
           local_in_reg[reg].descriptor_ = StringByTypeIdx(descriptor_idx);
-          if (opcode == DBG_START_LOCAL_EXTENDED) {
-            local_in_reg[reg].signature_ = StringDataByIdx(signature_idx);
-          }
+          local_in_reg[reg].signature_ =
+              (opcode == DBG_START_LOCAL_EXTENDED) ? StringDataByIdx(signature_idx)
+                                                   : nullptr;
           local_in_reg[reg].start_address_ = address;
           local_in_reg[reg].is_live_ = true;
         }
diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc
index ac7de63..c5b9f65 100644
--- a/runtime/gc/allocation_record.cc
+++ b/runtime/gc/allocation_record.cc
@@ -94,33 +94,58 @@
   CHECK_LE(recent_record_max_, alloc_record_max_);
   BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(visitor, RootInfo(kRootDebugger));
   size_t count = recent_record_max_;
-  // Only visit the last recent_record_max_ number of objects in entries_.
-  // They need to be retained for DDMS's recent allocation tracking.
-  // TODO: This will cause 098-ddmc test to run out of memory for GC stress test.
-  // There should be an option that do not keep these objects live if allocation tracking is only
-  // for the purpose of an HPROF dump. b/20037135
+  // Only visit the last recent_record_max_ number of allocation records in entries_ and mark the
+  // klass_ fields as strong roots.
   for (auto it = entries_.rbegin(), end = entries_.rend(); count > 0 && it != end; count--, ++it) {
-    buffered_visitor.VisitRoot(it->first);
+    buffered_visitor.VisitRoot(it->second->GetClassGcRoot());
+  }
+}
+
+static inline void SweepClassObject(AllocRecord* record, IsMarkedCallback* callback, void* arg)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+    EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_) {
+  GcRoot<mirror::Class>& klass = record->GetClassGcRoot();
+  // This does not need a read barrier because this is called by GC.
+  mirror::Object* old_object = klass.Read<kWithoutReadBarrier>();
+  mirror::Object* new_object = callback(old_object, arg);
+  if (UNLIKELY(old_object != new_object)) {
+    mirror::Class* new_klass = (UNLIKELY(new_object == nullptr) ? nullptr : new_object->AsClass());
+    klass = GcRoot<mirror::Class>(new_klass);
   }
 }
 
 void AllocRecordObjectMap::SweepAllocationRecords(IsMarkedCallback* callback, void* arg) {
   VLOG(heap) << "Start SweepAllocationRecords()";
-  size_t count_deleted = 0, count_moved = 0;
+  size_t count_deleted = 0, count_moved = 0, count = 0;
+  // Only the first (size - recent_record_max_) number of records can be deleted.
+  size_t delete_bound;
+  if (entries_.size() <= recent_record_max_) {
+    delete_bound = 0;
+  } else {
+    delete_bound = entries_.size() - recent_record_max_;
+  }
   for (auto it = entries_.begin(), end = entries_.end(); it != end;) {
+    ++count;
     // This does not need a read barrier because this is called by GC.
     mirror::Object* old_object = it->first.Read<kWithoutReadBarrier>();
     AllocRecord* record = it->second;
     mirror::Object* new_object = callback(old_object, arg);
     if (new_object == nullptr) {
-      delete record;
-      it = entries_.erase(it);
-      ++count_deleted;
+      if (count > delete_bound) {
+        it->first = GcRoot<mirror::Object>(nullptr);
+        SweepClassObject(record, callback, arg);
+        ++it;
+      } else {
+        delete record;
+        it = entries_.erase(it);
+        ++count_deleted;
+      }
     } else {
       if (old_object != new_object) {
         it->first = GcRoot<mirror::Object>(new_object);
         ++count_moved;
       }
+      SweepClassObject(record, callback, arg);
       ++it;
     }
   }
@@ -128,6 +153,20 @@
   VLOG(heap) << "Updated " << count_moved << " allocation records";
 }
 
+void AllocRecordObjectMap::AllowNewAllocationRecords() {
+  allow_new_record_ = true;
+  new_record_condition_.Broadcast(Thread::Current());
+}
+
+void AllocRecordObjectMap::DisallowNewAllocationRecords() {
+  allow_new_record_ = false;
+}
+
+void AllocRecordObjectMap::EnsureNewAllocationRecordsDisallowed() {
+  CHECK(!allow_new_record_);
+}
+
+
 struct AllocRecordStackVisitor : public StackVisitor {
   AllocRecordStackVisitor(Thread* thread, AllocRecordStackTrace* trace_in, size_t max)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
@@ -201,7 +240,8 @@
   }
 }
 
-void AllocRecordObjectMap::RecordAllocation(Thread* self, mirror::Object* obj, size_t byte_count) {
+void AllocRecordObjectMap::RecordAllocation(Thread* self, mirror::Object* obj, mirror::Class* klass,
+                                            size_t byte_count) {
   MutexLock mu(self, *Locks::alloc_tracker_lock_);
   Heap* heap = Runtime::Current()->GetHeap();
   if (!heap->IsAllocTrackingEnabled()) {
@@ -217,6 +257,11 @@
     return;
   }
 
+  // Wait for GC's sweeping to complete and allow new records
+  while (UNLIKELY(!records->allow_new_record_)) {
+    records->new_record_condition_.WaitHoldingLocks(self);
+  }
+
   DCHECK_LE(records->Size(), records->alloc_record_max_);
 
   // Get stack trace.
@@ -229,7 +274,7 @@
   AllocRecordStackTrace* trace = new AllocRecordStackTrace(records->scratch_trace_);
 
   // Fill in the basics.
-  AllocRecord* record = new AllocRecord(byte_count, trace);
+  AllocRecord* record = new AllocRecord(byte_count, klass, trace);
 
   records->Put(obj, record);
   DCHECK_LE(records->Size(), records->alloc_record_max_);
diff --git a/runtime/gc/allocation_record.h b/runtime/gc/allocation_record.h
index 5214b6b..f567153 100644
--- a/runtime/gc/allocation_record.h
+++ b/runtime/gc/allocation_record.h
@@ -161,8 +161,8 @@
 class AllocRecord {
  public:
   // All instances of AllocRecord should be managed by an instance of AllocRecordObjectMap.
-  AllocRecord(size_t count, AllocRecordStackTrace* trace)
-      : byte_count_(count), trace_(trace) {}
+  AllocRecord(size_t count, mirror::Class* klass, AllocRecordStackTrace* trace)
+      : byte_count_(count), klass_(klass), trace_(trace) {}
 
   ~AllocRecord() {
     delete trace_;
@@ -184,12 +184,22 @@
     return trace_->GetTid();
   }
 
+  mirror::Class* GetClass() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return klass_.Read();
+  }
+
+  GcRoot<mirror::Class>& GetClassGcRoot() {
+    return klass_;
+  }
+
   const AllocRecordStackTraceElement& StackElement(size_t index) const {
     return trace_->GetStackElement(index);
   }
 
  private:
   const size_t byte_count_;
+  // The klass_ could be a strong or weak root for GC
+  GcRoot<mirror::Class> klass_;
   // TODO: Currently trace_ is like a std::unique_ptr,
   // but in future with deduplication it could be a std::shared_ptr.
   const AllocRecordStackTrace* const trace_;
@@ -197,14 +207,17 @@
 
 class AllocRecordObjectMap {
  public:
-  // Since the entries contain weak roots, they need a read barrier. Do not directly access
-  // the mirror::Object pointers in it. Use functions that contain read barriers.
-  // No need for "const AllocRecord*" in the list, because all fields of AllocRecord are const.
+  // GcRoot<mirror::Object> pointers in the list are weak roots, and the last recent_record_max_
+  // number of AllocRecord::klass_ pointers are strong roots (and the rest of klass_ pointers are
+  // weak roots). The last recent_record_max_ number of pairs in the list are always kept for DDMS's
+  // recent allocation tracking, but GcRoot<mirror::Object> pointers in these pairs can become null.
+  // Both types of pointers need read barriers, do not directly access them.
   typedef std::list<std::pair<GcRoot<mirror::Object>, AllocRecord*>> EntryList;
 
   // "static" because it is part of double-checked locking. It needs to check a bool first,
   // in order to make sure the AllocRecordObjectMap object is not null.
-  static void RecordAllocation(Thread* self, mirror::Object* obj, size_t byte_count)
+  static void RecordAllocation(Thread* self, mirror::Object* obj, mirror::Class* klass,
+                               size_t byte_count)
       LOCKS_EXCLUDED(Locks::alloc_tracker_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -215,7 +228,9 @@
         recent_record_max_(kDefaultNumRecentRecords),
         max_stack_depth_(kDefaultAllocStackDepth),
         scratch_trace_(kMaxSupportedStackDepth),
-        alloc_ddm_thread_id_(0) {}
+        alloc_ddm_thread_id_(0),
+        allow_new_record_(true),
+        new_record_condition_("New allocation record condition", *Locks::alloc_tracker_lock_) {}
 
   ~AllocRecordObjectMap();
 
@@ -247,6 +262,16 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_);
 
+  void DisallowNewAllocationRecords()
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_);
+  void AllowNewAllocationRecords()
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_);
+  void EnsureNewAllocationRecordsDisallowed()
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_);
+
   // TODO: Is there a better way to hide the entries_'s type?
   EntryList::iterator Begin()
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
@@ -282,6 +307,9 @@
   size_t max_stack_depth_ GUARDED_BY(Locks::alloc_tracker_lock_);
   AllocRecordStackTrace scratch_trace_ GUARDED_BY(Locks::alloc_tracker_lock_);
   pid_t alloc_ddm_thread_id_ GUARDED_BY(Locks::alloc_tracker_lock_);
+  bool allow_new_record_ GUARDED_BY(Locks::alloc_tracker_lock_);
+  ConditionVariable new_record_condition_ GUARDED_BY(Locks::alloc_tracker_lock_);
+  // see the comment in typedef of EntryList
   EntryList entries_ GUARDED_BY(Locks::alloc_tracker_lock_);
 
   void SetProperties() EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_);
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index 0ed3b6d..2e66160 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -170,7 +170,8 @@
   }
   if (kInstrumented) {
     if (IsAllocTrackingEnabled()) {
-      AllocRecordObjectMap::RecordAllocation(self, obj, bytes_allocated);
+      // Use obj->GetClass() instead of klass, because PushOnAllocationStack() could move klass
+      AllocRecordObjectMap::RecordAllocation(self, obj, obj->GetClass(), bytes_allocated);
     }
   } else {
     DCHECK(!IsAllocTrackingEnabled());
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index f19cfcb..1b45ea1 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -3716,6 +3716,35 @@
   }
 }
 
+void Heap::AllowNewAllocationRecords() const {
+  if (IsAllocTrackingEnabled()) {
+    MutexLock mu(Thread::Current(), *Locks::alloc_tracker_lock_);
+    if (IsAllocTrackingEnabled()) {
+      GetAllocationRecords()->AllowNewAllocationRecords();
+    }
+  }
+}
+
+void Heap::DisallowNewAllocationRecords() const {
+  if (IsAllocTrackingEnabled()) {
+    MutexLock mu(Thread::Current(), *Locks::alloc_tracker_lock_);
+    if (IsAllocTrackingEnabled()) {
+      GetAllocationRecords()->DisallowNewAllocationRecords();
+    }
+  }
+}
+
+void Heap::EnsureNewAllocationRecordsDisallowed() const {
+  if (IsAllocTrackingEnabled()) {
+    // Lock and unlock once to ensure that no threads are still in the
+    // middle of adding new allocation records.
+    MutexLock mu(Thread::Current(), *Locks::alloc_tracker_lock_);
+    if (IsAllocTrackingEnabled()) {
+      GetAllocationRecords()->EnsureNewAllocationRecordsDisallowed();
+    }
+  }
+}
+
 // Based on debug malloc logic from libc/bionic/debug_stacktrace.cpp.
 class StackCrawlState {
  public:
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 54a3235..1c75bd0 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -706,10 +706,24 @@
       EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_);
 
   void VisitAllocationRecords(RootVisitor* visitor) const
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      LOCKS_EXCLUDED(Locks::alloc_tracker_lock_);
 
   void SweepAllocationRecords(IsMarkedCallback* visitor, void* arg) const
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      LOCKS_EXCLUDED(Locks::alloc_tracker_lock_);
+
+  void DisallowNewAllocationRecords() const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      LOCKS_EXCLUDED(Locks::alloc_tracker_lock_);
+
+  void AllowNewAllocationRecords() const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      LOCKS_EXCLUDED(Locks::alloc_tracker_lock_);
+
+  void EnsureNewAllocationRecordsDisallowed() const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      LOCKS_EXCLUDED(Locks::alloc_tracker_lock_);
 
  private:
   class ConcurrentGCTask;
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index 8ba6172..71a69aa 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -823,9 +823,14 @@
     CHECK(records != nullptr);
     HprofStackTraceSerialNumber next_trace_sn = kHprofNullStackTrace + 1;
     HprofStackFrameId next_frame_id = 0;
+    size_t count = 0;
 
     for (auto it = records->Begin(), end = records->End(); it != end; ++it) {
       const mirror::Object* obj = it->first.Read();
+      if (obj == nullptr) {
+        continue;
+      }
+      ++count;
       const gc::AllocRecordStackTrace* trace = it->second->GetStackTrace();
 
       // Copy the pair into a real hash map to speed up look up.
@@ -849,6 +854,7 @@
     }
     CHECK_EQ(traces_.size(), next_trace_sn - kHprofNullStackTrace - 1);
     CHECK_EQ(frames_.size(), next_frame_id);
+    VLOG(heap) << "hprof: found " << count << " objects with allocation stack traces";
   }
 
   // If direct_to_ddms_ is set, "filename_" and "fd" will be ignored.
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 0bbc014..fa103b1 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -530,12 +530,14 @@
   // (at this point the ArtMethod has already been replaced,
   // so we just need to fix-up the arguments)
   uint32_t string_init_vreg_this = is_range ? vregC : arg[0];
-  if (UNLIKELY(code_item == nullptr && string_init)) {
-    DCHECK(called_method->IsNative() || called_method->IsProxyMethod());
-
+  if (UNLIKELY(string_init)) {
     DCHECK_GT(num_regs, 0u);  // As the method is an instance method, there should be at least 1.
+
     // The new StringFactory call is static and has one fewer argument.
-    num_regs--;
+    if (code_item == nullptr) {
+      DCHECK(called_method->IsNative() || called_method->IsProxyMethod());
+      num_regs--;
+    }  // else ... don't need to change num_regs since it comes up from the string_init's code item
     number_of_inputs--;
 
     // Rewrite the var-args, dropping the 0th argument ("this")
@@ -583,11 +585,13 @@
       new_shadow_frame->SetVRegReference(dest_reg, shadow_frame.GetVRegReference(receiver_reg));
       ++dest_reg;
       ++arg_offset;
+      DCHECK(!string_init);  // All StringFactory methods are static.
     }
 
     // Copy the caller's invoke-* arguments into the callee's parameter registers.
     for (uint32_t shorty_pos = 0; dest_reg < num_regs; ++shorty_pos, ++dest_reg, ++arg_offset) {
-      DCHECK_LT(shorty_pos + 1, shorty_len);
+      // Skip the 0th 'shorty' type since it represents the return type.
+      DCHECK_LT(shorty_pos + 1, shorty_len) << "for shorty '" << shorty << "'";
       const size_t src_reg = (is_range) ? vregC + arg_offset : arg[arg_offset];
       switch (shorty[shorty_pos + 1]) {
         // Handle Object references. 1 virtual register slot.
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 094d8b7..b28adf9 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -151,7 +151,7 @@
     return kSelfPatchOatNeeded;
   }
 
-  return kDex2OatNeeded;
+  return HasOriginalDexFiles() ? kDex2OatNeeded : kNoDexOptNeeded;
 }
 
 bool OatFileAssistant::MakeUpToDate(std::string* error_msg) {
@@ -241,6 +241,14 @@
   return dex_files;
 }
 
+bool OatFileAssistant::HasOriginalDexFiles() {
+  // Ensure GetRequiredDexChecksum has been run so that
+  // has_original_dex_files_ is initialized. We don't care about the result of
+  // GetRequiredDexChecksum.
+  GetRequiredDexChecksum();
+  return has_original_dex_files_;
+}
+
 const std::string* OatFileAssistant::OdexFileName() {
   if (!cached_odex_file_name_attempted_) {
     CHECK(dex_location_ != nullptr) << "OatFileAssistant: null dex location";
@@ -817,17 +825,19 @@
 }
 
 const uint32_t* OatFileAssistant::GetRequiredDexChecksum() {
-  if (!required_dex_checksum_attempted) {
-    required_dex_checksum_attempted = true;
-    required_dex_checksum_found = false;
+  if (!required_dex_checksum_attempted_) {
+    required_dex_checksum_attempted_ = true;
+    required_dex_checksum_found_ = false;
     std::string error_msg;
     CHECK(dex_location_ != nullptr) << "OatFileAssistant provided no dex location";
-    if (DexFile::GetChecksum(dex_location_, &cached_required_dex_checksum, &error_msg)) {
-      required_dex_checksum_found = true;
+    if (DexFile::GetChecksum(dex_location_, &cached_required_dex_checksum_, &error_msg)) {
+      required_dex_checksum_found_ = true;
+      has_original_dex_files_ = true;
     } else {
       // This can happen if the original dex file has been stripped from the
       // apk.
       VLOG(oat) << "OatFileAssistant: " << error_msg;
+      has_original_dex_files_ = false;
 
       // Get the checksum from the odex if we can.
       const OatFile* odex_file = GetOdexFile();
@@ -835,13 +845,13 @@
         const OatFile::OatDexFile* odex_dex_file = odex_file->GetOatDexFile(
             dex_location_, nullptr, false);
         if (odex_dex_file != nullptr) {
-          cached_required_dex_checksum = odex_dex_file->GetDexFileLocationChecksum();
-          required_dex_checksum_found = true;
+          cached_required_dex_checksum_ = odex_dex_file->GetDexFileLocationChecksum();
+          required_dex_checksum_found_ = true;
         }
       }
     }
   }
-  return required_dex_checksum_found ? &cached_required_dex_checksum : nullptr;
+  return required_dex_checksum_found_ ? &cached_required_dex_checksum_ : nullptr;
 }
 
 const OatFile* OatFileAssistant::GetOdexFile() {
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 4c0b0e2..7216fc7 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -174,6 +174,12 @@
   static std::vector<std::unique_ptr<const DexFile>> LoadDexFiles(
       const OatFile& oat_file, const char* dex_location);
 
+  // Returns true if there are dex files in the original dex location that can
+  // be compiled with dex2oat for this dex location.
+  // Returns false if there is no original dex file, or if the original dex
+  // file is an apk/zip without a classes.dex entry.
+  bool HasOriginalDexFiles();
+
   // If the dex file has been installed with a compiled oat file alongside
   // it, the compiled oat file will have the extension .odex, and is referred
   // to as the odex file. It is called odex for legacy reasons; the file is
@@ -312,6 +318,8 @@
   // Returns dex_checksum if a required checksum was located. Returns
   // null if the required checksum was not found.
   // The caller shouldn't clean up or free the returned pointer.
+  // This sets the has_original_dex_files_ field to true if a checksum was
+  // found for the dex_location_ dex file.
   const uint32_t* GetRequiredDexChecksum();
 
   // Returns the loaded odex file.
@@ -374,9 +382,10 @@
 
   // Cached value of the required dex checksum.
   // This should be accessed only by the GetRequiredDexChecksum() method.
-  uint32_t cached_required_dex_checksum;
-  bool required_dex_checksum_attempted = false;
-  bool required_dex_checksum_found;
+  uint32_t cached_required_dex_checksum_;
+  bool required_dex_checksum_attempted_ = false;
+  bool required_dex_checksum_found_;
+  bool has_original_dex_files_;
 
   // Cached value of the odex file name.
   // This should be accessed only by the OdexFileName() method.
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index d8e3797..570c59c 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -309,16 +309,24 @@
   EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
   EXPECT_EQ(OatFileAssistant::kOatOutOfDate, oat_file_assistant.OatFileStatus());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
 // Case: We have no DEX file and no OAT file.
-// Expect: Status is kDex2OatNeeded. Loading should fail, but not crash.
+// Expect: Status is kNoDexOptNeeded. Loading should fail, but not crash.
 TEST_F(OatFileAssistantTest, NoDexNoOat) {
   std::string dex_location = GetScratchDir() + "/NoDexNoOat.jar";
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
 
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
+
+  // Trying to make the oat file up to date should not fail or crash.
+  std::string error_msg;
+  EXPECT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg));
+
+  // Trying to get the best oat file should fail, but not crash.
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
   EXPECT_EQ(nullptr, oat_file.get());
 }
@@ -342,6 +350,7 @@
   EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
   EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
   EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
 // Case: We have a MultiDEX file and up-to-date OAT file for it.
@@ -353,6 +362,7 @@
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 
   // Verify we can load both dex files.
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -378,6 +388,7 @@
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
   EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
 // Case: We have a MultiDEX file and up-to-date OAT file for it with relative
@@ -432,6 +443,7 @@
   EXPECT_TRUE(oat_file_assistant.OatFileExists());
   EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
 // Case: We have a DEX file and an ODEX file, but no OAT file.
@@ -457,6 +469,7 @@
   EXPECT_FALSE(oat_file_assistant.OatFileExists());
   EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
 // Case: We have a stripped DEX file and an ODEX file, but no OAT file.
@@ -484,6 +497,7 @@
   EXPECT_FALSE(oat_file_assistant.OatFileExists());
   EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
   // Make the oat file up to date.
   std::string error_msg;
@@ -498,6 +512,7 @@
   EXPECT_TRUE(oat_file_assistant.OatFileExists());
   EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
   // Verify we can load the dex files from it.
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -538,6 +553,7 @@
   EXPECT_TRUE(oat_file_assistant.OatFileExists());
   EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
   // Make the oat file up to date.
   std::string error_msg;
@@ -554,6 +570,7 @@
   EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
   EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
   // Verify we can load the dex files from it.
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -564,6 +581,45 @@
   EXPECT_EQ(1u, dex_files.size());
 }
 
+// Case: We have a stripped (or resource-only) DEX file, no ODEX file and no
+// OAT file. Expect: The status is kNoDexOptNeeded.
+TEST_F(OatFileAssistantTest, ResourceOnlyDex) {
+  std::string dex_location = GetScratchDir() + "/ResourceOnlyDex.jar";
+
+  Copy(GetStrippedDexSrc1(), dex_location);
+
+  // Verify the status.
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
+  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileExists());
+  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
+
+  // Make the oat file up to date. This should have no effect.
+  std::string error_msg;
+  EXPECT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg;
+
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
+  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileExists());
+  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
+}
+
 // Case: We have a DEX file, no ODEX file and an OAT file that needs
 // relocation.
 // Expect: The status is kSelfPatchOatNeeded.
@@ -589,6 +645,7 @@
   EXPECT_TRUE(oat_file_assistant.OatFileNeedsRelocation());
   EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 
   // Make the oat file up to date.
   std::string error_msg;
@@ -605,6 +662,7 @@
   EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
   EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
   ASSERT_TRUE(oat_file.get() != nullptr);
@@ -643,6 +701,7 @@
   EXPECT_TRUE(oat_file_assistant.OatFileExists());
   EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 
   // Things aren't relocated, so it should fall back to interpreted.
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -688,6 +747,7 @@
   EXPECT_FALSE(oat_file_assistant.OatFileExists());
   EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
 // Case: We have a DEX file and up-to-date OAT file for it.
@@ -756,27 +816,6 @@
   EXPECT_FALSE(ofm.OatFileExists());
 }
 
-// Case: Non-existent Dex location.
-// Expect: The dex code is out of date, and trying to update it fails.
-TEST_F(OatFileAssistantTest, NonExsistentDexLocation) {
-  std::string dex_location = GetScratchDir() + "/BadDexLocation.jar";
-
-  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
-
-  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
-  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
-  EXPECT_FALSE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
-
-  std::string error_msg;
-  EXPECT_FALSE(oat_file_assistant.MakeUpToDate(&error_msg));
-  EXPECT_FALSE(error_msg.empty());
-}
-
 // Turn an absolute path into a path relative to the current working
 // directory.
 static std::string MakePathRelative(std::string target) {
@@ -833,24 +872,26 @@
 }
 
 // Case: Very short, non-existent Dex location.
-// Expect: Dex code is out of date, and trying to update it fails.
+// Expect: kNoDexOptNeeded.
 TEST_F(OatFileAssistantTest, ShortDexLocation) {
   std::string dex_location = "/xx";
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
   EXPECT_FALSE(oat_file_assistant.OdexFileExists());
   EXPECT_FALSE(oat_file_assistant.OatFileExists());
   EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
   EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
+  // Trying to make it up to date should have no effect.
   std::string error_msg;
-  EXPECT_FALSE(oat_file_assistant.MakeUpToDate(&error_msg));
-  EXPECT_FALSE(error_msg.empty());
+  EXPECT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg));
+  EXPECT_TRUE(error_msg.empty());
 }
 
 // Case: Non-standard extension for dex file.
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index d0f01b3..96c15ea 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1483,17 +1483,14 @@
   monitor_list_->DisallowNewMonitors();
   intern_table_->DisallowNewInterns();
   java_vm_->DisallowNewWeakGlobals();
-  // TODO: add a similar call for heap.allocation_records_, otherwise some of the newly allocated
-  // objects that are not marked might be swept from the records, making the records incomplete.
-  // It is safe for now since the only effect is that those objects do not have allocation records.
-  // The number of such objects should be small, and current allocation tracker cannot collect
-  // allocation records for all objects anyway.
+  heap_->DisallowNewAllocationRecords();
 }
 
 void Runtime::AllowNewSystemWeaks() {
   monitor_list_->AllowNewMonitors();
   intern_table_->AllowNewInterns();
   java_vm_->AllowNewWeakGlobals();
+  heap_->AllowNewAllocationRecords();
 }
 
 void Runtime::EnsureNewSystemWeaksDisallowed() {
@@ -1502,6 +1499,7 @@
   monitor_list_->EnsureNewMonitorsDisallowed();
   intern_table_->EnsureNewInternsDisallowed();
   java_vm_->EnsureNewWeakGlobalsDisallowed();
+  heap_->EnsureNewAllocationRecordsDisallowed();
 }
 
 void Runtime::SetInstructionSet(InstructionSet instruction_set) {
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 95b327e..02929e8 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -400,6 +400,7 @@
       monitor_enter_dex_pcs_(nullptr),
       have_pending_hard_failure_(false),
       have_pending_runtime_throw_failure_(false),
+      have_any_pending_runtime_throw_failure_(false),
       new_instance_count_(0),
       monitor_enter_count_(0),
       can_load_classes_(can_load_classes),
@@ -1637,6 +1638,7 @@
   } else if (kIsDebugBuild) {
     saved_line_->FillWithGarbage();
   }
+  DCHECK(!have_pending_runtime_throw_failure_);  // Per-instruction flag, should not be set here.
 
 
   // We need to ensure the work line is consistent while performing validation. When we spot a
@@ -2971,6 +2973,10 @@
   } else if (have_pending_runtime_throw_failure_) {
     /* checking interpreter will throw, mark following code as unreachable */
     opcode_flags = Instruction::kThrow;
+    have_any_pending_runtime_throw_failure_ = true;
+    // Reset the pending_runtime_throw flag. The flag is a global to decouple Fail and is per
+    // instruction.
+    have_pending_runtime_throw_failure_ = false;
   }
   /*
    * If we didn't just set the result register, clear it out. This ensures that you can only use
@@ -4060,10 +4066,15 @@
       VerifyPrimitivePut(*field_type, insn_type, vregA);
     } else {
       if (!insn_type.IsAssignableFrom(*field_type)) {
-        Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
-                                                << " to be compatible with type '" << insn_type
-                                                << "' but found type '" << *field_type
-                                                << "' in put-object";
+        // If the field type is not a reference, this is a global failure rather than
+        // a class change failure as the instructions and the descriptors for the type
+        // should have been consistent within the same file at compile time.
+        VerifyError error = field_type->IsReferenceTypes() ? VERIFY_ERROR_BAD_CLASS_SOFT
+                                                           : VERIFY_ERROR_BAD_CLASS_HARD;
+        Fail(error) << "expected field " << PrettyField(field)
+                    << " to be compatible with type '" << insn_type
+                    << "' but found type '" << *field_type
+                    << "' in put-object";
         return;
       }
       work_line_->VerifyRegisterType(this, vregA, *field_type);
@@ -4087,10 +4098,15 @@
       }
     } else {
       if (!insn_type.IsAssignableFrom(*field_type)) {
-        Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
-                                          << " to be compatible with type '" << insn_type
-                                          << "' but found type '" << *field_type
-                                          << "' in get-object";
+        // If the field type is not a reference, this is a global failure rather than
+        // a class change failure as the instructions and the descriptors for the type
+        // should have been consistent within the same file at compile time.
+        VerifyError error = field_type->IsReferenceTypes() ? VERIFY_ERROR_BAD_CLASS_SOFT
+                                                           : VERIFY_ERROR_BAD_CLASS_HARD;
+        Fail(error) << "expected field " << PrettyField(field)
+                    << " to be compatible with type '" << insn_type
+                    << "' but found type '" << *field_type
+                    << "' in get-object";
         work_line_->SetRegisterType(this, vregA, reg_types_.Conflict());
         return;
       }
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index d7ddd67..2550694 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -255,7 +255,7 @@
   bool HasVirtualOrInterfaceInvokes() const;
   bool HasFailures() const;
   bool HasInstructionThatWillThrow() const {
-    return have_pending_runtime_throw_failure_;
+    return have_any_pending_runtime_throw_failure_;
   }
 
   const RegType& ResolveCheckedClass(uint32_t class_idx)
@@ -730,8 +730,12 @@
   // would fail at runtime throwing an exception. Such an instruction causes the following code
   // to be unreachable. This is set by Fail and used to ensure we don't process unreachable
   // instructions that would hard fail the verification.
+  // Note: this flag is reset after processing each instruction.
   bool have_pending_runtime_throw_failure_;
 
+  // A version of the above that is not reset and thus captures if there were *any* throw failures.
+  bool have_any_pending_runtime_throw_failure_;
+
   // Info message log use primarily for verifier diagnostics.
   std::ostringstream info_messages_;
 
diff --git a/test/003-omnibus-opcodes/src/UnresTest2.java b/test/003-omnibus-opcodes/src/UnresTest2.java
index 4135d73..d46b877 100644
--- a/test/003-omnibus-opcodes/src/UnresTest2.java
+++ b/test/003-omnibus-opcodes/src/UnresTest2.java
@@ -41,7 +41,8 @@
             new UnresClassSubclass();
             Main.assertTrue(false);
         } catch (NoClassDefFoundError ncdfe) {
-            Main.assertTrue(ncdfe.getCause() instanceof ClassNotFoundException);
+            // TODO b/22080519
+            // Main.assertTrue(ncdfe.getCause() instanceof ClassNotFoundException);
             // good
         }
 
@@ -49,7 +50,6 @@
             UnresClass[] uar = new UnresClass[3];
             Main.assertTrue(false);
         } catch (NoClassDefFoundError ncdfe) {
-            Main.assertTrue(ncdfe.getCause() instanceof ClassNotFoundException);
             // good
         }
 
diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt
index 9413c13..938cf5d 100644
--- a/test/800-smali/expected.txt
+++ b/test/800-smali/expected.txt
@@ -24,4 +24,6 @@
 b/22045582
 b/22045582 (int)
 b/22045582 (wide)
+b/21886894
+b/22080519
 Done!
diff --git a/test/800-smali/smali/b_21886894.smali b/test/800-smali/smali/b_21886894.smali
new file mode 100644
index 0000000..f1ac3e9
--- /dev/null
+++ b/test/800-smali/smali/b_21886894.smali
@@ -0,0 +1,15 @@
+.class public LB21886894;
+.super Ljava/lang/Object;
+
+.method public constructor <init>()V
+    .registers 2
+    invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+    return-void
+.end method
+
+.method public test()V
+    .registers 2
+    const v0, 0
+    iput-object v0, p0, Lsome/unresolved/Type;->a:I
+    return-void
+.end method
diff --git a/test/800-smali/smali/b_22080519.smali b/test/800-smali/smali/b_22080519.smali
new file mode 100644
index 0000000..bf062fb
--- /dev/null
+++ b/test/800-smali/smali/b_22080519.smali
@@ -0,0 +1,27 @@
+.class public LB22080519;
+.super Ljava/lang/Object;
+
+.method public static run()V
+.registers 6
+:Label1
+       const v1, 15
+       const v2, 0
+       # Have a branch to reach both the aget-object and something else.
+       if-eqz v1, :Label2
+
+       # This instruction will be marked runtime-throw.
+       aget-object v3, v2, v1
+
+:Label2
+       # This should *not* be flagged as a runtime throw
+       goto :Label4
+
+:Label3
+       move-exception v3
+       throw v3
+
+:Label4
+       return-void
+
+.catchall {:Label1 .. :Label3} :Label3
+.end method
\ No newline at end of file
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index 28954f8..cc194d5 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -87,7 +87,7 @@
                 null));
         testCases.add(new TestCase("b/21873167", "B21873167", "test", null, null, null));
         testCases.add(new TestCase("b/21614284", "B21614284", "test", new Object[] { null },
-            new NullPointerException(), null));
+                new NullPointerException(), null));
         testCases.add(new TestCase("b/21902684", "B21902684", "test", null, null, null));
         testCases.add(new TestCase("b/22045582", "B22045582", "run", null, new VerifyError(),
                 0));
@@ -95,6 +95,10 @@
                 new VerifyError(), 0));
         testCases.add(new TestCase("b/22045582 (wide)", "B22045582Wide", "run", null,
                 new VerifyError(), 0));
+        testCases.add(new TestCase("b/21886894", "B21886894", "test", null, new VerifyError(),
+                null));
+        testCases.add(new TestCase("b/22080519", "B22080519", "run", null,
+                new NullPointerException(), null));
     }
 
     public void runTests() {
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index ac9656b..29c3ea9 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -230,10 +230,7 @@
 TEST_ART_BROKEN_NO_RELOCATE_TESTS :=
 
 # Tests that are broken with GC stress.
-# 098-ddmc is broken until the allocation tracker does not mark recently allocated objects as roots.
-# Marking them roots is for consistent behavior with DDMS's getRecentAllocations(). b/20037135
-TEST_ART_BROKEN_GCSTRESS_RUN_TESTS := \
-  098-ddmc
+TEST_ART_BROKEN_GCSTRESS_RUN_TESTS :=
 
 ifneq (,$(filter gcstress,$(GC_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
diff --git a/tools/art b/tools/art
index 2ee8940..676d6ae 100644
--- a/tools/art
+++ b/tools/art
@@ -93,6 +93,7 @@
   ANDROID_ROOT=$ANDROID_ROOT \
   LD_LIBRARY_PATH=$LD_LIBRARY_PATH \
   PATH=$ANDROID_ROOT/bin:$PATH \
+  LD_USE_LOAD_BIAS=1 \
   $invoke_with $ANDROID_ROOT/bin/$DALVIKVM $lib \
     -XXlib:$LIBART \
     -Xnorelocate \
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index b053f0d..682a27b 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -110,9 +110,8 @@
   bug: 18869265
 },
 {
-  description: "Test sometimes timeouts on volantis",
+  description: "Test sometimes timeouts on volantis, and on most modes in debug mode",
   result: EXEC_TIMEOUT,
-  modes_variants: [[device,X64]],
   names: ["libcore.java.lang.SystemTest#testArrayCopyConcurrentModification"],
   bug: 19165288
 },
@@ -132,5 +131,11 @@
   result: EXEC_FAILED,
   names: ["libcore.javax.crypto.CipherTest#testCipher_ShortBlock_Failure",
           "libcore.javax.crypto.CipherTest#testCipher_Success"]
+},
+{
+  description: "Flake when running with libartd.so or interpreter",
+  result: EXEC_FAILED,
+  bug:22106064,
+  name: "libcore.java.lang.OldThreadGroupTest#test_enumerateLThreadArrayLZtest_enumerateLThreadArrayLZ"
 }
 ]