Proactively dequicken on debuggable switch.

Previously we would generally not really consider dex2dex quickening
with debuggable processes. This could cause problems for structural
redefinition since the -quick opcodes are incompatible with the types
of changes structural redefinition allows. Furthermore this can cause
some unexpected behavior where (for example) check-casts might appear
to pass even if debugger activity should cause it to fail.

In order to fix these issues we make the runtime more proactively
dequicken dex-files when we start or switch to JAVA_DEBUGGABLE mode.

Test: ./test.py --target --host
Test: adb install -t ~/misc/Bandhook-Kotlin/app/build/outputs/apk/debug/app-debug.apk && adb shell monkey -p com.antonioleiva.bandhookkotlin -c android.intent.category.LAUNCHER 1
Bug: 134162467
Bug: 144168550
Change-Id: I2673c91b72ae7048d2ff71a1cf68cf552d4e8004
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 978b1ab..2732de5 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -19,6 +19,7 @@
 
 #include <atomic>
 
+#include "android-base/thread_annotations.h"
 #include "art_field-inl.h"
 #include "art_method-inl.h"
 #include "base/mutex.h"
@@ -27,12 +28,14 @@
 #include "dex/dex_file_structs.h"
 #include "gc_root-inl.h"
 #include "handle_scope-inl.h"
+#include "jni/jni_internal.h"
 #include "mirror/class_loader.h"
 #include "mirror/dex_cache-inl.h"
 #include "mirror/iftable.h"
 #include "mirror/object_array-inl.h"
 #include "obj_ptr-inl.h"
 #include "scoped_thread_state_change-inl.h"
+#include "well_known_classes.h"
 
 namespace art {
 
@@ -449,6 +452,18 @@
   return class_roots;
 }
 
+template <typename Visitor>
+void ClassLinker::VisitKnownDexFiles(Thread* self, Visitor visitor) {
+  ReaderMutexLock rmu(self, *Locks::dex_lock_);
+  std::for_each(dex_caches_.begin(),
+                dex_caches_.end(),
+                [&](DexCacheData& dcd) REQUIRES(Locks::mutator_lock_) {
+                  if (dcd.IsValid()) {
+                    visitor(dcd.dex_file);
+                  }
+                });
+}
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_CLASS_LINKER_INL_H_
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 49e1430..4e38e6b 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -111,6 +111,18 @@
       REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) = 0;
 };
 
+template <typename Func>
+class ClassLoaderFuncVisitor final : public ClassLoaderVisitor {
+ public:
+  explicit ClassLoaderFuncVisitor(Func func) : func_(func) {}
+  void Visit(ObjPtr<mirror::ClassLoader> cl) override REQUIRES_SHARED(Locks::mutator_lock_) {
+    func_(cl);
+  }
+
+ private:
+  Func func_;
+};
+
 class AllocatorVisitor {
  public:
   virtual ~AllocatorVisitor() {}
@@ -461,6 +473,9 @@
   void VisitRoots(RootVisitor* visitor, VisitRootFlags flags)
       REQUIRES(!Locks::dex_lock_, !Locks::classlinker_classes_lock_, !Locks::trace_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
+  // Visits all dex-files accessible by any class-loader or the BCP.
+  template<typename Visitor>
+  void VisitKnownDexFiles(Thread* self, Visitor visitor) REQUIRES(Locks::mutator_lock_);
 
   bool IsDexFileRegistered(Thread* self, const DexFile& dex_file)
       REQUIRES(!Locks::dex_lock_)
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 30ba6dd..81356c2 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -183,6 +183,10 @@
   }
 
  private:
+  // Returns true if we want to remove quickened opcodes before loading the VDEX file, false
+  // otherwise.
+  bool ShouldUnquickenVDex() const;
+
   DISALLOW_COPY_AND_ASSIGN(OatFileBase);
 };
 
@@ -267,6 +271,13 @@
   return ret.release();
 }
 
