Merge "Add support for profiling boot class path"
am: 9533cc0a37

Change-Id: I3e44aa9aefc79084b7af226e4f5fd0bc6908ec6f
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/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index edce9cd..2cf7d9f 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);
     }
   }
@@ -469,24 +474,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/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";