Merge "Create the class loader context within a Runtime scope."
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index 1331fc3..15ced72 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -49,6 +49,7 @@
 #include "gc/heap-visit-objects-inl.h"
 #include "gc/heap.h"
 #include "gc/space/large_object_space.h"
+#include "gc/space/region_space.h"
 #include "gc/space/space-inl.h"
 #include "gc/verification.h"
 #include "handle_scope-inl.h"
@@ -2423,10 +2424,33 @@
   }
 
   // Calculate bin slot offsets.
-  for (ImageInfo& image_info : image_infos_) {
+  for (size_t oat_index = 0; oat_index < image_infos_.size(); ++oat_index) {
+    ImageInfo& image_info = image_infos_[oat_index];
     size_t bin_offset = image_objects_offset_begin_;
+    // Need to visit the objects in bin order since alignment requirements might change the
+    // section sizes.
+    // Avoid using ObjPtr since VisitObjects invalidates. This is safe since concurrent GC can not
+    // occur during image writing.
+    using BinPair = std::pair<BinSlot, mirror::Object*>;
+    std::vector<BinPair> objects;
+    heap->VisitObjects([&](mirror::Object* obj)
+        REQUIRES_SHARED(Locks::mutator_lock_) {
+      // Only visit the oat index for the current image.
+      if (IsImageObject(obj) && GetOatIndex(obj) == oat_index) {
+        objects.emplace_back(GetImageBinSlot(obj), obj);
+      }
+    });
+    std::sort(objects.begin(), objects.end(), [](const BinPair& a, const BinPair& b) -> bool {
+      if (a.first.GetBin() != b.first.GetBin()) {
+        return a.first.GetBin() < b.first.GetBin();
+      }
+      // Note that the index is really the relative offset in this case.
+      return a.first.GetIndex() < b.first.GetIndex();
+    });
+    auto it = objects.begin();
     for (size_t i = 0; i != kNumberOfBins; ++i) {
-      switch (static_cast<Bin>(i)) {
+      Bin bin = enum_cast<Bin>(i);
+      switch (bin) {
         case Bin::kArtMethodClean:
         case Bin::kArtMethodDirty: {
           bin_offset = RoundUp(bin_offset, method_alignment);
@@ -2445,13 +2469,49 @@
         }
       }
       image_info.bin_slot_offsets_[i] = bin_offset;
-      bin_offset += image_info.bin_slot_sizes_[i];
+
+      // If the bin is for mirror objects, assign the offsets since we may need to change sizes
+      // from alignment requirements.
+      if (i < static_cast<size_t>(Bin::kMirrorCount)) {
+        const size_t start_offset = bin_offset;
+        // Visit and assign offsets for all objects of the bin type.
+        while (it != objects.end() && it->first.GetBin() == bin) {
+          ObjPtr<mirror::Object> obj(it->second);
+          const size_t object_size = RoundUp(obj->SizeOf(), kObjectAlignment);
+          // If the object spans region bondaries, add padding objects between.
+          // TODO: Instead of adding padding, we should consider reordering the bins to reduce
+          // wasted space.
+          if (region_size_ != 0u) {
+            const size_t offset_after_header = bin_offset - sizeof(ImageHeader);
+            const size_t next_region = RoundUp(offset_after_header, region_size_);
+            if (offset_after_header != next_region &&
+                offset_after_header + object_size > next_region) {
+              // Add padding objects until aligned.
+              while (bin_offset - sizeof(ImageHeader) < next_region) {
+                image_info.padding_object_offsets_.push_back(bin_offset);
+                bin_offset += kObjectAlignment;
+                region_alignment_wasted_ += kObjectAlignment;
+                image_info.image_end_ += kObjectAlignment;
+              }
+              CHECK_EQ(bin_offset - sizeof(ImageHeader), next_region);
+            }
+          }
+          SetImageOffset(obj.Ptr(), bin_offset);
+          bin_offset = bin_offset + object_size;
+          ++it;
+        }
+        image_info.bin_slot_sizes_[i] = bin_offset - start_offset;
+      } else {
+        bin_offset += image_info.bin_slot_sizes_[i];
+      }
     }
     // NOTE: There may be additional padding between the bin slots and the intern table.
     DCHECK_EQ(image_info.image_end_,
               image_info.GetBinSizeSum(Bin::kMirrorCount) + image_objects_offset_begin_);
   }
 
+  VLOG(image) << "Space wasted for region alignment " << region_alignment_wasted_;
+
   // Calculate image offsets.
   size_t image_offset = 0;
   for (ImageInfo& image_info : image_infos_) {
@@ -2462,17 +2522,6 @@
     image_offset += image_info.image_size_;
   }
 
-  // Transform each object's bin slot into an offset which will be used to do the final copy.
-  {
-    auto unbin_objects_into_offset = [&](mirror::Object* obj)
-        REQUIRES_SHARED(Locks::mutator_lock_) {
-      if (IsImageObject(obj)) {
-        UnbinObjectsIntoOffset(obj);
-      }
-    };
-    heap->VisitObjects(unbin_objects_into_offset);
-  }
-
   size_t i = 0;
   for (ImageInfo& image_info : image_infos_) {
     image_info.image_roots_address_ = PointerToLowMemUInt32(GetImageAddress(image_roots[i].Get()));
@@ -2887,16 +2936,6 @@
   }
 }
 
-void ImageWriter::CopyAndFixupObjects() {
-  auto visitor = [&](Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
-    DCHECK(obj != nullptr);
-    CopyAndFixupObject(obj);
-  };
-  Runtime::Current()->GetHeap()->VisitObjects(visitor);
-  // We no longer need the hashcode map, values have already been copied to target objects.
-  saved_hashcode_map_.clear();
-}
-
 void ImageWriter::FixupPointerArray(mirror::Object* dst,
                                     mirror::PointerArray* arr,
                                     Bin array_type) {
@@ -2944,6 +2983,18 @@
   image_info.image_bitmap_->Set(dst);  // Mark the obj as live.
 
   const size_t n = obj->SizeOf();
+
+  if (kIsDebugBuild && region_size_ != 0u) {
+    const size_t offset_after_header = offset - sizeof(ImageHeader);
+    const size_t next_region = RoundUp(offset_after_header, region_size_);
+    if (offset_after_header != next_region) {
+      // If the object is not on a region bondary, it must not be cross region.
+      CHECK_LT(offset_after_header, next_region)
+          << "offset_after_header=" << offset_after_header << " size=" << n;
+      CHECK_LE(offset_after_header + n, next_region)
+          << "offset_after_header=" << offset_after_header << " size=" << n;
+    }
+  }
   DCHECK_LE(offset + n, image_info.image_.Size());
   memcpy(dst, src, n);
 
@@ -2973,7 +3024,6 @@
       const {}
   void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {}
 
-
   void operator()(ObjPtr<Object> obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
     ObjPtr<Object> ref = obj->GetFieldObject<Object, kVerifyNone>(offset);
@@ -2994,6 +3044,25 @@
   mirror::Object* const copy_;
 };
 
+void ImageWriter::CopyAndFixupObjects() {
+  auto visitor = [&](Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+    DCHECK(obj != nullptr);
+    CopyAndFixupObject(obj);
+  };
+  Runtime::Current()->GetHeap()->VisitObjects(visitor);
+  // Copy the padding objects since they are required for in order traversal of the image space.
+  for (const ImageInfo& image_info : image_infos_) {
+    for (const size_t offset : image_info.padding_object_offsets_) {
+      auto* dst = reinterpret_cast<Object*>(image_info.image_.Begin() + offset);
+      dst->SetClass<kVerifyNone>(GetImageAddress(GetClassRoot<mirror::Object>().Ptr()));
+      dst->SetLockWord<kVerifyNone>(LockWord::Default(), /*as_volatile=*/ false);
+      image_info.image_bitmap_->Set(dst);  // Mark the obj as live.
+    }
+  }
+  // We no longer need the hashcode map, values have already been copied to target objects.
+  saved_hashcode_map_.clear();
+}
+
 class ImageWriter::FixupClassVisitor final : public FixupVisitor {
  public:
   FixupClassVisitor(ImageWriter* image_writer, Object* copy)
@@ -3572,6 +3641,10 @@
   CHECK_EQ(compiler_options.IsBootImage(),
            Runtime::Current()->GetHeap()->GetBootImageSpaces().empty())
       << "Compiling a boot image should occur iff there are no boot image spaces loaded";
+  if (compiler_options_.IsAppImage()) {
+    // Make sure objects are not crossing region boundaries for app images.
+    region_size_ = gc::space::RegionSpace::kRegionSize;
+  }
 }
 
 ImageWriter::ImageInfo::ImageInfo()
diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h
index b680265..8896e07 100644
--- a/dex2oat/linker/image_writer.h
+++ b/dex2oat/linker/image_writer.h
@@ -279,7 +279,7 @@
 
    private:
     // Must be the same size as LockWord, any larger and we would truncate the data.
-    const uint32_t lockword_;
+    uint32_t lockword_;
   };
 
   struct ImageInfo {
@@ -397,6 +397,9 @@
 
     // Class table associated with this image for serialization.
     std::unique_ptr<ClassTable> class_table_;
+
+    // Padding objects to ensure region alignment (if required).
+    std::vector<size_t> padding_object_offsets_;
   };
 
   // We use the lock word to store the offset of the object in the image.
@@ -798,6 +801,12 @@
   // Set of objects known to be dirty in the image. Can be nullptr if there are none.
   const HashSet<std::string>* dirty_image_objects_;
 
+  // Objects are guaranteed to not cross the region size boundary.
+  size_t region_size_ = 0u;
+
+  // Region alignment bytes wasted.
+  size_t region_alignment_wasted_ = 0u;
+
   class ImageFileGuard;
   class FixupClassVisitor;
   class FixupRootVisitor;
diff --git a/libdexfile/external/dex_file_ext.cc b/libdexfile/external/dex_file_ext.cc
index 5c353b5..7bf01a8 100644
--- a/libdexfile/external/dex_file_ext.cc
+++ b/libdexfile/external/dex_file_ext.cc
@@ -39,7 +39,7 @@
 #include <dex/dex_file-inl.h>
 #include <dex/dex_file_loader.h>
 
-#include "art_api/ext_dex_file.h"
+#include "art_api/dex_file_support.h"
 
 extern "C" class ExtDexFileString {
  public:
@@ -226,7 +226,10 @@
 
   if (length < offset + sizeof(art::DexFile::Header)) {
     *ext_error_msg = new ExtDexFileString{android::base::StringPrintf(
-        "Offset %" PRId64 " too large for '%s' of size %zu", int64_t{offset}, location, length)};
+        "Offset %" PRId64 " too large for '%s' of size %zu",
+        int64_t{offset},
+        location,
+        length)};
     return false;
   }
 