+bool OatFileBase::ShouldUnquickenVDex() const {
+  // We sometimes load oat files without a runtime (eg oatdump) and don't want to do anything in
+  // that case. If we are debuggable there are no -quick opcodes to unquicken. If the runtime is not
+  // debuggable we don't care whether there are -quick opcodes or not so no need to do anything.
+  return Runtime::Current() != nullptr && !IsDebuggable() && Runtime::Current()->IsJavaDebuggable();
+}
+
 bool OatFileBase::LoadVdex(const std::string& vdex_filename,
                            bool writable,
                            bool low_4gb,
@@ -277,7 +288,7 @@
                                   vdex_filename,
                                   writable,
                                   low_4gb,
-                                  /* unquicken=*/ false,
+                                  ShouldUnquickenVDex(),
                                   error_msg);
   if (vdex_.get() == nullptr) {
     *error_msg = StringPrintf("Failed to load vdex file '%s' %s",
@@ -299,16 +310,17 @@
     if (rc == -1) {
       PLOG(WARNING) << "Failed getting length of vdex file";
     } else {
-      vdex_ = VdexFile::OpenAtAddress(vdex_begin_,
-                                      vdex_end_ - vdex_begin_,
-                                      /*mmap_reuse=*/ vdex_begin_ != nullptr,
-                                      vdex_fd,
-                                      s.st_size,
-                                      vdex_filename,
-                                      writable,
-                                      low_4gb,
-                                      /*unquicken=*/ false,
-                                      error_msg);
+      vdex_ = VdexFile::OpenAtAddress(
+          vdex_begin_,
+          vdex_end_ - vdex_begin_,
+          /*mmap_reuse=*/ vdex_begin_ != nullptr,
+          vdex_fd,
+          s.st_size,
+          vdex_filename,
+          writable,
+          low_4gb,
+          ShouldUnquickenVDex(),
+          error_msg);
       if (vdex_.get() == nullptr) {
         *error_msg = "Failed opening vdex file.";
         return false;
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 92222a2..8861a09 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -35,6 +35,7 @@
 #include <cstdlib>
 #include <limits>
 #include <thread>
+#include <unordered_set>
 #include <vector>
 
 #include "android-base/strings.h"
@@ -104,7 +105,7 @@
 #include "mirror/class-alloc-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_ext.h"
-#include "mirror/class_loader.h"
+#include "mirror/class_loader-inl.h"
 #include "mirror/emulated_stack_frame.h"
 #include "mirror/field.h"
 #include "mirror/method.h"
@@ -2851,6 +2852,27 @@
       jit->GetCodeCache()->ClearEntryPointsInZygoteExecSpace();
     }
   }
+  // Also de-quicken all -quick opcodes. We do this for both BCP and non-bcp so if we are swapping
+  // debuggable during startup by a plugin (eg JVMTI) even non-BCP code has its vdex files deopted.
+  std::unordered_set<const VdexFile*> vdexs;
+  GetClassLinker()->VisitKnownDexFiles(Thread::Current(), [&](const art::DexFile* df) {
+    const OatDexFile* odf = df->GetOatDexFile();
+    if (odf == nullptr) {
+      return;
+    }
+    const OatFile* of = odf->GetOatFile();
+    if (of == nullptr || of->IsDebuggable()) {
+      // no Oat or already debuggable so no -quick.
+      return;
+    }
+    vdexs.insert(of->GetVdexFile());
+  });
+  LOG(INFO) << "Unquickening " << vdexs.size() << " vdex files!";
+  for (const VdexFile* vf : vdexs) {
+    vf->AllowWriting(true);
+    vf->UnquickenInPlace(/*decompile_return_instruction=*/true);
+    vf->AllowWriting(false);
+  }
 }
 
 Runtime::ScopedThreadPoolUsage::ScopedThreadPoolUsage()
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index e01d21e..d67a968 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -155,11 +155,13 @@
     mmap_reuse = false;
   }
   CHECK(!mmap_reuse || mmap_addr != nullptr);
+  CHECK(!(writable && unquicken)) << "We don't want to be writing unquickened files out to disk!";
+  // Start as PROT_WRITE so we can mprotect back to it if we want to.
   MemMap mmap = MemMap::MapFileAtAddress(
       mmap_addr,
       vdex_length,
-      (writable || unquicken) ? PROT_READ | PROT_WRITE : PROT_READ,
-      unquicken ? MAP_PRIVATE : MAP_SHARED,
+      PROT_READ | PROT_WRITE,
+      writable ? MAP_SHARED : MAP_PRIVATE,
       file_fd,
       /* start= */ 0u,
       low_4gb,
@@ -183,13 +185,19 @@
     if (!vdex->OpenAllDexFiles(&unique_ptr_dex_files, error_msg)) {
       return nullptr;
     }
+    // TODO: It would be nice to avoid doing the return-instruction stuff but then we end up not
+    // being able to tell if we need dequickening later. Instead just get rid of that too.
     vdex->Unquicken(MakeNonOwningPointerVector(unique_ptr_dex_files),
-                    /* decompile_return_instruction= */ false);
+                    /* decompile_return_instruction= */ true);
     // Update the quickening info size to pretend there isn't any.
     size_t offset = vdex->GetDexSectionHeaderOffset();
     reinterpret_cast<DexSectionHeader*>(vdex->mmap_.Begin() + offset)->quickening_info_size_ = 0;
   }
 
