diff options
| author | 2017-06-13 16:40:58 +0000 | |
|---|---|---|
| committer | 2017-06-13 16:41:00 +0000 | |
| commit | 9533cc0a3725e46c29c4e5ce7f6d62073ee03150 (patch) | |
| tree | f3bc66aaff2f38edc3673c3cfb4aba80f5e08242 | |
| parent | 16d59b2b0ff202be99cbe24830e8a5080b774357 (diff) | |
| parent | 885a7133472fd01321cbe545ace9c2acae543bf1 (diff) | |
Merge "Add support for profiling boot class path"
| -rw-r--r-- | cmdline/cmdline_parser_test.cc | 5 | ||||
| -rw-r--r-- | cmdline/cmdline_types.h | 5 | ||||
| -rw-r--r-- | runtime/jit/profile_saver.cc | 46 | ||||
| -rw-r--r-- | runtime/jit/profile_saver_options.h | 18 | ||||
| -rw-r--r-- | test/595-profile-saving/profile-saving.cc | 54 | ||||
| -rw-r--r-- | test/595-profile-saving/run | 1 | ||||
| -rw-r--r-- | test/595-profile-saving/src/Main.java | 32 |
7 files changed, 97 insertions, 64 deletions
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc index 07639e8a7d..b224ec72de 100644 --- a/cmdline/cmdline_parser_test.cc +++ b/cmdline/cmdline_parser_test.cc @@ -484,7 +484,7 @@ TEST_F(CmdlineParserTest, TestJitOptions) { * -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 @@ TEST_F(CmdlineParserTest, ProfileSaverOptions) { "-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 185a0e403e..4de8a48d45 100644 --- a/cmdline/cmdline_types.h +++ b/cmdline/cmdline_types.h @@ -712,6 +712,11 @@ struct CmdlineType<ProfileSaverOptions> : CmdlineTypeParser<ProfileSaverOptions> 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 edce9cd96c..2cf7d9f3e8 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -194,21 +194,24 @@ class GetClassesAndMethodsVisitor : public ClassVisitor { 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 @@ class GetClassesAndMethodsVisitor : public ClassVisitor { 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 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() { GetClassesAndMethodsVisitor visitor(&hot_methods, &startup_methods, &resolved_classes, - hot_threshold); + hot_threshold, + options_.GetProfileBootClassPath()); runtime->GetClassLinker()->VisitClasses(&visitor); } } @@ -469,24 +474,49 @@ void ProfileSaver::Start(const ProfileSaverOptions& options, 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 44550f4ddb..251227e89c 100644 --- a/runtime/jit/profile_saver_options.h +++ b/runtime/jit/profile_saver_options.h @@ -40,7 +40,8 @@ struct ProfileSaverOptions { 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 @@ struct ProfileSaverOptions { 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 @@ struct ProfileSaverOptions { 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 @@ struct ProfileSaverOptions { 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 @@ struct ProfileSaverOptions { << ", 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 @@ struct ProfileSaverOptions { 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 019ddad595..0bdbadef48 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 fce6ac15d8..055035b3e0 100644 --- a/test/595-profile-saving/run +++ b/test/595-profile-saving/run @@ -24,4 +24,5 @@ exec ${RUN} \ --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 faf94c4fcc..18c0598bef 100644 --- a/test/595-profile-saving/src/Main.java +++ b/test/595-profile-saving/src/Main.java @@ -31,11 +31,17 @@ public class Main { 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 class Main { } } - 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"; |