ART: Allow to change boot image pickup order

Allow to change the pickup order of boot image files between
system-first and data-first.

Bug: 126307038
Test: m test-art-host
Test: Boot device with image in /data/dalvik-cache
Change-Id: Id80cfc06aeb023559e1a3706833e57ba4880f43b
diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc
index 9c0ac8f..0624525 100644
--- a/runtime/dexopt_test.cc
+++ b/runtime/dexopt_test.cc
@@ -120,7 +120,11 @@
   EXPECT_EQ(filter, odex_file->GetCompilerFilter());
 
   std::string boot_image_checksums = gc::space::ImageSpace::GetBootClassPathChecksums(
-      Runtime::Current()->GetBootClassPath(), image_location, kRuntimeISA, &error_msg);
+      Runtime::Current()->GetBootClassPath(),
+      image_location,
+      kRuntimeISA,
+      gc::space::ImageSpaceLoadingOrder::kSystemFirst,
+      &error_msg);
   ASSERT_FALSE(boot_image_checksums.empty()) << error_msg;
 
   const OatHeader& oat_header = odex_file->GetOatHeader();
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 5c171e6..6f72740 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -205,7 +205,8 @@
            bool use_generational_cc,
            uint64_t min_interval_homogeneous_space_compaction_by_oom,
            bool dump_region_info_before_gc,
-           bool dump_region_info_after_gc)
+           bool dump_region_info_after_gc,
+           space::ImageSpaceLoadingOrder image_space_loading_order)
     : non_moving_space_(nullptr),
       rosalloc_space_(nullptr),
       dlmalloc_space_(nullptr),
@@ -370,6 +371,7 @@
                                        boot_class_path_locations,
                                        image_file_name,
                                        image_instruction_set,
+                                       image_space_loading_order,
                                        heap_reservation_size,
                                        &boot_image_spaces,
                                        &heap_reservation)) {
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 18dfbf5..898a51c 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -35,6 +35,7 @@
 #include "gc/collector/iteration.h"
 #include "gc/collector_type.h"
 #include "gc/gc_cause.h"
+#include "gc/space/image_space_loading_order.h"
 #include "gc/space/large_object_space.h"
 #include "handle.h"
 #include "obj_ptr.h"
@@ -215,7 +216,8 @@
        bool use_generational_cc,
        uint64_t min_interval_homogeneous_space_compaction_by_oom,
        bool dump_region_info_before_gc,
-       bool dump_region_info_after_gc);
+       bool dump_region_info_after_gc,
+       space::ImageSpaceLoadingOrder image_space_loading_order);
 
   ~Heap();
 
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 4a2dbf5..173e879 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -259,6 +259,7 @@
 
 std::unique_ptr<ImageHeader> ImageSpace::ReadImageHeader(const char* image_location,
                                                          const InstructionSet image_isa,
+                                                         ImageSpaceLoadingOrder order,
                                                          std::string* error_msg) {
   std::string system_filename;
   bool has_system = false;
@@ -274,10 +275,20 @@
                         &dalvik_cache_exists,
                         &has_cache,
                         &is_global_cache)) {
-    if (has_system) {
-      return ReadSpecificImageHeader(system_filename.c_str(), error_msg);
-    } else if (has_cache) {
-      return ReadSpecificImageHeader(cache_filename.c_str(), error_msg);
+    if (order == ImageSpaceLoadingOrder::kSystemFirst) {
+      if (has_system) {
+        return ReadSpecificImageHeader(system_filename.c_str(), error_msg);
+      }
+      if (has_cache) {
+        return ReadSpecificImageHeader(cache_filename.c_str(), error_msg);
+      }
+    } else {
+      if (has_cache) {
+        return ReadSpecificImageHeader(cache_filename.c_str(), error_msg);
+      }
+      if (has_system) {
+        return ReadSpecificImageHeader(system_filename.c_str(), error_msg);
+      }
     }
   }
 
@@ -1447,7 +1458,8 @@
     return cache_filename_;
   }
 