@@ -282,6 +285,7 @@
 
 int ExtDexFileGetMethodInfoForOffset(ExtDexFile* ext_dex_file,
                                      int64_t dex_offset,
+                                     int with_signature,
                                      /*out*/ ExtDexFileMethodInfo* method_info) {
   if (!ext_dex_file->dex_file_->IsInDataSection(ext_dex_file->dex_file_->Begin() + dex_offset)) {
     return false;  // The DEX offset is not within the bytecode of this dex file.
@@ -304,7 +308,7 @@
     method_info->offset = entry->offset;
     method_info->len = entry->len;
     method_info->name =
-        new ExtDexFileString{ext_dex_file->dex_file_->PrettyMethod(entry->index, false)};
+        new ExtDexFileString{ext_dex_file->dex_file_->PrettyMethod(entry->index, with_signature)};
     return true;
   }
 
diff --git a/libdexfile/external/dex_file_supp.cc b/libdexfile/external/dex_file_supp.cc
index 6514c8a..5bd25fc 100644
--- a/libdexfile/external/dex_file_supp.cc
+++ b/libdexfile/external/dex_file_supp.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "art_api/ext_dex_file.h"
+#include "art_api/dex_file_support.h"
 
 namespace art_api {
 namespace dex {
diff --git a/libdexfile/external/include/art_api/ext_dex_file.h b/libdexfile/external/include/art_api/dex_file_support.h
similarity index 88%
rename from libdexfile/external/include/art_api/ext_dex_file.h
rename to libdexfile/external/include/art_api/dex_file_support.h
index 4a52a2b..99d60d1 100644
--- a/libdexfile/external/include/art_api/ext_dex_file.h
+++ b/libdexfile/external/include/art_api/dex_file_support.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_LIBDEXFILE_EXTERNAL_INCLUDE_ART_API_EXT_DEX_FILE_H_
-#define ART_LIBDEXFILE_EXTERNAL_INCLUDE_ART_API_EXT_DEX_FILE_H_
+#ifndef ART_LIBDEXFILE_EXTERNAL_INCLUDE_ART_API_DEX_FILE_SUPPORT_H_
+#define ART_LIBDEXFILE_EXTERNAL_INCLUDE_ART_API_DEX_FILE_SUPPORT_H_
 
 // Dex file external API
 
@@ -76,6 +76,7 @@
 // See art_api::dex::DexFile::GetMethodInfoForOffset. Returns true on success.
 int ExtDexFileGetMethodInfoForOffset(ExtDexFile* ext_dex_file,
                                      int64_t dex_offset,
+                                     int with_signature,
                                      /*out*/ ExtDexFileMethodInfo* method_info);
 
 typedef void ExtDexFileMethodInfoCallback(const ExtDexFileMethodInfo* ext_method_info,
@@ -211,10 +212,15 @@
 
   // Given an offset relative to the start of the dex file header, if there is a
   // method whose instruction range includes that offset then returns info about
-  // it, otherwise returns a struct with offset == 0.
-  MethodInfo GetMethodInfoForOffset(int64_t dex_offset) {
+  // it, otherwise returns a struct with offset == 0. MethodInfo.name receives
+  // the full function signature if with_signature is set, otherwise it gets the
+  // class and method name only.
+  MethodInfo GetMethodInfoForOffset(int64_t dex_offset, bool with_signature) {
     ExtDexFileMethodInfo ext_method_info;
-    if (ExtDexFileGetMethodInfoForOffset(ext_dex_file_, dex_offset, &ext_method_info)) {
+    if (ExtDexFileGetMethodInfoForOffset(ext_dex_file_,
+                                         dex_offset,
+                                         with_signature,
+                                         &ext_method_info)) {
       return AbsorbMethodInfo(ext_method_info);
     }
     return {/*offset=*/0, /*len=*/0, /*name=*/DexString()};
@@ -223,10 +229,12 @@
   // Returns info structs about all methods in the dex file. MethodInfo.name
   // receives the full function signature if with_signature is set, otherwise it
   // gets the class and method name only.
-  std::vector<MethodInfo> GetAllMethodInfos(bool with_signature = true) {
+  std::vector<MethodInfo> GetAllMethodInfos(bool with_signature) {
     MethodInfoVector res;
-    ExtDexFileGetAllMethodInfos(
-        ext_dex_file_, with_signature, AddMethodInfoCallback, static_cast<void*>(&res));
+    ExtDexFileGetAllMethodInfos(ext_dex_file_,
+                                with_signature,
+                                AddMethodInfoCallback,
+                                static_cast<void*>(&res));
     return res;
   }
 
@@ -245,4 +253,4 @@
 }  // namespace dex
 }  // namespace art_api
 
-#endif  // ART_LIBDEXFILE_EXTERNAL_INCLUDE_ART_API_EXT_DEX_FILE_H_
+#endif  // ART_LIBDEXFILE_EXTERNAL_INCLUDE_ART_API_DEX_FILE_SUPPORT_H_
diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc
index bd5b598..7a2b638 100644
--- a/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/openjdkjvmti/OpenjdkJvmTi.cc
@@ -1552,7 +1552,7 @@
 
   {
     // Make sure we can deopt anything we need to.
-    art::ScopedObjectAccess soa(art::Thread::Current());
+    art::ScopedSuspendAll ssa(__FUNCTION__);
     gDeoptManager->FinishSetup();
   }
 
diff --git a/openjdkjvmti/deopt_manager.h b/openjdkjvmti/deopt_manager.h
index d9f34a5..065baa7 100644
--- a/openjdkjvmti/deopt_manager.h
+++ b/openjdkjvmti/deopt_manager.h
@@ -107,7 +107,7 @@
 
   void FinishSetup()
       REQUIRES(!deoptimization_status_lock_, !art::Roles::uninterruptible_)
-      REQUIRES_SHARED(art::Locks::mutator_lock_);
+      REQUIRES(art::Locks::mutator_lock_);
 
   static DeoptManager* Get();
 
diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc
index 7fb6a15..22c622a 100644
--- a/openjdkjvmti/events.cc
+++ b/openjdkjvmti/events.cc
@@ -34,6 +34,7 @@
 #include <array>
 #include <sys/time.h>
 
+#include "arch/context.h"
 #include "art_field-inl.h"
 #include "art_jvmti.h"
 #include "art_method-inl.h"
diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc
index 7d69c89..e88539f 100644
--- a/openjdkjvmti/ti_method.cc
+++ b/openjdkjvmti/ti_method.cc
@@ -33,6 +33,7 @@
 
 #include <type_traits>
 
+#include "arch/context.h"
 #include "art_jvmti.h"
 #include "art_method-inl.h"
 #include "base/enums.h"
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index 4bcb7b2..3d175a8 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -188,7 +188,9 @@
     if (obsoleted_methods_.find(old_method) != obsoleted_methods_.end()) {
       // We cannot ensure that the right dex file is used in inlined frames so we don't support
       // redefining them.
-      DCHECK(!IsInInlinedFrame()) << "Inlined frames are not supported when using redefinition";
+      DCHECK(!IsInInlinedFrame()) << "Inlined frames are not supported when using redefinition: "
+                                  << old_method->PrettyMethod() << " is inlined into "
+                                  << GetOuterMethod()->PrettyMethod();
       art::ArtMethod* new_obsolete_method = obsolete_maps_->FindObsoleteVersion(old_method);
       if (new_obsolete_method == nullptr) {
         // Create a new Obsolete Method and put it in the list.
diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc
index 4a3eac8..385ac45 100644
--- a/openjdkjvmti/ti_stack.cc
+++ b/openjdkjvmti/ti_stack.cc
@@ -36,6 +36,7 @@
 #include <unordered_map>
 #include <vector>
 
+#include "arch/context.h"
 #include "art_field-inl.h"
 #include "art_method-inl.h"
 #include "art_jvmti.h"
diff --git a/runtime/art_method.h b/runtime/art_method.h
index aed9f62..aabd093 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -476,7 +476,7 @@
   }
 
   ProfilingInfo* GetProfilingInfo(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_) {
-    if (UNLIKELY(IsNative()) || UNLIKELY(IsProxyMethod())) {
+    if (UNLIKELY(IsNative() || IsProxyMethod() || !IsInvokable())) {
       return nullptr;
     }
     return reinterpret_cast<ProfilingInfo*>(GetDataPtrSize(pointer_size));
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index adf01c3..663af81 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -681,13 +681,13 @@
   //
   // The performance cost of this is non-negligible during native-debugging due to the
   // forced JIT, so we keep the AOT code in that case in exchange for limited native debugging.
+  ScopedSuspendAll ssa(__FUNCTION__);
   if (!runtime->IsJavaDebuggable() &&
       !runtime->GetInstrumentation()->IsForcedInterpretOnly() &&
       !runtime->IsNativeDebuggable()) {
     runtime->DeoptimizeBootImage();
   }
 
-  ScopedSuspendAll ssa(__FUNCTION__);
   if (RequiresDeoptimization()) {
     runtime->GetInstrumentation()->EnableDeoptimization();
   }
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 27c47bc..36f7b3d 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -848,7 +848,7 @@
         new_quick_code = GetQuickInstrumentationEntryPoint();
         if (!method->IsNative() && Runtime::Current()->GetJit() != nullptr) {
           // Native methods use trampoline entrypoints during interpreter tracing.
-          DCHECK(!Runtime::Current()->GetJit()->GetCodeCache()->GetGarbageCollectCode());
+          DCHECK(!Runtime::Current()->GetJit()->GetCodeCache()->GetGarbageCollectCodeUnsafe());
           ProfilingInfo* profiling_info = method->GetProfilingInfo(kRuntimePointerSize);
           // Tracing will look at the saved entry point in the profiling info to know the actual
           // entrypoint, so we store it here.
@@ -1050,14 +1050,6 @@
     level = InstrumentationLevel::kInstrumentWithInterpreter;
   } else {
     level = InstrumentationLevel::kInstrumentWithInstrumentationStubs;
-    if (Runtime::Current()->GetJit() != nullptr) {
-      // TODO b/110263880 It would be better if we didn't need to do this.
-      // Since we need to hold the method entrypoint across a suspend to ensure instrumentation
-      // hooks are called correctly we have to disable jit-gc to ensure that the entrypoint doesn't
-      // go away. Furthermore we need to leave this off permanently since one could get the same
-      // effect by causing this to be toggled on and off.
-      Runtime::Current()->GetJit()->GetCodeCache()->SetGarbageCollectCode(false);
-    }
   }
   ConfigureStubs(key, level);
 }
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index d976fec..c1b9a1a 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -390,7 +390,7 @@
 
   // Zygote should never collect code to share the memory with the children.
   if (is_zygote) {
-    jit_code_cache->SetGarbageCollectCode(false);
+    jit_code_cache->garbage_collect_code_ = false;
   }
 
   if (!jit_code_cache->InitializeMappings(rwx_memory_allowed, is_zygote, error_msg)) {
@@ -1247,6 +1247,24 @@
   }
 }
 
+void JitCodeCache::ClearEntryPointsInZygoteExecSpace() {
+  MutexLock mu(Thread::Current(), lock_);
+  // Iterate over profiling infos to know which methods may have been JITted. Note that
+  // to be JITted, a method must have a profiling info.
+  for (ProfilingInfo* info : profiling_infos_) {
+    ArtMethod* method = info->GetMethod();
+    if (IsInZygoteExecSpace(method->GetEntryPointFromQuickCompiledCode())) {
+      method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
+    }
+    // If zygote does method tracing, or in some configuration where
+    // the JIT zygote does GC, we also need to clear the saved entry point
+    // in the profiling info.
+    if (IsInZygoteExecSpace(info->GetSavedEntryPoint())) {
+      info->SetSavedEntryPoint(nullptr);
+    }
+  }
+}
+
 size_t JitCodeCache::CodeCacheSizeLocked() {
   return used_memory_for_code_;
 }
@@ -1437,17 +1455,14 @@
 
 void JitCodeCache::GarbageCollectCache(Thread* self) {
   ScopedTrace trace(__FUNCTION__);
-  if (!garbage_collect_code_) {
-    MutexLock mu(self, lock_);
-    IncreaseCodeCacheCapacity();
-    return;
-  }
-
   // Wait for an existing collection, or let everyone know we are starting one.
   {
     ScopedThreadSuspension sts(self, kSuspended);
     MutexLock mu(self, lock_);
-    if (WaitForPotentialCollectionToComplete(self)) {
+    if (!garbage_collect_code_) {
+      IncreaseCodeCacheCapacity();
+      return;
+    } else if (WaitForPotentialCollectionToComplete(self)) {
       return;
     } else {
       number_of_collections_++;
@@ -1573,6 +1588,29 @@
   FreeAllMethodHeaders(method_headers);
 }
 
+bool JitCodeCache::GetGarbageCollectCode() {
+  MutexLock mu(Thread::Current(), lock_);
+  return garbage_collect_code_;
+}
+
+void JitCodeCache::SetGarbageCollectCode(bool value) {
+  Thread* self = Thread::Current();
+  MutexLock mu(self, lock_);
+  if (garbage_collect_code_ != value) {
+    if (garbage_collect_code_) {
+      // When dynamically disabling the garbage collection, we neee
+      // to make sure that a potential current collection is finished, and also
+      // clear the saved entry point in profiling infos to avoid dangling pointers.
+      WaitForPotentialCollectionToComplete(self);
+      for (ProfilingInfo* info : profiling_infos_) {
+        info->SetSavedEntryPoint(nullptr);
+      }
+    }
+    // Update the flag while holding the lock to ensure no thread will try to GC.
+    garbage_collect_code_ = value;
+  }
+}
+
 void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) {
   ScopedTrace trace(__FUNCTION__);
   {
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index e2f3357..8a6cebe 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -246,13 +246,13 @@
   void MoveObsoleteMethod(ArtMethod* old_method, ArtMethod* new_method)
       REQUIRES(!lock_) REQUIRES(Locks::mutator_lock_);
 
-  // Dynamically change whether we want to garbage collect code. Should only be used during JIT
-  // initialization or by tests.
-  void SetGarbageCollectCode(bool value) {
-    garbage_collect_code_ = value;
-  }
+  // Dynamically change whether we want to garbage collect code.
+  void SetGarbageCollectCode(bool value) REQUIRES(!lock_);
 
-  bool GetGarbageCollectCode() const {
+  bool GetGarbageCollectCode() REQUIRES(!lock_);
+
+  // Unsafe variant for debug checks.
+  bool GetGarbageCollectCodeUnsafe() const NO_THREAD_SAFETY_ANALYSIS {
     return garbage_collect_code_;
   }
 
@@ -264,6 +264,11 @@
 
   void PostForkChildAction(bool is_system_server, bool is_zygote);
 
+  // Clear the entrypoints of JIT compiled methods that belong in the zygote space.
+  // This is used for removing non-debuggable JIT code at the point we realize the runtime
+  // is debuggable.
+  void ClearEntryPointsInZygoteExecSpace() REQUIRES(!lock_) REQUIRES(Locks::mutator_lock_);
+
  private:
   JitCodeCache();
 
@@ -451,7 +456,7 @@
   bool last_collection_increased_code_cache_ GUARDED_BY(lock_);
 
   // Whether we can do garbage collection. Not 'const' as tests may override this.
-  bool garbage_collect_code_;
+  bool garbage_collect_code_ GUARDED_BY(lock_);
 
   // The size in bytes of used memory for the data portion of the code cache.
   size_t used_memory_for_data_ GUARDED_BY(lock_);
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 9ce4749..26fc5e9 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -203,8 +203,11 @@
     runtime->AddCompilerOption("--debuggable");
     runtime_flags |= DEBUG_GENERATE_MINI_DEBUG_INFO;
     runtime->SetJavaDebuggable(true);
-    // Deoptimize the boot image as it may be non-debuggable.
-    runtime->DeoptimizeBootImage();
+    {
+      // Deoptimize the boot image as it may be non-debuggable.
+      ScopedSuspendAll ssa(__FUNCTION__);
+      runtime->DeoptimizeBootImage();
+    }
     runtime_flags &= ~DEBUG_JAVA_DEBUGGABLE;
     needs_non_debuggable_classes = true;
   }
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index f30ba0c..eea7c67 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -42,6 +42,7 @@
 #include "aot_class_linker.h"
 #include "arch/arm/registers_arm.h"
 #include "arch/arm64/registers_arm64.h"
+#include "arch/context.h"
 #include "arch/instruction_set_features.h"
 #include "arch/mips/registers_mips.h"
 #include "arch/mips64/registers_mips64.h"
@@ -1472,6 +1473,8 @@
     if (IsJavaDebuggable()) {
       // Now that we have loaded the boot image, deoptimize its methods if we are running
       // debuggable, as the code may have been compiled non-debuggable.
+      ScopedThreadSuspension sts(self, ThreadState::kNative);
+      ScopedSuspendAll ssa(__FUNCTION__);
       DeoptimizeBootImage();
     }
   } else {
@@ -1571,10 +1574,14 @@
   // Runtime initialization is largely done now.
   // We load plugins first since that can modify the runtime state slightly.
   // Load all plugins
-  for (auto& plugin : plugins_) {
-    std::string err;
-    if (!plugin.Load(&err)) {
-      LOG(FATAL) << plugin << " failed to load: " << err;
+  {
+    // The init method of plugins expect the state of the thread to be non runnable.
+    ScopedThreadSuspension sts(self, ThreadState::kNative);
+    for (auto& plugin : plugins_) {
+      std::string err;
+      if (!plugin.Load(&err)) {
+        LOG(FATAL) << plugin << " failed to load: " << err;
+      }
     }
   }
 
@@ -2647,6 +2654,7 @@
       : instrumentation_(instrumentation) {}
 
   bool operator()(ObjPtr<mirror::Class> klass) override REQUIRES(Locks::mutator_lock_) {
+    DCHECK(Locks::mutator_lock_->IsExclusiveHeld(Thread::Current()));
     auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
     for (auto& m : klass->GetMethods(pointer_size)) {
       const void* code = m.GetEntryPointFromQuickCompiledCode();
@@ -2673,9 +2681,13 @@
   // we patch entry points of methods in boot image to interpreter bridge, as
   // boot image code may be AOT compiled as not debuggable.
   if (!GetInstrumentation()->IsForcedInterpretOnly()) {
-    ScopedObjectAccess soa(Thread::Current());
     UpdateEntryPointsClassVisitor visitor(GetInstrumentation());
     GetClassLinker()->VisitClasses(&visitor);
+    jit::Jit* jit = GetJit();
+    if (jit != nullptr) {
+      // Code JITted by the zygote is not compiled debuggable.
+      jit->GetCodeCache()->ClearEntryPointsInZygoteExecSpace();
+    }
   }
 }
 
diff --git a/runtime/runtime.h b/runtime/runtime.h
index a249c9f..1acb75b 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -634,7 +634,7 @@
   void SetJavaDebuggable(bool value);
 
   // Deoptimize the boot image, called for Java debuggable apps.
-  void DeoptimizeBootImage();
+  void DeoptimizeBootImage() REQUIRES(Locks::mutator_lock_);
 
   bool IsNativeDebuggable() const {
     return is_native_debuggable_;
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 25939d2..9a677de 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -568,7 +568,9 @@
     cur_shadow_frame_->SetMethod(method);
   } else {
     DCHECK(cur_quick_frame_ != nullptr);
-    CHECK(!IsInInlinedFrame()) << "We do not support setting inlined method's ArtMethod!";
+    CHECK(!IsInInlinedFrame()) << "We do not support setting inlined method's ArtMethod: "
+                               << GetMethod()->PrettyMethod() << " is inlined into "
+                               << GetOuterMethod()->PrettyMethod();
     *cur_quick_frame_ = method;
   }
 }
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 7c050a4..309c04e 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -48,6 +48,7 @@
 #include "base/atomic.h"
 #include "base/bit_utils.h"
 #include "base/casts.h"
+#include "arch/context.h"
 #include "base/file_utils.h"
 #include "base/memory_tool.h"
 #include "base/mutex.h"
@@ -4232,4 +4233,17 @@
   Runtime::Current()->GetThreadList()->RunCheckpoint(&closure);
 }
 
+void Thread::ReleaseLongJumpContextInternal() {
+  // Each QuickExceptionHandler gets a long jump context and uses
+  // it for doing the long jump, after finding catch blocks/doing deoptimization.
+  // Both finding catch blocks and deoptimization can trigger another
+  // exception such as a result of class loading. So there can be nested
+  // cases of exception handling and multiple contexts being used.
+  // ReleaseLongJumpContext tries to save the context in tlsPtr_.long_jump_context
+  // for reuse so there is no need to always allocate a new one each time when
+  // getting a context. Since we only keep one context for reuse, delete the
+  // existing one since the passed in context is yet to be used for longjump.
+  delete tlsPtr_.long_jump_context;
+}
+
 }  // namespace art
diff --git a/runtime/thread.h b/runtime/thread.h
index 592013b..7a14fd7 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -25,7 +25,6 @@
 #include <memory>
 #include <string>
 
-#include "arch/context.h"
 #include "base/atomic.h"
 #include "base/enums.h"
 #include "base/locks.h"
@@ -471,16 +470,7 @@
   Context* GetLongJumpContext();
   void ReleaseLongJumpContext(Context* context) {
     if (tlsPtr_.long_jump_context != nullptr) {
-      // Each QuickExceptionHandler gets a long jump context and uses
-      // it for doing the long jump, after finding catch blocks/doing deoptimization.
-      // Both finding catch blocks and deoptimization can trigger another
-      // exception such as a result of class loading. So there can be nested
-      // cases of exception handling and multiple contexts being used.
-      // ReleaseLongJumpContext tries to save the context in tlsPtr_.long_jump_context
-      // for reuse so there is no need to always allocate a new one each time when
-      // getting a context. Since we only keep one context for reuse, delete the
-      // existing one since the passed in context is yet to be used for longjump.
-      delete tlsPtr_.long_jump_context;
+      ReleaseLongJumpContextInternal();
     }
     tlsPtr_.long_jump_context = context;
   }
@@ -1409,6 +1399,8 @@
 
   static bool IsAotCompiler();
 
+  void ReleaseLongJumpContextInternal();
+
   // 32 bits of atomically changed state and flags. Keeping as 32 bits allows and atomic CAS to
   // change from being Suspended to Runnable without a suspend request occurring.
   union PACKED(4) StateAndFlags {
diff --git a/runtime/trace.cc b/runtime/trace.cc
index ce955d8..074e846 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -38,6 +38,8 @@
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "gc/scoped_gc_critical_section.h"
 #include "instrumentation.h"
+#include "jit/jit.h"
+#include "jit/jit_code_cache.h"
 #include "mirror/class-inl.h"
 #include "mirror/dex_cache-inl.h"
 #include "mirror/object-inl.h"
@@ -390,6 +392,15 @@
   // Enable count of allocs if specified in the flags.
   bool enable_stats = false;
 
+  if (runtime->GetJit() != nullptr) {
+    // TODO b/110263880 It would be better if we didn't need to do this.
+    // Since we need to hold the method entrypoint across a suspend to ensure instrumentation
+    // hooks are called correctly we have to disable jit-gc to ensure that the entrypoint doesn't
+    // go away. Furthermore we need to leave this off permanently since one could get the same
+    // effect by causing this to be toggled on and off.
+    runtime->GetJit()->GetCodeCache()->SetGarbageCollectCode(false);
+  }
+
   // Create Trace object.
   {
     // Required since EnableMethodTracing calls ConfigureStubs which visits class linker classes.
diff --git a/test/1001-app-image-regions/app_image_regions.cc b/test/1001-app-image-regions/app_image_regions.cc
new file mode 100644
index 0000000..dc16a84
--- /dev/null
+++ b/test/1001-app-image-regions/app_image_regions.cc
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019 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 "gc/heap.h"
+#include "gc/space/image_space.h"
+#include "gc/space/space-inl.h"
+#include "gc/space/region_space.h"
+#include "image.h"
+#include "mirror/class.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+
+namespace art {
+
+namespace {
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_getRegionSize(JNIEnv*, jclass) {
+  return gc::space::RegionSpace::kRegionSize;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_checkAppImageSectionSize(JNIEnv*, jclass, jclass c) {
+  ScopedObjectAccess soa(Thread::Current());
+  ObjPtr<mirror::Class> klass_ptr = soa.Decode<mirror::Class>(c);
+  for (auto* space : Runtime::Current()->GetHeap()->GetContinuousSpaces()) {
+    if (space->IsImageSpace()) {
+      auto* image_space = space->AsImageSpace();
+      const auto& image_header = image_space->GetImageHeader();
+      if (image_header.IsAppImage() && image_space->HasAddress(klass_ptr.Ptr())) {
+        return image_header.GetObjectsSection().Size();
+      }
+    }
+  }
+  return 0;
+}
+
+}  // namespace
+
+}  // namespace art
diff --git a/test/1001-app-image-regions/build b/test/1001-app-image-regions/build
new file mode 100755
index 0000000..16c3a5b
--- /dev/null
+++ b/test/1001-app-image-regions/build
@@ -0,0 +1,34 @@
+#!/bin/bash
+#
+# Copyright 2019 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.
+
+# make us exit on a failure
+set -e
+
+count=10000
+echo "LMain;" >> profile
+for i in $(seq 1 "$count"); do
+  echo "LOther\$Inner${i};" >> "profile"
+done
+
+# Generate the other class.
+other_file="src/Other.java"
+echo "class Other {" >> "${other_file}"
+for i in $(seq 1 "$count"); do
+  echo "  static class Inner${i} { void test(){} }" >> "${other_file}"
+done
+echo "}" >> "${other_file}"
+
+./default-build "$@"
diff --git a/test/1001-app-image-regions/expected.txt b/test/1001-app-image-regions/expected.txt
new file mode 100644
index 0000000..9fd87ca
--- /dev/null
+++ b/test/1001-app-image-regions/expected.txt
@@ -0,0 +1,4 @@
+JNI_OnLoad called
+App image loaded true
+Region size 262144
+App image section size large enough true
diff --git a/test/1001-app-image-regions/info.txt b/test/1001-app-image-regions/info.txt
new file mode 100644
index 0000000..b13f276
--- /dev/null
+++ b/test/1001-app-image-regions/info.txt
@@ -0,0 +1 @@
+Tests that an app image with many classes is generated and loaded correctly.
diff --git a/test/1001-app-image-regions/run b/test/1001-app-image-regions/run
new file mode 100644
index 0000000..128aa2e
--- /dev/null
+++ b/test/1001-app-image-regions/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 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.
+
+exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile
diff --git a/test/1001-app-image-regions/src/Main.java b/test/1001-app-image-regions/src/Main.java
new file mode 100644
index 0000000..c41a606
--- /dev/null
+++ b/test/1001-app-image-regions/src/Main.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 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.
+ */
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+class Main {
+  public static void main(String[] args) {
+    System.loadLibrary(args[0]);
+    System.out.println("App image loaded " + checkAppImageLoaded());
+    int regionSize = getRegionSize();
+    int objectsSectionSize = checkAppImageSectionSize(Main.class);
+    System.out.println("Region size " + regionSize);
+    System.out.println("App image section size large enough " + (objectsSectionSize > regionSize));
+    if (objectsSectionSize <= regionSize) {
+      System.out.println("Section size " + objectsSectionSize);
+    }
+  }
+
+  public static native boolean checkAppImageLoaded();
+  public static native int getRegionSize();
+  public static native int checkAppImageSectionSize(Class c);
+}
diff --git a/test/689-zygote-jit-deopt/expected.txt b/test/689-zygote-jit-deopt/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/689-zygote-jit-deopt/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/689-zygote-jit-deopt/info.txt b/test/689-zygote-jit-deopt/info.txt
new file mode 100644
index 0000000..100032e
--- /dev/null
+++ b/test/689-zygote-jit-deopt/info.txt
@@ -0,0 +1,2 @@
+Regression test for debuggable apps that need to deoptimize
+methods JIT-compiled by the zygote.
diff --git a/test/689-zygote-jit-deopt/src/Main.java b/test/689-zygote-jit-deopt/src/Main.java
new file mode 100644
index 0000000..330663e
--- /dev/null
+++ b/test/689-zygote-jit-deopt/src/Main.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 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) {
+    System.loadLibrary(args[0]);
+    if (!hasJit()) {
+      return;
+    }
+    ensureJitCompiled(Object.class, "toString");
+    transitionJitFromZygote();
+    deoptimizeBootImage();
+    if (hasJitCompiledEntrypoint(Object.class, "toString")) {
+      throw new Error("Expected Object.toString to be deoptimized");
+    }
+  }
+
+  private static native boolean hasJit();
+  private static native void ensureJitCompiled(Class<?> cls, String name);
+  private static native boolean hasJitCompiledEntrypoint(Class<?> cls, String name);
+  private static native void deoptimizeBootImage();
+  private static native void transitionJitFromZygote();
+}
diff --git a/test/Android.bp b/test/Android.bp
index 57fcd86..467a717 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -495,6 +495,7 @@
         "708-jit-cache-churn/jit.cc",
         "800-smali/jni.cc",
         "909-attach-agent/disallow_debugging.cc",
+        "1001-app-image-regions/app_image_regions.cc",
         "1947-breakpoint-redefine-deopt/check_deopt.cc",
         "common/runtime_state.cc",
         "common/stack_inspect.cc",
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index 55631a9..bbc3039 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -161,6 +161,19 @@
   return !interpreter;
 }
 
+static ArtMethod* GetMethod(ScopedObjectAccess& soa, jclass cls, const ScopedUtfChars& chars)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  CHECK(chars.c_str() != nullptr);
+  ArtMethod* method = soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName(
+        chars.c_str(), kRuntimePointerSize);
+  if (method == nullptr) {
+    method = soa.Decode<mirror::Class>(cls)->FindDeclaredVirtualMethodByName(
+        chars.c_str(), kRuntimePointerSize);
+  }
+  DCHECK(method != nullptr) << "Unable to find method called " << chars.c_str();
+  return method;
+}
+
 extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJitCompiledEntrypoint(JNIEnv* env,
                                                                          jclass,
                                                                          jclass cls,
@@ -172,9 +185,7 @@
   Thread* self = Thread::Current();
   ScopedObjectAccess soa(self);
   ScopedUtfChars chars(env, method_name);
-  CHECK(chars.c_str() != nullptr);
-  ArtMethod* method = soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName(
-        chars.c_str(), kRuntimePointerSize);
+  ArtMethod* method = GetMethod(soa, cls, chars);
   ScopedAssertNoThreadSuspension sants(__FUNCTION__);
   return jit->GetCodeCache()->ContainsPc(
       Runtime::Current()->GetInstrumentation()->GetCodeForInvoke(method));
@@ -191,9 +202,7 @@
   Thread* self = Thread::Current();
   ScopedObjectAccess soa(self);
   ScopedUtfChars chars(env, method_name);
-  CHECK(chars.c_str() != nullptr);
-  ArtMethod* method = soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName(
-        chars.c_str(), kRuntimePointerSize);
+  ArtMethod* method = GetMethod(soa, cls, chars);
   return jit->GetCodeCache()->ContainsMethod(method);
 }
 
@@ -262,14 +271,7 @@
     ScopedObjectAccess soa(self);
 
     ScopedUtfChars chars(env, method_name);
-    CHECK(chars.c_str() != nullptr);
-    method = soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName(
-        chars.c_str(), kRuntimePointerSize);
-    if (method == nullptr) {
-      method = soa.Decode<mirror::Class>(cls)->FindDeclaredVirtualMethodByName(
-          chars.c_str(), kRuntimePointerSize);
-    }
-    DCHECK(method != nullptr) << "Unable to find method called " << chars.c_str();
+    method = GetMethod(soa, cls, chars);
   }
   ForceJitCompiled(self, method);
 }
@@ -353,4 +355,20 @@
   return (jit != nullptr) ? jit->HotMethodThreshold() : 0;
 }
 
+extern "C" JNIEXPORT void JNICALL Java_Main_transitionJitFromZygote(JNIEnv*, jclass) {
+  jit::Jit* jit = Runtime::Current()->GetJit();
+  if (jit == nullptr) {
+    return;
+  }
+  // Mimic the transition behavior a zygote fork would have.
+  jit->PreZygoteFork();
+  jit->GetCodeCache()->PostForkChildAction(/*is_system_server=*/ false, /*is_zygote=*/ false);
+  jit->PostForkChildAction(/*is_zygote=*/ false);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_deoptimizeBootImage(JNIEnv*, jclass) {
+  ScopedSuspendAll ssa(__FUNCTION__);
+  Runtime::Current()->DeoptimizeBootImage();
+}
+
 }  // namespace art
diff --git a/test/common/stack_inspect.cc b/test/common/stack_inspect.cc
index 393e773..cb011a8 100644
--- a/test/common/stack_inspect.cc
+++ b/test/common/stack_inspect.cc
@@ -18,6 +18,7 @@
 
 #include <android-base/logging.h>
 
+#include "arch/context.h"
 #include "base/mutex.h"
 #include "dex/dex_file-inl.h"
 #include "jni/jni_internal.h"
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 983c16a..9c01ba9 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -81,11 +81,13 @@
         "variant": "no-prebuild"
     },
     {
-        "tests": ["118-noimage-dex2oat"],
+        "tests": ["118-noimage-dex2oat",
+                  "1001-app-image-regions"],
         "variant": "no-relocate",
         "description": ["118-noimage-dex2oat is not broken per-se it just ",
                         "doesn't work (and isn't meant to) without --prebuild ",
-                        "--relocate"]
+                        "--relocate. 1001-app-image-regions is disabled since it",
+                        "doesn't have the app image loaded for no-relocate"]
     },
     {
         "tests" : "629-vdex-speed",
@@ -1085,6 +1087,7 @@
                   "688-shared-library",
                   "999-redefine-hiddenapi",
                   "1000-non-moving-space-stress",
+                  "1001-app-image-regions",
                   "1951-monitor-enter-no-suspend",
                   "1957-error-ext"],
         "variant": "jvm",
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index da463d5..ce1a246 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -73,7 +73,7 @@
     exit 1
   fi
   make_command="make $j_arg $extra_args $showcommands build-art-target-tests $common_targets"
-  make_command+=" libjavacrypto-target libnetd_client-target linker toybox toolbox sh"
+  make_command+=" libjavacrypto-target libnetd_client-target linker toybox toolbox sh unzip"
   make_command+=" debuggerd su"
   make_command+=" libstdc++ "
   make_command+=" ${ANDROID_PRODUCT_OUT#"${ANDROID_BUILD_TOP}/"}/system/etc/public.libraries.txt"
@@ -81,8 +81,8 @@
     # These targets are needed for the chroot environment.
     make_command+=" crash_dump event-log-tags"
   fi
-  # Build the Runtime APEX.
-  make_command+=" com.android.runtime"
+  # Build the Debug Runtime APEX (which is a superset of the Release Runtime APEX).
+  make_command+=" com.android.runtime.debug"
   mode_suffix="-target"
 fi
 
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index 3c65b01..3f9ceea 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -223,5 +223,17 @@
     "libcore.libcore.net.InetAddressUtilsTest#test_parseNonNumericAddress[8]",
     "libcore.libcore.net.InetAddressUtilsTest#test_parseNonNumericAddress[9]"
   ]
+},
+{
+  description: "Apex related",
+  result: EXEC_FAILED,
+  modes: [device],
+  bug: 122642227,
+  names: [
+    "libcore.java.lang.SystemTest#testSystemProperties_mutable",
+    "libcore.libcore.icu.TimeZoneIntegrationTest#testTimeZoneDataVersion",
+    "libcore.libcore.icu.TimeZoneIntegrationTest#testTimeZoneDebugInfo",
+    "libcore.libcore.icu.TimeZoneIntegrationTest#testTzDataSetVersions"
+  ]
 }
 ]
diff --git a/tools/mount-buildbot-apexes.sh b/tools/mount-buildbot-apexes.sh
new file mode 100755
index 0000000..778d634
--- /dev/null
+++ b/tools/mount-buildbot-apexes.sh
@@ -0,0 +1,79 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 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.
+
+# Mount Android Runtime and Core Libraries APEX packages required in the chroot directory.
+# This script emulates some the actions performed by `apexd`.
+
+green='\033[0;32m'
+nc='\033[0m'
+
+# Setup as root, as some actions performed here require it.
+adb root
+adb wait-for-device
+
+# Exit early if there is no chroot.
+[[ -n "$ART_TEST_CHROOT" ]] || exit
+
+# Check that ART_TEST_CHROOT is correctly defined.
+[[ "$ART_TEST_CHROOT" = /* ]] || { echo "$ART_TEST_CHROOT is not an absolute path"; exit 1; }
+
+# Check that the "$ART_TEST_CHROOT/apex" directory exists.
+adb shell test -d "$ART_TEST_CHROOT/apex" \
+  || { echo "$ART_TEST_CHROOT/apex does not exist or is not a directory"; exit 1; }
+
+# Create a directory where we extract APEX packages' payloads (ext4 images)
+# under the chroot directory.
+apex_image_dir="/tmp/apex"
+adb shell mkdir -p "$ART_TEST_CHROOT$apex_image_dir"
+
+# activate_system_package APEX_PACKAGE APEX_NAME
+# ----------------------------------------------
+# Extract payload (ext4 image) from system APEX_PACKAGE and mount it as
+# APEX_NAME in `/apex` under the chroot directory.
+activate_system_package() {
+  local apex_package=$1
+  local apex_name=$2
+  local apex_package_path="/system/apex/$apex_package"
+  local abs_mount_point="$ART_TEST_CHROOT/apex/$apex_name"
+  local abs_image_filename="$ART_TEST_CHROOT$apex_image_dir/$apex_name.img"
+
+  # Make sure that the (absolute) path to the mounted ext4 image is less than
+  # 64 characters, which is a hard limit set in the kernel for loop device
+  # filenames (otherwise, we would get an error message from `losetup`, used
+  # by `mount` to manage the loop device).
+  [[ "${#abs_image_filename}" -ge 64 ]] \
+    && { echo "Filename $abs_image_filename is too long to be used with a loop device"; exit 1; }
+
+  echo -e "${green}Activating package $apex_package as $apex_name${nc}"
+
+  # Extract payload (ext4 image). As standard Android builds do not contain
+  # `unzip`, we use the one we built and sync'd to the chroot directory instead.
+  local payload_filename="apex_payload.img"
+  adb shell chroot "$ART_TEST_CHROOT" \
+    /system/bin/unzip -q "$apex_package_path" "$payload_filename" -d "$apex_image_dir"
+  # Rename the extracted payload to have its name match the APEX's name.
+  adb shell mv "$ART_TEST_CHROOT$apex_image_dir/$payload_filename" "$abs_image_filename"
+  # Check that the mount point is available.
+  adb shell mount | grep -q " on $abs_mount_point" && \
+    { echo "$abs_mount_point is already used as mount point"; exit 1; }
+  # Mount the ext4 image.
+  adb shell mkdir -p "$abs_mount_point"
+  adb shell mount -o loop,ro "$abs_image_filename" "$abs_mount_point"
+}
+
+# Activate the Android Runtime APEX.
+# Note: We use the Debug Runtime APEX (which is a superset of the Release Runtime APEX).
+activate_system_package com.android.runtime.debug.apex com.android.runtime
diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh
index ef958d6..92b3672 100755
--- a/tools/setup-buildbot-device.sh
+++ b/tools/setup-buildbot-device.sh
@@ -165,4 +165,9 @@
   adb shell mkdir -p "$ART_TEST_CHROOT/dev"
   adb shell mount | grep -q "^tmpfs on $ART_TEST_CHROOT/dev type tmpfs " \
     || adb shell mount -o bind /dev "$ART_TEST_CHROOT/dev"
+
+  # Create /apex tmpfs in chroot.
+  adb shell mkdir -p "$ART_TEST_CHROOT/apex"
+  adb shell mount | grep -q "^tmpfs on $ART_TEST_CHROOT/apex type tmpfs " \
+    || adb shell mount -t tmpfs -o nodev,noexec,nosuid tmpfs "$ART_TEST_CHROOT/apex"
 fi
diff --git a/tools/teardown-buildbot-device.sh b/tools/teardown-buildbot-device.sh
index 6634fb4..7eb5cc3 100755
--- a/tools/teardown-buildbot-device.sh
+++ b/tools/teardown-buildbot-device.sh
@@ -89,6 +89,9 @@
            fi
     }
 
+    # Remove /apex from chroot.
+    remove_filesystem_from_chroot apex tmpfs true
+
     # Remove /dev from chroot.
     remove_filesystem_from_chroot dev tmpfs true
 
diff --git a/tools/unmount-buildbot-apexes.sh b/tools/unmount-buildbot-apexes.sh
new file mode 100755
index 0000000..8f0ad5f
--- /dev/null
+++ b/tools/unmount-buildbot-apexes.sh
@@ -0,0 +1,64 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 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.
+
+# Unmount Android Runtime and Core Libraries APEX packages required in the chroot directory.
+# This script emulates some the actions performed by `apexd`.
+
+# This script undoes the work done by tools/mount-buildbot-apexes.sh.
+# Make sure to keep these files in sync.
+
+green='\033[0;32m'
+nc='\033[0m'
+
+# Setup as root, as some actions performed here require it.
+adb root
+adb wait-for-device
+
+# Exit early if there is no chroot.
+[[ -n "$ART_TEST_CHROOT" ]] || exit
+
+# Check that ART_TEST_CHROOT is correctly defined.
+[[ "$ART_TEST_CHROOT" = /* ]] || { echo "$ART_TEST_CHROOT is not an absolute path"; exit 1; }
+
+# Directory containing extracted APEX packages' payloads (ext4 images) under
+# the chroot directory.
+apex_image_dir="/tmp/apex"
+
+# deactivate_system_package APEX_NAME
+# -----------------------------------
+# Unmount APEX_NAME in `/apex` under the chroot directory and delete the
+# corresponding APEX package payload (ext4 image).
+deactivate_system_package() {
+  local apex_name=$1
+  local abs_image_filename="$ART_TEST_CHROOT$apex_image_dir/$apex_name.img"
+  local abs_mount_point="$ART_TEST_CHROOT/apex/$apex_name"
+
+  echo -e "${green}Deactivating package $apex_name${nc}"
+
+  # Unmount the package's payload (ext4 image).
+  if adb shell mount | grep -q "^/dev/block/loop[0-9]\+ on $abs_mount_point type ext4"; then
+    adb shell umount "$abs_mount_point"
+    adb shell rmdir "$abs_mount_point"
+    # Delete the ext4 image.
+    adb shell rm "$abs_image_filename"
+  fi
+}
+
+# Deactivate the Android Runtime APEX.
+deactivate_system_package com.android.runtime
+
+# Delete the image's directory.
+adb shell rmdir "$ART_TEST_CHROOT$apex_image_dir"