Merge "Fix some indentation errors in compiler driver"
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index bcf48fd..b6d4ef6 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -121,7 +121,7 @@
 ART_GTEST_proxy_test_DEX_DEPS := Interfaces
 ART_GTEST_reflection_test_DEX_DEPS := Main NonStaticLeafMethods StaticLeafMethods
 ART_GTEST_profile_assistant_test_DEX_DEPS := ProfileTestMultiDex
-ART_GTEST_profile_compilation_info_test_DEX_DEPS := ProfileTestMultiDex
+ART_GTEST_profile_compilation_info_test_DEX_DEPS := ManyMethods ProfileTestMultiDex
 ART_GTEST_runtime_callbacks_test_DEX_DEPS := XandY
 ART_GTEST_stub_test_DEX_DEPS := AllFields
 ART_GTEST_transaction_test_DEX_DEPS := Transaction
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 07639e8..b224ec7 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -484,7 +484,7 @@
 * -Xps-*
 */
 TEST_F(CmdlineParserTest, ProfileSaverOptions) {
-  ProfileSaverOptions opt = ProfileSaverOptions(true, 1, 2, 3, 4, 5, 6, 7, "abc");
+  ProfileSaverOptions opt = ProfileSaverOptions(true, 1, 2, 3, 4, 5, 6, 7, "abc", true);
 
   EXPECT_SINGLE_PARSE_VALUE(opt,
                             "-Xjitsaveprofilinginfo "
@@ -495,7 +495,8 @@
                             "-Xps-min-classes-to-save:5 "
                             "-Xps-min-notification-before-wake:6 "
                             "-Xps-max-notification-before-wake:7 "
-                            "-Xps-profile-path:abc",
+                            "-Xps-profile-path:abc "
+                            "-Xps-profile-boot-class-path",
                             M::ProfileSaverOpts);
 }  // TEST_F
 
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index 185a0e4..4de8a48 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -712,6 +712,11 @@
       return Result::SuccessNoValue();
     }
 
+    if (option == "profile-boot-class-path") {
+      existing.profile_boot_class_path_ = true;
+      return Result::SuccessNoValue();
+    }
+
     // The rest of these options are always the wildcard from '-Xps-*'
     std::string suffix = RemovePrefix(option);
 
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 104dc35..91b58e1 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -2272,6 +2272,8 @@
     const char* descriptor = dex_file.StringDataByIdx(class_type_id.descriptor_idx_);
     ScopedObjectAccessUnchecked soa(Thread::Current());
     StackHandleScope<3> hs(soa.Self());
+    const bool is_boot_image = manager_->GetCompiler()->GetCompilerOptions().IsBootImage();
+    const bool is_app_image = manager_->GetCompiler()->GetCompilerOptions().IsAppImage();
 
     mirror::Class::Status old_status = klass->GetStatus();;
     // Only try to initialize classes that were successfully verified.
@@ -2293,32 +2295,28 @@
         ObjectLock<mirror::Class> lock(soa.Self(), h_klass);
         // Attempt to initialize allowing initialization of parent classes but still not static
         // fields.
-        bool is_superclass_initialized = true;
-        if (!manager_->GetCompiler()->GetCompilerOptions().IsAppImage()) {
-          // If not an app image case, the compiler won't initialize too much things and do a fast
-          // fail, don't check dependencies.
-          manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, false, true);
-        } else {
-          // For app images, do the initialization recursively and resolve types encountered to make
-          // sure the compiler runs without error.
-          is_superclass_initialized = InitializeDependencies(klass, class_loader, soa.Self());
-          if (is_superclass_initialized) {
-            manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, false, true);
-          }
-        }
+        manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, false, true);
         old_status = klass->GetStatus();
-        // If superclass cannot be initialized, no need to proceed.
+        // If the class was not initialized, we can proceed to see if we can initialize static
+        // fields.
         if (!klass->IsInitialized() &&
-            is_superclass_initialized &&
+            (is_app_image || is_boot_image) &&
             manager_->GetCompiler()->IsImageClass(descriptor)) {
           bool can_init_static_fields = false;
-          if (manager_->GetCompiler()->GetCompilerOptions().IsBootImage()) {
+          if (is_boot_image) {
             // We need to initialize static fields, we only do this for image classes that aren't
-            // marked with the $NoPreloadHolder (which implies this should not be initialized early).
+            // marked with the $NoPreloadHolder (which implies this should not be initialized
+            // early).
             can_init_static_fields = !StringPiece(descriptor).ends_with("$NoPreloadHolder;");
           } else {
-            can_init_static_fields = manager_->GetCompiler()->GetCompilerOptions().IsAppImage() &&
+            CHECK(is_app_image);
+            // The boot image case doesn't need to recursively initialize the dependencies with
+            // special logic since the class linker already does this.
+            bool is_superclass_initialized =
+                InitializeDependencies(klass, class_loader, soa.Self());
+            can_init_static_fields =
                 !soa.Self()->IsExceptionPending() &&
+                is_superclass_initialized &&
                 NoClinitInDependency(klass, soa.Self(), &class_loader);
             // TODO The checking for clinit can be removed since it's already
             // checked when init superclass. Currently keep it because it contains
@@ -2361,6 +2359,10 @@
                 soa.Self()->ClearException();
                 transaction.Rollback();
                 CHECK_EQ(old_status, klass->GetStatus()) << "Previous class status not restored";
+              } else if (is_boot_image) {
+                // For boot image, we want to put the updated status in the oat class since we can't
+                // reject the image anyways.
+                old_status = klass->GetStatus();
               }
             }
 
@@ -2370,6 +2372,8 @@
               // above as we will allocate strings, so must be allowed to suspend.
               if (&klass->GetDexFile() == manager_->GetDexFile()) {
                 InternStrings(klass, class_loader);
+              } else {
+                DCHECK(!is_boot_image) << "Boot image must have equal dex files";
               }
             }
           }
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 1274a36..0197703 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -132,8 +132,7 @@
 
   std::vector<std::unique_ptr<const DexFile>> OpenTestDexFiles(const char* name);
 
