Add support for booting with a boot classpath not fully AOTed.

Bug: 119800099
Test: adb shell setprop dalvik.vm.boot-image "/system/framework/nonexistent.art"

Change-Id: I6641399f43c24702f19f4d976c6054d77186799e
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 96d6d2a..18784da 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -589,9 +589,12 @@
 
   StackHandleScope<1> hs(Thread::Current());
   Handle<mirror::ObjectArray<mirror::Class>> inline_cache;
-  InlineCacheType inline_cache_type = Runtime::Current()->IsAotCompiler()
-      ? GetInlineCacheAOT(caller_dex_file, invoke_instruction, &hs, &inline_cache)
-      : GetInlineCacheJIT(invoke_instruction, &hs, &inline_cache);
+  // The Zygote JIT compiles based on a profile, so we shouldn't use runtime inline caches
+  // for it.
+  InlineCacheType inline_cache_type =
+      (Runtime::Current()->IsAotCompiler() || Runtime::Current()->IsZygote())
+          ? GetInlineCacheAOT(caller_dex_file, invoke_instruction, &hs, &inline_cache)
+          : GetInlineCacheJIT(invoke_instruction, &hs, &inline_cache);
 
   switch (inline_cache_type) {
     case kInlineCacheNoData: {
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 1f18172..81d2c35 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -808,8 +808,7 @@
     }
 
     if (!IsBootImage() && parser_options->boot_image_filename.empty()) {
-      parser_options->boot_image_filename += android_root_;
-      parser_options->boot_image_filename += "/framework/boot.art";
+      parser_options->boot_image_filename = GetDefaultBootImageLocation(android_root_);
     }
     if (!parser_options->boot_image_filename.empty()) {
       boot_image_filename_ = parser_options->boot_image_filename;
diff --git a/libartbase/base/file_utils.cc b/libartbase/base/file_utils.cc
index 4953bab..2436e45 100644
--- a/libartbase/base/file_utils.cc
+++ b/libartbase/base/file_utils.cc
@@ -176,12 +176,16 @@
   return GetAndroidDirSafe("ANDROID_DATA", "/data", error_msg);
 }
 
+std::string GetDefaultBootImageLocation(const std::string& android_root) {
+  return StringPrintf("%s/framework/boot.art", android_root.c_str());
+}
+
 std::string GetDefaultBootImageLocation(std::string* error_msg) {
   std::string android_root = GetAndroidRootSafe(error_msg);
   if (android_root.empty()) {
     return "";
   }
-  return StringPrintf("%s/framework/boot.art", android_root.c_str());
+  return GetDefaultBootImageLocation(android_root);
 }
 
 void GetDalvikCache(const char* subdir, const bool create_if_absent, std::string* dalvik_cache,
diff --git a/libartbase/base/file_utils.h b/libartbase/base/file_utils.h
index bddfaa1..c8eca59 100644
--- a/libartbase/base/file_utils.h
+++ b/libartbase/base/file_utils.h
@@ -43,6 +43,9 @@
 // Returns an empty string if ANDROID_ROOT is not set.
 std::string GetDefaultBootImageLocation(std::string* error_msg);
 
+// Returns the default boot image location, based on the passed android root.
+std::string GetDefaultBootImageLocation(const std::string& android_root);
+
 // Returns the dalvik-cache location, with subdir appended. Returns the empty string if the cache
 // could not be found.
 std::string GetDalvikCache(const char* subdir);
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index d44bd59..69139cd 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -20,9 +20,11 @@
 
 #include "art_method-inl.h"
 #include "base/enums.h"
+#include "base/file_utils.h"
 #include "base/logging.h"  // For VLOG.
 #include "base/memory_tool.h"
 #include "base/runtime_debug.h"
+#include "base/scoped_flock.h"
 #include "base/utils.h"
 #include "class_root.h"
 #include "debugger.h"
@@ -559,7 +561,7 @@
     kCompileOsr,
   };
 
-  JitCompileTask(ArtMethod* method, TaskKind kind) : method_(method), kind_(kind) {
+  JitCompileTask(ArtMethod* method, TaskKind kind) : method_(method), kind_(kind), klass_(nullptr) {
     ScopedObjectAccess soa(Thread::Current());
     // Add a global ref to the class to prevent class unloading until compilation is done.
     klass_ = soa.Vm()->AddGlobalRef(soa.Self(), method_->GetDeclaringClass());
@@ -606,6 +608,18 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(JitCompileTask);
 };
 
+class ZygoteTask final : public Task {
+ public:
+  ZygoteTask() {}
+
+  void Run(Thread* self) override {
+    Runtime::Current()->GetJit()->AddNonAotBootMethodsToQueue(self);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ZygoteTask);
+};
+
 void Jit::CreateThreadPool() {
   // There is a DCHECK in the 'AddSamples' method to ensure the tread pool
   // is not null when we instrument.
@@ -616,6 +630,91 @@
 
   thread_pool_->SetPthreadPriority(options_->GetThreadPoolPthreadPriority());
   Start();
+
+  // If we're not using the default boot image location, request a JIT task to
+  // compile all methods in the boot image profile.
+  Runtime* runtime = Runtime::Current();
+  if (runtime->IsZygote() && !runtime->IsUsingDefaultBootImageLocation()) {
+    thread_pool_->AddTask(Thread::Current(), new ZygoteTask());
+  }
+}
+
+void Jit::AddNonAotBootMethodsToQueue(Thread* self) {
+  Runtime* runtime = Runtime::Current();
+  std::string profile_location;
+  for (const std::string& option : runtime->GetImageCompilerOptions()) {
+    if (android::base::StartsWith(option, "--profile-file=")) {
+      profile_location = option.substr(strlen("--profile-file="));
+      break;
+    }
+  }
+  if (profile_location.empty()) {
+    LOG(WARNING) << "Expected a profile location in JIT zygote mode";
+    return;
+  }
+
+  std::string error_msg;
+  ScopedFlock profile_file = LockedFile::Open(
+      profile_location.c_str(), O_RDONLY, true, &error_msg);
+
+  // Return early if we're unable to obtain a lock on the profile.
+  if (profile_file.get() == nullptr) {
+    LOG(ERROR) << "Cannot lock profile: " << error_msg;
+    return;
+  }
+
+  ProfileCompilationInfo profile_info;
+  if (!profile_info.Load(profile_file->Fd())) {
+    LOG(ERROR) << "Could not load profile file";
+    return;
+  }
+
+  const std::vector<const DexFile*>& boot_class_path =
+      runtime->GetClassLinker()->GetBootClassPath();
+  ScopedObjectAccess soa(self);
+  StackHandleScope<1> hs(self);
+  MutableHandle<mirror::DexCache> dex_cache = hs.NewHandle<mirror::DexCache>(nullptr);
+  ScopedNullHandle<mirror::ClassLoader> null_handle;
+  ClassLinker* class_linker = runtime->GetClassLinker();
+
+  for (const DexFile* dex_file : boot_class_path) {
+    std::set<dex::TypeIndex> class_types;
+    std::set<uint16_t> hot_methods;
+    std::set<uint16_t> startup_methods;
+    std::set<uint16_t> post_startup_methods;
+    std::set<uint16_t> combined_methods;
+    if (!profile_info.GetClassesAndMethods(*dex_file,
+                                           &class_types,
+                                           &hot_methods,
+                                           &startup_methods,
+                                           &post_startup_methods)) {
+      LOG(ERROR) << "Unable to get classes and methods for " << dex_file->GetLocation();
+      continue;
+    }
+    dex_cache.Assign(class_linker->FindDexCache(self, *dex_file));
+    CHECK(dex_cache != nullptr) << "Could not find dex cache for " << dex_file->GetLocation();
+    for (uint16_t method_idx : startup_methods) {
+      ArtMethod* method = class_linker->ResolveMethodWithoutInvokeType(
+          method_idx, dex_cache, null_handle);
+      if (method == nullptr) {
+        self->ClearException();
+        continue;
+      }
+      if (!method->IsCompilable() || !method->IsInvokable()) {
+        continue;
+      }
+      const void* entry_point = method->GetEntryPointFromQuickCompiledCode();
+      if (class_linker->IsQuickToInterpreterBridge(entry_point) ||
+          class_linker->IsQuickGenericJniStub(entry_point)) {
+        if (!method->IsNative()) {
+          // The compiler requires a ProfilingInfo object for non-native methods.
+          ProfilingInfo::Create(self, method, /* retry_allocation= */ true);
+        }
+        thread_pool_->AddTask(self,
+            new JitCompileTask(method, JitCompileTask::TaskKind::kCompile));
+      }
+    }
+  }
 }
 
 static bool IgnoreSamplesForMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index 714db3a..f709de3 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -303,6 +303,10 @@
   // Adjust state after forking.
   void PostZygoteFork();
 
+  // In case the boot classpath is not fully AOTed, add methods from the boot profile to the
+  // compilation queue.
+  void AddNonAotBootMethodsToQueue(Thread* self);
+
  private:
   Jit(JitCodeCache* code_cache, JitOptions* options);
 
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 891ecef..da0e048 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -287,7 +287,7 @@
     runtime_flags &= ~DISABLE_VERIFIER;
   }
 
-  if ((runtime_flags & ONLY_USE_SYSTEM_OAT_FILES) != 0) {
+  if ((runtime_flags & ONLY_USE_SYSTEM_OAT_FILES) != 0 || is_system_server) {
     Runtime::Current()->GetOatFileManager().SetOnlyUseSystemOatFiles();
     runtime_flags &= ~ONLY_USE_SYSTEM_OAT_FILES;
   }
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 80ac01f..f1708b4 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -783,6 +783,7 @@
       if (executable && oat_file_assistant_->only_load_system_executable_) {
         executable = LocationIsOnSystem(filename_.c_str());
       }
+      VLOG(oat) << "Loading " << filename_ << " with executable: " << executable;
       std::string error_msg;
       if (use_fd_) {
         if (oat_fd_ >= 0 && vdex_fd_ >= 0) {
@@ -809,6 +810,8 @@
       if (file_.get() == nullptr) {
         VLOG(oat) << "OatFileAssistant test for existing oat file "
           << filename_ << ": " << error_msg;
+      } else {
+        VLOG(oat) << "Successfully loaded " << filename_ << " with executable: " << executable;
       }
     }
   }
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index f516d0d..e644c04 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -609,8 +609,7 @@
   }
 
   if (!args.Exists(M::CompilerCallbacksPtr) && !args.Exists(M::Image)) {
-    std::string image = GetAndroidRoot();
-    image += "/framework/boot.art";
+    std::string image = GetDefaultBootImageLocation(GetAndroidRoot());
     args.Set(M::Image, image);
   }
 
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 81c17a5..f550b84 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -27,6 +27,7 @@
 #include <memory>
 #include <vector>
 
+#include "base/file_utils.h"
 #include "base/locks.h"
 #include "base/macros.h"
 #include "base/mem_map.h"
@@ -184,6 +185,11 @@
     return image_location_;
   }
 
+  bool IsUsingDefaultBootImageLocation() const {
+    std::string error_msg;
+    return GetImageLocation().compare(GetDefaultBootImageLocation(&error_msg)) == 0;
+  }
+
   // Starts a runtime, which may cause threads to be started and code to run.
   bool Start() UNLOCK_FUNCTION(Locks::mutator_lock_);
 
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index 72c42b9..61cd982 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -178,7 +178,6 @@
     reinterpret_cast<DexSectionHeader*>(vdex->mmap_.Begin() + offset)->quickening_info_size_ = 0;
   }
 
-  *error_msg = "Success";
   return vdex;
 }