bool UsuallyEquals(double expected, double actual);
// This has a gtest dependency, which is why it's in the gtest only.
- bool operator==(const TestProfilerOptions& lhs, const TestProfilerOptions& rhs) {
+ bool operator==(const ProfileSaverOptions& lhs, const ProfileSaverOptions& rhs) {
return lhs.enabled_ == rhs.enabled_ &&
- lhs.output_file_name_ == rhs.output_file_name_ &&
- lhs.period_s_ == rhs.period_s_ &&
- lhs.duration_s_ == rhs.duration_s_ &&
- lhs.interval_us_ == rhs.interval_us_ &&
- UsuallyEquals(lhs.backoff_coefficient_, rhs.backoff_coefficient_) &&
- UsuallyEquals(lhs.start_immediately_, rhs.start_immediately_) &&
- UsuallyEquals(lhs.top_k_threshold_, rhs.top_k_threshold_) &&
- UsuallyEquals(lhs.top_k_change_threshold_, rhs.top_k_change_threshold_) &&
- lhs.profile_type_ == rhs.profile_type_ &&
- lhs.max_stack_depth_ == rhs.max_stack_depth_;
+ lhs.min_save_period_ms_ == rhs.min_save_period_ms_ &&
+ lhs.save_resolved_classes_delay_ms_ == rhs.save_resolved_classes_delay_ms_ &&
+ lhs.startup_method_samples_ == rhs.startup_method_samples_ &&
+ lhs.min_methods_to_save_ == rhs.min_methods_to_save_ &&
+ lhs.min_classes_to_save_ == rhs.min_classes_to_save_ &&
+ lhs.min_notification_before_wake_ == rhs.min_notification_before_wake_ &&
+ lhs.max_notification_before_wake_ == rhs.max_notification_before_wake_;
}
bool UsuallyEquals(double expected, double actual) {
} // TEST_F
/*
-* -X-profile-*
+* -Xps-*
*/
-TEST_F(CmdlineParserTest, TestProfilerOptions) {
- /*
- * Test successes
- */
-
- {
- TestProfilerOptions opt;
- opt.enabled_ = true;
-
- EXPECT_SINGLE_PARSE_VALUE(opt,
- "-Xenable-profiler",
- M::ProfilerOpts);
- }
-
- {
- TestProfilerOptions opt;
- // also need to test 'enabled'
- opt.output_file_name_ = "hello_world.txt";
-
- EXPECT_SINGLE_PARSE_VALUE(opt,
- "-Xprofile-filename:hello_world.txt ",
- M::ProfilerOpts);
- }
-
- {
- TestProfilerOptions opt = TestProfilerOptions();
- // also need to test 'enabled'
- opt.output_file_name_ = "output.txt";
- opt.period_s_ = 123u;
- opt.duration_s_ = 456u;
- opt.interval_us_ = 789u;
- opt.backoff_coefficient_ = 2.0;
- opt.start_immediately_ = true;
- opt.top_k_threshold_ = 50.0;
- opt.top_k_change_threshold_ = 60.0;
- opt.profile_type_ = kProfilerMethod;
- opt.max_stack_depth_ = 1337u;
-
- EXPECT_SINGLE_PARSE_VALUE(opt,
- "-Xprofile-filename:output.txt "
- "-Xprofile-period:123 "
- "-Xprofile-duration:456 "
- "-Xprofile-interval:789 "
- "-Xprofile-backoff:2.0 "
- "-Xprofile-start-immediately "
- "-Xprofile-top-k-threshold:50.0 "
- "-Xprofile-top-k-change-threshold:60.0 "
- "-Xprofile-type:method "
- "-Xprofile-max-stack-depth:1337",
- M::ProfilerOpts);
- }
-
- {
- TestProfilerOptions opt = TestProfilerOptions();
- opt.profile_type_ = kProfilerBoundedStack;
-
- EXPECT_SINGLE_PARSE_VALUE(opt,
- "-Xprofile-type:stack",
- M::ProfilerOpts);
- }
+TEST_F(CmdlineParserTest, ProfileSaverOptions) {
+ ProfileSaverOptions opt = ProfileSaverOptions(true, 1, 2, 3, 4, 5, 6, 7);
+
+ EXPECT_SINGLE_PARSE_VALUE(opt,
+ "-Xjitsaveprofilinginfo "
+ "-Xps-min-save-period-ms:1 "
+ "-Xps-save-resolved-classes-delay-ms:2 "
+ "-Xps-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",
+ M::ProfileSaverOpts);
} // TEST_F
/* -Xexperimental:_ */
#include "experimental_flags.h"
#include "gc/collector_type.h"
#include "gc/space/large_object_space.h"
-#include "profiler_options.h"
+#include "jit/profile_saver_options.h"
namespace art {
static const char* Name() { return "LogVerbosity"; }
};
-// TODO: Replace with art::ProfilerOptions for the real thing.
-struct TestProfilerOptions {
- // Whether or not the applications should be profiled.
- bool enabled_;
- // Destination file name where the profiling data will be saved into.
- std::string output_file_name_;
- // Generate profile every n seconds.
- uint32_t period_s_;
- // Run profile for n seconds.
- uint32_t duration_s_;
- // Microseconds between samples.
- uint32_t interval_us_;
- // Coefficient to exponential backoff.
- double backoff_coefficient_;
- // Whether the profile should start upon app startup or be delayed by some random offset.
- bool start_immediately_;
- // Top K% of samples that are considered relevant when deciding if the app should be recompiled.
- double top_k_threshold_;
- // How much the top K% samples needs to change in order for the app to be recompiled.
- double top_k_change_threshold_;
- // The type of profile data dumped to the disk.
- ProfileDataType profile_type_;
- // The max depth of the stack collected by the profiler
- uint32_t max_stack_depth_;
-
- TestProfilerOptions() :
- enabled_(false),
- output_file_name_(),
- period_s_(0),
- duration_s_(0),
- interval_us_(0),
- backoff_coefficient_(0),
- start_immediately_(0),
- top_k_threshold_(0),
- top_k_change_threshold_(0),
- profile_type_(ProfileDataType::kProfilerMethod),
- max_stack_depth_(0) {
- }
-
- TestProfilerOptions(const TestProfilerOptions&) = default;
- TestProfilerOptions(TestProfilerOptions&&) = default;
-};
-
-static inline std::ostream& operator<<(std::ostream& stream, const TestProfilerOptions& options) {
- stream << "TestProfilerOptions {" << std::endl;
-
-#define PRINT_TO_STREAM(field) \
- stream << #field << ": '" << options.field << "'" << std::endl;
-
- PRINT_TO_STREAM(enabled_);
- PRINT_TO_STREAM(output_file_name_);
- PRINT_TO_STREAM(period_s_);
- PRINT_TO_STREAM(duration_s_);
- PRINT_TO_STREAM(interval_us_);
- PRINT_TO_STREAM(backoff_coefficient_);
- PRINT_TO_STREAM(start_immediately_);
- PRINT_TO_STREAM(top_k_threshold_);
- PRINT_TO_STREAM(top_k_change_threshold_);
- PRINT_TO_STREAM(profile_type_);
- PRINT_TO_STREAM(max_stack_depth_);
-
- stream << "}";
-
- return stream;
-#undef PRINT_TO_STREAM
-}
-
template <>
-struct CmdlineType<TestProfilerOptions> : CmdlineTypeParser<TestProfilerOptions> {
- using Result = CmdlineParseResult<TestProfilerOptions>;
+struct CmdlineType<ProfileSaverOptions> : CmdlineTypeParser<ProfileSaverOptions> {
+ using Result = CmdlineParseResult<ProfileSaverOptions>;
private:
using StringResult = CmdlineParseResult<std::string>;
using DoubleResult = CmdlineParseResult<double>;
template <typename T>
- static Result ParseInto(TestProfilerOptions& options,
- T TestProfilerOptions::*pField,
+ static Result ParseInto(ProfileSaverOptions& options,
+ T ProfileSaverOptions::*pField,
CmdlineParseResult<T>&& result) {
assert(pField != nullptr);
return Result::CastError(result);
}
- template <typename T>
- static Result ParseIntoRangeCheck(TestProfilerOptions& options,
- T TestProfilerOptions::*pField,
- CmdlineParseResult<T>&& result,
- T min,
- T max) {
- if (result.IsSuccess()) {
- const T& value = result.GetValue();
-
- if (value < min || value > max) {
- CmdlineParseResult<T> out_of_range = CmdlineParseResult<T>::OutOfRange(value, min, max);
- return Result::CastError(out_of_range);
- }
- }
-
- return ParseInto(options, pField, std::forward<CmdlineParseResult<T>>(result));
- }
-
- static StringResult ParseStringAfterChar(const std::string& s, char c) {
- std::string parsed_value;
-
- std::string::size_type colon = s.find(c);
- if (colon == std::string::npos) {
- return StringResult::Usage(std::string() + "Missing char " + c + " in option " + s);
- }
- // Add one to remove the char we were trimming until.
- parsed_value = s.substr(colon + 1);
- return StringResult::Success(parsed_value);
- }
-
static std::string RemovePrefix(const std::string& source) {
size_t prefix_idx = source.find(":");
}
public:
- Result ParseAndAppend(const std::string& option, TestProfilerOptions& existing) {
+ Result ParseAndAppend(const std::string& option, ProfileSaverOptions& existing) {
// Special case which doesn't include a wildcard argument definition.
// We pass-it through as-is.
- if (option == "-Xenable-profiler") {
+ if (option == "-Xjitsaveprofilinginfo") {
existing.enabled_ = true;
return Result::SuccessNoValue();
}
- // The rest of these options are always the wildcard from '-Xprofile-*'
+ // The rest of these options are always the wildcard from '-Xps-*'
std::string suffix = RemovePrefix(option);
- if (StartsWith(option, "filename:")) {
- CmdlineType<std::string> type_parser;
-
+ if (StartsWith(option, "min-save-period-ms:")) {
+ CmdlineType<unsigned int> type_parser;
return ParseInto(existing,
- &TestProfilerOptions::output_file_name_,
- type_parser.Parse(suffix));
- } else if (StartsWith(option, "period:")) {
+ &ProfileSaverOptions::min_save_period_ms_,
+ type_parser.Parse(suffix));
+ }
+ if (StartsWith(option, "save-resolved-classes-delay-ms:")) {
CmdlineType<unsigned int> type_parser;
-
return ParseInto(existing,
- &TestProfilerOptions::period_s_,
- type_parser.Parse(suffix));
- } else if (StartsWith(option, "duration:")) {
+ &ProfileSaverOptions::save_resolved_classes_delay_ms_,
+ type_parser.Parse(suffix));
+ }
+ if (StartsWith(option, "startup-method-samples:")) {
CmdlineType<unsigned int> type_parser;
-
return ParseInto(existing,
- &TestProfilerOptions::duration_s_,
- type_parser.Parse(suffix));
- } else if (StartsWith(option, "interval:")) {
+ &ProfileSaverOptions::startup_method_samples_,
+ type_parser.Parse(suffix));
+ }
+ if (StartsWith(option, "min-methods-to-save:")) {
CmdlineType<unsigned int> type_parser;
-
return ParseInto(existing,
- &TestProfilerOptions::interval_us_,
- type_parser.Parse(suffix));
- } else if (StartsWith(option, "backoff:")) {
- CmdlineType<double> type_parser;
-
- return ParseIntoRangeCheck(existing,
- &TestProfilerOptions::backoff_coefficient_,
- type_parser.Parse(suffix),
- 1.0,
- 10.0);
-
- } else if (option == "start-immediately") {
- existing.start_immediately_ = true;
- return Result::SuccessNoValue();
- } else if (StartsWith(option, "top-k-threshold:")) {
- CmdlineType<double> type_parser;
-
- return ParseIntoRangeCheck(existing,
- &TestProfilerOptions::top_k_threshold_,
- type_parser.Parse(suffix),
- 0.0,
- 100.0);
- } else if (StartsWith(option, "top-k-change-threshold:")) {
- CmdlineType<double> type_parser;
-
- return ParseIntoRangeCheck(existing,
- &TestProfilerOptions::top_k_change_threshold_,
- type_parser.Parse(suffix),
- 0.0,
- 100.0);
- } else if (option == "type:method") {
- existing.profile_type_ = kProfilerMethod;
- return Result::SuccessNoValue();
- } else if (option == "type:stack") {
- existing.profile_type_ = kProfilerBoundedStack;
- return Result::SuccessNoValue();
- } else if (StartsWith(option, "max-stack-depth:")) {
+ &ProfileSaverOptions::min_methods_to_save_,
+ type_parser.Parse(suffix));
+ }
+ if (StartsWith(option, "min-classes-to-save:")) {
+ CmdlineType<unsigned int> type_parser;
+ return ParseInto(existing,
+ &ProfileSaverOptions::min_classes_to_save_,
+ type_parser.Parse(suffix));
+ }
+ if (StartsWith(option, "min-notification-before-wake:")) {
+ CmdlineType<unsigned int> type_parser;
+ return ParseInto(existing,
+ &ProfileSaverOptions::min_notification_before_wake_,
+ type_parser.Parse(suffix));
+ }
+ if (StartsWith(option, "max-notification-before-wake:")) {
CmdlineType<unsigned int> type_parser;
-
return ParseInto(existing,
- &TestProfilerOptions::max_stack_depth_,
- type_parser.Parse(suffix));
+ &ProfileSaverOptions::max_notification_before_wake_,
+ type_parser.Parse(suffix));
} else {
return Result::Failure(std::string("Invalid suboption '") + option + "'");
}
}
- static const char* Name() { return "TestProfilerOptions"; }
+ static const char* Name() { return "ProfileSaverOptions"; }
static constexpr bool kCanParseBlankless = true;
};
oat.h \
object_callbacks.h \
process_state.h \
- profiler_options.h \
quick/inline_method_analyser.h \
runtime.h \
stack.h \
options.GetOrDefault(RuntimeArgumentMap::JITCodeCacheMaxCapacity);
jit_options->dump_info_on_shutdown_ =
options.Exists(RuntimeArgumentMap::DumpJITInfoOnShutdown);
- jit_options->save_profiling_info_ =
- options.GetOrDefault(RuntimeArgumentMap::JITSaveProfilingInfo);
+ jit_options->profile_saver_options_ =
+ options.GetOrDefault(RuntimeArgumentMap::ProfileSaverOpts);
jit_options->compile_threshold_ = options.GetOrDefault(RuntimeArgumentMap::JITCompileThreshold);
if (jit_options->compile_threshold_ > std::numeric_limits<uint16_t>::max()) {
cumulative_timings_("JIT timings"),
memory_use_("Memory used for compilation", 16),
lock_("JIT memory use lock"),
- use_jit_compilation_(true),
- save_profiling_info_(false) {}
+ use_jit_compilation_(true) {}
Jit* Jit::Create(JitOptions* options, std::string* error_msg) {
- DCHECK(options->UseJitCompilation() || options->GetSaveProfilingInfo());
+ DCHECK(options->UseJitCompilation() || options->GetProfileSaverOptions().IsEnabled());
std::unique_ptr<Jit> jit(new Jit);
jit->dump_info_on_shutdown_ = options->DumpJitInfoOnShutdown();
if (jit_compiler_handle_ == nullptr && !LoadCompiler(error_msg)) {
return nullptr;
}
jit->use_jit_compilation_ = options->UseJitCompilation();
- jit->save_profiling_info_ = options->GetSaveProfilingInfo();
+ jit->profile_saver_options_ = options->GetProfileSaverOptions();
VLOG(jit) << "JIT created with initial_capacity="
<< PrettySize(options->GetCodeCacheInitialCapacity())
<< ", max_capacity=" << PrettySize(options->GetCodeCacheMaxCapacity())
<< ", compile_threshold=" << options->GetCompileThreshold()
- << ", save_profiling_info=" << options->GetSaveProfilingInfo();
+ << ", profile_saver_options=" << options->GetProfileSaverOptions();
jit->hot_method_threshold_ = options->GetCompileThreshold();
const std::vector<std::string>& code_paths,
const std::string& foreign_dex_profile_path,
const std::string& app_dir) {
- if (save_profiling_info_) {
- ProfileSaver::Start(filename, code_cache_.get(), code_paths, foreign_dex_profile_path, app_dir);
+ if (profile_saver_options_.IsEnabled()) {
+ ProfileSaver::Start(profile_saver_options_,
+ filename,
+ code_cache_.get(),
+ code_paths,
+ foreign_dex_profile_path,
+ app_dir);
}
}
void Jit::StopProfileSaver() {
- if (save_profiling_info_ && ProfileSaver::IsStarted()) {
+ if (profile_saver_options_.IsEnabled() && ProfileSaver::IsStarted()) {
ProfileSaver::Stop(dump_info_on_shutdown_);
}
}
}
Jit::~Jit() {
- DCHECK(!save_profiling_info_ || !ProfileSaver::IsStarted());
+ DCHECK(!profile_saver_options_.IsEnabled() || !ProfileSaver::IsStarted());
if (dump_info_on_shutdown_) {
DumpInfo(LOG(INFO));
}
#include "base/timing_logger.h"
#include "object_callbacks.h"
#include "offline_profiling_info.h"
+#include "jit/profile_saver_options.h"
#include "thread_pool.h"
namespace art {
return use_jit_compilation_;
}
- bool SaveProfilingInfo() const {
- return save_profiling_info_;
+ bool GetSaveProfilingInfo() const {
+ return profile_saver_options_.IsEnabled();
}
// Wait until there is no more pending compilation tasks.
std::unique_ptr<jit::JitCodeCache> code_cache_;
bool use_jit_compilation_;
- bool save_profiling_info_;
+ ProfileSaverOptions profile_saver_options_;
static bool generate_debug_info_;
uint16_t hot_method_threshold_;
uint16_t warm_method_threshold_;
bool DumpJitInfoOnShutdown() const {
return dump_info_on_shutdown_;
}
+ const ProfileSaverOptions& GetProfileSaverOptions() const {
+ return profile_saver_options_;
+ }
bool GetSaveProfilingInfo() const {
- return save_profiling_info_;
+ return profile_saver_options_.IsEnabled();
}
bool UseJitCompilation() const {
return use_jit_compilation_;
void SetUseJitCompilation(bool b) {
use_jit_compilation_ = b;
}
- void SetSaveProfilingInfo(bool b) {
- save_profiling_info_ = b;
+ void SetSaveProfilingInfo(bool save_profiling_info) {
+ profile_saver_options_.SetEnabled(save_profiling_info);
}
void SetJitAtFirstUse() {
use_jit_compilation_ = true;
uint16_t priority_thread_weight_;
size_t invoke_transition_weight_;
bool dump_info_on_shutdown_;
- bool save_profiling_info_;
+ ProfileSaverOptions profile_saver_options_;
JitOptions()
: use_jit_compilation_(false),
code_cache_initial_capacity_(0),
code_cache_max_capacity_(0),
compile_threshold_(0),
- dump_info_on_shutdown_(false),
- save_profiling_info_(false) { }
+ dump_info_on_shutdown_(false) {}
DISALLOW_COPY_AND_ASSIGN(JitOptions);
};
namespace art {
-// 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 kMinSavePeriodNs = MsToNs(20 * 1000); // 20 seconds
-static constexpr const uint64_t kSaveResolvedClassesDelayMs = 2 * 1000; // 2 seconds
-// Minimum number of JIT samples during launch to include a method into the profile.
-static constexpr const size_t kStartupMethodSamples = 1;
-
-static constexpr const uint32_t kMinimumNumberOfMethodsToSave = 10;
-static constexpr const uint32_t kMinimumNumberOfClassesToSave = 10;
-static constexpr const uint32_t kMinimumNumberOfNotificationBeforeWake =
- kMinimumNumberOfMethodsToSave;
-static constexpr const uint32_t kMaximumNumberOfNotificationBeforeWake = 50;
-
-
ProfileSaver* ProfileSaver::instance_ = nullptr;
pthread_t ProfileSaver::profiler_pthread_ = 0U;
-ProfileSaver::ProfileSaver(const std::string& output_filename,
+ProfileSaver::ProfileSaver(const ProfileSaverOptions& options,
+ const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
const std::vector<std::string>& code_paths,
const std::string& foreign_dex_profile_path,
total_number_of_foreign_dex_marks_(0),
max_number_of_profile_entries_cached_(0),
total_number_of_hot_spikes_(0),
- total_number_of_wake_ups_(0) {
+ total_number_of_wake_ups_(0),
+ options_(options) {
+ DCHECK(options_.IsEnabled());
AddTrackedLocations(output_filename, app_data_dir, code_paths);
if (!app_data_dir.empty()) {
// The application directory is used to determine which dex files are owned by app.
Thread* self = Thread::Current();
// Fetch the resolved classes for the app images after sleeping for
- // kSaveResolvedClassesDelayMs.
+ // options_.GetSaveResolvedClassesDelayMs().
// 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).
{
MutexLock mu(self, wait_lock_);
- constexpr uint64_t kSleepTime = kSaveResolvedClassesDelayMs;
- const uint64_t end_time = NanoTime() + MsToNs(kSleepTime);
+ const uint64_t end_time = NanoTime() + MsToNs(options_.GetSaveResolvedClassesDelayMs());
while (true) {
const uint64_t current_time = NanoTime();
if (current_time >= end_time) {
}
period_condition_.TimedWait(self, NsToMs(end_time - current_time), 0);
}
- total_ms_of_sleep_ += kSaveResolvedClassesDelayMs;
+ total_ms_of_sleep_ += options_.GetSaveResolvedClassesDelayMs();
}
FetchAndCacheResolvedClassesAndMethods();
// 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).
- while (kMinSavePeriodNs * 0.9 > sleep_time) {
+ uint64_t min_save_period_ns = MsToNs(options_.GetMinSavePeriodMs());
+ while (min_save_period_ns * 0.9 > sleep_time) {
{
MutexLock mu(self, wait_lock_);
- period_condition_.TimedWait(self, NsToMs(kMinSavePeriodNs - sleep_time), 0);
+ period_condition_.TimedWait(self, NsToMs(min_save_period_ns - sleep_time), 0);
sleep_time = NanoTime() - sleep_start;
}
// Check if the thread was woken up for shutdown.
jit_activity_notifications_++;
// Note that we are not as precise as we could be here but we don't want to wake the saver
// every time we see a hot method.
- if (jit_activity_notifications_ > kMinimumNumberOfNotificationBeforeWake) {
+ if (jit_activity_notifications_ > options_.GetMinNotificationBeforeWake()) {
MutexLock wait_mutex(Thread::Current(), wait_lock_);
- if ((NanoTime() - last_time_ns_saver_woke_up_) > kMinSavePeriodNs) {
+ if ((NanoTime() - last_time_ns_saver_woke_up_) > MsToNs(options_.GetMinSavePeriodMs())) {
WakeUpSaver();
}
- } else if (jit_activity_notifications_ > kMaximumNumberOfNotificationBeforeWake) {
+ } else if (jit_activity_notifications_ > options_.GetMaxNotificationBeforeWake()) {
// Make sure to wake up the saver if we see a spike in the number of notifications.
// This is a precaution to avoid "loosing" a big number of methods in case
// this is a spike with no jit after.
// Excludes native methods and classes in the boot image.
class GetMethodsVisitor : public ClassVisitor {
public:
- explicit GetMethodsVisitor(std::vector<MethodReference>* methods) : methods_(methods) {}
+ GetMethodsVisitor(std::vector<MethodReference>* methods, uint32_t startup_method_samples)
+ : methods_(methods),
+ startup_method_samples_(startup_method_samples) {}
virtual bool operator()(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_) {
if (Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) {
}
for (ArtMethod& method : klass->GetMethods(sizeof(void*))) {
if (!method.IsNative()) {
- if (method.GetCounter() >= kStartupMethodSamples ||
+ if (method.GetCounter() >= startup_method_samples_ ||
method.GetProfilingInfo(sizeof(void*)) != nullptr) {
// Have samples, add to profile.
const DexFile* dex_file = method.GetInterfaceMethodIfProxy(sizeof(void*))->GetDexFile();
private:
std::vector<MethodReference>* const methods_;
+ uint32_t startup_method_samples_;
};
void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() {
std::vector<MethodReference> methods;
{
ScopedTrace trace2("Get hot methods");
- GetMethodsVisitor visitor(&methods);
+ GetMethodsVisitor visitor(&methods, options_.GetStartupMethodSamples());
ScopedObjectAccess soa(Thread::Current());
class_linker->VisitClasses(&visitor);
VLOG(profiler) << "Methods with samples greater than "
- << kStartupMethodSamples << " = " << methods.size();
+ << options_.GetStartupMethodSamples() << " = " << methods.size();
}
MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
uint64_t total_number_of_profile_entries_cached = 0;
cached_info->GetNumberOfResolvedClasses() -
static_cast<int64_t>(last_save_number_of_classes_);
- if (delta_number_of_methods < kMinimumNumberOfMethodsToSave &&
- delta_number_of_classes < kMinimumNumberOfClassesToSave) {
+ if (delta_number_of_methods < options_.GetMinMethodsToSave() &&
+ delta_number_of_classes < options_.GetMinClassesToSave()) {
VLOG(profiler) << "Not enough information to save to: " << filename
- << " Nr of methods: " << delta_number_of_methods
- << " Nr of classes: " << delta_number_of_classes;
+ << " Number of methods: " << delta_number_of_methods
+ << " Number of classes: " << delta_number_of_classes;
total_number_of_skipped_writes_++;
continue;
}
return true;
}
-void ProfileSaver::Start(const std::string& output_filename,
+void ProfileSaver::Start(const ProfileSaverOptions& options,
+ const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
const std::vector<std::string>& code_paths,
const std::string& foreign_dex_profile_path,
const std::string& app_data_dir) {
- DCHECK(Runtime::Current()->SaveProfileInfo());
+ DCHECK(options.IsEnabled());
+ DCHECK(Runtime::Current()->GetJit() != nullptr);
DCHECK(!output_filename.empty());
DCHECK(jit_code_cache != nullptr);
VLOG(profiler) << "Starting profile saver using output file: " << output_filename
<< ". Tracking: " << Join(code_paths_to_profile, ':');
- instance_ = new ProfileSaver(output_filename,
+ instance_ = new ProfileSaver(options,
+ output_filename,
jit_code_cache,
code_paths_to_profile,
foreign_dex_profile_path,
#include "base/mutex.h"
#include "jit_code_cache.h"
#include "offline_profiling_info.h"
+#include "profile_saver_options.h"
#include "safe_map.h"
namespace art {
public:
// Starts the profile saver thread if not already started.
// If the saver is already running it adds (output_filename, code_paths) to its tracked locations.
- static void Start(const std::string& output_filename,
+ static void Start(const ProfileSaverOptions& options,
+ const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
const std::vector<std::string>& code_paths,
const std::string& foreign_dex_profile_path,
uint16_t method_idx);
private:
- ProfileSaver(const std::string& output_filename,
+ ProfileSaver(const ProfileSaverOptions& options,
+ const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
const std::vector<std::string>& code_paths,
const std::string& foreign_dex_profile_path,
uint64_t total_number_of_hot_spikes_;
uint64_t total_number_of_wake_ups_;
+ const ProfileSaverOptions options_;
DISALLOW_COPY_AND_ASSIGN(ProfileSaver);
};
--- /dev/null
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_JIT_PROFILE_SAVER_OPTIONS_H_
+#define ART_RUNTIME_JIT_PROFILE_SAVER_OPTIONS_H_
+
+#include <string>
+#include <ostream>
+
+namespace art {
+
+struct ProfileSaverOptions {
+ public:
+ static constexpr uint32_t kMinSavePeriodMs = 20 * 1000; // 20 seconds
+ static constexpr uint32_t kSaveResolvedClassesDelayMs = 2 * 1000; // 2 seconds
+ // Minimum number of JIT samples during launch to include a method into the profile.
+ static constexpr uint32_t kStartupMethodSamples = 1;
+ static constexpr uint32_t kMinMethodsToSave = 10;
+ static constexpr uint32_t kMinClassesToSave = 10;
+ static constexpr uint32_t kMinNotificationBeforeWake = 10;
+ static constexpr uint32_t kMaxNotificationBeforeWake = 50;
+
+ ProfileSaverOptions() :
+ enabled_(false),
+ min_save_period_ms_(kMinSavePeriodMs),
+ save_resolved_classes_delay_ms_(kSaveResolvedClassesDelayMs),
+ startup_method_samples_(kStartupMethodSamples),
+ min_methods_to_save_(kMinMethodsToSave),
+ min_classes_to_save_(kMinClassesToSave),
+ min_notification_before_wake_(kMinNotificationBeforeWake),
+ max_notification_before_wake_(kMaxNotificationBeforeWake) {}
+
+ ProfileSaverOptions(
+ bool enabled,
+ uint32_t min_save_period_ms,
+ uint32_t save_resolved_classes_delay_ms,
+ uint32_t startup_method_samples,
+ uint32_t min_methods_to_save,
+ uint32_t min_classes_to_save,
+ uint32_t min_notification_before_wake,
+ uint32_t max_notification_before_wake):
+ enabled_(enabled),
+ min_save_period_ms_(min_save_period_ms),
+ save_resolved_classes_delay_ms_(save_resolved_classes_delay_ms),
+ startup_method_samples_(startup_method_samples),
+ min_methods_to_save_(min_methods_to_save),
+ min_classes_to_save_(min_classes_to_save),
+ min_notification_before_wake_(min_notification_before_wake),
+ max_notification_before_wake_(max_notification_before_wake) {}
+
+ bool IsEnabled() const {
+ return enabled_;
+ }
+ void SetEnabled(bool enabled) {
+ enabled_ = enabled;
+ }
+
+ uint32_t GetMinSavePeriodMs() const {
+ return min_save_period_ms_;
+ }
+ uint32_t GetSaveResolvedClassesDelayMs() const {
+ return save_resolved_classes_delay_ms_;
+ }
+ uint32_t GetStartupMethodSamples() const {
+ return startup_method_samples_;
+ }
+ uint32_t GetMinMethodsToSave() const {
+ return min_methods_to_save_;
+ }
+ uint32_t GetMinClassesToSave() const {
+ return min_classes_to_save_;
+ }
+ uint32_t GetMinNotificationBeforeWake() const {
+ return min_notification_before_wake_;
+ }
+ uint32_t GetMaxNotificationBeforeWake() const {
+ return max_notification_before_wake_;
+ }
+
+ friend std::ostream & operator<<(std::ostream &os, const ProfileSaverOptions& pso) {
+ os << "enabled_" << pso.enabled_
+ << ", min_save_period_ms_" << pso.min_save_period_ms_
+ << ", save_resolved_classes_delay_ms_" << pso.save_resolved_classes_delay_ms_
+ << ", startup_method_samples_" << pso.startup_method_samples_
+ << ", 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_;
+ return os;
+ }
+
+ bool enabled_;
+ uint32_t min_save_period_ms_;
+ uint32_t save_resolved_classes_delay_ms_;
+ uint32_t startup_method_samples_;
+ uint32_t min_methods_to_save_;
+ uint32_t min_classes_to_save_;
+ uint32_t min_notification_before_wake_;
+ uint32_t max_notification_before_wake_;
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_JIT_PROFILE_SAVER_OPTIONS_H_
.WithType<unsigned int>()
.IntoKey(M::JITInvokeTransitionWeight)
.Define("-Xjitsaveprofilinginfo")
- .WithValue(true)
- .IntoKey(M::JITSaveProfilingInfo)
+ .WithType<ProfileSaverOptions>()
+ .AppendValues()
+ .IntoKey(M::ProfileSaverOpts)
+ .Define("-Xps-_") // profile saver options -Xps-<key>:<value>
+ .WithType<ProfileSaverOptions>()
+ .AppendValues()
+ .IntoKey(M::ProfileSaverOpts) // NOTE: Appends into same key as -Xjitsaveprofilinginfo
.Define("-XX:HspaceCompactForOOMMinIntervalMs=_") // in ms
.WithType<MillisecondsToNanoseconds>() // store as ns
.IntoKey(M::HSpaceCompactForOOMMinIntervalsMs)
{"wallclock", TraceClockSource::kWall},
{"dualclock", TraceClockSource::kDual}})
.IntoKey(M::ProfileClock)
- .Define("-Xenable-profiler")
- .WithType<TestProfilerOptions>()
- .AppendValues()
- .IntoKey(M::ProfilerOpts) // NOTE: Appends into same key as -Xprofile-*
- .Define("-Xprofile-_") // -Xprofile-<key>:<value>
- .WithType<TestProfilerOptions>()
- .AppendValues()
- .IntoKey(M::ProfilerOpts) // NOTE: Appends into same key as -Xenable-profiler
.Define("-Xcompiler:_")
.WithType<std::string>()
.IntoKey(M::Compiler)
UsageMessage(stream, " -Xmethod-trace\n");
UsageMessage(stream, " -Xmethod-trace-file:filename");
UsageMessage(stream, " -Xmethod-trace-file-size:integervalue\n");
- UsageMessage(stream, " -Xenable-profiler\n");
- UsageMessage(stream, " -Xprofile-filename:filename\n");
- UsageMessage(stream, " -Xprofile-period:integervalue\n");
- UsageMessage(stream, " -Xprofile-duration:integervalue\n");
- UsageMessage(stream, " -Xprofile-interval:integervalue\n");
- UsageMessage(stream, " -Xprofile-backoff:doublevalue\n");
- UsageMessage(stream, " -Xprofile-start-immediately\n");
- UsageMessage(stream, " -Xprofile-top-k-threshold:doublevalue\n");
- UsageMessage(stream, " -Xprofile-top-k-change-threshold:doublevalue\n");
- UsageMessage(stream, " -Xprofile-type:{method,stack}\n");
- UsageMessage(stream, " -Xprofile-max-stack-depth:integervalue\n");
+ UsageMessage(stream, " -Xps-min-save-period-ms:integervalue\n");
+ UsageMessage(stream, " -Xps-save-resolved-classes-delay-ms:integervalue\n");
+ UsageMessage(stream, " -Xps-startup-method-samples:integervalue\n");
+ UsageMessage(stream, " -Xps-min-methods-to-save:integervalue\n");
+ UsageMessage(stream, " -Xps-min-classes-to-save:integervalue\n");
+ UsageMessage(stream, " -Xps-min-notification-before-wake:integervalue\n");
+ UsageMessage(stream, " -Xps-max-notification-before-wake:integervalue\n");
UsageMessage(stream, " -Xcompiler:filename\n");
UsageMessage(stream, " -Xcompiler-option dex2oat-option\n");
UsageMessage(stream, " -Ximage-compiler-option dex2oat-option\n");
#include "gc/collector_type.h"
#include "gc/space/large_object_space.h"
#include "arch/instruction_set.h"
-#include "profiler_options.h"
+#include "jit/profile_saver_options.h"
#include "runtime_options.h"
namespace art {
+++ /dev/null
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_RUNTIME_PROFILER_OPTIONS_H_
-#define ART_RUNTIME_PROFILER_OPTIONS_H_
-
-#include <string>
-#include <ostream>
-
-namespace art {
-
-enum ProfileDataType {
- kProfilerMethod, // Method only
- kProfilerBoundedStack, // Methods with Dex PC on top of the stack
-};
-std::ostream& operator<<(std::ostream& os, const ProfileDataType& rhs);
-
-class ProfilerOptions {
- public:
- static constexpr bool kDefaultEnabled = false;
- static constexpr uint32_t kDefaultPeriodS = 10;
- static constexpr uint32_t kDefaultDurationS = 20;
- static constexpr uint32_t kDefaultIntervalUs = 500;
- static constexpr double kDefaultBackoffCoefficient = 2.0;
- static constexpr bool kDefaultStartImmediately = false;
- static constexpr double kDefaultTopKThreshold = 90.0;
- static constexpr double kDefaultChangeInTopKThreshold = 10.0;
- static constexpr ProfileDataType kDefaultProfileData = kProfilerMethod;
- static constexpr uint32_t kDefaultMaxStackDepth = 3;
-
- ProfilerOptions() :
- enabled_(kDefaultEnabled),
- period_s_(kDefaultPeriodS),
- duration_s_(kDefaultDurationS),
- interval_us_(kDefaultIntervalUs),
- backoff_coefficient_(kDefaultBackoffCoefficient),
- start_immediately_(kDefaultStartImmediately),
- top_k_threshold_(kDefaultTopKThreshold),
- top_k_change_threshold_(kDefaultChangeInTopKThreshold),
- profile_type_(kDefaultProfileData),
- max_stack_depth_(kDefaultMaxStackDepth) {}
-
- ProfilerOptions(bool enabled,
- uint32_t period_s,
- uint32_t duration_s,
- uint32_t interval_us,
- double backoff_coefficient,
- bool start_immediately,
- double top_k_threshold,
- double top_k_change_threshold,
- ProfileDataType profile_type,
- uint32_t max_stack_depth):
- enabled_(enabled),
- period_s_(period_s),
- duration_s_(duration_s),
- interval_us_(interval_us),
- backoff_coefficient_(backoff_coefficient),
- start_immediately_(start_immediately),
- top_k_threshold_(top_k_threshold),
- top_k_change_threshold_(top_k_change_threshold),
- profile_type_(profile_type),
- max_stack_depth_(max_stack_depth) {}
-
- bool IsEnabled() const {
- return enabled_;
- }
-
- uint32_t GetPeriodS() const {
- return period_s_;
- }
-
- uint32_t GetDurationS() const {
- return duration_s_;
- }
-
- uint32_t GetIntervalUs() const {
- return interval_us_;
- }
-
- double GetBackoffCoefficient() const {
- return backoff_coefficient_;
- }
-
- bool GetStartImmediately() const {
- return start_immediately_;
- }
-
- double GetTopKThreshold() const {
- return top_k_threshold_;
- }
-
- double GetTopKChangeThreshold() const {
- return top_k_change_threshold_;
- }
-
- ProfileDataType GetProfileType() const {
- return profile_type_;
- }
-
- uint32_t GetMaxStackDepth() const {
- return max_stack_depth_;
- }
-
- private:
- friend std::ostream & operator<<(std::ostream &os, const ProfilerOptions& po) {
- os << "enabled=" << po.enabled_
- << ", period_s=" << po.period_s_
- << ", duration_s=" << po.duration_s_
- << ", interval_us=" << po.interval_us_
- << ", backoff_coefficient=" << po.backoff_coefficient_
- << ", start_immediately=" << po.start_immediately_
- << ", top_k_threshold=" << po.top_k_threshold_
- << ", top_k_change_threshold=" << po.top_k_change_threshold_
- << ", profile_type=" << po.profile_type_
- << ", max_stack_depth=" << po.max_stack_depth_;
- return os;
- }
-
- friend class ParsedOptions;
-
- // Whether or not the applications should be profiled.
- bool enabled_;
- // Generate profile every n seconds.
- uint32_t period_s_;
- // Run profile for n seconds.
- uint32_t duration_s_;
- // Microseconds between samples.
- uint32_t interval_us_;
- // Coefficient to exponential backoff.
- double backoff_coefficient_;
- // Whether the profile should start upon app startup or be delayed by some random offset.
- bool start_immediately_;
- // Top K% of samples that are considered relevant when deciding if the app should be recompiled.
- double top_k_threshold_;
- // How much the top K% samples needs to change in order for the app to be recompiled.
- double top_k_change_threshold_;
- // The type of profile data dumped to the disk.
- ProfileDataType profile_type_;
- // The max depth of the stack collected by the profiler
- uint32_t max_stack_depth_;
-};
-
-} // namespace art
-
-
-#endif // ART_RUNTIME_PROFILER_OPTIONS_H_
VLOG(startup) << "Runtime::Start exiting";
finished_starting_ = true;
- if (profiler_options_.IsEnabled() && !profile_output_filename_.empty()) {
- // User has asked for a profile using -Xenable-profiler.
- // Create the profile file if it doesn't exist.
- int fd = open(profile_output_filename_.c_str(), O_RDWR|O_CREAT|O_EXCL, 0660);
- if (fd >= 0) {
- close(fd);
- } else if (errno != EEXIST) {
- LOG(WARNING) << "Failed to access the profile file. Profiler disabled.";
- }
- }
-
if (trace_config_.get() != nullptr && trace_config_->trace_file != "") {
ScopedThreadStateChange tsc(self, kWaitingForMethodTracingStart);
Trace::Start(trace_config_->trace_file.c_str(),
Trace::TraceOutputMode::kFile;
}
- {
- auto&& profiler_options = runtime_options.ReleaseOrDefault(Opt::ProfilerOpts);
- profile_output_filename_ = profiler_options.output_file_name_;
-
- // TODO: Don't do this, just change ProfilerOptions to include the output file name?
- ProfilerOptions other_options(
- profiler_options.enabled_,
- profiler_options.period_s_,
- profiler_options.duration_s_,
- profiler_options.interval_us_,
- profiler_options.backoff_coefficient_,
- profiler_options.start_immediately_,
- profiler_options.top_k_threshold_,
- profiler_options.top_k_change_threshold_,
- profiler_options.profile_type_,
- profiler_options.max_stack_depth_);
-
- profiler_options_ = other_options;
- }
-
// TODO: move this to just be an Trace::Start argument
Trace::SetDefaultClockSource(runtime_options.GetOrDefault(Opt::ProfileClock));
return;
}
- profile_output_filename_ = profile_output_filename;
jit_->StartProfileSaver(profile_output_filename,
code_paths,
foreign_dex_profile_path,
return (jit_ != nullptr) && jit_->UseJitCompilation();
}
-// Returns true if profile saving is enabled. GetJit() will be not null in this case.
-bool Runtime::SaveProfileInfo() const {
- return (jit_ != nullptr) && jit_->SaveProfilingInfo();
-}
-
} // namespace art
#include "object_callbacks.h"
#include "offsets.h"
#include "process_state.h"
-#include "profiler_options.h"
#include "quick/quick_method_frame_info.h"
#include "runtime_stats.h"
#include "safe_map.h"
return image_location_;
}
- const ProfilerOptions& GetProfilerOptions() const {
- return profiler_options_;
- }
-
// Starts a runtime, which may cause threads to be started and code to run.
bool Start() UNLOCK_FUNCTION(Locks::mutator_lock_);
// Returns true if JIT compilations are enabled. GetJit() will be not null in this case.
bool UseJitCompilation() const;
- // Returns true if profile saving is enabled. GetJit() will be not null in this case.
- bool SaveProfileInfo() const;
void PreZygoteFork();
bool InitZygote();
const bool is_running_on_memory_tool_;
- std::string profile_output_filename_;
- ProfilerOptions profiler_options_;
-
std::unique_ptr<TraceConfig> trace_config_;
instrumentation::Instrumentation instrumentation_;
RUNTIME_OPTIONS_KEY (unsigned int, JITInvokeTransitionWeight)
RUNTIME_OPTIONS_KEY (MemoryKiB, JITCodeCacheInitialCapacity, jit::JitCodeCache::kInitialCapacity)
RUNTIME_OPTIONS_KEY (MemoryKiB, JITCodeCacheMaxCapacity, jit::JitCodeCache::kMaxCapacity)
-RUNTIME_OPTIONS_KEY (bool, JITSaveProfilingInfo, false)
RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \
HSpaceCompactForOOMMinIntervalsMs,\
MsToNs(100 * 1000)) // 100s
RUNTIME_OPTIONS_KEY (unsigned int, MethodTraceFileSize, 10 * MB)
RUNTIME_OPTIONS_KEY (Unit, MethodTraceStreaming)
RUNTIME_OPTIONS_KEY (TraceClockSource, ProfileClock, kDefaultTraceClockSource) // -Xprofile:
-RUNTIME_OPTIONS_KEY (TestProfilerOptions, ProfilerOpts) // -Xenable-profiler, -Xprofile-*
+RUNTIME_OPTIONS_KEY (ProfileSaverOptions, ProfileSaverOpts) // -Xjitsaveprofilinginfo, -Xps-*
RUNTIME_OPTIONS_KEY (std::string, Compiler)
RUNTIME_OPTIONS_KEY (std::vector<std::string>, \
CompilerOptions) // -Xcompiler-option ...
#include "jit/jit_code_cache.h"
#include "gc/collector_type.h"
#include "gc/space/large_object_space.h"
-#include "profiler_options.h"
#include "arch/instruction_set.h"
+#include "jit/profile_saver_options.h"
#include "verifier/verify_mode.h"
#include <stdio.h>
#include <stdarg.h>
class DexFile;
struct XGcOption;
struct BackgroundGcOption;
-struct TestProfilerOptions;
#define DECLARE_KEY(Type, Name) static const Key<Type> Name