-  std::unique_ptr<const DexFile> OpenTestDexFile(const char* name)
-      REQUIRES_SHARED(Locks::mutator_lock_);
+  std::unique_ptr<const DexFile> OpenTestDexFile(const char* name);
 
   jobject LoadDex(const char* dex_name) REQUIRES_SHARED(Locks::mutator_lock_);
   jobject LoadMultiDex(const char* first_dex_name, const char* second_dex_name)
diff --git a/runtime/dex_reference_collection.h b/runtime/dex_reference_collection.h
index 76355d6..01b9b97 100644
--- a/runtime/dex_reference_collection.h
+++ b/runtime/dex_reference_collection.h
@@ -63,12 +63,13 @@
 
  private:
   DexFileMap map_;
-  // Optimize for adding to same vector in succession.
   const DexFile* current_dex_file_ = nullptr;
   IndexVector* current_vector_ = nullptr;
   VectorAllocator vector_allocator_;
 
   ALWAYS_INLINE IndexVector* GetOrInsertVector(const DexFile* dex) {
+    // Optimize for adding to same vector in succession, the cached dex file and vector aims to
+    // prevent map lookups.
     if (UNLIKELY(current_dex_file_ != dex)) {
       // There is an assumption that constructing an empty vector wont do any allocations. If this
       // incorrect, this might leak for the arena case.
diff --git a/runtime/jit/profile_compilation_info-inl.h b/runtime/jit/profile_compilation_info-inl.h
index 8a067a5..b71a95e 100644
--- a/runtime/jit/profile_compilation_info-inl.h
+++ b/runtime/jit/profile_compilation_info-inl.h
@@ -22,10 +22,11 @@
 namespace art {
 
 template <class Iterator>
-inline bool ProfileCompilationInfo::AddSampledMethodsForDex(bool startup,
-                                                            const DexFile* dex_file,
-                                                            Iterator index_begin,
-                                                            Iterator index_end) {
+inline bool ProfileCompilationInfo::AddMethodsForDex(bool startup,
+                                                     bool hot,
+                                                     const DexFile* dex_file,
+                                                     Iterator index_begin,
+                                                     Iterator index_end) {
   DexFileData* data = GetOrAddDexFileData(dex_file);
   if (data == nullptr) {
     return false;
@@ -33,21 +34,9 @@
   for (auto it = index_begin; it != index_end; ++it) {
     DCHECK_LT(*it, data->num_method_ids);
     data->AddSampledMethod(startup, *it);
-  }
-  return true;
-}
-
-template <class Iterator>
-inline bool ProfileCompilationInfo::AddHotMethodsForDex(const DexFile* dex_file,
-                                                        Iterator index_begin,
-                                                        Iterator index_end) {
-  DexFileData* data = GetOrAddDexFileData(dex_file);
-  if (data == nullptr) {
-    return false;
-  }
-  for (auto it = index_begin; it != index_end; ++it) {
-    DCHECK_LT(*it, data->num_method_ids);
-    data->FindOrAddMethod(*it);
+    if (hot) {
+      data->FindOrAddMethod(*it);
+    }
   }
   return true;
 }
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index ea27d3b..a67fb38 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -1223,6 +1223,15 @@
                               method_ref.dex_method_index);
 }
 
+const ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::FindDexData(
+    const DexFile* dex_file) const {
+  const DexFileData* dex_data = FindDexData(GetProfileDexFileKey(dex_file->GetLocation()));
+  if (dex_data == nullptr || !ChecksumMatch(*dex_file, dex_data->checksum)) {
+    return nullptr;
+  }
+  return dex_data;
+}
+
 bool ProfileCompilationInfo::IsStartupOrHotMethod(const std::string& dex_location,
                                                   uint32_t dex_checksum,
                                                   uint16_t dex_method_index) const {
@@ -1238,6 +1247,15 @@
   return method_it != methods.end();
 }
 
+bool ProfileCompilationInfo::ContainsSampledMethod(bool startup,
+                                                   const MethodReference& method_ref) const {
+  const DexFileData* dex_data = FindDexData(method_ref.dex_file);
+  if (dex_data == nullptr) {
+    return false;
+  }
+  return dex_data->HasSampledMethod(startup, method_ref.dex_method_index);
+}
+
 bool ProfileCompilationInfo::ContainsHotMethod(const MethodReference& method_ref) const {
   return FindMethod(method_ref.dex_file->GetLocation(),
                     method_ref.dex_file->GetLocationChecksum(),
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index bd1b9d6..7bcaffb 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -211,24 +211,22 @@
   bool AddMethod(const ProfileMethodInfo& pmi);
 
   // Add methods that have samples but are are not necessarily hot. These are partitioned into two
-  // possibly intersecting sets startup and post startup.
+  // possibly intersecting sets startup and post startup. Sampled methods are used for layout but
+  // not necessarily determining what gets compiled.
   bool AddSampledMethod(bool startup,
                         const std::string& dex_location,
                         uint32_t checksum,
                         uint16_t method_idx,
                         uint32_t num_method_ids);
-  // Bulk add sampled methods for a single dex, fast since it only has one GetOrAddDexFileData call.
-  template <class Iterator>
-  bool AddSampledMethodsForDex(bool startup,
-                               const DexFile* dex_file,
-                               Iterator index_begin,
-                               Iterator index_end);
 
-  // Bulk add hot methods for a single dex, fast since it only has one GetOrAddDexFileData call.
+  // Bulk add sampled methods and/or hot methods for a single dex, fast since it only has one
+  // GetOrAddDexFileData call.
   template <class Iterator>
-  bool AddHotMethodsForDex(const DexFile* dex_file,
-                           Iterator index_begin,
-                           Iterator index_end);
+  ALWAYS_INLINE bool AddMethodsForDex(bool startup,
+                                      bool hot,
+                                      const DexFile* dex_file,
+                                      Iterator index_begin,
+                                      Iterator index_end);
 
   // Load profile information from the given file descriptor.
   // If the current profile is non-empty the load will fail.
@@ -264,6 +262,10 @@
   // Return true if the method reference iS present and hot in the profiling info.
   bool ContainsHotMethod(const MethodReference& method_ref) const;
 
+
+  // Return true if the profile contains a startup or post startup method.
+  bool ContainsSampledMethod(bool startup, const MethodReference& method_ref) const;
+
   // Return true if the class's type is present in the profiling info.
   bool ContainsClass(const DexFile& dex_file, dex::TypeIndex type_idx) const;
 
@@ -358,7 +360,7 @@
           class_set(std::less<dex::TypeIndex>(), arena->Adapter(kArenaAllocProfile)),
           num_method_ids(num_methods),
           bitmap_storage(arena->Adapter(kArenaAllocProfile)) {
-      const size_t num_bits = num_method_ids * kBitmapCount;
+      const size_t num_bits = num_method_ids * kBitmapIndexCount;
       bitmap_storage.resize(RoundUp(num_bits, kBitsPerByte) / kBitsPerByte);
       if (!bitmap_storage.empty()) {
         method_bitmap =
@@ -409,9 +411,9 @@
 
    private:
     enum BitmapIndex {
-      kBitmapStartup,
-      kBitmapPostStartup,
-      kBitmapCount,
+      kBitmapIndexStartup,
+      kBitmapIndexPostStartup,
+      kBitmapIndexCount,
     };
 
     size_t MethodBitIndex(bool startup, size_t index) const {
@@ -420,8 +422,8 @@
       // This compresses better than ([startup bit][post statup bit])*
 
       return index + (startup
-          ? kBitmapStartup * num_method_ids
-          : kBitmapPostStartup * num_method_ids);
+          ? kBitmapIndexStartup * num_method_ids
+          : kBitmapIndexPostStartup * num_method_ids);
     }
   };
 
@@ -468,6 +470,10 @@
   // doesn't contain the key.
   const DexFileData* FindDexData(const std::string& profile_key) const;
 
+  // Return the dex data associated with the given dex file or null if the profile doesn't contain
+  // the key or the checksum mismatches.
+  const DexFileData* FindDexData(const DexFile* dex_file) const;
+
   // Checks if the profile is empty.
   bool IsEmpty() const;
 
diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc
index 39670af..5528366 100644
--- a/runtime/jit/profile_compilation_info_test.cc
+++ b/runtime/jit/profile_compilation_info_test.cc
@@ -25,7 +25,7 @@
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
 #include "handle_scope-inl.h"
-#include "jit/profile_compilation_info.h"
+#include "jit/profile_compilation_info-inl.h"
 #include "linear_alloc.h"
 #include "scoped_thread_state_change-inl.h"
 #include "type_reference.h"
@@ -891,6 +891,49 @@
     test_info.MergeWith(merge_info);
   }
   EXPECT_TRUE(test_info.IsStartupOrHotMethod(kDex1, kChecksum1, 11));
+
+  // Test bulk adding.
+  {
+    std::unique_ptr<const DexFile> dex(OpenTestDexFile("ManyMethods"));
+    ProfileCompilationInfo info;
+    std::vector<uint16_t> hot_methods = {1, 3, 5};
+    std::vector<uint16_t> startup_methods = {1, 2};
+    std::vector<uint16_t> post_methods = {0, 2, 6};
+    ASSERT_GE(dex->NumMethodIds(), 7u);
+    info.AddMethodsForDex(/*startup*/true,
+                          /*hot*/true,
+                          dex.get(),
+                          hot_methods.begin(),
+                          hot_methods.end());
+    info.AddMethodsForDex(/*startup*/true,
+                          /*hot*/false,
+                          dex.get(),
+                          startup_methods.begin(),
+                          startup_methods.end());
+    info.AddMethodsForDex(/*startup*/false,
+                          /*hot*/false,
+                          dex.get(),
+                          post_methods.begin(),
+                          post_methods.end());
+    for (uint16_t id : hot_methods) {
+      EXPECT_TRUE(info.ContainsHotMethod(MethodReference(dex.get(), id)));
+      EXPECT_TRUE(info.ContainsSampledMethod(/*startup*/true, MethodReference(dex.get(), id)));
+    }
+    for (uint16_t id : startup_methods) {
+      EXPECT_TRUE(info.ContainsSampledMethod(/*startup*/true, MethodReference(dex.get(), id)));
+    }
+    for (uint16_t id : post_methods) {
+      EXPECT_TRUE(info.ContainsSampledMethod(/*startup*/false, MethodReference(dex.get(), id)));
+    }
+    EXPECT_TRUE(info.ContainsSampledMethod(/*startup*/false, MethodReference(dex.get(), 6)));
+    // Check that methods that shouldn't have been touched are OK.
+    EXPECT_FALSE(info.ContainsHotMethod(MethodReference(dex.get(), 0)));
+    EXPECT_FALSE(info.ContainsHotMethod(MethodReference(dex.get(), 2)));
+    EXPECT_FALSE(info.ContainsHotMethod(MethodReference(dex.get(), 4)));
+    EXPECT_FALSE(info.ContainsSampledMethod(/*startup*/false, MethodReference(dex.get(), 1)));
+    EXPECT_FALSE(info.ContainsSampledMethod(/*startup*/true, MethodReference(dex.get(), 4)));
+    EXPECT_FALSE(info.ContainsSampledMethod(/*startup*/true, MethodReference(dex.get(), 6)));
+  }
 }
 
 }  // namespace art
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index edce9cd..c6c46de 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -194,21 +194,24 @@
   GetClassesAndMethodsVisitor(MethodReferenceCollection* hot_methods,
                               MethodReferenceCollection* sampled_methods,
                               TypeReferenceCollection* resolved_classes,
-                              uint32_t hot_method_sample_threshold)
+                              uint32_t hot_method_sample_threshold,
+                              bool profile_boot_class_path)
     : hot_methods_(hot_methods),
       sampled_methods_(sampled_methods),
       resolved_classes_(resolved_classes),
-      hot_method_sample_threshold_(hot_method_sample_threshold) {}
+      hot_method_sample_threshold_(hot_method_sample_threshold),
+      profile_boot_class_path_(profile_boot_class_path) {}
 
   virtual bool operator()(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
     if (klass->IsProxyClass() ||
         klass->IsArrayClass() ||
+        klass->IsPrimitive() ||
         !klass->IsResolved() ||
         klass->IsErroneousResolved() ||
-        klass->GetClassLoader() == nullptr) {
+        (!profile_boot_class_path_ && klass->GetClassLoader() == nullptr)) {
       return true;
     }
-    DCHECK(klass->GetDexCache() != nullptr) << klass->PrettyClass();
+    CHECK(klass->GetDexCache() != nullptr) << klass->PrettyClass();
     resolved_classes_->AddReference(&klass->GetDexFile(), klass->GetDexTypeIndex());
     for (ArtMethod& method : klass->GetMethods(kRuntimePointerSize)) {
       if (!method.IsNative()) {
@@ -235,6 +238,7 @@
   MethodReferenceCollection* const sampled_methods_;
   TypeReferenceCollection* const resolved_classes_;
   uint32_t hot_method_sample_threshold_;
+  const bool profile_boot_class_path_;
 };
 
 void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() {
@@ -263,7 +267,8 @@
       GetClassesAndMethodsVisitor visitor(&hot_methods,
                                           &startup_methods,
                                           &resolved_classes,
-                                          hot_threshold);
+                                          hot_threshold,
+                                          options_.GetProfileBootClassPath());
       runtime->GetClassLinker()->VisitClasses(&visitor);
     }
   }
@@ -283,21 +288,23 @@
     for (const auto& pair : hot_methods.GetMap()) {
       const DexFile* const dex_file = pair.first;
       if (locations.find(dex_file->GetBaseLocation()) != locations.end()) {
-        cached_info->AddSampledMethodsForDex(/*startup*/ true,
-                                             dex_file,
-                                             pair.second.begin(),
-                                             pair.second.end());
-        // Adding hot methods is a bit slow, TODO: optimize.
-        cached_info->AddHotMethodsForDex(dex_file, pair.second.begin(), pair.second.end());
+        const MethodReferenceCollection::IndexVector& indices = pair.second;
+        cached_info->AddMethodsForDex(/*startup*/ true,
+                                      /*hot*/ true,
+                                      dex_file,
+                                      indices.begin(),
+                                      indices.end());
       }
     }
     for (const auto& pair : startup_methods.GetMap()) {
       const DexFile* const dex_file = pair.first;
       if (locations.find(dex_file->GetBaseLocation()) != locations.end()) {
-        cached_info->AddSampledMethodsForDex(/*startup*/ true,
-                                             dex_file,
-                                             pair.second.begin(),
-                                             pair.second.end());
+        const MethodReferenceCollection::IndexVector& indices = pair.second;
+        cached_info->AddMethodsForDex(/*startup*/ true,
+                                      /*hot*/ false,
+                                      dex_file,
+                                      indices.begin(),
+                                      indices.end());
       }
     }
     for (const auto& pair : resolved_classes.GetMap()) {
@@ -469,24 +476,49 @@
                          const std::string& output_filename,
                          jit::JitCodeCache* jit_code_cache,
                          const std::vector<std::string>& code_paths) {
+  Runtime* const runtime = Runtime::Current();
   DCHECK(options.IsEnabled());
-  DCHECK(Runtime::Current()->GetJit() != nullptr);
+  DCHECK(runtime->GetJit() != nullptr);
   DCHECK(!output_filename.empty());
   DCHECK(jit_code_cache != nullptr);
 
   std::vector<std::string> code_paths_to_profile;
-
   for (const std::string& location : code_paths) {
     if (ShouldProfileLocation(location))  {
       code_paths_to_profile.push_back(location);
     }
   }
+
+  MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
+  // Support getting profile samples for the boot class path. This will be used to generate the boot
+  // image profile. The intention is to use this code to generate to boot image but not use it in
+  // production. b/37966211
+  if (options.GetProfileBootClassPath()) {
+    std::set<std::string> code_paths_keys;
+    for (const std::string& location : code_paths) {
+      code_paths_keys.insert(ProfileCompilationInfo::GetProfileDexFileKey(location));
+    }
+    for (const DexFile* dex_file : runtime->GetClassLinker()->GetBootClassPath()) {
+      // Don't check ShouldProfileLocation since the boot class path may be speed compiled.
+      const std::string& location = dex_file->GetLocation();
+      const std::string key = ProfileCompilationInfo::GetProfileDexFileKey(location);
+      VLOG(profiler) << "Registering boot dex file " << location;
+      if (code_paths_keys.find(key) != code_paths_keys.end()) {
+        LOG(WARNING) << "Boot class path location key conflicts with code path " << location;
+      } else if (instance_ == nullptr) {
+        // Only add the boot class path once since Start may be called multiple times for secondary
+        // dexes.
+        // We still do the collision check above. This handles any secondary dexes that conflict
+        // with the boot class path dex files.
+        code_paths_to_profile.push_back(location);
+      }
+    }
+  }
   if (code_paths_to_profile.empty()) {
     VLOG(profiler) << "No code paths should be profiled.";
     return;
   }
 
-  MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
   if (instance_ != nullptr) {
     // If we already have an instance, make sure it uses the same jit_code_cache.
     // This may be called multiple times via Runtime::registerAppInfo (e.g. for
diff --git a/runtime/jit/profile_saver_options.h b/runtime/jit/profile_saver_options.h
index 44550f4..251227e 100644
--- a/runtime/jit/profile_saver_options.h
+++ b/runtime/jit/profile_saver_options.h
@@ -40,7 +40,8 @@
     min_classes_to_save_(kMinClassesToSave),
     min_notification_before_wake_(kMinNotificationBeforeWake),
     max_notification_before_wake_(kMaxNotificationBeforeWake),
-    profile_path_("") {}
+    profile_path_(""),
+    profile_boot_class_path_(false) {}
 
   ProfileSaverOptions(
       bool enabled,
@@ -51,8 +52,9 @@
       uint32_t min_classes_to_save,
       uint32_t min_notification_before_wake,
       uint32_t max_notification_before_wake,
-      const std::string& profile_path):
-    enabled_(enabled),
+      const std::string& profile_path,
+      bool profile_boot_class_path)
+  : enabled_(enabled),
     min_save_period_ms_(min_save_period_ms),
     save_resolved_classes_delay_ms_(save_resolved_classes_delay_ms),
     hot_startup_method_samples_(hot_startup_method_samples),
@@ -60,7 +62,8 @@
     min_classes_to_save_(min_classes_to_save),
     min_notification_before_wake_(min_notification_before_wake),
     max_notification_before_wake_(max_notification_before_wake),
-    profile_path_(profile_path) {}
+    profile_path_(profile_path),
+    profile_boot_class_path_(profile_boot_class_path) {}
 
   bool IsEnabled() const {
     return enabled_;
@@ -97,6 +100,9 @@
   std::string GetProfilePath() const {
     return profile_path_;
   }
+  bool GetProfileBootClassPath() const {
+    return profile_boot_class_path_;
+  }
 
   friend std::ostream & operator<<(std::ostream &os, const ProfileSaverOptions& pso) {
     os << "enabled_" << pso.enabled_
@@ -106,7 +112,8 @@
         << ", min_methods_to_save_" << pso.min_methods_to_save_
         << ", min_classes_to_save_" << pso.min_classes_to_save_
         << ", min_notification_before_wake_" << pso.min_notification_before_wake_
-        << ", max_notification_before_wake_" << pso.max_notification_before_wake_;
+        << ", max_notification_before_wake_" << pso.max_notification_before_wake_
+        << ", profile_boot_class_path_" << pso.profile_boot_class_path_;
     return os;
   }
 
@@ -121,6 +128,7 @@
   uint32_t min_notification_before_wake_;
   uint32_t max_notification_before_wake_;
   std::string profile_path_;
+  bool profile_boot_class_path_;
 };
 
 }  // namespace art
diff --git a/runtime/openjdkjvmti/ti_stack.cc b/runtime/openjdkjvmti/ti_stack.cc
index 22da2d2..550b972 100644
--- a/runtime/openjdkjvmti/ti_stack.cc
+++ b/runtime/openjdkjvmti/ti_stack.cc
@@ -59,13 +59,18 @@
 
 namespace openjdkjvmti {
 
+template <typename FrameFn>
 struct GetStackTraceVisitor : public art::StackVisitor {
   GetStackTraceVisitor(art::Thread* thread_in,
                        size_t start_,
-                       size_t stop_)
+                       size_t stop_,
+                       FrameFn fn_)
       : StackVisitor(thread_in, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+        fn(fn_),
         start(start_),
         stop(stop_) {}
+  GetStackTraceVisitor(const GetStackTraceVisitor&) = default;
+  GetStackTraceVisitor(GetStackTraceVisitor&&) = default;
 
   bool VisitFrame() REQUIRES_SHARED(art::Locks::mutator_lock_) {
     art::ArtMethod* m = GetMethod();
@@ -81,7 +86,7 @@
       jlong dex_location = (dex_pc == art::DexFile::kDexNoIndex) ? -1 : static_cast<jlong>(dex_pc);
 
       jvmtiFrameInfo info = { id, dex_location };
-      frames.push_back(info);
+      fn(info);
 
       if (stop == 1) {
         return false;  // We're done.
@@ -95,24 +100,34 @@
     return true;
   }
 
-  std::vector<jvmtiFrameInfo> frames;
+  FrameFn fn;
   size_t start;
   size_t stop;
 };
 
-struct GetStackTraceClosure : public art::Closure {
+template <typename FrameFn>
+GetStackTraceVisitor<FrameFn> MakeStackTraceVisitor(art::Thread* thread_in,
+                                                    size_t start,
+                                                    size_t stop,
+                                                    FrameFn fn) {
+  return GetStackTraceVisitor<FrameFn>(thread_in, start, stop, fn);
+}
+
+struct GetStackTraceVectorClosure : public art::Closure {
  public:
-  GetStackTraceClosure(size_t start, size_t stop)
+  GetStackTraceVectorClosure(size_t start, size_t stop)
       : start_input(start),
         stop_input(stop),
         start_result(0),
         stop_result(0) {}
 
   void Run(art::Thread* self) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
-    GetStackTraceVisitor visitor(self, start_input, stop_input);
-    visitor.WalkStack(false);
+    auto frames_fn = [&](jvmtiFrameInfo info) {
+      frames.push_back(info);
+    };
+    auto visitor = MakeStackTraceVisitor(self, start_input, stop_input, frames_fn);
+    visitor.WalkStack(/* include_transitions */ false);
 
-    frames.swap(visitor.frames);
     start_result = visitor.start;
     stop_result = visitor.stop;
   }
@@ -163,6 +178,33 @@
   return ERR(NONE);
 }
 
+struct GetStackTraceDirectClosure : public art::Closure {
+ public:
+  GetStackTraceDirectClosure(jvmtiFrameInfo* frame_buffer_, size_t start, size_t stop)
+      : frame_buffer(frame_buffer_),
+        start_input(start),
+        stop_input(stop),
+        index(0) {
+    DCHECK_GE(start_input, 0u);
+  }
+
+  void Run(art::Thread* self) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    auto frames_fn = [&](jvmtiFrameInfo info) {
+      frame_buffer[index] = info;
+      ++index;
+    };
+    auto visitor = MakeStackTraceVisitor(self, start_input, stop_input, frames_fn);
+    visitor.WalkStack(/* include_transitions */ false);
+  }
+
+  jvmtiFrameInfo* frame_buffer;
+
+  const size_t start_input;
+  const size_t stop_input;
+
+  size_t index = 0;
+};
+
 static jvmtiError GetThread(JNIEnv* env, jthread java_thread, art::Thread** thread) {
   if (java_thread == nullptr) {
     *thread = art::Thread::Current();
@@ -220,8 +262,20 @@
     return ERR(NONE);
   }
 
-  GetStackTraceClosure closure(start_depth >= 0 ? static_cast<size_t>(start_depth) : 0,
-                               start_depth >= 0 ? static_cast<size_t>(max_frame_count) : 0);
+  if (start_depth >= 0) {
+    // Fast path: Regular order of stack trace. Fill into the frame_buffer directly.
+    GetStackTraceDirectClosure closure(frame_buffer,
+                                       static_cast<size_t>(start_depth),
+                                       static_cast<size_t>(max_frame_count));
+    thread->RequestSynchronousCheckpoint(&closure);
+    *count_ptr = static_cast<jint>(closure.index);
+    if (closure.index < static_cast<size_t>(start_depth)) {
+      return ERR(ILLEGAL_ARGUMENT);
+    }
+    return ERR(NONE);
+  }
+
+  GetStackTraceVectorClosure closure(0, 0);
   thread->RequestSynchronousCheckpoint(&closure);
 
   return TranslateFrameVector(closure.frames,
@@ -232,42 +286,6 @@
                               count_ptr);
 }
 
-struct GetAllStackTraceClosure : public art::Closure {
- public:
-  explicit GetAllStackTraceClosure(size_t stop)
-      : start_input(0),
-        stop_input(stop),
-        frames_lock("GetAllStackTraceGuard", art::LockLevel::kAbortLock),
-        start_result(0),
-        stop_result(0) {}
-
-  void Run(art::Thread* self)
-      OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(!frames_lock) {
-    // self should be live here (so it could be suspended). No need to filter.
-
-    art::Thread* current = art::Thread::Current();
-    std::vector<jvmtiFrameInfo> self_frames;
-
-    GetStackTraceVisitor visitor(self, start_input, stop_input);
-    visitor.WalkStack(false);
-
-    self_frames.swap(visitor.frames);
-
-    art::MutexLock mu(current, frames_lock);
-    frames.emplace(self, self_frames);
-  }
-
-  const size_t start_input;
-  const size_t stop_input;
-
-  art::Mutex frames_lock;
-  std::unordered_map<art::Thread*, std::vector<jvmtiFrameInfo>> frames GUARDED_BY(frames_lock);
-  size_t start_result;
-  size_t stop_result;
-};
-
-
-
 jvmtiError StackUtil::GetAllStackTraces(jvmtiEnv* env,
                                         jint max_frame_count,
                                         jvmtiStackInfo** stack_info_ptr,
@@ -300,7 +318,7 @@
         continue;
       }
 
-      GetStackTraceClosure closure(0u, static_cast<size_t>(max_frame_count));
+      GetStackTraceVectorClosure closure(0u, static_cast<size_t>(max_frame_count));
       thread->RequestSynchronousCheckpoint(&closure);
 
       threads.push_back(thread);
@@ -460,7 +478,7 @@
         for (size_t index = 0; index != handles.size(); ++index) {
           if (peer == handles[index].Get()) {
             // Found the thread.
-            GetStackTraceClosure closure(0u, static_cast<size_t>(max_frame_count));
+            GetStackTraceVectorClosure closure(0u, static_cast<size_t>(max_frame_count));
             thread->RequestSynchronousCheckpoint(&closure);
 
             threads.push_back(thread);
diff --git a/runtime/stack.h b/runtime/stack.h
index 8c74a8c..fd86f5d 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -532,6 +532,8 @@
 
  public:
   virtual ~StackVisitor() {}
+  StackVisitor(const StackVisitor&) = default;
+  StackVisitor(StackVisitor&&) = default;
 
   // Return 'true' if we should continue to visit more frames, 'false' to stop.
   virtual bool VisitFrame() REQUIRES_SHARED(Locks::mutator_lock_) = 0;
diff --git a/test/595-profile-saving/profile-saving.cc b/test/595-profile-saving/profile-saving.cc
index 019ddad..0bdbade 100644
--- a/test/595-profile-saving/profile-saving.cc
+++ b/test/595-profile-saving/profile-saving.cc
@@ -22,63 +22,41 @@
 #include "jni.h"
 #include "method_reference.h"
 #include "mirror/class-inl.h"
+#include "mirror/executable.h"
 #include "oat_file_assistant.h"
 #include "oat_file_manager.h"
 #include "scoped_thread_state_change-inl.h"
 #include "ScopedUtfChars.h"
-#include "stack.h"
 #include "thread.h"
 
 namespace art {
 namespace {
 
-class CreateProfilingInfoVisitor : public StackVisitor {
- public:
-  explicit CreateProfilingInfoVisitor(Thread* thread, const char* method_name)
-      REQUIRES_SHARED(Locks::mutator_lock_)
-      : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
-        method_name_(method_name) {}
-
-  bool VisitFrame() REQUIRES_SHARED(Locks::mutator_lock_) {
-    ArtMethod* m = GetMethod();
-    std::string m_name(m->GetName());
-
-    if (m_name.compare(method_name_) == 0) {
-      ProfilingInfo::Create(Thread::Current(), m, /* retry_allocation */ true);
-      method_index_ = m->GetDexMethodIndex();
-      return false;
-    }
-    return true;
-  }
-
-  int method_index_ = -1;
-  const char* const method_name_;
-};
-
-extern "C" JNIEXPORT jint JNICALL Java_Main_ensureProfilingInfo(JNIEnv* env,
+extern "C" JNIEXPORT void JNICALL Java_Main_ensureProfilingInfo(JNIEnv* env,
                                                                 jclass,
-                                                                jstring method_name) {
-  ScopedUtfChars chars(env, method_name);
-  CHECK(chars.c_str() != nullptr);
-  ScopedObjectAccess soa(Thread::Current());
-  CreateProfilingInfoVisitor visitor(soa.Self(), chars.c_str());
-  visitor.WalkStack();
-  return visitor.method_index_;
+                                                                jobject method) {
+  CHECK(method != nullptr);
+  ScopedObjectAccess soa(env);
+  ObjPtr<mirror::Executable> exec = soa.Decode<mirror::Executable>(method);
+  ProfilingInfo::Create(soa.Self(), exec->GetArtMethod(), /* retry_allocation */ true);
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_ensureProfileProcessing(JNIEnv*, jclass) {
   ProfileSaver::ForceProcessProfiles();
 }
 
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_presentInProfile(
-      JNIEnv* env, jclass cls, jstring filename, jint method_index) {
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_presentInProfile(JNIEnv* env,
+                                                                 jclass,
+                                                                 jstring filename,
+                                                                 jobject method) {
   ScopedUtfChars filename_chars(env, filename);
   CHECK(filename_chars.c_str() != nullptr);
-  ScopedObjectAccess soa(Thread::Current());
-  const DexFile* dex_file = soa.Decode<mirror::Class>(cls)->GetDexCache()->GetDexFile();
+  ScopedObjectAccess soa(env);
+  ObjPtr<mirror::Executable> exec = soa.Decode<mirror::Executable>(method);
+  ArtMethod* art_method = exec->GetArtMethod();
   return ProfileSaver::HasSeenMethod(std::string(filename_chars.c_str()),
-                                     dex_file,
-                                     static_cast<uint16_t>(method_index));
+                                     art_method->GetDexFile(),
+                                     art_method->GetDexMethodIndex());
 }
 
 }  // namespace
diff --git a/test/595-profile-saving/run b/test/595-profile-saving/run
index fce6ac1..055035b 100644
--- a/test/595-profile-saving/run
+++ b/test/595-profile-saving/run
@@ -24,4 +24,5 @@
   --runtime-option '-Xcompiler-option --compiler-filter=quicken' \
   --runtime-option -Xjitsaveprofilinginfo \
   --runtime-option -Xusejit:false \
+  --runtime-option -Xps-profile-boot-class-path \
   "${@}"
diff --git a/test/595-profile-saving/src/Main.java b/test/595-profile-saving/src/Main.java
index faf94c4..18c0598 100644
--- a/test/595-profile-saving/src/Main.java
+++ b/test/595-profile-saving/src/Main.java
@@ -31,11 +31,17 @@
       VMRuntime.registerAppInfo(file.getPath(),
                                 new String[] {codePath});
 
-      int methodIdx = $opt$noinline$testProfile();
-      ensureProfileProcessing();
-      if (!presentInProfile(file.getPath(), methodIdx)) {
-        throw new RuntimeException("Method with index " + methodIdx + " not in the profile");
+      // Test that the profile saves an app method with a profiling info.
+      Method appMethod = Main.class.getDeclaredMethod("testAddMethodToProfile",
+          File.class, Method.class);
+      testAddMethodToProfile(file, appMethod);
+
+      // Test that the profile saves a boot class path method with a profiling info.
+      Method bootMethod = File.class.getDeclaredMethod("delete");
+      if (bootMethod.getDeclaringClass().getClassLoader() != Object.class.getClassLoader()) {
+        System.out.println("Class loader does not match boot class");
       }
+      testAddMethodToProfile(file, bootMethod);
     } finally {
       if (file != null) {
         file.delete();
@@ -43,20 +49,24 @@
     }
   }
 
-  public static int $opt$noinline$testProfile() {
-    if (doThrow) throw new Error();
+  static void testAddMethodToProfile(File file, Method m) {
     // Make sure we have a profile info for this method without the need to loop.
-    return ensureProfilingInfo("$opt$noinline$testProfile");
+    ensureProfilingInfo(m);
+    // Make sure the profile gets saved.
+    ensureProfileProcessing();
+    // Verify that the profile was saved and contains the method.
+    if (!presentInProfile(file.getPath(), m)) {
+      throw new RuntimeException("Method with index " + m + " not in the profile");
+    }
   }
 
-  // Return the dex method index.
-  public static native int ensureProfilingInfo(String methodName);
+  // Ensure a method has a profiling info.
+  public static native void ensureProfilingInfo(Method method);
   // Ensures the profile saver does its usual processing.
   public static native void ensureProfileProcessing();
   // Checks if the profiles saver knows about the method.
-  public static native boolean presentInProfile(String profile, int methodIdx);
+  public static native boolean presentInProfile(String profile, Method method);
 
-  public static boolean doThrow = false;
   private static final String TEMP_FILE_NAME_PREFIX = "dummy";
   private static final String TEMP_FILE_NAME_SUFFIX = "-file";
 
diff --git a/test/knownfailures.json b/test/knownfailures.json
index f515226..67ca316 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -706,13 +706,6 @@
         "description": "Test disabled due to redefine-stress disabling intrinsics which changes the trace output slightly."
     },
     {
-        "tests": "137-cfi",
-        "description": [ "ASan is reporting out-of-bounds reads in libunwind."],
-        "variant": "host",
-        "env_vars": {"SANITIZE_HOST": "address"},
-        "bug": "b/62350406"
-    },
-    {
         "tests": ["137-cfi", "629-vdex-speed"],
         "description": [ "Tests require speed compilation which is no longer the default for",
                           "no-prebuild or no-image configs."],
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index 963efa4..bf7692a 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -30,8 +30,13 @@
   out_dir=${OUT_DIR}
 fi
 
+using_jack=true
+if [[ $ANDROID_COMPILE_WITH_JACK == false ]]; then
+  using_jack=false
+fi
+
 java_libraries_dir=${out_dir}/target/common/obj/JAVA_LIBRARIES
-common_targets="vogar core-tests apache-harmony-jdwp-tests-hostdex jsr166-tests mockito-target ${out_dir}/host/linux-x86/bin/jack"
+common_targets="vogar core-tests apache-harmony-jdwp-tests-hostdex jsr166-tests mockito-target"
 mode="target"
 j_arg="-j$(nproc)"
 showcommands=
@@ -58,6 +63,10 @@
   fi
 done
 
+if $using_jack; then
+  common_targets="$common_targets ${out_dir}/host/linux-x86/bin/jack"
+fi
+
 if [[ $mode == "host" ]]; then
   make_command="make $j_arg $showcommands build-art-host-tests $common_targets"
   make_command+=" ${out_dir}/host/linux-x86/lib/libjavacoretests.so "
diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh
index d48d857..f742767 100755
--- a/tools/run-jdwp-tests.sh
+++ b/tools/run-jdwp-tests.sh
@@ -23,10 +23,24 @@
   ANDROID_HOST_OUT=${OUT_DIR-$ANDROID_BUILD_TOP/out}/host/linux-x86
 fi
 
-# Jar containing all the tests.
-test_jack=${ANDROID_HOST_OUT}/../common/obj/JAVA_LIBRARIES/apache-harmony-jdwp-tests-hostdex_intermediates/classes.jack
+using_jack=true
+if [[ $ANDROID_COMPILE_WITH_JACK == false ]]; then
+  using_jack=false
+fi
 
-if [ ! -f $test_jack ]; then
+function jlib_suffix {
+  local str=$1
+  local suffix="jar"
+  if $using_jack; then
+    suffix="jack"
+  fi
+  echo "$str.$suffix"
+}
+
+# Jar containing all the tests.
+test_jar=$(jlib_suffix "${ANDROID_HOST_OUT}/../common/obj/JAVA_LIBRARIES/apache-harmony-jdwp-tests-hostdex_intermediates/classes")
+
+if [ ! -f $test_jar ]; then
   echo "Before running, you must build jdwp tests and vogar:" \
        "make apache-harmony-jdwp-tests-hostdex vogar"
   exit 1
@@ -147,6 +161,12 @@
   art_debugee="$art_debugee -verbose:jdwp"
 fi
 
+if $using_jack; then
+  toolchain_args="--toolchain jack --language JN --jack-arg -g"
+else
+  toolchain_args="--toolchain jdk --language CUR"
+fi
+
 # Run the tests using vogar.
 vogar $vm_command \
       $vm_args \
@@ -160,10 +180,9 @@
       --vm-arg -Djpda.settings.waitingTime=$jdwp_test_timeout \
       --vm-arg -Djpda.settings.transportAddress=127.0.0.1:55107 \
       --vm-arg -Djpda.settings.debuggeeJavaPath="$art_debugee $image $debuggee_args" \
-      --classpath $test_jack \
-      --toolchain jack --language JN \
+      --classpath "$test_jar" \
+      $toolchain_args \
       --vm-arg -Xcompiler-option --vm-arg --debuggable \
-      --jack-arg -g \
       $test
 
 vogar_exit_status=$?
diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh
index b860a62..f9f3754 100755
--- a/tools/run-libcore-tests.sh
+++ b/tools/run-libcore-tests.sh
@@ -25,10 +25,26 @@
   JAVA_LIBRARIES=${ANDROID_PRODUCT_OUT}/../../common/obj/JAVA_LIBRARIES
 fi
 
+using_jack=true
+if [[ $ANDROID_COMPILE_WITH_JACK == false ]]; then
+  using_jack=false
+fi
+
+function classes_jar_path {
+  local var="$1"
+  local suffix="jar"
+
+  if $using_jack; then
+    suffix="jack"
+  fi
+
+  echo "${JAVA_LIBRARIES}/${var}_intermediates/classes.${suffix}"
+}
+
 function cparg {
   for var
   do
-    printf -- "--classpath ${JAVA_LIBRARIES}/${var}_intermediates/classes.jack ";
+    printf -- "--classpath $(classes_jar_path "$var") ";
   done
 }
 
@@ -36,7 +52,7 @@
 
 for lib in $DEPS
 do
-  if [ ! -f "${JAVA_LIBRARIES}/${lib}_intermediates/classes.jack" ]; then
+  if [[ ! -f "$(classes_jar_path "$lib")" ]]; then
     echo "${lib} is missing. Before running, you must run art/tools/buildbot-build.sh"
     exit 1
   fi
@@ -122,8 +138,12 @@
 # the default timeout.
 vogar_args="$vogar_args --timeout 480"
 
-# Use Jack with "1.8" configuration.
-vogar_args="$vogar_args --toolchain jack --language JO"
+# Switch between using jack or javac+desugar+dx
+if $using_jack; then
+  vogar_args="$vogar_args --toolchain jack --language JO"
+else
+  vogar_args="$vogar_args --toolchain jdk --language CUR"
+fi
 
 # JIT settings.
 if $use_jit; then