diff options
| author | 2017-07-13 07:59:39 +0000 | |
|---|---|---|
| committer | 2017-07-13 07:59:39 +0000 | |
| commit | e63a91111d13f33028c2988ded53a4659140ca2e (patch) | |
| tree | e575d817d4cdc79626d4c70b87d1e09b02a78d81 | |
| parent | 9f3a7c5e59a0a0caec950970fe5c636973d0421c (diff) | |
| parent | a867f7ab437dd6c1458088964986d0fe7275625c (diff) | |
Merge "Revert "Record post startup methods in profile""
| -rw-r--r-- | runtime/class_linker.h | 7 | ||||
| -rw-r--r-- | runtime/jit/profile_saver.cc | 254 | ||||
| -rw-r--r-- | runtime/jit/profile_saver.h | 9 | ||||
| -rw-r--r-- | test/595-profile-saving/profile-saving.cc | 8 | ||||
| -rw-r--r-- | test/595-profile-saving/src/Main.java | 31 |
5 files changed, 103 insertions, 206 deletions
diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 00ae7582c3..de1fefd20e 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -671,10 +671,6 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); - // Visit all of the class loaders in the class linker. - void VisitClassLoaders(ClassLoaderVisitor* visitor) const - REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_); - struct DexCacheData { // Construct an invalid data object. DexCacheData() @@ -724,6 +720,9 @@ class ClassLinker { static void DeleteClassLoader(Thread* self, const ClassLoaderData& data) REQUIRES_SHARED(Locks::mutator_lock_); + void VisitClassLoaders(ClassLoaderVisitor* visitor) const + REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_); + void VisitClassesInternal(ClassVisitor* visitor) REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_); diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index 556fe666e9..10dddaefc8 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -29,7 +29,6 @@ #include "base/stl_util.h" #include "base/systrace.h" #include "base/time_utils.h" -#include "class_table-inl.h" #include "compiler_filter.h" #include "dex_reference_collection.h" #include "gc/collector_type.h" @@ -122,7 +121,7 @@ void ProfileSaver::Run() { } total_ms_of_sleep_ += options_.GetSaveResolvedClassesDelayMs(); } - FetchAndCacheResolvedClassesAndMethods(/*startup*/ true); + FetchAndCacheResolvedClassesAndMethods(); // Loop for the profiled methods. while (!ShuttingDown(self)) { @@ -211,49 +210,24 @@ void ProfileSaver::NotifyJitActivityInternal() { } } -class ScopedDefaultPriority { - public: - explicit ScopedDefaultPriority(pthread_t thread) : thread_(thread) { - SetProfileSaverThreadPriority(thread_, GetDefaultThreadPriority()); - } - - ~ScopedDefaultPriority() { - SetProfileSaverThreadPriority(thread_, kProfileSaverPthreadPriority); - } - - private: - const pthread_t thread_; -}; - -// GetClassLoadersVisitor takes a snapshot of the class loaders and stores them in the out -// class_loaders argument. Not affected by class unloading since there are no suspend points in -// the caller. -class GetClassLoadersVisitor : public ClassLoaderVisitor { - public: - explicit GetClassLoadersVisitor(VariableSizedHandleScope* hs, - std::vector<Handle<mirror::ClassLoader>>* class_loaders) - : hs_(hs), - class_loaders_(class_loaders) {} - - void Visit(ObjPtr<mirror::ClassLoader> class_loader) - REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) OVERRIDE { - class_loaders_->push_back(hs_->NewHandle(class_loader)); - } - - private: - VariableSizedHandleScope* const hs_; - std::vector<Handle<mirror::ClassLoader>>* const class_loaders_; -}; +using MethodReferenceCollection = DexReferenceCollection<uint16_t, ScopedArenaAllocatorAdapter>; +using TypeReferenceCollection = DexReferenceCollection<dex::TypeIndex, + ScopedArenaAllocatorAdapter>; -// GetClassesVisitor takes a snapshot of the loaded classes that we may want to visit and stores -// them in the out argument. Not affected by class unloading since there are no suspend points in -// the caller. -class GetClassesVisitor : public ClassVisitor { +// Get resolved methods that have a profile info or more than kStartupMethodSamples samples. +// Excludes native methods and classes in the boot image. +class GetClassesAndMethodsVisitor : public ClassVisitor { public: - explicit GetClassesVisitor(bool profile_boot_class_path, - ScopedArenaVector<ObjPtr<mirror::Class>>* out) - : profile_boot_class_path_(profile_boot_class_path), - out_(out) {} + GetClassesAndMethodsVisitor(MethodReferenceCollection* hot_methods, + MethodReferenceCollection* sampled_methods, + TypeReferenceCollection* resolved_classes, + 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), + profile_boot_class_path_(profile_boot_class_path) {} virtual bool operator()(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) { if (klass->IsProxyClass() || @@ -264,101 +238,51 @@ class GetClassesVisitor : public ClassVisitor { (!profile_boot_class_path_ && klass->GetClassLoader() == nullptr)) { return true; } - out_->push_back(klass); + CHECK(klass->GetDexCache() != nullptr) << klass->PrettyClass(); + resolved_classes_->AddReference(&klass->GetDexFile(), klass->GetDexTypeIndex()); + for (ArtMethod& method : klass->GetMethods(kRuntimePointerSize)) { + if (!method.IsNative()) { + DCHECK(!method.IsProxyMethod()); + const uint16_t counter = method.GetCounter(); + // Mark startup methods as hot if they have more than hot_method_sample_threshold_ samples. + // This means they will get compiled by the compiler driver. + if (method.GetProfilingInfo(kRuntimePointerSize) != nullptr || + (method.GetAccessFlags() & kAccPreviouslyWarm) != 0 || + counter >= hot_method_sample_threshold_) { + hot_methods_->AddReference(method.GetDexFile(), method.GetDexMethodIndex()); + } else if (counter != 0) { + sampled_methods_->AddReference(method.GetDexFile(), method.GetDexMethodIndex()); + } + } else { + CHECK_EQ(method.GetCounter(), 0u); + } + } return true; } private: + MethodReferenceCollection* const hot_methods_; + MethodReferenceCollection* const sampled_methods_; + TypeReferenceCollection* const resolved_classes_; + uint32_t hot_method_sample_threshold_; const bool profile_boot_class_path_; - ScopedArenaVector<ObjPtr<mirror::Class>>* const out_; }; -using MethodReferenceCollection = DexReferenceCollection<uint16_t, ScopedArenaAllocatorAdapter>; -using TypeReferenceCollection = DexReferenceCollection<dex::TypeIndex, - ScopedArenaAllocatorAdapter>; +class ScopedDefaultPriority { + public: + explicit ScopedDefaultPriority(pthread_t thread) : thread_(thread) { + SetProfileSaverThreadPriority(thread_, GetDefaultThreadPriority()); + } -// Iterate over all of the loaded classes and visit each one. For each class, add it to the -// resolved_classes out argument if startup is true. -// Add methods to the hot_methods out argument if the number of samples is greater or equal to -// hot_method_sample_threshold, add it to sampled_methods if it has at least one sample. -static void SampleClassesAndExecutedMethods(pthread_t profiler_pthread, - bool profile_boot_class_path, - ScopedArenaAllocator* allocator, - uint32_t hot_method_sample_threshold, - bool startup, - TypeReferenceCollection* resolved_classes, - MethodReferenceCollection* hot_methods, - MethodReferenceCollection* sampled_methods) { - Thread* const self = Thread::Current(); - ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); - // Restore profile saver thread priority during the GC critical section. This helps prevent - // priority inversions blocking the GC for long periods of time. - ScopedDefaultPriority sdp(profiler_pthread); - // Do ScopedGCCriticalSection before acquiring mutator lock to prevent the GC running and - // blocking threads during thread root flipping. Since the GC is a background thread, blocking it - // is not a problem. - ScopedObjectAccess soa(self); - gc::ScopedGCCriticalSection sgcs(self, - gc::kGcCauseProfileSaver, - gc::kCollectorTypeCriticalSection); - VariableSizedHandleScope hs(soa.Self()); - std::vector<Handle<mirror::ClassLoader>> class_loaders; - if (profile_boot_class_path) { - // First add the boot class loader since visit classloaders doesn't visit it. - class_loaders.push_back(hs.NewHandle<mirror::ClassLoader>(nullptr)); - } - GetClassLoadersVisitor class_loader_visitor(&hs, &class_loaders); - { - // Read the class loaders into a temporary array to prevent contention problems on the - // class_linker_classes_lock. - ScopedTrace trace2("Get class loaders"); - ReaderMutexLock mu(soa.Self(), *Locks::classlinker_classes_lock_); - class_linker->VisitClassLoaders(&class_loader_visitor); - } - ScopedArenaVector<ObjPtr<mirror::Class>> classes(allocator->Adapter()); - for (Handle<mirror::ClassLoader> class_loader : class_loaders) { - ClassTable* table = class_linker->ClassTableForClassLoader(class_loader.Get()); - if (table == nullptr) { - // If the class loader has not loaded any classes, it may have a null table. - continue; - } - GetClassesVisitor get_classes_visitor(profile_boot_class_path, &classes); - { - // Collect the classes into a temporary array to prevent lock contention on the class - // table lock. We want to avoid blocking class loading in other threads as much as - // possible. - ScopedTrace trace3("Visiting class table"); - table->Visit(get_classes_visitor); - } - for (ObjPtr<mirror::Class> klass : classes) { - if (startup) { - // We only record classes for the startup case. This may change in the future. - resolved_classes->AddReference(&klass->GetDexFile(), klass->GetDexTypeIndex()); - } - // Visit all of the methods in the class to see which ones were executed. - for (ArtMethod& method : klass->GetMethods(kRuntimePointerSize)) { - if (!method.IsNative()) { - DCHECK(!method.IsProxyMethod()); - const uint16_t counter = method.GetCounter(); - // Mark startup methods as hot if they have more than hot_method_sample_threshold - // samples. This means they will get compiled by the compiler driver. - if (method.GetProfilingInfo(kRuntimePointerSize) != nullptr || - (method.GetAccessFlags() & kAccPreviouslyWarm) != 0 || - counter >= hot_method_sample_threshold) { - hot_methods->AddReference(method.GetDexFile(), method.GetDexMethodIndex()); - } else if (counter != 0) { - sampled_methods->AddReference(method.GetDexFile(), method.GetDexMethodIndex()); - } - } else { - CHECK_EQ(method.GetCounter(), 0u); - } - } - } - classes.clear(); + ~ScopedDefaultPriority() { + SetProfileSaverThreadPriority(thread_, kProfileSaverPthreadPriority); } -} -void ProfileSaver::FetchAndCacheResolvedClassesAndMethods(bool startup) { + private: + const pthread_t thread_; +}; + +void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() { ScopedTrace trace(__PRETTY_FUNCTION__); const uint64_t start_time = NanoTime(); @@ -370,25 +294,34 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods(bool startup) { ArenaStack stack(runtime->GetArenaPool()); ScopedArenaAllocator allocator(&stack); MethodReferenceCollection hot_methods(allocator.Adapter(), allocator.Adapter()); - MethodReferenceCollection sampled_methods(allocator.Adapter(), allocator.Adapter()); + MethodReferenceCollection startup_methods(allocator.Adapter(), allocator.Adapter()); TypeReferenceCollection resolved_classes(allocator.Adapter(), allocator.Adapter()); const bool is_low_ram = Runtime::Current()->GetHeap()->IsLowMemoryMode(); + const size_t hot_threshold = options_.GetHotStartupMethodSamples(is_low_ram); pthread_t profiler_pthread; { MutexLock mu(self, *Locks::profiler_lock_); profiler_pthread = profiler_pthread_; } - const uint32_t hot_method_sample_threshold = startup ? - options_.GetHotStartupMethodSamples(is_low_ram) : - std::numeric_limits<uint32_t>::max(); - SampleClassesAndExecutedMethods(profiler_pthread, - options_.GetProfileBootClassPath(), - &allocator, - hot_method_sample_threshold, - startup, - &resolved_classes, - &hot_methods, - &sampled_methods); + { + // Restore profile saver thread priority during the GC critical section. This helps prevent + // priority inversions blocking the GC for long periods of time. + ScopedDefaultPriority sdp(profiler_pthread); + ScopedObjectAccess soa(self); + gc::ScopedGCCriticalSection sgcs(self, + gc::kGcCauseProfileSaver, + gc::kCollectorTypeCriticalSection); + { + ScopedTrace trace2("Get hot methods"); + GetClassesAndMethodsVisitor visitor(&hot_methods, + &startup_methods, + &resolved_classes, + hot_threshold, + options_.GetProfileBootClassPath()); + runtime->GetClassLinker()->VisitClasses(&visitor); + } + } + MutexLock mu(self, *Locks::profiler_lock_); uint64_t total_number_of_profile_entries_cached = 0; using Hotness = ProfileCompilationInfo::MethodHotness; @@ -396,12 +329,9 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods(bool startup) { for (const auto& it : tracked_dex_base_locations_) { std::set<DexCacheResolvedClasses> resolved_classes_for_location; const std::string& filename = it.first; - auto info_it = profile_cache_.find(filename); - if (info_it == profile_cache_.end()) { - info_it = profile_cache_.Put( - filename, - new ProfileCompilationInfo(Runtime::Current()->GetArenaPool())); - } + auto info_it = profile_cache_.Put( + filename, + new ProfileCompilationInfo(Runtime::Current()->GetArenaPool())); ProfileCompilationInfo* cached_info = info_it->second; const std::set<std::string>& locations = it.second; @@ -409,20 +339,18 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods(bool startup) { const DexFile* const dex_file = pair.first; if (locations.find(dex_file->GetBaseLocation()) != locations.end()) { const MethodReferenceCollection::IndexVector& indices = pair.second; - uint8_t flags = Hotness::kFlagHot; - flags |= startup ? Hotness::kFlagStartup : Hotness::kFlagPostStartup; cached_info->AddMethodsForDex( - static_cast<Hotness::Flag>(flags), + static_cast<Hotness::Flag>(Hotness::kFlagHot | Hotness::kFlagStartup), dex_file, indices.begin(), indices.end()); } } - for (const auto& pair : sampled_methods.GetMap()) { + for (const auto& pair : startup_methods.GetMap()) { const DexFile* const dex_file = pair.first; if (locations.find(dex_file->GetBaseLocation()) != locations.end()) { const MethodReferenceCollection::IndexVector& indices = pair.second; - cached_info->AddMethodsForDex(startup ? Hotness::kFlagStartup : Hotness::kFlagPostStartup, + cached_info->AddMethodsForDex(Hotness::kFlagStartup, dex_file, indices.begin(), indices.end()); @@ -447,9 +375,8 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods(bool startup) { max_number_of_profile_entries_cached_, total_number_of_profile_entries_cached); VLOG(profiler) << "Profile saver recorded " << hot_methods.NumReferences() << " hot methods and " - << sampled_methods.NumReferences() << " sampled methods with threshold " - << hot_method_sample_threshold << " in " - << PrettyDuration(NanoTime() - start_time); + << startup_methods.NumReferences() << " startup methods with threshold " + << hot_threshold << " in " << PrettyDuration(NanoTime() - start_time); } bool ProfileSaver::ProcessProfilingInfo(bool force_save, /*out*/uint16_t* number_of_new_methods) { @@ -470,10 +397,6 @@ bool ProfileSaver::ProcessProfilingInfo(bool force_save, /*out*/uint16_t* number *number_of_new_methods = 0; } - // We only need to do this once, not once per dex location. - // TODO: Figure out a way to only do it when stuff has changed? It takes 30-50ms. - FetchAndCacheResolvedClassesAndMethods(/*startup*/ false); - for (const auto& it : tracked_locations) { if (!force_save && ShuttingDown(Thread::Current())) { // The ProfileSaver is in shutdown mode, meaning a stop request was made and @@ -519,7 +442,6 @@ bool ProfileSaver::ProcessProfilingInfo(bool force_save, /*out*/uint16_t* number total_number_of_skipped_writes_++; continue; } - if (number_of_new_methods != nullptr) { *number_of_new_methods = std::max(static_cast<uint16_t>(delta_number_of_methods), @@ -551,12 +473,11 @@ bool ProfileSaver::ProcessProfilingInfo(bool force_save, /*out*/uint16_t* number total_number_of_failed_writes_++; } } + // Trim the maps to madvise the pages used for profile info. + // It is unlikely we will need them again in the near feature. + Runtime::Current()->GetArenaPool()->TrimMaps(); } - // Trim the maps to madvise the pages used for profile info. - // It is unlikely we will need them again in the near feature. - Runtime::Current()->GetArenaPool()->TrimMaps(); - return profile_file_saved; } @@ -792,15 +713,16 @@ void ProfileSaver::ForceProcessProfiles() { } } -bool ProfileSaver::HasSeenMethod(const std::string& profile, bool hot, MethodReference ref) { +bool ProfileSaver::HasSeenMethod(const std::string& profile, + const DexFile* dex_file, + uint16_t method_idx) { MutexLock mu(Thread::Current(), *Locks::profiler_lock_); if (instance_ != nullptr) { ProfileCompilationInfo info(Runtime::Current()->GetArenaPool()); if (!info.Load(profile, /*clear_if_invalid*/false)) { return false; } - ProfileCompilationInfo::MethodHotness hotness = info.GetMethodHotness(ref); - return hot ? hotness.IsHot() : hotness.IsInProfile(); + return info.GetMethodHotness(MethodReference(dex_file, method_idx)).IsInProfile(); } return false; } diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h index ce8233bbea..01d72fec3d 100644 --- a/runtime/jit/profile_saver.h +++ b/runtime/jit/profile_saver.h @@ -19,7 +19,6 @@ #include "base/mutex.h" #include "jit_code_cache.h" -#include "method_reference.h" #include "profile_compilation_info.h" #include "profile_saver_options.h" #include "safe_map.h" @@ -56,8 +55,10 @@ class ProfileSaver { // For testing or manual purposes (SIGUSR1). static void ForceProcessProfiles(); - // Just for testing purposes. - static bool HasSeenMethod(const std::string& profile, bool hot, MethodReference ref); + // Just for testing purpose. + static bool HasSeenMethod(const std::string& profile, + const DexFile* dex_file, + uint16_t method_idx); private: ProfileSaver(const ProfileSaverOptions& options, @@ -96,7 +97,7 @@ class ProfileSaver { // Fetches the current resolved classes and methods from the ClassLinker and stores them in the // profile_cache_ for later save. - void FetchAndCacheResolvedClassesAndMethods(bool startup); + void FetchAndCacheResolvedClassesAndMethods(); void DumpInfo(std::ostream& os); diff --git a/test/595-profile-saving/profile-saving.cc b/test/595-profile-saving/profile-saving.cc index b2b7ef2a00..0bdbadef48 100644 --- a/test/595-profile-saving/profile-saving.cc +++ b/test/595-profile-saving/profile-saving.cc @@ -45,9 +45,8 @@ extern "C" JNIEXPORT void JNICALL Java_Main_ensureProfileProcessing(JNIEnv*, jcl ProfileSaver::ForceProcessProfiles(); } -extern "C" JNIEXPORT jboolean JNICALL Java_Main_profileHasMethod(JNIEnv* env, +extern "C" JNIEXPORT jboolean JNICALL Java_Main_presentInProfile(JNIEnv* env, jclass, - jboolean hot, jstring filename, jobject method) { ScopedUtfChars filename_chars(env, filename); @@ -56,9 +55,8 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_profileHasMethod(JNIEnv* env, ObjPtr<mirror::Executable> exec = soa.Decode<mirror::Executable>(method); ArtMethod* art_method = exec->GetArtMethod(); return ProfileSaver::HasSeenMethod(std::string(filename_chars.c_str()), - hot != JNI_FALSE, - MethodReference(art_method->GetDexFile(), - art_method->GetDexMethodIndex())); + art_method->GetDexFile(), + art_method->GetDexMethodIndex()); } } // namespace diff --git a/test/595-profile-saving/src/Main.java b/test/595-profile-saving/src/Main.java index 197c4e74ff..18c0598bef 100644 --- a/test/595-profile-saving/src/Main.java +++ b/test/595-profile-saving/src/Main.java @@ -42,14 +42,6 @@ public class Main { System.out.println("Class loader does not match boot class"); } testAddMethodToProfile(file, bootMethod); - - // Test a sampled method that is only warm and not hot. - Method reflectMethod = Main.class.getDeclaredMethod("testReflectionInvoke"); - reflectMethod.invoke(null); - testSampledMethodInProfile(file, reflectMethod); - if (staticObj == null) { - throw new AssertionError("Object was not set"); - } } finally { if (file != null) { file.delete(); @@ -57,38 +49,23 @@ public class Main { } } - static Object staticObj = null; - - static void testReflectionInvoke() { - staticObj = new Object(); - } - static void testAddMethodToProfile(File file, Method m) { // Make sure we have a profile info for this method without the need to loop. ensureProfilingInfo(m); - // Make sure the profile gets processed. + // Make sure the profile gets saved. ensureProfileProcessing(); // Verify that the profile was saved and contains the method. - if (!profileHasMethod(true, file.getPath(), m)) { + if (!presentInProfile(file.getPath(), m)) { throw new RuntimeException("Method with index " + m + " not in the profile"); } } - static void testSampledMethodInProfile(File file, Method m) { - // Make sure the profile gets processed. - ensureProfileProcessing(); - // Verify that the profile was saved and contains the method. - if (!profileHasMethod(false, file.getPath(), m)) { - throw new RuntimeException("Method with index " + m + " not sampled in the profile"); - } - } - // 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 profile saver has the method as a warm/sampled method. - public static native boolean profileHasMethod(boolean hot, String profile, Method method); + // Checks if the profiles saver knows about the method. + public static native boolean presentInProfile(String profile, Method method); private static final String TEMP_FILE_NAME_PREFIX = "dummy"; private static final String TEMP_FILE_NAME_SUFFIX = "-file"; |