-  bool LoadFromSystem(size_t extra_reservation_size,
+  bool LoadFromSystem(bool validate_oat_file,
+                      size_t extra_reservation_size,
                       /*out*/std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
                       /*out*/MemMap* extra_reservation,
                       /*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -1455,7 +1467,7 @@
     std::string filename = GetSystemImageFilename(image_location_.c_str(), image_isa_);
 
     if (!LoadFromFile(filename,
-                      /*validate_oat_file=*/ false,
+                      validate_oat_file,
                       extra_reservation_size,
                       &logger,
                       boot_image_spaces,
@@ -2020,6 +2032,7 @@
     const std::vector<std::string>& boot_class_path_locations,
     const std::string& image_location,
     const InstructionSet image_isa,
+    ImageSpaceLoadingOrder order,
     size_t extra_reservation_size,
     /*out*/std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
     /*out*/MemMap* extra_reservation) {
@@ -2076,30 +2089,41 @@
   // Collect all the errors.
   std::vector<std::string> error_msgs;
 
-  // Step 1: Check if we have an existing image in /system.
+  auto try_load_from = [&](auto has_fn, auto load_fn, bool validate_oat_file) {
+    if ((loader.*has_fn)()) {
+      std::string local_error_msg;
+      if ((loader.*load_fn)(validate_oat_file,
+                            extra_reservation_size,
+                            boot_image_spaces,
+                            extra_reservation,
+                            &local_error_msg)) {
+        return true;
+      }
+      error_msgs.push_back(local_error_msg);
+    }
+    return false;
+  };
 
-  if (loader.HasSystem()) {
-    std::string local_error_msg;
-    if (loader.LoadFromSystem(extra_reservation_size,
-                              boot_image_spaces,
-                              extra_reservation,
-                              &local_error_msg)) {
+  auto try_load_from_system = [&]() {
+    return try_load_from(&BootImageLoader::HasSystem, &BootImageLoader::LoadFromSystem, false);
+  };
+  auto try_load_from_cache = [&]() {
+    return try_load_from(&BootImageLoader::HasCache, &BootImageLoader::LoadFromDalvikCache, true);
+  };
+
+  auto invoke_sequentially = [](auto first, auto second) {
+    return first() || second();
+  };
+
+  // Step 1+2: Check system and cache images in the asked-for order.
+  if (order == ImageSpaceLoadingOrder::kSystemFirst) {
+    if (invoke_sequentially(try_load_from_system, try_load_from_cache)) {
       return true;
     }
-    error_msgs.push_back(local_error_msg);
-  }
-
-  // Step 2: Check if we have an existing image in the dalvik cache.
-  if (loader.HasCache()) {
-    std::string local_error_msg;
-    if (loader.LoadFromDalvikCache(/*validate_oat_file=*/ true,
-                                   extra_reservation_size,
-                                   boot_image_spaces,
-                                   extra_reservation,
-                                   &local_error_msg)) {
+  } else {
+    if (invoke_sequentially(try_load_from_cache, try_load_from_system)) {
       return true;
     }
-    error_msgs.push_back(local_error_msg);
   }
 
   // Step 3: We do not have an existing image in /system,
@@ -2259,6 +2283,7 @@
 std::string ImageSpace::GetBootClassPathChecksums(const std::vector<std::string>& boot_class_path,
                                                   const std::string& image_location,
                                                   InstructionSet image_isa,
+                                                  ImageSpaceLoadingOrder order,
                                                   /*out*/std::string* error_msg) {
   std::string system_filename;
   bool has_system = false;
@@ -2281,7 +2306,9 @@
   }
 
   DCHECK(has_system || has_cache);
-  const std::string& filename = has_system ? system_filename : cache_filename;
+  const std::string& filename = (order == ImageSpaceLoadingOrder::kSystemFirst)
+      ? (has_system ? system_filename : cache_filename)
+      : (has_cache ? cache_filename : system_filename);
   std::unique_ptr<ImageHeader> header = ReadSpecificImageHeader(filename.c_str(), error_msg);
   if (header == nullptr) {
     return std::string();
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index bb19097..1c61f06 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -19,6 +19,7 @@
 
 #include "gc/accounting/space_bitmap.h"
 #include "image.h"
+#include "image_space_loading_order.h"
 #include "space.h"
 
 namespace art {
@@ -48,6 +49,7 @@
       const std::vector<std::string>& boot_class_path_locations,
       const std::string& image_location,
       const InstructionSet image_isa,
+      ImageSpaceLoadingOrder order,
       size_t extra_reservation_size,
       /*out*/std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
       /*out*/MemMap* extra_reservation) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -63,6 +65,7 @@
   // reason in error_msg.
   static std::unique_ptr<ImageHeader> ReadImageHeader(const char* image_location,
                                                       InstructionSet image_isa,
+                                                      ImageSpaceLoadingOrder order,
                                                       std::string* error_msg);
 
   // Give access to the OatFile.
@@ -132,6 +135,7 @@
   static std::string GetBootClassPathChecksums(const std::vector<std::string>& boot_class_path,
                                                const std::string& image_location,
                                                InstructionSet image_isa,
+                                               ImageSpaceLoadingOrder order,
                                                /*out*/std::string* error_msg);
 
   // Returns the checksums for the boot image and extra boot class path dex files,
diff --git a/runtime/gc/space/image_space_fs.h b/runtime/gc/space/image_space_fs.h
index 262c6e0..0eab35f 100644
--- a/runtime/gc/space/image_space_fs.h
+++ b/runtime/gc/space/image_space_fs.h
@@ -20,7 +20,8 @@
 #include <dirent.h>
 #include <dlfcn.h>
 
-#include "android-base/stringprintf.h"
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
 
 #include "base/file_utils.h"
 #include "base/logging.h"  // For VLOG.
diff --git a/runtime/gc/space/image_space_loading_order.h b/runtime/gc/space/image_space_loading_order.h
new file mode 100644
index 0000000..d8b0be4
--- /dev/null
+++ b/runtime/gc/space/image_space_loading_order.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_GC_SPACE_IMAGE_SPACE_LOADING_ORDER_H_
+#define ART_RUNTIME_GC_SPACE_IMAGE_SPACE_LOADING_ORDER_H_
+
+namespace art {
+namespace gc {
+namespace space {
+
+enum class ImageSpaceLoadingOrder : char {
+  kSystemFirst,
+  kDataFirst,
+};
+
+}  // namespace space
+}  // namespace gc
+}  // namespace art
+
+#endif  // ART_RUNTIME_GC_SPACE_IMAGE_SPACE_LOADING_ORDER_H_
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 8115b6b..530dbd6 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -653,8 +653,9 @@
     return JNI_FALSE;
   }
   std::string error_msg;
+  Runtime* runtime = Runtime::Current();
   std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeader(
-      Runtime::Current()->GetImageLocation().c_str(), isa, &error_msg));
+      runtime->GetImageLocation().c_str(), isa, runtime->GetImageSpaceLoadingOrder(), &error_msg));
   return image_header.get() != nullptr;
 }
 
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index f1708b4..ca09339 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -584,7 +584,11 @@
   std::unique_ptr<ImageInfo> info(new ImageInfo());
   info->location = runtime->GetImageLocation();
   info->boot_class_path_checksums = gc::space::ImageSpace::GetBootClassPathChecksums(
-      runtime->GetBootClassPath(), info->location, isa, error_msg);
+      runtime->GetBootClassPath(),
+      info->location,
+      isa,
+      runtime->GetImageSpaceLoadingOrder(),
+      error_msg);
   if (info->boot_class_path_checksums.empty()) {
     return nullptr;
   }
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 6423f3b..cf0d478 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -89,6 +89,11 @@
       .Define("-Ximage:_")
           .WithType<std::string>()
           .IntoKey(M::Image)
