diff options
| -rw-r--r-- | profman/profile_assistant.cc | 2 | ||||
| -rw-r--r-- | profman/profile_assistant_test.cc | 10 | ||||
| -rw-r--r-- | runtime/jit/offline_profiling_info.cc | 99 | ||||
| -rw-r--r-- | runtime/jit/offline_profiling_info.h | 18 | ||||
| -rw-r--r-- | runtime/jit/profile_compilation_info_test.cc | 26 | ||||
| -rw-r--r-- | runtime/jit/profile_saver.cc | 175 | ||||
| -rw-r--r-- | runtime/jit/profile_saver.h | 24 |
7 files changed, 237 insertions, 117 deletions
diff --git a/profman/profile_assistant.cc b/profman/profile_assistant.cc index 58e8a3a7d1..ac1865785e 100644 --- a/profman/profile_assistant.cc +++ b/profman/profile_assistant.cc @@ -54,7 +54,7 @@ ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfilesInternal( for (size_t i = 0; i < new_info.size(); i++) { // Merge all data into a single object. - if (!info.Load(new_info[i])) { + if (!info.MergeWith(new_info[i])) { LOG(WARNING) << "Could not merge profile data at index " << i; return kErrorBadProfiles; } diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc index b0d5df2b3b..157ffc4c71 100644 --- a/profman/profile_assistant_test.cc +++ b/profman/profile_assistant_test.cc @@ -102,8 +102,8 @@ TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) { ASSERT_TRUE(result.Load(reference_profile_fd)); ProfileCompilationInfo expected; - ASSERT_TRUE(expected.Load(info1)); - ASSERT_TRUE(expected.Load(info2)); + ASSERT_TRUE(expected.MergeWith(info1)); + ASSERT_TRUE(expected.MergeWith(info2)); ASSERT_TRUE(expected.Equals(result)); // The information from profiles must remain the same. @@ -145,9 +145,9 @@ TEST_F(ProfileAssistantTest, AdviseCompilationNonEmptyReferences) { ASSERT_TRUE(result.Load(reference_profile_fd)); ProfileCompilationInfo expected; - ASSERT_TRUE(expected.Load(info1)); - ASSERT_TRUE(expected.Load(info2)); - ASSERT_TRUE(expected.Load(reference_info)); + ASSERT_TRUE(expected.MergeWith(info1)); + ASSERT_TRUE(expected.MergeWith(info2)); + ASSERT_TRUE(expected.MergeWith(reference_info)); ASSERT_TRUE(expected.Equals(result)); // The information from profiles must remain the same. diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc index 2d4777bcc5..81954f0020 100644 --- a/runtime/jit/offline_profiling_info.cc +++ b/runtime/jit/offline_profiling_info.cc @@ -49,19 +49,27 @@ std::string ProfileCompilationInfo::GetProfileDexFileKey(const std::string& dex_ } } -bool ProfileCompilationInfo::SaveProfilingInfo( - const std::string& filename, +bool ProfileCompilationInfo::AddMethodsAndClasses( const std::vector<ArtMethod*>& methods, - const std::set<DexCacheResolvedClasses>& resolved_classes, - uint64_t* bytes_written) { - if (methods.empty() && resolved_classes.empty()) { - VLOG(profiler) << "No info to save to " << filename; - if (bytes_written != nullptr) { - *bytes_written = 0; + const std::set<DexCacheResolvedClasses>& resolved_classes) { + ScopedObjectAccess soa(Thread::Current()); + for (ArtMethod* method : methods) { + const DexFile* dex_file = method->GetDexFile(); + if (!AddMethodIndex(GetProfileDexFileKey(dex_file->GetLocation()), + dex_file->GetLocationChecksum(), + method->GetDexMethodIndex())) { + return false; } - return true; } + for (const DexCacheResolvedClasses& dex_cache : resolved_classes) { + if (!AddResolvedClasses(dex_cache)) { + return false; + } + } + return true; +} +bool ProfileCompilationInfo::MergeAndSave(const std::string& filename, uint64_t* bytes_written) { ScopedTrace trace(__PRETTY_FUNCTION__); ScopedFlock flock; std::string error; @@ -72,26 +80,28 @@ bool ProfileCompilationInfo::SaveProfilingInfo( int fd = flock.GetFile()->Fd(); - ProfileCompilationInfo info; - if (!info.Load(fd)) { + // Load the file but keep a copy around to be able to infer if the content has changed. + ProfileCompilationInfo fileInfo; + if (!fileInfo.Load(fd)) { LOG(WARNING) << "Could not load previous profile data from file " << filename; return false; } - { - ScopedObjectAccess soa(Thread::Current()); - for (ArtMethod* method : methods) { - const DexFile* dex_file = method->GetDexFile(); - if (!info.AddMethodIndex(GetProfileDexFileKey(dex_file->GetLocation()), - dex_file->GetLocationChecksum(), - method->GetDexMethodIndex())) { - return false; - } - } - for (const DexCacheResolvedClasses& dex_cache : resolved_classes) { - info.AddResolvedClasses(dex_cache); + + // Merge the content of file into the current object. + if (!MergeWith(fileInfo)) { + LOG(WARNING) << "Could not merge previous profile data from file " << filename; + } + + // If after the merge we have the same data as what is the file there's no point + // in actually doing the write. The file will be exactly the same as before. + if (Equals(fileInfo)) { + if (bytes_written != nullptr) { + *bytes_written = 0; } + return true; } + // We need to clear the data because we don't support append to the profiles yet. if (!flock.GetFile()->ClearContent()) { PLOG(WARNING) << "Could not clear profile file: " << filename; return false; @@ -99,7 +109,7 @@ bool ProfileCompilationInfo::SaveProfilingInfo( // This doesn't need locking because we are trying to lock the file for exclusive // access and fail immediately if we can't. - bool result = info.Save(fd); + bool result = Save(fd); if (result) { VLOG(profiler) << "Successfully saved profile info to " << filename << " Size: " << GetFileSizeBytes(filename); @@ -112,6 +122,31 @@ bool ProfileCompilationInfo::SaveProfilingInfo( return result; } +bool ProfileCompilationInfo::SaveProfilingInfo( + const std::string& filename, + const std::vector<ArtMethod*>& methods, + const std::set<DexCacheResolvedClasses>& resolved_classes, + uint64_t* bytes_written) { + if (methods.empty() && resolved_classes.empty()) { + VLOG(profiler) << "No info to save to " << filename; + if (bytes_written != nullptr) { + *bytes_written = 0; + } + return true; + } + + ProfileCompilationInfo info; + if (!info.AddMethodsAndClasses(methods, resolved_classes)) { + LOG(WARNING) << "Checksum mismatch when processing methods and resolved classes for " + << filename; + if (bytes_written != nullptr) { + *bytes_written = 0; + } + return false; + } + return info.MergeAndSave(filename, bytes_written); +} + static bool WriteToFile(int fd, const std::ostringstream& os) { std::string data(os.str()); const char *p = data.c_str(); @@ -341,7 +376,7 @@ bool ProfileCompilationInfo::Load(int fd) { return true; } -bool ProfileCompilationInfo::Load(const ProfileCompilationInfo& other) { +bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other) { for (const auto& other_it : other.info_) { const std::string& other_dex_location = other_it.first; const DexFileData& other_dex_data = other_it.second; @@ -382,6 +417,14 @@ uint32_t ProfileCompilationInfo::GetNumberOfMethods() const { return total; } +uint32_t ProfileCompilationInfo::GetNumberOfResolvedClasses() const { + uint32_t total = 0; + for (const auto& it : info_) { + total += it.second.class_set.size(); + } + return total; +} + std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* dex_files, bool print_full_dex_location) const { std::ostringstream os; @@ -437,4 +480,10 @@ std::set<DexCacheResolvedClasses> ProfileCompilationInfo::GetResolvedClasses() c return ret; } +void ProfileCompilationInfo::ClearResolvedClasses() { + for (auto& pair : info_) { + pair.second.class_set.clear(); + } +} + } // namespace art diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h index 52cab92d10..14c7915afb 100644 --- a/runtime/jit/offline_profiling_info.h +++ b/runtime/jit/offline_profiling_info.h @@ -49,14 +49,23 @@ class ProfileCompilationInfo { const std::set<DexCacheResolvedClasses>& resolved_classes, uint64_t* bytes_written = nullptr); + // Add the given methods and classes to the current profile object. + bool AddMethodsAndClasses(const std::vector<ArtMethod*>& methods, + const std::set<DexCacheResolvedClasses>& resolved_classes); // Loads profile information from the given file descriptor. bool Load(int fd); - // Loads the data from another ProfileCompilationInfo object. - bool Load(const ProfileCompilationInfo& info); + // Merge the data from another ProfileCompilationInfo into the current object. + bool MergeWith(const ProfileCompilationInfo& info); // Saves the profile data to the given file descriptor. bool Save(int fd); + // Loads and merges profile information from the given file into the current + // object and tries to save it back to disk. + bool MergeAndSave(const std::string& filename, uint64_t* bytes_written); + // Returns the number of methods that were profiled. uint32_t GetNumberOfMethods() const; + // Returns the number of resolved classes that were profiled. + uint32_t GetNumberOfResolvedClasses() const; // Returns true if the method reference is present in the profiling info. bool ContainsMethod(const MethodReference& method_ref) const; @@ -68,8 +77,8 @@ class ProfileCompilationInfo { std::string DumpInfo(const std::vector<const DexFile*>* dex_files, bool print_full_dex_location = true) const; - // For testing purposes. bool Equals(const ProfileCompilationInfo& other); + static std::string GetProfileDexFileKey(const std::string& dex_location); // Returns the class descriptors for all of the classes in the profiles' class sets. @@ -77,6 +86,9 @@ class ProfileCompilationInfo { // profile info stuff to generate a map back to the dex location. std::set<DexCacheResolvedClasses> GetResolvedClasses() const; + // Clears the resolved classes from the current object. + void ClearResolvedClasses(); + private: struct DexFileData { explicit DexFileData(uint32_t location_checksum) : checksum(location_checksum) {} diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc index fdd8c6e600..54fd69f149 100644 --- a/runtime/jit/profile_compilation_info_test.cc +++ b/runtime/jit/profile_compilation_info_test.cc @@ -49,7 +49,7 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { return methods; } - bool AddData(const std::string& dex_location, + bool AddMethod(const std::string& dex_location, uint32_t checksum, uint16_t method_index, ProfileCompilationInfo* info) { @@ -118,8 +118,8 @@ TEST_F(ProfileCompilationInfoTest, SaveFd) { ProfileCompilationInfo saved_info; // Save a few methods. for (uint16_t i = 0; i < 10; i++) { - ASSERT_TRUE(AddData("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info)); - ASSERT_TRUE(AddData("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info)); + ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info)); + ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info)); } ASSERT_TRUE(saved_info.Save(GetFd(profile))); ASSERT_EQ(0, profile.GetFile()->Flush()); @@ -132,9 +132,9 @@ TEST_F(ProfileCompilationInfoTest, SaveFd) { // Save more methods. for (uint16_t i = 0; i < 100; i++) { - ASSERT_TRUE(AddData("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info)); - ASSERT_TRUE(AddData("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info)); - ASSERT_TRUE(AddData("dex_location3", /* checksum */ 3, /* method_idx */ i, &saved_info)); + ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info)); + ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info)); + ASSERT_TRUE(AddMethod("dex_location3", /* checksum */ 3, /* method_idx */ i, &saved_info)); } ASSERT_TRUE(profile.GetFile()->ResetOffset()); ASSERT_TRUE(saved_info.Save(GetFd(profile))); @@ -147,25 +147,25 @@ TEST_F(ProfileCompilationInfoTest, SaveFd) { ASSERT_TRUE(loaded_info2.Equals(saved_info)); } -TEST_F(ProfileCompilationInfoTest, AddDataFail) { +TEST_F(ProfileCompilationInfoTest, AddMethodsAndClassesFail) { ScratchFile profile; ProfileCompilationInfo info; - ASSERT_TRUE(AddData("dex_location", /* checksum */ 1, /* method_idx */ 1, &info)); + ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 1, /* method_idx */ 1, &info)); // Trying to add info for an existing file but with a different checksum. - ASSERT_FALSE(AddData("dex_location", /* checksum */ 2, /* method_idx */ 2, &info)); + ASSERT_FALSE(AddMethod("dex_location", /* checksum */ 2, /* method_idx */ 2, &info)); } -TEST_F(ProfileCompilationInfoTest, LoadFail) { +TEST_F(ProfileCompilationInfoTest, MergeFail) { ScratchFile profile; ProfileCompilationInfo info1; - ASSERT_TRUE(AddData("dex_location", /* checksum */ 1, /* method_idx */ 1, &info1)); + ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 1, /* method_idx */ 1, &info1)); // Use the same file, change the checksum. ProfileCompilationInfo info2; - ASSERT_TRUE(AddData("dex_location", /* checksum */ 2, /* method_idx */ 2, &info2)); + ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 2, /* method_idx */ 2, &info2)); - ASSERT_FALSE(info1.Load(info2)); + ASSERT_FALSE(info1.MergeWith(info2)); } } // namespace art diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index 3d3f3dd0bf..81d81a5c74 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -29,18 +29,18 @@ namespace art { // An arbitrary value to throttle save requests. Set to 2s for now. static constexpr const uint64_t kMilisecondsToNano = 1000000; -static constexpr const uint64_t kMinimumTimeBetweenCodeCacheUpdatesNs = 2000 * kMilisecondsToNano; // TODO: read the constants from ProfileOptions, // Add a random delay each time we go to sleep so that we don't hammer the CPU // with all profile savers running at the same time. -static constexpr const uint64_t kRandomDelayMaxMs = 40 * 1000; // 40 seconds +static constexpr const uint64_t kRandomDelayMaxMs = 30 * 1000; // 30 seconds static constexpr const uint64_t kMaxBackoffMs = 5 * 60 * 1000; // 5 minutes -static constexpr const uint64_t kSavePeriodMs = 40 * 1000; // 40 seconds +static constexpr const uint64_t kSavePeriodMs = 20 * 1000; // 20 seconds static constexpr const uint64_t kSaveResolvedClassesDelayMs = 2 * 1000; // 2 seconds static constexpr const double kBackoffCoef = 1.5; -static constexpr const uint32_t kMinimumNrOrMethodsToSave = 10; +static constexpr const uint32_t kMinimumNumberOfMethodsToSave = 10; +static constexpr const uint32_t kMinimumNumberOfClassesToSave = 10; ProfileSaver* ProfileSaver::instance_ = nullptr; pthread_t ProfileSaver::profiler_pthread_ = 0U; @@ -52,8 +52,9 @@ ProfileSaver::ProfileSaver(const std::string& output_filename, const std::string& app_data_dir) : jit_code_cache_(jit_code_cache), foreign_dex_profile_path_(foreign_dex_profile_path), - code_cache_last_update_time_ns_(0), shutting_down_(false), + last_save_number_of_methods_(0), + last_save_number_of_classes__(0), wait_lock_("ProfileSaver wait lock"), period_condition_("ProfileSaver period condition", wait_lock_), total_bytes_written_(0), @@ -61,17 +62,14 @@ ProfileSaver::ProfileSaver(const std::string& output_filename, total_number_of_code_cache_queries_(0), total_number_of_skipped_writes_(0), total_number_of_failed_writes_(0), - total_ns_of_sleep_(0), + total_ms_of_sleep_(0), total_ns_of_work_(0), - total_number_of_foreign_dex_marks_(0) { + total_number_of_foreign_dex_marks_(0), + max_number_of_profile_entries_cached_(0) { AddTrackedLocations(output_filename, code_paths); // We only need to save the resolved classes if the profile file is empty. // Otherwise we must have already save them (we always do it during the first // ever profile save). - // TODO(calin) This only considers the case of the primary profile file. - // Anything that gets loaded in the same VM will not have their resolved - // classes save (unless they started before the initial saving was done). - save_resolved_classes_ = !FileExistsAndNotEmpty(output_filename); app_data_dir_ = ""; if (!app_data_dir.empty()) { // The application directory is used to determine which dex files are owned by app. @@ -94,9 +92,10 @@ void ProfileSaver::Run() { uint64_t save_period_ms = kSavePeriodMs; VLOG(profiler) << "Save profiling information every " << save_period_ms << " ms"; + bool cache_resolved_classes = true; while (!ShuttingDown(self)) { uint64_t sleep_time_ms; - if (save_resolved_classes_) { + if (cache_resolved_classes) { // Sleep less long for the first iteration since we want to record loaded classes shortly // after app launch. sleep_time_ms = kSaveResolvedClassesDelayMs; @@ -108,41 +107,72 @@ void ProfileSaver::Run() { MutexLock mu(self, wait_lock_); period_condition_.TimedWait(self, sleep_time_ms, 0); } - total_ns_of_sleep_ += sleep_time_ms; + total_ms_of_sleep_ += sleep_time_ms; if (ShuttingDown(self)) { break; } uint64_t start = NanoTime(); - - if (!ProcessProfilingInfo(save_resolved_classes_) && save_period_ms < kMaxBackoffMs) { - // If we don't need to save now it is less likely that we will need to do - // so in the future. Increase the time between saves according to the - // kBackoffCoef, but make it no larger than kMaxBackoffMs. - save_period_ms = static_cast<uint64_t>(kBackoffCoef * save_period_ms); + if (cache_resolved_classes) { + // TODO(calin) This only considers the case of the primary profile file. + // Anything that gets loaded in the same VM will not have their resolved + // classes save (unless they started before the initial saving was done). + FetchAndCacheResolvedClasses(); } else { - // Reset the period to the initial value as it's highly likely to JIT again. - save_period_ms = kSavePeriodMs; + bool profile_saved_to_disk = ProcessProfilingInfo(); + if (!profile_saved_to_disk && save_period_ms < kMaxBackoffMs) { + // If we don't need to save now it is less likely that we will need to do + // so in the future. Increase the time between saves according to the + // kBackoffCoef, but make it no larger than kMaxBackoffMs. + save_period_ms = static_cast<uint64_t>(kBackoffCoef * save_period_ms); + } else { + // Reset the period to the initial value as it's highly likely to JIT again. + save_period_ms = kSavePeriodMs; + } } - save_resolved_classes_ = false; + cache_resolved_classes = false; total_ns_of_work_ += (NanoTime() - start); } } -bool ProfileSaver::ProcessProfilingInfo(bool save_resolved_classes) { +ProfileCompilationInfo* ProfileSaver::GetCachedProfiledInfo(const std::string& filename) { + auto info_it = profile_cache_.find(filename); + if (info_it == profile_cache_.end()) { + info_it = profile_cache_.Put(filename, ProfileCompilationInfo()); + } + return &info_it->second; +} + +void ProfileSaver::FetchAndCacheResolvedClasses() { ScopedTrace trace(__PRETTY_FUNCTION__); - uint64_t last_update_time_ns = jit_code_cache_->GetLastUpdateTimeNs(); - if (!save_resolved_classes && last_update_time_ns - code_cache_last_update_time_ns_ - < kMinimumTimeBetweenCodeCacheUpdatesNs) { - VLOG(profiler) << "Not enough time has passed since the last code cache update." - << "Last update: " << last_update_time_ns - << " Last save: " << code_cache_last_update_time_ns_; - total_number_of_skipped_writes_++; - return false; + + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); + std::set<DexCacheResolvedClasses> resolved_classes = + class_linker->GetResolvedClasses(/*ignore boot classes*/ true); + MutexLock mu(Thread::Current(), *Locks::profiler_lock_); + uint64_t total_number_of_profile_entries_cached = 0; + for (const auto& it : tracked_dex_base_locations_) { + std::set<DexCacheResolvedClasses> resolved_classes_for_location; + const std::string& filename = it.first; + const std::set<std::string>& locations = it.second; + + for (const DexCacheResolvedClasses& classes : resolved_classes) { + if (locations.find(classes.GetDexLocation()) != locations.end()) { + resolved_classes_for_location.insert(classes); + } + } + ProfileCompilationInfo* info = GetCachedProfiledInfo(filename); + info->AddMethodsAndClasses(std::vector<ArtMethod*>(), resolved_classes_for_location); + total_number_of_profile_entries_cached += resolved_classes_for_location.size(); } + max_number_of_profile_entries_cached_ = std::max( + max_number_of_profile_entries_cached_, + total_number_of_profile_entries_cached); +} - code_cache_last_update_time_ns_ = last_update_time_ns; +bool ProfileSaver::ProcessProfilingInfo() { + ScopedTrace trace(__PRETTY_FUNCTION__); SafeMap<std::string, std::set<std::string>> tracked_locations; { // Make a copy so that we don't hold the lock while doing I/O. @@ -150,11 +180,8 @@ bool ProfileSaver::ProcessProfilingInfo(bool save_resolved_classes) { tracked_locations = tracked_dex_base_locations_; } - std::set<DexCacheResolvedClasses> resolved_classes; - if (save_resolved_classes) { - ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); - resolved_classes = class_linker->GetResolvedClasses(/*ignore boot classes*/true); - } + bool profile_file_saved = false; + uint64_t total_number_of_profile_entries_cached = 0; for (const auto& it : tracked_locations) { if (ShuttingDown(Thread::Current())) { return true; @@ -168,41 +195,53 @@ bool ProfileSaver::ProcessProfilingInfo(bool save_resolved_classes) { total_number_of_code_cache_queries_++; } - std::set<DexCacheResolvedClasses> resolved_classes_for_location; - if (save_resolved_classes) { - bool resolved_classes_already_in_file = FileExistsAndNotEmpty(filename); - if (!resolved_classes_already_in_file) { - for (const DexCacheResolvedClasses& classes : resolved_classes) { - if (locations.find(classes.GetDexLocation()) != locations.end()) { - resolved_classes_for_location.insert(classes); - } - } - } - } - // Always save for the first one for loaded classes profile. - if (methods.size() < kMinimumNrOrMethodsToSave && !save_resolved_classes) { - VLOG(profiler) << "Not enough information to save to: " << filename - <<" Nr of methods: " << methods.size(); + ProfileCompilationInfo* cached_info = GetCachedProfiledInfo(filename); + cached_info->AddMethodsAndClasses(methods, std::set<DexCacheResolvedClasses>()); + uint32_t delta_number_of_methods = + cached_info->GetNumberOfMethods() - last_save_number_of_methods_; + uint32_t delta_number_of_classes = + cached_info->GetNumberOfResolvedClasses() - last_save_number_of_classes__; + + if (delta_number_of_methods < kMinimumNumberOfMethodsToSave && + delta_number_of_classes < kMinimumNumberOfClassesToSave) { + VLOG(profiler) << "Not enough information to save to: " << filename + << " Nr of methods: " << delta_number_of_methods + << " Nr of classes: " << delta_number_of_classes; total_number_of_skipped_writes_++; - return false; + continue; } uint64_t bytes_written; - if (!ProfileCompilationInfo::SaveProfilingInfo( - filename, - methods, - resolved_classes_for_location, - &bytes_written)) { - LOG(WARNING) << "Could not save profiling info to " << filename; - total_number_of_failed_writes_++; - return false; - } else { + if (cached_info->MergeAndSave(filename, &bytes_written)) { + last_save_number_of_methods_ = cached_info->GetNumberOfMethods(); + last_save_number_of_classes__ = cached_info->GetNumberOfResolvedClasses(); + // Clear resolved classes. No need to store them around as + // they don't change after the first write. + cached_info->ClearResolvedClasses(); if (bytes_written > 0) { total_number_of_writes_++; total_bytes_written_ += bytes_written; + } else { + // At this point we could still have avoided the write. + // We load and merge the data from the file lazily at its first ever + // save attempt. So, whatever we are trying to save could already be + // in the file. + total_number_of_skipped_writes_++; } + profile_file_saved = true; + } else { + LOG(WARNING) << "Could not save profiling info to " << filename; + total_number_of_failed_writes_++; + // TODO: (calin): if we failed because of bad profiling data or parsing + // errors we should clear the profile file. } + total_number_of_profile_entries_cached += + cached_info->GetNumberOfMethods() + + cached_info->GetNumberOfResolvedClasses(); } - return true; + max_number_of_profile_entries_cached_ = std::max( + max_number_of_profile_entries_cached_, + total_number_of_profile_entries_cached); + return profile_file_saved; } void* ProfileSaver::RunProfileSaverThread(void* arg) { @@ -431,12 +470,16 @@ void ProfileSaver::DumpInstanceInfo(std::ostream& os) { void ProfileSaver::DumpInfo(std::ostream& os) { os << "ProfileSaver total_bytes_written=" << total_bytes_written_ << '\n' << "ProfileSaver total_number_of_writes=" << total_number_of_writes_ << '\n' - << "ProfileSaver total_number_of_code_cache_queries=" << total_number_of_code_cache_queries_ << '\n' + << "ProfileSaver total_number_of_code_cache_queries=" + << total_number_of_code_cache_queries_ << '\n' << "ProfileSaver total_number_of_skipped_writes=" << total_number_of_skipped_writes_ << '\n' << "ProfileSaver total_number_of_failed_writes=" << total_number_of_failed_writes_ << '\n' - << "ProfileSaver total_ms_of_sleep=" << (total_ns_of_sleep_ / kMilisecondsToNano) << '\n' + << "ProfileSaver total_ms_of_sleep=" << total_ms_of_sleep_ << '\n' << "ProfileSaver total_ms_of_work=" << (total_ns_of_work_ / kMilisecondsToNano) << '\n' - << "ProfileSaver total_number_of_foreign_dex_marks=" << total_number_of_foreign_dex_marks_ << '\n'; + << "ProfileSaver total_number_of_foreign_dex_marks=" + << total_number_of_foreign_dex_marks_ << '\n' + << "ProfileSaver max_number_profile_entries_cached=" + << max_number_of_profile_entries_cached_ << '\n'; } } // namespace art diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h index d810c81234..91390fef90 100644 --- a/runtime/jit/profile_saver.h +++ b/runtime/jit/profile_saver.h @@ -65,7 +65,7 @@ class ProfileSaver { void Run() REQUIRES(!Locks::profiler_lock_, !wait_lock_); // Processes the existing profiling info from the jit code cache and returns // true if it needed to be saved to disk. - bool ProcessProfilingInfo(bool save_resolved_classes); + bool ProcessProfilingInfo(); // Returns true if the saver is shutting down (ProfileSaver::Stop() has been called). bool ShuttingDown(Thread* self) REQUIRES(!Locks::profiler_lock_); @@ -73,6 +73,14 @@ class ProfileSaver { const std::vector<std::string>& code_paths) REQUIRES(Locks::profiler_lock_); + // Retrieves the cached profile compilation info for the given profile file. + // If no entry exists, a new empty one will be created, added to the cache and + // then returned. + ProfileCompilationInfo* GetCachedProfiledInfo(const std::string& filename); + // Fetches the current resolved classes from the ClassLinker and stores them + // in the profile_cache_ for later save. + void FetchAndCacheResolvedClasses(); + static bool MaybeRecordDexUseInternal( const std::string& dex_location, const std::set<std::string>& tracked_locations, @@ -91,9 +99,15 @@ class ProfileSaver { GUARDED_BY(Locks::profiler_lock_); std::string foreign_dex_profile_path_; std::string app_data_dir_; - uint64_t code_cache_last_update_time_ns_; bool shutting_down_ GUARDED_BY(Locks::profiler_lock_); - bool save_resolved_classes_; + uint32_t last_save_number_of_methods_; + uint32_t last_save_number_of_classes__; + + // A local cache for the profile information. Maps each tracked file to its + // profile information. The size of this cache is usually very small and tops + // to just a few hundreds entries in the ProfileCompilationInfo objects. + // It helps avoiding unnecessary writes to disk. + SafeMap<std::string, ProfileCompilationInfo> profile_cache_; // Save period condition support. Mutex wait_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; @@ -104,9 +118,11 @@ class ProfileSaver { uint64_t total_number_of_code_cache_queries_; uint64_t total_number_of_skipped_writes_; uint64_t total_number_of_failed_writes_; - uint64_t total_ns_of_sleep_; + uint64_t total_ms_of_sleep_; uint64_t total_ns_of_work_; uint64_t total_number_of_foreign_dex_marks_; + // TODO(calin): replace with an actual size. + uint64_t max_number_of_profile_entries_cached_; DISALLOW_COPY_AND_ASSIGN(ProfileSaver); }; |