Enable ProfileSaver to have a different delay for the first ever save
The runtime now supports -Xps-first-save-ms, which when configured
may alter the delay for the first ever profile save. Subsequent
saves will proceed based on the existing -Xps-min-save-period-ms.
The first ever save is an approximation, and computed by checking
the profiles size.
Test: gtest & manual
Bug: 185979271
Change-Id: I7119b9d2b8829653046565426090c89f6a619a27
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 779e3e0..10c651b 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -487,17 +487,18 @@
* -Xps-*
*/
TEST_F(CmdlineParserTest, ProfileSaverOptions) {
- ProfileSaverOptions opt = ProfileSaverOptions(true, 1, 2, 3, 4, 5, 6, 7, "abc", true);
+ ProfileSaverOptions opt = ProfileSaverOptions(true, 1, 2, 3, 4, 5, 6, 7, 8, "abc", true);
EXPECT_SINGLE_PARSE_VALUE(opt,
"-Xjitsaveprofilinginfo "
"-Xps-min-save-period-ms:1 "
- "-Xps-save-resolved-classes-delay-ms:2 "
- "-Xps-hot-startup-method-samples:3 "
- "-Xps-min-methods-to-save:4 "
- "-Xps-min-classes-to-save:5 "
- "-Xps-min-notification-before-wake:6 "
- "-Xps-max-notification-before-wake:7 "
+ "-Xps-min-first-save-ms:2 "
+ "-Xps-save-resolved-classes-delay-ms:3 "
+ "-Xps-hot-startup-method-samples:4 "
+ "-Xps-min-methods-to-save:5 "
+ "-Xps-min-classes-to-save:6 "
+ "-Xps-min-notification-before-wake:7 "
+ "-Xps-max-notification-before-wake:8 "
"-Xps-profile-path:abc "
"-Xps-profile-boot-class-path",
M::ProfileSaverOpts);
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index 7b38b8e..c506e03 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -788,6 +788,12 @@
&ProfileSaverOptions::min_save_period_ms_,
type_parser.Parse(suffix));
}
+ if (android::base::StartsWith(option, "min-first-save-ms:")) {
+ CmdlineType<unsigned int> type_parser;
+ return ParseInto(existing,
+ &ProfileSaverOptions::min_first_save_ms_,
+ type_parser.Parse(suffix));
+ }
if (android::base::StartsWith(option, "save-resolved-classes-delay-ms:")) {
CmdlineType<unsigned int> type_parser;
return ParseInto(existing,
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index ff711fa..263a9ad 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -20,6 +20,7 @@
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <unistd.h>
#include "android-base/strings.h"
@@ -180,7 +181,11 @@
// We might have been woken up by a huge number of notifications to guarantee saving.
// If we didn't meet the minimum saving period go back to sleep (only if missed by
// a reasonable margin).
- uint64_t min_save_period_ns = MsToNs(options_.GetMinSavePeriodMs());
+ bool check_for_first_save = options_.GetMinFirstSaveMs() !=
+ ProfileSaverOptions::kMinFirstSaveMsNotSet;
+ uint64_t min_save_period_ns = MsToNs(check_for_first_save && IsFirstSave()
+ ? options_.GetMinFirstSaveMs()
+ : options_.GetMinSavePeriodMs());
while (min_save_period_ns * 0.9 > sleep_time) {
{
MutexLock mu(self, wait_lock_);
@@ -215,6 +220,48 @@
}
}
+// TODO(b/185979271): include reference profiles in the test.
+// The current profiles are cleared after bg-dexopt so this test will currently
+// return True after every bg-dexopt call.
+bool ProfileSaver::IsFirstSave() {
+ // Resolve any new registered locations.
+ ResolveTrackedLocations();
+ Thread* self = Thread::Current();
+ SafeMap<std::string, std::set<std::string>> tracked_locations;
+ {
+ // Make a copy so that we don't hold the lock while doing I/O.
+ MutexLock mu(self, *Locks::profiler_lock_);
+ tracked_locations = tracked_dex_base_locations_;
+ }
+
+ for (const auto& it : tracked_locations) {
+ if (ShuttingDown(self)) {
+ return false;
+ }
+ const std::set<std::string>& locations = it.second;
+
+ // Check if any profile is non empty. If so, then this is not the first save.
+ for (const auto& location : locations) {
+ struct stat stat_buffer;
+ if (stat(location.c_str(), &stat_buffer) != 0) {
+ if (VLOG_IS_ON(profiler)) {
+ PLOG(WARNING) << "Failed to stat profile location for IsFirstUse: " << location;
+ }
+ continue;
+ }
+ if (stat_buffer.st_size > 0) {
+ return false;
+ } else {
+ VLOG(profiler) << "Profile location is empty: " << location;
+ }
+ }
+ }
+
+ // All locations are empty. Assume this is the first use.
+ VLOG(profiler) << "All profile locations are empty. This is considered to be first save";
+ return true;
+}
+
void ProfileSaver::NotifyJitActivity() {
MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
if (instance_ == nullptr || instance_->shutting_down_) {
diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h
index 036e717..dc44f24 100644
--- a/runtime/jit/profile_saver.h
+++ b/runtime/jit/profile_saver.h
@@ -134,6 +134,12 @@
// to just a few hundreds entries in the ProfileCompilationInfo objects.
SafeMap<std::string, ProfileCompilationInfo*> profile_cache_ GUARDED_BY(Locks::profiler_lock_);
+ // Whether or not this is the first ever profile save.
+ // Note this is an approximation and is not 100% precise. It relies on checking
+ // whether or not the profiles are empty which is not a precise indication
+ // of being the first save (they could have been cleared in the meantime).
+ bool IsFirstSave() REQUIRES(!Locks::profiler_lock_);
+
// Save period condition support.
Mutex wait_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
ConditionVariable period_condition_ GUARDED_BY(wait_lock_);
diff --git a/runtime/jit/profile_saver_options.h b/runtime/jit/profile_saver_options.h
index 1cff713..7492054 100644
--- a/runtime/jit/profile_saver_options.h
+++ b/runtime/jit/profile_saver_options.h
@@ -21,6 +21,9 @@
struct ProfileSaverOptions {
public:
static constexpr uint32_t kMinSavePeriodMs = 40 * 1000; // 40 seconds
+ // Default value for the min save period on first use, indicating that the
+ // period is not configured.
+ static constexpr uint32_t kMinFirstSaveMsNotSet = 0;
static constexpr uint32_t kSaveResolvedClassesDelayMs = 5 * 1000; // 5 seconds
// Minimum number of JIT samples during launch to mark a method as hot in the profile.
static constexpr uint32_t kHotStartupMethodSamples = 1;
@@ -34,6 +37,7 @@
ProfileSaverOptions() :
enabled_(false),
min_save_period_ms_(kMinSavePeriodMs),
+ min_first_save_ms_(kMinFirstSaveMsNotSet),
save_resolved_classes_delay_ms_(kSaveResolvedClassesDelayMs),
hot_startup_method_samples_(kHotStartupMethodSamplesNotSet),
min_methods_to_save_(kMinMethodsToSave),
@@ -48,6 +52,7 @@
ProfileSaverOptions(
bool enabled,
uint32_t min_save_period_ms,
+ uint32_t min_first_save_ms,
uint32_t save_resolved_classes_delay_ms,
uint32_t hot_startup_method_samples,
uint32_t min_methods_to_save,
@@ -60,6 +65,7 @@
bool wait_for_jit_notifications_to_save = true)
: enabled_(enabled),
min_save_period_ms_(min_save_period_ms),
+ min_first_save_ms_(min_first_save_ms),
save_resolved_classes_delay_ms_(save_resolved_classes_delay_ms),
hot_startup_method_samples_(hot_startup_method_samples),
min_methods_to_save_(min_methods_to_save),
@@ -81,6 +87,9 @@
uint32_t GetMinSavePeriodMs() const {
return min_save_period_ms_;
}
+ uint32_t GetMinFirstSaveMs() const {
+ return min_first_save_ms_;
+ }
uint32_t GetSaveResolvedClassesDelayMs() const {
return save_resolved_classes_delay_ms_;
}
@@ -122,6 +131,7 @@
friend std::ostream & operator<<(std::ostream &os, const ProfileSaverOptions& pso) {
os << "enabled_" << pso.enabled_
<< ", min_save_period_ms_" << pso.min_save_period_ms_
+ << ", min_first_save_ms_" << pso.min_first_save_ms_
<< ", save_resolved_classes_delay_ms_" << pso.save_resolved_classes_delay_ms_
<< ", hot_startup_method_samples_" << pso.hot_startup_method_samples_
<< ", min_methods_to_save_" << pso.min_methods_to_save_
@@ -136,6 +146,7 @@
bool enabled_;
uint32_t min_save_period_ms_;
+ uint32_t min_first_save_ms_;
uint32_t save_resolved_classes_delay_ms_;
// Do not access hot_startup_method_samples_ directly for reading since it may be set to the
// placeholder default.
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index fec2587..471a3fa 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -288,6 +288,7 @@
// TODO This should be redone.
.Define({"-Xps-_",
"-Xps-min-save-period-ms:_",
+ "-Xps-min-first-save-ms:_",
"-Xps-save-resolved-classes-delayed-ms:_",
"-Xps-hot-startup-method-samples:_",
"-Xps-min-methods-to-save:_",