+      .Define("-Ximage-load-order:_")
+          .WithType<gc::space::ImageSpaceLoadingOrder>()
+          .WithValueMap({{"system", gc::space::ImageSpaceLoadingOrder::kSystemFirst},
+                         {"data", gc::space::ImageSpaceLoadingOrder::kDataFirst}})
+          .IntoKey(M::ImageSpaceLoadingOrder)
       .Define("-Xcheck:jni")
           .IntoKey(M::CheckJni)
       .Define("-Xjniopts:forcecopy")
diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h
index 095d66e..39ce4ce 100644
--- a/runtime/parsed_options.h
+++ b/runtime/parsed_options.h
@@ -24,6 +24,7 @@
 
 #include "arch/instruction_set.h"
 #include "gc/collector_type.h"
+#include "gc/space/image_space_loading_order.h"
 #include "gc/space/large_object_space.h"
 // #include "jit/profile_saver_options.h"
 #include "runtime_globals.h"
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 5539205..0d32e0b 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1270,6 +1270,8 @@
   // Generational CC collection is currently only compatible with Baker read barriers.
   bool use_generational_cc = kUseBakerReadBarrier && xgc_option.generational_cc;
 
+  image_space_loading_order_ = runtime_options.GetOrDefault(Opt::ImageSpaceLoadingOrder);
+
   heap_ = new gc::Heap(runtime_options.GetOrDefault(Opt::MemoryInitialSize),
                        runtime_options.GetOrDefault(Opt::HeapGrowthLimit),
                        runtime_options.GetOrDefault(Opt::HeapMinFree),
@@ -1307,7 +1309,8 @@
                        use_generational_cc,
                        runtime_options.GetOrDefault(Opt::HSpaceCompactForOOMMinIntervalsMs),
                        runtime_options.Exists(Opt::DumpRegionInfoBeforeGC),
-                       runtime_options.Exists(Opt::DumpRegionInfoAfterGC));
+                       runtime_options.Exists(Opt::DumpRegionInfoAfterGC),
+                       image_space_loading_order_);
 
   if (!heap_->HasBootImageSpace() && !allow_dex_file_fallback_) {
     LOG(ERROR) << "Dex file fallback disabled, cannot continue without image.";
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 6a9e1e9..449a5a4 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -34,6 +34,7 @@
 #include "deoptimization_kind.h"
 #include "dex/dex_file_types.h"
 #include "experimental_flags.h"
+#include "gc/space/image_space_loading_order.h"
 #include "gc_root.h"
 #include "instrumentation.h"
 #include "jdwp_provider.h"
@@ -847,6 +848,10 @@
   // Return true if startup is already completed.
   bool GetStartupCompleted() const;
 
+  gc::space::ImageSpaceLoadingOrder GetImageSpaceLoadingOrder() const {
+    return image_space_loading_order_;
+  }
+
  private:
   static void InitPlatformSignalHandlers();
 
@@ -1175,6 +1180,9 @@
   // If startup has completed, must happen at most once.
   std::atomic<bool> startup_completed_ = false;
 
+  gc::space::ImageSpaceLoadingOrder image_space_loading_order_ =
+      gc::space::ImageSpaceLoadingOrder::kSystemFirst;
+
   // Note: See comments on GetFaultMessage.
   friend std::string GetFaultMessageForAbortLogging();
   friend class ScopedThreadPoolUsage;
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 9b4aa0f..41f0634 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -155,4 +155,8 @@
 RUNTIME_OPTIONS_KEY (Unit,                OnlyUseSystemOatFiles)
 RUNTIME_OPTIONS_KEY (unsigned int,        VerifierLoggingThreshold,       100)
 
+RUNTIME_OPTIONS_KEY (gc::space::ImageSpaceLoadingOrder, \
+                     ImageSpaceLoadingOrder, \
+                     gc::space::ImageSpaceLoadingOrder::kSystemFirst)
+
 #undef RUNTIME_OPTIONS_KEY
diff --git a/runtime/runtime_options.h b/runtime/runtime_options.h
index 39b44e7..4f46d89 100644
--- a/runtime/runtime_options.h
+++ b/runtime/runtime_options.h
@@ -26,6 +26,7 @@
 #include "base/variant_map.h"
 #include "cmdline_types.h"  // TODO: don't need to include this file here
 #include "gc/collector_type.h"
+#include "gc/space/image_space_loading_order.h"
 #include "gc/space/large_object_space.h"
 #include "jdwp/jdwp.h"
 #include "jit/jit.h"