Merge changes Ic0d4d9cc,I1fd9b45a,I454c0436 am: 392a0098fc am: 3ea82a304b
am: b68fb45d5d
Change-Id: I70ac75655d68a70a707745896248f543e5ad4b23
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index 9ea5ece..4d5c9d6 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -117,9 +117,7 @@
return true;
}
-bool ProfileCompilationInfo::MergeAndSave(const std::string& filename,
- uint64_t* bytes_written,
- bool force) {
+bool ProfileCompilationInfo::Load(const std::string& filename, bool clear_if_invalid) {
ScopedTrace trace(__PRETTY_FUNCTION__);
ScopedFlock flock;
std::string error;
@@ -134,36 +132,42 @@
int fd = flock.GetFile()->Fd();
- // Load the file but keep a copy around to be able to infer if the content has changed.
- ProfileCompilationInfo fileInfo;
- ProfileLoadSatus status = fileInfo.LoadInternal(fd, &error);
+ ProfileLoadSatus status = LoadInternal(fd, &error);
if (status == kProfileLoadSuccess) {
- // Merge the content of file into the current object.
- if (MergeWith(fileInfo)) {
- // 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;
- }
+ return true;
+ }
+
+ if (clear_if_invalid &&
+ ((status == kProfileLoadVersionMismatch) || (status == kProfileLoadBadData))) {
+ LOG(WARNING) << "Clearing bad or obsolete profile data from file "
+ << filename << ": " << error;
+ if (flock.GetFile()->ClearContent()) {
+ return true;
} else {
- LOG(WARNING) << "Could not merge previous profile data from file " << filename;
- if (!force) {
- return false;
- }
+ PLOG(WARNING) << "Could not clear profile file: " << filename;
+ return false;
}
- } else if (force &&
- ((status == kProfileLoadVersionMismatch) || (status == kProfileLoadBadData))) {
- // Log a warning but don't return false. We will clear the profile anyway.
- LOG(WARNING) << "Clearing bad or obsolete profile data from file "
- << filename << ": " << error;
- } else {
- LOG(WARNING) << "Could not load profile data from file " << filename << ": " << error;
+ }
+
+ LOG(WARNING) << "Could not load profile data from file " << filename << ": " << error;
+ return false;
+}
+
+bool ProfileCompilationInfo::Save(const std::string& filename, uint64_t* bytes_written) {
+ ScopedTrace trace(__PRETTY_FUNCTION__);
+ ScopedFlock flock;
+ std::string error;
+ int flags = O_WRONLY | O_NOFOLLOW | O_CLOEXEC;
+ // There's no need to fsync profile data right away. We get many chances
+ // to write it again in case something goes wrong. We can rely on a simple
+ // close(), no sync, and let to the kernel decide when to write to disk.
+ if (!flock.Init(filename.c_str(), flags, /*block*/false, /*flush_on_close*/false, &error)) {
+ LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error;
return false;
}
+ int fd = flock.GetFile()->Fd();
+
// We need to clear the data because we don't support appending to the profiles yet.
if (!flock.GetFile()->ClearContent()) {
PLOG(WARNING) << "Could not clear profile file: " << filename;
@@ -174,10 +178,14 @@
// access and fail immediately if we can't.
bool result = Save(fd);
if (result) {
- VLOG(profiler) << "Successfully saved profile info to " << filename
- << " Size: " << GetFileSizeBytes(filename);
- if (bytes_written != nullptr) {
- *bytes_written = GetFileSizeBytes(filename);
+ int64_t size = GetFileSizeBytes(filename);
+ if (size != -1) {
+ VLOG(profiler)
+ << "Successfully saved profile info to " << filename << " Size: "
+ << size;
+ if (bytes_written != nullptr) {
+ *bytes_written = static_cast<uint64_t>(size);
+ }
}
} else {
VLOG(profiler) << "Failed to save profile info to " << filename;
@@ -886,6 +894,7 @@
}
}
+// TODO(calin): fail fast if the dex checksums don't match.
ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::LoadInternal(
int fd, std::string* error) {
ScopedTrace trace(__PRETTY_FUNCTION__);
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index 9e47cc1..ee1935f 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -196,18 +196,20 @@
// If the current profile is non-empty the load will fail.
bool Load(int fd);
+ // Load profile information from the given file
+ // If the current profile is non-empty the load will fail.
+ // If clear_if_invalid is true and the file is invalid the method clears the
+ // the file and returns true.
+ bool Load(const std::string& filename, bool clear_if_invalid);
+
// Merge the data from another ProfileCompilationInfo into the current object.
bool MergeWith(const ProfileCompilationInfo& info);
// Save the profile data to the given file descriptor.
bool Save(int fd);
- // Load and merge profile information from the given file into the current
- // object and tries to save it back to disk.
- // If `force` is true then the save will go through even if the given file
- // has bad data or its version does not match. In this cases the profile content
- // is ignored.
- bool MergeAndSave(const std::string& filename, uint64_t* bytes_written, bool force);
+ // Save the current profile into the given file. The file will be cleared before saving.
+ bool Save(const std::string& filename, uint64_t* bytes_written);
// Return the number of methods that were profiled.
uint32_t GetNumberOfMethods() const;
diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc
index c9f2d0e..e8f4ce2 100644
--- a/runtime/jit/profile_compilation_info_test.cc
+++ b/runtime/jit/profile_compilation_info_test.cc
@@ -92,7 +92,15 @@
if (info.GetNumberOfMethods() != profile_methods.size()) {
return false;
}
- return info.MergeAndSave(filename, nullptr, false);
+ ProfileCompilationInfo file_profile;
+ if (!file_profile.Load(filename, false)) {
+ return false;
+ }
+ if (!info.MergeWith(file_profile)) {
+ return false;
+ }
+
+ return info.Save(filename, nullptr);
}
// Saves the given art methods to a profile backed by 'filename' and adds
@@ -145,7 +153,7 @@
if (info.GetNumberOfMethods() != profile_methods.size()) {
return false;
}
- return info.MergeAndSave(filename, nullptr, false);
+ return info.Save(filename, nullptr);
}
ProfileCompilationInfo::OfflineProfileMethodInfo ConvertProfileMethodInfo(
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 1441987..1e6f7a8 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -171,14 +171,6 @@
}
}
-ProfileSaver::ProfileInfoCache* 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, ProfileInfoCache());
- }
- return &info_it->second;
-}
-
// Get resolved methods that have a profile info or more than kStartupMethodSamples samples.
// Excludes native methods and classes in the boot image.
class GetMethodsVisitor : public ClassVisitor {
@@ -252,9 +244,11 @@
<< " (" << classes.GetDexLocation() << ")";
}
}
- ProfileInfoCache* cached_info = GetCachedProfiledInfo(filename);
- cached_info->profile.AddMethodsAndClasses(profile_methods_for_location,
- resolved_classes_for_location);
+ auto info_it = profile_cache_.Put(filename, ProfileCompilationInfo());
+
+ ProfileCompilationInfo* cached_info = &(info_it->second);
+ cached_info->AddMethodsAndClasses(profile_methods_for_location,
+ resolved_classes_for_location);
total_number_of_profile_entries_cached += resolved_classes_for_location.size();
}
max_number_of_profile_entries_cached_ = std::max(
@@ -297,16 +291,22 @@
jit_code_cache_->GetProfiledMethods(locations, profile_methods);
total_number_of_code_cache_queries_++;
}
+ ProfileCompilationInfo info;
+ if (!info.Load(filename, /*clear_if_invalid*/ true)) {
+ LOG(WARNING) << "Could not forcefully load profile " << filename;
+ continue;
+ }
+ uint64_t last_save_number_of_methods = info.GetNumberOfMethods();
+ uint64_t last_save_number_of_classes = info.GetNumberOfResolvedClasses();
- ProfileInfoCache* cached_info = GetCachedProfiledInfo(filename);
- ProfileCompilationInfo* cached_profile = &cached_info->profile;
- cached_profile->AddMethodsAndClasses(profile_methods, std::set<DexCacheResolvedClasses>());
- int64_t delta_number_of_methods =
- cached_profile->GetNumberOfMethods() -
- static_cast<int64_t>(cached_info->last_save_number_of_methods);
- int64_t delta_number_of_classes =
- cached_profile->GetNumberOfResolvedClasses() -
- static_cast<int64_t>(cached_info->last_save_number_of_classes);
+ info.AddMethodsAndClasses(profile_methods, std::set<DexCacheResolvedClasses>());
+ auto profile_cache_it = profile_cache_.find(filename);
+ if (profile_cache_it != profile_cache_.end()) {
+ info.MergeWith(profile_cache_it->second);
+ }
+
+ int64_t delta_number_of_methods = info.GetNumberOfMethods() - last_save_number_of_methods;
+ int64_t delta_number_of_classes = info.GetNumberOfResolvedClasses() - last_save_number_of_classes;
if (!force_save &&
delta_number_of_methods < options_.GetMinMethodsToSave() &&
@@ -324,12 +324,12 @@
uint64_t bytes_written;
// Force the save. In case the profile data is corrupted or the the profile
// has the wrong version this will "fix" the file to the correct format.
- if (cached_profile->MergeAndSave(filename, &bytes_written, /*force*/ true)) {
- cached_info->last_save_number_of_methods = cached_profile->GetNumberOfMethods();
- cached_info->last_save_number_of_classes = cached_profile->GetNumberOfResolvedClasses();
- // Clear resolved classes. No need to store them around as
- // they don't change after the first write.
- cached_profile->ClearResolvedClasses();
+ if (info.Save(filename, &bytes_written)) {
+ // We managed to save the profile. Clear the cache stored during startup.
+ if (profile_cache_it != profile_cache_.end()) {
+ profile_cache_.erase(profile_cache_it);
+ total_number_of_profile_entries_cached = 0;
+ }
if (bytes_written > 0) {
total_number_of_writes_++;
total_bytes_written_ += bytes_written;
@@ -345,13 +345,8 @@
LOG(WARNING) << "Could not save profiling info to " << filename;
total_number_of_failed_writes_++;
}
- total_number_of_profile_entries_cached +=
- cached_profile->GetNumberOfMethods() +
- cached_profile->GetNumberOfResolvedClasses();
}
- max_number_of_profile_entries_cached_ = std::max(
- max_number_of_profile_entries_cached_,
- total_number_of_profile_entries_cached);
+
return profile_file_saved;
}
@@ -575,7 +570,10 @@
uint16_t method_idx) {
MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
if (instance_ != nullptr) {
- const ProfileCompilationInfo& info = instance_->GetCachedProfiledInfo(profile)->profile;
+ ProfileCompilationInfo info;
+ if (!info.Load(profile, /*clear_if_invalid*/false)) {
+ return false;
+ }
return info.ContainsMethod(MethodReference(dex_file, method_idx));
}
return false;
diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h
index bd539a4..60c9cc6 100644
--- a/runtime/jit/profile_saver.h
+++ b/runtime/jit/profile_saver.h
@@ -61,14 +61,6 @@
uint16_t method_idx);
private:
- // A cache structure which keeps track of the data saved to disk.
- // It is used to reduce the number of disk read/writes.
- struct ProfileInfoCache {
- ProfileCompilationInfo profile;
- uint32_t last_save_number_of_methods = 0;
- uint32_t last_save_number_of_classes = 0;
- };
-
ProfileSaver(const ProfileSaverOptions& options,
const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
@@ -102,10 +94,6 @@
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.
- ProfileInfoCache* GetCachedProfiledInfo(const std::string& filename);
// Fetches the current resolved classes and methods from the ClassLinker and stores them in the
// profile_cache_ for later save.
void FetchAndCacheResolvedClassesAndMethods();
@@ -139,10 +127,11 @@
uint32_t jit_activity_notifications_;
// 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
+ // profile information. This is used to cache the startup classes so that
+ // we don't hammer the disk to save them right away.
+ // 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, ProfileInfoCache> profile_cache_;
+ SafeMap<std::string, ProfileCompilationInfo> profile_cache_;
// Save period condition support.
Mutex wait_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index a89fe5b..b722c68 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -616,6 +616,8 @@
vdex_cmdline="${dex2oat_cmdline} ${VDEX_FILTER} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex --output-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex"
elif [ "$TEST_VDEX" = "y" ]; then
vdex_cmdline="${dex2oat_cmdline} ${VDEX_FILTER} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex"
+ elif [ "$PROFILE" = "y" ] || [ "$RANDOM_PROFILE" = "y" ]; then
+ vdex_cmdline="${dex2oat_cmdline} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex --output-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex"
fi
fi