+  if (!writable) {
+    vdex->AllowWriting(false);
+  }
+
   return vdex;
 }
 
@@ -209,8 +217,12 @@
   }
 }
 
+void VdexFile::AllowWriting(bool val) const {
+  CHECK(mmap_.Protect(val ? (PROT_READ | PROT_WRITE) : PROT_READ));
+}
+
 bool VdexFile::OpenAllDexFiles(std::vector<std::unique_ptr<const DexFile>>* dex_files,
-                               std::string* error_msg) {
+                               std::string* error_msg) const {
   const ArtDexFileLoader dex_file_loader;
   size_t i = 0;
   for (const uint8_t* dex_file_start = GetNextDexFileData(nullptr);
@@ -239,6 +251,23 @@
   return true;
 }
 
+void VdexFile::UnquickenInPlace(bool decompile_return_instruction) const {
+  CHECK_NE(mmap_.GetProtect() & PROT_WRITE, 0)
+      << "File not mapped writable. Cannot unquicken! " << mmap_;
+  if (HasDexSection()) {
+    std::vector<std::unique_ptr<const DexFile>> unique_ptr_dex_files;
+    std::string error_msg;
+    if (!OpenAllDexFiles(&unique_ptr_dex_files, &error_msg)) {
+      return;
+    }
+    Unquicken(MakeNonOwningPointerVector(unique_ptr_dex_files),
+              decompile_return_instruction);
+    // Update the quickening info size to pretend there isn't any.
+    size_t offset = GetDexSectionHeaderOffset();
+    reinterpret_cast<DexSectionHeader*>(mmap_.Begin() + offset)->quickening_info_size_ = 0;
+  }
+}
+
 void VdexFile::Unquicken(const std::vector<const DexFile*>& target_dex_files,
                          bool decompile_return_instruction) const {
   const uint8_t* source_dex = GetNextDexFileData(nullptr);
@@ -279,7 +308,8 @@
 void VdexFile::UnquickenDexFile(const DexFile& target_dex_file,
                                 const DexFile& source_dex_file,
                                 bool decompile_return_instruction) const {
-  UnquickenDexFile(target_dex_file, source_dex_file.Begin(), decompile_return_instruction);
+  UnquickenDexFile(
+      target_dex_file, source_dex_file.Begin(), decompile_return_instruction);
 }
 
 void VdexFile::UnquickenDexFile(const DexFile& target_dex_file,
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 102b73f..d205904 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -309,7 +309,7 @@
 
   // Open all the dex files contained in this vdex file.
   bool OpenAllDexFiles(std::vector<std::unique_ptr<const DexFile>>* dex_files,
-                       std::string* error_msg);
+                       std::string* error_msg) const;
 
   // In-place unquicken the given `dex_files` based on `quickening_info`.
   // `decompile_return_instruction` controls if RETURN_VOID_BARRIER instructions are
@@ -319,6 +319,8 @@
   void Unquicken(const std::vector<const DexFile*>& target_dex_files,
                  bool decompile_return_instruction) const;
 
+  void UnquickenInPlace(bool decompile_return_instruction) const;
+
   // Fully unquicken `target_dex_file` based on `quickening_info`.
   void UnquickenDexFile(const DexFile& target_dex_file,
                         const DexFile& source_dex_file,
@@ -354,6 +356,10 @@
   // Returns true if the class loader context stored in the vdex matches `context`.
   bool MatchesClassLoaderContext(const ClassLoaderContext& context) const;
 
+  // Make the Vdex file & underlying dex-files RW or RO. Should only be used for in-place
+  // dequickening.
+  void AllowWriting(bool value) const;
+
  private:
   uint32_t GetQuickeningInfoTableOffset(const uint8_t* source_dex_begin) const;
 
@@ -382,7 +388,8 @@
     return DexBegin() + GetDexSectionHeader().GetDexSize();
   }
 
-  MemMap mmap_;
+  // mutable for AllowWriting()
+  mutable MemMap mmap_;
 
   DISALLOW_COPY_AND_ASSIGN(VdexFile);
 };
diff --git a/test/1995-final-virtual-structural-multithread/run b/test/1995-final-virtual-structural-multithread/run
index 421f7b0..e912529 100755
--- a/test/1995-final-virtual-structural-multithread/run
+++ b/test/1995-final-virtual-structural-multithread/run
@@ -18,4 +18,4 @@
 # iget-object-quick during dex2dex compilation. This breaks the test since the
 # -quick opcode encodes the exact byte offset of fields. Since this test changes
 # the offset this causes problems.
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true -Xcompiler-option --debuggable
+./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true