Merge "Fixed and refactored profiler options handling"
diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc
index 58d2ed2..5b9c763 100644
--- a/compiler/dex/frontend.cc
+++ b/compiler/dex/frontend.cc
@@ -907,7 +907,7 @@
     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 @@
     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 @@
                                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 @@
 
   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 @@
   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 @@
   }
 
 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 @@
   }
 
   // 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 @@
   }
 
   bool ProfilePresent() const {
-    return profile_ok_;
+    return profile_present_;
   }
 
   // Are we compiling and creating an image file?
@@ -596,7 +596,7 @@
   }
 
   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 @@
   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 @@
 
   // 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 @@
   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 @@
     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 @@
                   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 @@
     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 @@
     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 @@
   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 f27da89..35149cf 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -708,6 +708,38 @@
   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) {
 #if defined(__linux__) && defined(__arm__)
   int major, minor;
@@ -778,6 +810,7 @@
 
   // Profile file to use
   std::string profile_file;
+  double top_k_profile_threshold = CompilerOptions::kDefaultTopKProfileThreshold;
 
   bool is_host = false;
   bool dump_stats = false;
@@ -941,6 +974,8 @@
       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=")) {
@@ -1086,7 +1121,8 @@
                                    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 6085722..2c24e33 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -328,7 +328,7 @@
   // 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 @@
       // 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 a369365..f1a987f 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -509,15 +509,13 @@
  * 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 72a868e..29868e3 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -248,12 +248,6 @@
   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 @@
       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-lazy") {
-      profile_start_immediately_ = 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 (StartsWith(option, "-implicit-checks:")) {
       std::string checks;
       if (!ParseStringAfterChar(option, ':', &checks)) {
@@ -791,11 +794,15 @@
   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 @@
   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 @@
 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 @@
 
   Thread* self = Thread::Current();
 
+  double backoff = 1.0;
   while (true) {
     if (ShuttingDown(self)) {
       break;
@@ -133,13 +133,13 @@
 
     {
       // 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 @@
       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 @@
 
 
     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 @@
         break;
       }
 
-      usleep(profiler->interval_us_);    // Non-interruptible sleep.
+      usleep(profiler->options_.GetIntervalUs());    // Non-interruptible sleep.
 
       ThreadList* thread_list = runtime->GetThreadList();
 
@@ -228,7 +225,7 @@
 
 // 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 @@
   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 @@
   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 @@
   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 @@
  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 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 @@
   }
 
  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 @@
   // 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 @@
   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 @@
     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 68b10cc..f16eddf 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -123,12 +123,7 @@
       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_(),
@@ -166,7 +161,7 @@
     shutting_down_ = true;
   }
   // Shut down background profiler before the runtime exits.
-  if (profile_) {
+  if (profiler_started_) {
     BackgroundMethodSamplingProfiler::Shutdown();
   }
 
@@ -416,17 +411,16 @@
   }
 
   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;
@@ -664,15 +658,9 @@
   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_);
 
@@ -1125,9 +1113,10 @@
   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 afb5aa7..808d706 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 @@
     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 @@
   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.
@@ -559,15 +564,9 @@
 
   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 @@
   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 @@
   }
 };
 
-// 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_