diff options
-rw-r--r-- | compiler/dex/frontend.cc | 2 | ||||
-rw-r--r-- | compiler/dex/mir_analysis.cc | 2 | ||||
-rw-r--r-- | compiler/driver/compiler_driver.cc | 29 | ||||
-rw-r--r-- | compiler/driver/compiler_driver.h | 4 | ||||
-rw-r--r-- | compiler/driver/compiler_options.h | 20 | ||||
-rw-r--r-- | dex2oat/dex2oat.cc | 38 | ||||
-rw-r--r-- | runtime/native/dalvik_system_DexFile.cc | 6 | ||||
-rw-r--r-- | runtime/native/dalvik_system_VMRuntime.cc | 12 | ||||
-rw-r--r-- | runtime/parsed_options.cc | 33 | ||||
-rw-r--r-- | runtime/parsed_options.h | 7 | ||||
-rw-r--r-- | runtime/profiler.cc | 74 | ||||
-rw-r--r-- | runtime/profiler.h | 62 | ||||
-rw-r--r-- | runtime/profiler_options.h | 131 | ||||
-rw-r--r-- | runtime/runtime.cc | 33 | ||||
-rw-r--r-- | runtime/runtime.h | 17 | ||||
-rw-r--r-- | runtime/utils.cc | 19 | ||||
-rw-r--r-- | runtime/utils.h | 4 |
17 files changed, 300 insertions, 193 deletions
diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc index 3d22774..7848b06 100644 --- a/compiler/dex/frontend.cc +++ b/compiler/dex/frontend.cc @@ -903,7 +903,7 @@ static CompiledMethod* CompileMethod(CompilerDriver& driver, cu.mir_graph->EnableOpcodeCounting(); } - // Check early if we should skip this compilation if using the profiled filter. + // Check early if we should skip this compilation if the profiler is enabled. if (cu.compiler_driver->ProfilePresent()) { std::string methodname = PrettyMethod(method_idx, dex_file); if (cu.mir_graph->SkipCompilation(methodname)) { diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc index 7129f8a..2ec17de 100644 --- a/compiler/dex/mir_analysis.cc +++ b/compiler/dex/mir_analysis.cc @@ -1015,7 +1015,7 @@ bool MIRGraph::SkipCompilation() { return true; } - if (!compiler_options.IsCompilationEnabled() || compiler_filter == CompilerOptions::kProfiled) { + if (!compiler_options.IsCompilationEnabled()) { return true; } diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 15a086b..1cfd194 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -55,7 +55,6 @@ #include "thread_pool.h" #include "trampolines/trampoline_compiler.h" #include "transaction.h" -#include "utils.h" #include "verifier/method_verifier.h" #include "verifier/method_verifier-inl.h" @@ -331,7 +330,7 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, bool image, DescriptorSet* image_classes, size_t thread_count, bool dump_stats, bool dump_passes, CumulativeLogger* timer, std::string profile_file) - : profile_ok_(false), compiler_options_(compiler_options), + : profile_present_(false), compiler_options_(compiler_options), verification_results_(verification_results), method_inliner_map_(method_inliner_map), compiler_(Compiler::Create(this, compiler_kind)), @@ -365,11 +364,6 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, CHECK_PTHREAD_CALL(pthread_key_create, (&tls_key_, NULL), "compiler tls key"); - // Read the profile file if one is provided. - if (profile_file != "") { - profile_ok_ = profile_file_.LoadFile(profile_file); - } - dex_to_dex_compiler_ = reinterpret_cast<DexToDexCompilerFn>(ArtCompileDEX); compiler_->Init(); @@ -385,6 +379,16 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, if (compiler_options->GetGenerateGDBInformation()) { cfi_info_.reset(compiler_->GetCallFrameInformationInitialization(*this)); } + + // Read the profile file if one is provided. + if (!profile_file.empty()) { + profile_present_ = profile_file_.LoadFile(profile_file); + if (profile_present_) { + LOG(INFO) << "Using profile data form file " << profile_file; + } else { + LOG(INFO) << "Failed to load profile file " << profile_file; + } + } } std::vector<uint8_t>* CompilerDriver::DeduplicateCode(const std::vector<uint8_t>& code) { @@ -2044,7 +2048,7 @@ void CompilerDriver::InstructionSetToLLVMTarget(InstructionSet instruction_set, } bool CompilerDriver::SkipCompilation(const std::string& method_name) { - if (!profile_ok_) { + if (!profile_present_) { return false; } // First find the method in the profile file. @@ -2056,18 +2060,17 @@ bool CompilerDriver::SkipCompilation(const std::string& method_name) { } // Methods that comprise top_k_threshold % of the total samples will be compiled. - double top_k_threshold = GetDoubleProperty("dalvik.vm.profiler.compile_thr", 10.0, 90.0, 90.0); - // Compare against the start of the topK percentage bucket just in case the threshold // falls inside a bucket. - bool compile = data.GetTopKUsedPercentage() - data.GetUsedPercent() <= top_k_threshold; + bool compile = data.GetTopKUsedPercentage() - data.GetUsedPercent() + <= compiler_options_->GetTopKProfileThreshold(); if (compile) { LOG(INFO) << "compiling method " << method_name << " because its usage is part of top " << data.GetTopKUsedPercentage() << "% with a percent of " << data.GetUsedPercent() << "%" - << " (topKThreshold=" << top_k_threshold << ")"; + << " (topKThreshold=" << compiler_options_->GetTopKProfileThreshold() << ")"; } else { VLOG(compiler) << "not compiling method " << method_name << " because it's not part of leading " - << top_k_threshold << "% samples)"; + << compiler_options_->GetTopKProfileThreshold() << "% samples)"; } return !compile; } diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index e952f63..fad6798 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -143,7 +143,7 @@ class CompilerDriver { } bool ProfilePresent() const { - return profile_ok_; + return profile_present_; } // Are we compiling and creating an image file? @@ -596,7 +596,7 @@ class CompilerDriver { } ProfileFile profile_file_; - bool profile_ok_; + bool profile_present_; // Should the compiler run on this method given profile information? bool SkipCompilation(const std::string& method_name); diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index 20c6bc8..05a9ac7 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -24,7 +24,6 @@ class CompilerOptions { enum CompilerFilter { kVerifyNone, // Skip verification and compile nothing except JNI stubs. kInterpretOnly, // Compile nothing except JNI stubs. - kProfiled, // Compile based on profile. kSpace, // Maximize space savings. kBalanced, // Try to get the best performance return on compilation investment. kSpeed, // Maximize runtime performance. @@ -33,7 +32,7 @@ class CompilerOptions { // Guide heuristics to determine whether to compile method if profile data not available. #if ART_SMALL_MODE - static const CompilerFilter kDefaultCompilerFilter = kProfiled; + static const CompilerFilter kDefaultCompilerFilter = kInterpretOnly; #else static const CompilerFilter kDefaultCompilerFilter = kSpeed; #endif @@ -42,6 +41,7 @@ class CompilerOptions { static const size_t kDefaultSmallMethodThreshold = 60; static const size_t kDefaultTinyMethodThreshold = 20; static const size_t kDefaultNumDexMethodsThreshold = 900; + static constexpr double kDefaultTopKProfileThreshold = 90.0; CompilerOptions() : compiler_filter_(kDefaultCompilerFilter), @@ -50,7 +50,8 @@ class CompilerOptions { small_method_threshold_(kDefaultSmallMethodThreshold), tiny_method_threshold_(kDefaultTinyMethodThreshold), num_dex_methods_threshold_(kDefaultNumDexMethodsThreshold), - generate_gdb_information_(false) + generate_gdb_information_(false), + top_k_profile_threshold_(kDefaultTopKProfileThreshold) #ifdef ART_SEA_IR_MODE , sea_ir_mode_(false) #endif @@ -62,7 +63,8 @@ class CompilerOptions { size_t small_method_threshold, size_t tiny_method_threshold, size_t num_dex_methods_threshold, - bool generate_gdb_information + bool generate_gdb_information, + double top_k_profile_threshold #ifdef ART_SEA_IR_MODE , bool sea_ir_mode #endif @@ -73,7 +75,8 @@ class CompilerOptions { small_method_threshold_(small_method_threshold), tiny_method_threshold_(tiny_method_threshold), num_dex_methods_threshold_(num_dex_methods_threshold), - generate_gdb_information_(generate_gdb_information) + generate_gdb_information_(generate_gdb_information), + top_k_profile_threshold_(top_k_profile_threshold) #ifdef ART_SEA_IR_MODE , sea_ir_mode_(sea_ir_mode) #endif @@ -132,6 +135,10 @@ class CompilerOptions { return num_dex_methods_threshold_; } + double GetTopKProfileThreshold() const { + return top_k_profile_threshold_; + } + #ifdef ART_SEA_IR_MODE bool GetSeaIrMode(); #endif @@ -148,7 +155,8 @@ class CompilerOptions { size_t tiny_method_threshold_; size_t num_dex_methods_threshold_; bool generate_gdb_information_; - + // When using a profile file only the top K% of the profiled samples will be compiled. + double top_k_profile_threshold_; #ifdef ART_SEA_IR_MODE bool sea_ir_mode_; #endif diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index c6b1aa5..e75e6d2 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -703,6 +703,38 @@ static InstructionSetFeatures ParseFeatureList(std::string str) { return result; } +void ParseStringAfterChar(const std::string& s, char c, std::string* parsed_value) { + std::string::size_type colon = s.find(c); + if (colon == std::string::npos) { + Usage("Missing char %c in option %s\n", c, s.c_str()); + } + // Add one to remove the char we were trimming until. + *parsed_value = s.substr(colon + 1); +} + +void ParseDouble(const std::string& option, char after_char, + double min, double max, double* parsed_value) { + std::string substring; + ParseStringAfterChar(option, after_char, &substring); + bool sane_val = true; + double value; + if (false) { + // TODO: this doesn't seem to work on the emulator. b/15114595 + std::stringstream iss(substring); + iss >> value; + // Ensure that we have a value, there was no cruft after it and it satisfies a sensible range. + sane_val = iss.eof() && (value >= min) && (value <= max); + } else { + char* end = nullptr; + value = strtod(substring.c_str(), &end); + sane_val = *end == '\0' && value >= min && value <= max; + } + if (!sane_val) { + Usage("Invalid double value %s for option %s\n", substring.c_str(), option.c_str()); + } + *parsed_value = value; +} + static int dex2oat(int argc, char** argv) { original_argc = argc; original_argv = argv; @@ -755,6 +787,7 @@ static int dex2oat(int argc, char** argv) { // Profile file to use std::string profile_file; + double top_k_profile_threshold = CompilerOptions::kDefaultTopKProfileThreshold; bool is_host = false; bool dump_stats = false; @@ -918,6 +951,8 @@ static int dex2oat(int argc, char** argv) { VLOG(compiler) << "dex2oat: profile file is " << profile_file; } else if (option == "--no-profile-file") { // No profile + } else if (option.starts_with("--top-k-profile-threshold=")) { + ParseDouble(option.data(), '=', 10.0, 90.0, &top_k_profile_threshold); } else if (option == "--print-pass-names") { PassDriverMEOpts::PrintPassNames(); } else if (option.starts_with("--disable-passes=")) { @@ -1063,7 +1098,8 @@ static int dex2oat(int argc, char** argv) { small_method_threshold, tiny_method_threshold, num_dex_methods_threshold, - generate_gdb_information + generate_gdb_information, + top_k_profile_threshold #ifdef ART_SEA_IR_MODE , compiler_options.sea_ir_ = true; #endif diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index 585c88e..e3fbd5c 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -328,7 +328,7 @@ static jboolean IsDexOptNeededInternal(JNIEnv* env, const char* filename, // If the 'defer' argument is true then this will be retried later. In this case we // need to make sure that the profile file copy is not made so that we will get the // same result second time. - if (pkgname != nullptr) { + if (Runtime::Current()->GetProfilerOptions().IsEnabled() && (pkgname != nullptr)) { const std::string profile_file = GetDalvikCacheOrDie("profiles", false /* create_if_absent */) + std::string("/") + pkgname; const std::string profile_cache_dir = GetDalvikCacheOrDie("profile-cache", @@ -357,8 +357,8 @@ static jboolean IsDexOptNeededInternal(JNIEnv* env, const char* filename, // There is a previous profile file. Check if the profile has changed significantly. // A change in profile is considered significant if X% (change_thr property) of the top K% // (compile_thr property) samples has changed. - double top_k_threshold = GetDoubleProperty("dalvik.vm.profiler.dex2oat.compile_thr", 10.0, 90.0, 90.0); - double change_threshold = GetDoubleProperty("dalvik.vm.profiler.dex2oat.change_thr", 1.0, 90.0, 10.0); + double top_k_threshold = Runtime::Current()->GetProfilerOptions().GetTopKThreshold(); + double change_threshold = Runtime::Current()->GetProfilerOptions().GetTopKChangeThreshold(); double change_percent = 0.0; ProfileFile new_profile, old_profile; bool new_ok = new_profile.LoadFile(profile_file); diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index e5cc671..63d7c77 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -510,15 +510,13 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) { * process name. We use this information to start up the sampling profiler for * for ART. */ -static void VMRuntime_registerAppInfo(JNIEnv* env, jclass, jstring pkgName, jstring appDir, jstring procName) { +static void VMRuntime_registerAppInfo(JNIEnv* env, jclass, jstring pkgName, + jstring appDir, jstring procName) { const char *pkgNameChars = env->GetStringUTFChars(pkgName, NULL); - const char *appDirChars = env->GetStringUTFChars(appDir, NULL); - const char *procNameChars = env->GetStringUTFChars(procName, NULL); - std::string profileFile = StringPrintf("/data/dalvik-cache/profiles/%s", pkgNameChars); - Runtime::Current()->StartProfiler(profileFile.c_str(), procNameChars); - env->ReleaseStringUTFChars(appDir, appDirChars); - env->ReleaseStringUTFChars(procName, procNameChars); + + Runtime::Current()->StartProfiler(profileFile.c_str()); + env->ReleaseStringUTFChars(pkgName, pkgNameChars); } diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index db2a61b..fac9965 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -248,12 +248,6 @@ bool ParsedOptions::Parse(const Runtime::Options& options, bool ignore_unrecogni method_trace_file_ = "/data/method-trace-file.bin"; method_trace_file_size_ = 10 * MB; - profile_ = false; - profile_period_s_ = 10; // Seconds. - profile_duration_s_ = 20; // Seconds. - profile_interval_us_ = 500; // Microseconds. - profile_backoff_coefficient_ = 2.0; - profile_start_immediately_ = true; profile_clock_source_ = kDefaultProfilerClockSource; verify_ = true; @@ -534,29 +528,38 @@ bool ParsedOptions::Parse(const Runtime::Options& options, bool ignore_unrecogni Trace::SetDefaultClockSource(kProfilerClockSourceWall); } else if (option == "-Xprofile:dualclock") { Trace::SetDefaultClockSource(kProfilerClockSourceDual); + } else if (option == "-Xenable-profiler") { + profiler_options_.enabled_ = true; } else if (StartsWith(option, "-Xprofile-filename:")) { if (!ParseStringAfterChar(option, ':', &profile_output_filename_)) { return false; } - profile_ = true; } else if (StartsWith(option, "-Xprofile-period:")) { - if (!ParseUnsignedInteger(option, ':', &profile_period_s_)) { + if (!ParseUnsignedInteger(option, ':', &profiler_options_.period_s_)) { return false; } } else if (StartsWith(option, "-Xprofile-duration:")) { - if (!ParseUnsignedInteger(option, ':', &profile_duration_s_)) { + if (!ParseUnsignedInteger(option, ':', &profiler_options_.duration_s_)) { return false; } } else if (StartsWith(option, "-Xprofile-interval:")) { - if (!ParseUnsignedInteger(option, ':', &profile_interval_us_)) { + if (!ParseUnsignedInteger(option, ':', &profiler_options_.interval_us_)) { return false; } } else if (StartsWith(option, "-Xprofile-backoff:")) { - if (!ParseDouble(option, ':', 1.0, 10.0, &profile_backoff_coefficient_)) { + if (!ParseDouble(option, ':', 1.0, 10.0, &profiler_options_.backoff_coefficient_)) { + return false; + } + } else if (option == "-Xprofile-start-immediately") { + profiler_options_.start_immediately_ = true; + } else if (StartsWith(option, "-Xprofile-top-k-threshold:")) { + if (!ParseDouble(option, ':', 10.0, 90.0, &profiler_options_.top_k_threshold_)) { + return false; + } + } else if (StartsWith(option, "-Xprofile-top-k-change-threshold:")) { + if (!ParseDouble(option, ':', 10.0, 90.0, &profiler_options_.top_k_change_threshold_)) { return false; } - } else if (option == "-Xprofile-start-lazy") { - profile_start_immediately_ = false; } else if (StartsWith(option, "-implicit-checks:")) { std::string checks; if (!ParseStringAfterChar(option, ':', &checks)) { @@ -791,11 +794,15 @@ void ParsedOptions::Usage(const char* fmt, ...) { 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, " -Xcompiler:filename\n"); UsageMessage(stream, " -Xcompiler-option dex2oat-option\n"); UsageMessage(stream, " -Ximage-compiler-option dex2oat-option\n"); diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h index 25fc12a..a27eec6 100644 --- a/runtime/parsed_options.h +++ b/runtime/parsed_options.h @@ -77,13 +77,8 @@ class ParsedOptions { std::string compiler_executable_; std::vector<std::string> compiler_options_; std::vector<std::string> image_compiler_options_; - bool profile_; + ProfilerOptions profiler_options_; std::string profile_output_filename_; - uint32_t profile_period_s_; - uint32_t profile_duration_s_; - uint32_t profile_interval_us_; - double profile_backoff_coefficient_; - bool profile_start_immediately_; ProfilerClockSource profile_clock_source_; bool verify_; InstructionSet image_isa_; diff --git a/runtime/profiler.cc b/runtime/profiler.cc index 75db9da..80ce205 100644 --- a/runtime/profiler.cc +++ b/runtime/profiler.cc @@ -53,7 +53,6 @@ BackgroundMethodSamplingProfiler* BackgroundMethodSamplingProfiler::profiler_ = pthread_t BackgroundMethodSamplingProfiler::profiler_pthread_ = 0U; volatile bool BackgroundMethodSamplingProfiler::shutting_down_ = false; - // TODO: this profiler runs regardless of the state of the machine. Maybe we should use the // wakelock or something to modify the run characteristics. This can be done when we // have some performance data after it's been used for a while. @@ -126,6 +125,7 @@ void* BackgroundMethodSamplingProfiler::RunProfilerThread(void* arg) { Thread* self = Thread::Current(); + double backoff = 1.0; while (true) { if (ShuttingDown(self)) { break; @@ -133,13 +133,13 @@ void* BackgroundMethodSamplingProfiler::RunProfilerThread(void* arg) { { // wait until we need to run another profile - uint64_t delay_secs = profiler->period_s_ * profiler->backoff_factor_; + uint64_t delay_secs = profiler->options_.GetPeriodS() * backoff; // Add a startup delay to prevent all the profiles running at once. delay_secs += startup_delay; // Immediate startup for benchmarking? - if (profiler->start_immediately_ && startup_delay > 0) { + if (profiler->options_.GetStartImmediately() && startup_delay > 0) { delay_secs = 0; } @@ -150,10 +150,7 @@ void* BackgroundMethodSamplingProfiler::RunProfilerThread(void* arg) { profiler->period_condition_.TimedWait(self, delay_secs * 1000, 0); // Expand the backoff by its coefficient, but don't go beyond the max. - double new_backoff = profiler->backoff_factor_ * profiler->backoff_coefficient_; - if (new_backoff < kMaxBackoffSecs) { - profiler->backoff_factor_ = new_backoff; - } + backoff = std::min(backoff * profiler->options_.GetBackoffCoefficient(), kMaxBackoffSecs); } if (ShuttingDown(self)) { @@ -162,11 +159,11 @@ void* BackgroundMethodSamplingProfiler::RunProfilerThread(void* arg) { uint64_t start_us = MicroTime(); - uint64_t end_us = start_us + profiler->duration_s_ * UINT64_C(1000000); + uint64_t end_us = start_us + profiler->options_.GetDurationS() * UINT64_C(1000000); uint64_t now_us = start_us; - VLOG(profiler) << "Starting profiling run now for " << PrettyDuration((end_us - start_us) * 1000); - + VLOG(profiler) << "Starting profiling run now for " + << PrettyDuration((end_us - start_us) * 1000); SampleCheckpoint check_point(profiler); @@ -176,7 +173,7 @@ void* BackgroundMethodSamplingProfiler::RunProfilerThread(void* arg) { break; } - usleep(profiler->interval_us_); // Non-interruptible sleep. + usleep(profiler->options_.GetIntervalUs()); // Non-interruptible sleep. ThreadList* thread_list = runtime->GetThreadList(); @@ -228,7 +225,7 @@ void* BackgroundMethodSamplingProfiler::RunProfilerThread(void* arg) { // Write out the profile file if we are generating a profile. uint32_t BackgroundMethodSamplingProfiler::WriteProfile() { - std::string full_name = profile_file_name_; + std::string full_name = output_filename_; VLOG(profiler) << "Saving profile to " << full_name; int fd = open(full_name.c_str(), O_RDWR); @@ -283,45 +280,35 @@ uint32_t BackgroundMethodSamplingProfiler::WriteProfile() { return num_methods; } -// Start a profile thread with the user-supplied arguments. -void BackgroundMethodSamplingProfiler::Start(int period, int duration, - const std::string& profile_file_name, const std::string& procName, - int interval_us, - double backoff_coefficient, bool startImmediately) { +bool BackgroundMethodSamplingProfiler::Start( + const std::string& output_filename, const ProfilerOptions& options) { + if (!options.IsEnabled()) { + LOG(INFO) << "Profiler disabled. To enable setprop dalvik.vm.profiler 1."; + return false; + } + + CHECK(!output_filename.empty()); + Thread* self = Thread::Current(); { MutexLock mu(self, *Locks::profiler_lock_); // Don't start two profiler threads. if (profiler_ != nullptr) { - return; + return true; } } - // Only on target... -#ifdef HAVE_ANDROID_OS - // Switch off profiler if the dalvik.vm.profiler property has value 0. - char buf[PROP_VALUE_MAX]; - property_get("dalvik.vm.profiler", buf, "0"); - if (strcmp(buf, "0") == 0) { - LOG(INFO) << "Profiler disabled. To enable setprop dalvik.vm.profiler 1"; - return; - } -#endif - - LOG(INFO) << "Starting profile with period " << period << "s, duration " << duration << - "s, interval " << interval_us << "us. Profile file " << profile_file_name; - + LOG(INFO) << "Starting profiler using output file: " << output_filename + << " and options: " << options; { MutexLock mu(self, *Locks::profiler_lock_); - profiler_ = new BackgroundMethodSamplingProfiler(period, duration, profile_file_name, - procName, - backoff_coefficient, - interval_us, startImmediately); + profiler_ = new BackgroundMethodSamplingProfiler(output_filename, options); CHECK_PTHREAD_CALL(pthread_create, (&profiler_pthread_, nullptr, &RunProfilerThread, reinterpret_cast<void*>(profiler_)), "Profiler thread"); } + return true; } @@ -357,14 +344,10 @@ void BackgroundMethodSamplingProfiler::Shutdown() { Stop(); } -BackgroundMethodSamplingProfiler::BackgroundMethodSamplingProfiler(int period, int duration, - const std::string& profile_file_name, - const std::string& process_name, - double backoff_coefficient, int interval_us, bool startImmediately) - : profile_file_name_(profile_file_name), process_name_(process_name), - period_s_(period), start_immediately_(startImmediately), - interval_us_(interval_us), backoff_factor_(1.0), - backoff_coefficient_(backoff_coefficient), duration_s_(duration), +BackgroundMethodSamplingProfiler::BackgroundMethodSamplingProfiler( + const std::string& output_filename, const ProfilerOptions& options) + : output_filename_(output_filename), + options_(options), wait_lock_("Profile wait lock"), period_condition_("Profile condition", wait_lock_), profile_table_(wait_lock_), @@ -466,7 +449,8 @@ uint32_t ProfileSampleResults::Write(std::ostream &os) { num_null_methods_ += previous_num_null_methods_; num_boot_methods_ += previous_num_boot_methods_; - VLOG(profiler) << "Profile: " << num_samples_ << "/" << num_null_methods_ << "/" << num_boot_methods_; + VLOG(profiler) << "Profile: " + << num_samples_ << "/" << num_null_methods_ << "/" << num_boot_methods_; os << num_samples_ << "/" << num_null_methods_ << "/" << num_boot_methods_ << "\n"; uint32_t num_methods = 0; for (int i = 0 ; i < kHashSize; i++) { diff --git a/runtime/profiler.h b/runtime/profiler.h index 2502289..0b18dbb 100644 --- a/runtime/profiler.h +++ b/runtime/profiler.h @@ -28,6 +28,7 @@ #include "base/mutex.h" #include "globals.h" #include "instrumentation.h" +#include "profiler_options.h" #include "os.h" #include "safe_map.h" @@ -62,17 +63,18 @@ class ProfileSampleResults { private: uint32_t Hash(mirror::ArtMethod* method); static constexpr int kHashSize = 17; - Mutex& lock_; // Reference to the main profiler lock - we don't need two of them. - uint32_t num_samples_; // Total number of samples taken. - uint32_t num_null_methods_; // Number of samples where can don't know the method. - uint32_t num_boot_methods_; // Number of samples in the boot path. + Mutex& lock_; // Reference to the main profiler lock - we don't need two of them. + uint32_t num_samples_; // Total number of samples taken. + uint32_t num_null_methods_; // Number of samples where can don't know the method. + uint32_t num_boot_methods_; // Number of samples in the boot path. typedef std::map<mirror::ArtMethod*, uint32_t> Map; // Map of method vs its count. Map *table[kHashSize]; struct PreviousValue { PreviousValue() : count_(0), method_size_(0) {} - PreviousValue(uint32_t count, uint32_t method_size) : count_(count), method_size_(method_size) {} + PreviousValue(uint32_t count, uint32_t method_size) + : count_(count), method_size_(method_size) {} uint32_t count_; uint32_t method_size_; }; @@ -101,9 +103,9 @@ class ProfileSampleResults { class BackgroundMethodSamplingProfiler { public: - static void Start(int period, int duration, const std::string& profile_filename, - const std::string& procName, int interval_us, - double backoff_coefficient, bool startImmediately) + // Start a profile thread with the user-supplied arguments. + // Returns true if the profile was started or if it was already running. Returns false otherwise. + static bool Start(const std::string& output_filename, const ProfilerOptions& options) LOCKS_EXCLUDED(Locks::mutator_lock_, Locks::thread_list_lock_, Locks::thread_suspend_count_lock_, @@ -119,10 +121,8 @@ class BackgroundMethodSamplingProfiler { } private: - explicit BackgroundMethodSamplingProfiler(int period, int duration, - const std::string& profile_filename, - const std::string& process_name, - double backoff_coefficient, int interval_us, bool startImmediately); + explicit BackgroundMethodSamplingProfiler( + const std::string& output_filename, const ProfilerOptions& options); // The sampling interval in microseconds is passed as an argument. static void* RunProfilerThread(void* arg) LOCKS_EXCLUDED(Locks::profiler_lock_); @@ -141,35 +141,14 @@ class BackgroundMethodSamplingProfiler { // Sampling thread, non-zero when sampling. static pthread_t profiler_pthread_; - // Some measure of the number of samples that are significant + // Some measure of the number of samples that are significant. static constexpr uint32_t kSignificantSamples = 10; - // File to write profile data out to. Cannot be empty if we are profiling. - std::string profile_file_name_; + // The name of the file where profile data will be written. + std::string output_filename_; + // The options used to start the profiler. + const ProfilerOptions& options_; - // Process name. - std::string process_name_; - - // Number of seconds between profile runs. - uint32_t period_s_; - - // Most of the time we want to delay the profiler startup to prevent everything - // running at the same time (all processes). This is the default, but if we - // want to override this, set the 'start_immediately_' to true. This is done - // if the -Xprofile option is given on the command line. - bool start_immediately_; - - uint32_t interval_us_; - - // A backoff coefficent to adjust the profile period based on time. - double backoff_factor_; - - // How much to increase the backoff by on each profile iteration. - double backoff_coefficient_; - - // Duration of each profile run. The profile file will be written at the end - // of each run. - uint32_t duration_s_; // Profile condition support. Mutex wait_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; @@ -188,6 +167,7 @@ class BackgroundMethodSamplingProfiler { DISALLOW_COPY_AND_ASSIGN(BackgroundMethodSamplingProfiler); }; +// // Contains profile data generated from previous runs of the program and stored // in a file. It is used to determine whether to compile a particular method or not. class ProfileFile { @@ -212,12 +192,12 @@ class ProfileFile { uint32_t count_; // Number of times it has been called. uint32_t method_size_; // Size of the method on dex instructions. double used_percent_; // Percentage of how many times this method was called. - double top_k_used_percentage_; // The percentage of the group that comprise K% of the total used - // methods this methods belongs to. + double top_k_used_percentage_; // The percentage of the group that comprise K% of the total + // used methods this methods belongs to. }; public: - // Loads profile data from the given file. The data are merged with any existing data. + // Loads profile data from the given file. The new data are merged with any existing data. // Returns true if the file was loaded successfully and false otherwise. bool LoadFile(const std::string& filename); diff --git a/runtime/profiler_options.h b/runtime/profiler_options.h new file mode 100644 index 0000000..08e32cc --- /dev/null +++ b/runtime/profiler_options.h @@ -0,0 +1,131 @@ +/* + * 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 { + +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; + + 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) {} + + 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): + 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) {} + + 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_; + } + + 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_; + 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_; +}; + +} // namespace art + + +#endif // ART_RUNTIME_PROFILER_OPTIONS_H_ diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 361070c..526d8f7 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -123,12 +123,7 @@ Runtime::Runtime() abort_(nullptr), stats_enabled_(false), running_on_valgrind_(RUNNING_ON_VALGRIND > 0), - profile_(false), - profile_period_s_(0), - profile_duration_s_(0), - profile_interval_us_(0), - profile_backoff_coefficient_(0), - profile_start_immediately_(true), + profiler_started_(false), method_trace_(false), method_trace_file_size_(0), instrumentation_(), @@ -165,7 +160,7 @@ Runtime::~Runtime() { shutting_down_ = true; } // Shut down background profiler before the runtime exits. - if (profile_) { + if (profiler_started_) { BackgroundMethodSamplingProfiler::Shutdown(); } @@ -415,17 +410,16 @@ bool Runtime::Start() { } VLOG(startup) << "Runtime::Start exiting"; - finished_starting_ = true; - if (profile_) { - // User has asked for a profile using -Xprofile + 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); } - StartProfiler(profile_output_filename_.c_str(), ""); + StartProfiler(profile_output_filename_.c_str()); } return true; @@ -663,15 +657,9 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { method_trace_file_ = options->method_trace_file_; method_trace_file_size_ = options->method_trace_file_size_; - // Extract the profile options. - // TODO: move into a Trace options struct? - profile_period_s_ = options->profile_period_s_; - profile_duration_s_ = options->profile_duration_s_; - profile_interval_us_ = options->profile_interval_us_; - profile_backoff_coefficient_ = options->profile_backoff_coefficient_; - profile_start_immediately_ = options->profile_start_immediately_; - profile_ = options->profile_; profile_output_filename_ = options->profile_output_filename_; + profiler_options_ = options->profiler_options_; + // TODO: move this to just be an Trace::Start argument Trace::SetDefaultClockSource(options->profile_clock_source_); @@ -1124,9 +1112,10 @@ void Runtime::RemoveMethodVerifier(verifier::MethodVerifier* verifier) { method_verifiers_.erase(it); } -void Runtime::StartProfiler(const char* appDir, const char* procName) { - BackgroundMethodSamplingProfiler::Start(profile_period_s_, profile_duration_s_, appDir, - procName, profile_interval_us_, profile_backoff_coefficient_, profile_start_immediately_); +void Runtime::StartProfiler(const char* profile_output_filename) { + profile_output_filename_ = profile_output_filename; + profiler_started_ = + BackgroundMethodSamplingProfiler::Start(profile_output_filename_, profiler_options_); } // Transaction support. diff --git a/runtime/runtime.h b/runtime/runtime.h index 261429e..be07828 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -34,6 +34,7 @@ #include "instrumentation.h" #include "jobject_comparator.h" #include "object_callbacks.h" +#include "profiler_options.h" #include "quick/quick_method_frame_info.h" #include "runtime_stats.h" #include "safe_map.h" @@ -114,6 +115,10 @@ class Runtime { return image_compiler_options_; } + 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_); @@ -386,7 +391,7 @@ class Runtime { const std::vector<const DexFile*>& GetCompileTimeClassPath(jobject class_loader); void SetCompileTimeClassPath(jobject class_loader, std::vector<const DexFile*>& class_path); - void StartProfiler(const char* appDir, const char* procName); + void StartProfiler(const char* profile_output_filename); void UpdateProfilerState(int state); // Transaction support. @@ -551,15 +556,9 @@ class Runtime { const bool running_on_valgrind_; - // Runtime profile support. - bool profile_; std::string profile_output_filename_; - uint32_t profile_period_s_; // Generate profile every n seconds. - uint32_t profile_duration_s_; // Run profile for n seconds. - uint32_t profile_interval_us_; // Microseconds between samples. - double profile_backoff_coefficient_; // Coefficient to exponential backoff. - bool profile_start_immediately_; // Whether the profile should start upon app - // startup or be delayed by some random offset. + ProfilerOptions profiler_options_; + bool profiler_started_; bool method_trace_; std::string method_trace_file_; diff --git a/runtime/utils.cc b/runtime/utils.cc index 05ff5ff..f562252 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -1302,23 +1302,4 @@ bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg) { return true; } -double GetDoubleProperty(const char* property, double min_value, double max_value, double default_value) { -#ifndef HAVE_ANDROID_OS - return default_value; -#else - char buf[PROP_VALUE_MAX]; - char* endptr; - - property_get(property, buf, ""); - double value = strtod(buf, &endptr); - - // Return the defalt value if the string is invalid or the value is outside the given range - if ((value == 0 && endptr == buf) // Invalid string - || (value < min_value) || (value > max_value)) { // Out of range value - return default_value; - } - return value; -#endif -} - } // namespace art diff --git a/runtime/utils.h b/runtime/utils.h index 0f9b22b..7be5d44 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -443,10 +443,6 @@ class VoidFunctor { } }; -// Returns the given property as a double or its default_value if the property string is not valid -// or the parsed value is outside the interval [min_value, max_value]. -double GetDoubleProperty(const char* property, double min_value, double max_value, double default_value); - } // namespace art #endif // ART_RUNTIME_UTILS_H_ |