Merge "Revert "Integer.bitCount and Long.bitCount intrinsics for ARM""
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index c09116f..424aa7a 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -65,15 +65,18 @@
 	$(call dexpreopt-remove-classes.dex,$@)
 
 # Dex file dependencies for each gtest.
+ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested
+
 ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MultiDex MyClass Nested Statics StaticsFromCode
 ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex
 ART_GTEST_dex_cache_test_DEX_DEPS := Main
 ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested
+ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
 ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle
 ART_GTEST_instrumentation_test_DEX_DEPS := Instrumentation
 ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives
 ART_GTEST_jni_internal_test_DEX_DEPS := AllFields StaticLeafMethods
-ART_GTEST_oat_file_assistant_test_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested
+ART_GTEST_oat_file_assistant_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
 ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex
 ART_GTEST_oat_test_DEX_DEPS := Main
 ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY
@@ -89,14 +92,25 @@
 ART_GTEST_elf_writer_test_HOST_DEPS := $(HOST_CORE_IMAGE_default_no-pic_64) $(HOST_CORE_IMAGE_default_no-pic_32)
 ART_GTEST_elf_writer_test_TARGET_DEPS := $(TARGET_CORE_IMAGE_default_no-pic_64) $(TARGET_CORE_IMAGE_default_no-pic_32)
 
-ART_GTEST_oat_file_assistant_test_HOST_DEPS := \
+ART_GTEST_dex2oat_environment_tests_HOST_DEPS := \
   $(HOST_CORE_IMAGE_default_no-pic_64) \
-  $(HOST_CORE_IMAGE_default_no-pic_32) \
-  $(HOST_OUT_EXECUTABLES)/patchoatd
-ART_GTEST_oat_file_assistant_test_TARGET_DEPS := \
+  $(HOST_CORE_IMAGE_default_no-pic_32)
+ART_GTEST_dex2oat_environment_tests_TARGET_DEPS := \
   $(TARGET_CORE_IMAGE_default_no-pic_64) \
-  $(TARGET_CORE_IMAGE_default_no-pic_32) \
-  $(TARGET_OUT_EXECUTABLES)/patchoatd
+  $(TARGET_CORE_IMAGE_default_no-pic_32)
+
+ART_GTEST_oat_file_assistant_test_HOST_DEPS := \
+   $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) \
+   $(HOST_OUT_EXECUTABLES)/patchoatd
+ART_GTEST_oat_file_assistant_test_TARGET_DEPS := \
+   $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) \
+   $(TARGET_OUT_EXECUTABLES)/patchoatd
+
+
+ART_GTEST_dex2oat_test_HOST_DEPS := \
+  $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS)
+ART_GTEST_dex2oat_test_TARGET_DEPS := \
+  $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS)
 
 # TODO: document why this is needed.
 ART_GTEST_proxy_test_HOST_DEPS := $(HOST_CORE_IMAGE_default_no-pic_64) $(HOST_CORE_IMAGE_default_no-pic_32)
@@ -157,6 +171,7 @@
   cmdline/cmdline_parser_test.cc \
   dexdump/dexdump_test.cc \
   dexlist/dexlist_test.cc \
+  dex2oat/dex2oat_test.cc \
   imgdiag/imgdiag_test.cc \
   oatdump/oatdump_test.cc \
   profman/profile_assistant_test.cc \
@@ -808,11 +823,15 @@
 ART_GTEST_oat_file_assistant_test_DEX_DEPS :=
 ART_GTEST_oat_file_assistant_test_HOST_DEPS :=
 ART_GTEST_oat_file_assistant_test_TARGET_DEPS :=
+ART_GTEST_dex2oat_test_DEX_DEPS :=
+ART_GTEST_dex2oat_test_HOST_DEPS :=
+ART_GTEST_dex2oat_test_TARGET_DEPS :=
 ART_GTEST_object_test_DEX_DEPS :=
 ART_GTEST_proxy_test_DEX_DEPS :=
 ART_GTEST_reflection_test_DEX_DEPS :=
 ART_GTEST_stub_test_DEX_DEPS :=
 ART_GTEST_transaction_test_DEX_DEPS :=
+ART_GTEST_dex2oat_environment_tests_DEX_DEPS :=
 ART_VALGRIND_DEPENDENCIES :=
 ART_VALGRIND_TARGET_DEPENDENCIES :=
 $(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval ART_TEST_TARGET_GTEST_$(dir)_DEX :=))
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 7c53e01..7ded3bf 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -30,18 +30,15 @@
   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) {
@@ -476,68 +473,21 @@
 }  // TEST_F
 
 /*
-* -X-profile-*
+* -Xps-*
 */
-TEST_F(CmdlineParserTest, TestProfilerOptions) {
- /*
-  * Test successes
-  */
+TEST_F(CmdlineParserTest, ProfileSaverOptions) {
+  ProfileSaverOptions opt = ProfileSaverOptions(true, 1, 2, 3, 4, 5, 6, 7);
 
-  {
-    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);
-  }
+  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:_ */
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index 4797540..9b4042c 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -31,7 +31,7 @@
 #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 {
 
@@ -633,84 +633,17 @@
   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);
 
@@ -722,36 +655,6 @@
     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(":");
 
@@ -763,87 +666,64 @@
   }
 
  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;
-
-      return ParseInto(existing,
-                       &TestProfilerOptions::output_file_name_,
-                       type_parser.Parse(suffix));
-    } else if (StartsWith(option, "period:")) {
+    if (StartsWith(option, "min-save-period-ms:")) {
       CmdlineType<unsigned int> type_parser;
-
       return ParseInto(existing,
-                       &TestProfilerOptions::period_s_,
-                       type_parser.Parse(suffix));
-    } else if (StartsWith(option, "duration:")) {
+             &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::duration_s_,
-                       type_parser.Parse(suffix));
-    } else if (StartsWith(option, "interval:")) {
+             &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::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::startup_method_samples_,
+             type_parser.Parse(suffix));
+    }
+    if (StartsWith(option, "min-methods-to-save:")) {
       CmdlineType<unsigned int> type_parser;
-
       return ParseInto(existing,
-                       &TestProfilerOptions::max_stack_depth_,
-                       type_parser.Parse(suffix));
+             &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,
+             &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;
 };
 
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index 696b8c6..8fb5396 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -146,7 +146,11 @@
     instruction->ReplaceInput(GetGraph()->GetIntConstant(load_class->GetTypeIndex()), 0);
     // The allocation entry point that deals with access checks does not work with inlined
     // methods, so we need to check whether this allocation comes from an inlined method.
-    if (has_only_one_use && !instruction->GetEnvironment()->IsFromInlinedInvoke()) {
+    // We also need to make the same check as for moving clinit check, whether the HLoadClass
+    // has the clinit check responsibility or not (HLoadClass can throw anyway).
+    if (has_only_one_use &&
+        !instruction->GetEnvironment()->IsFromInlinedInvoke() &&
+        CanMoveClinitCheck(load_class, instruction)) {
       // We can remove the load class from the graph. If it needed access checks, we delegate
       // the access check to the allocation.
       if (load_class->NeedsAccessCheck()) {
@@ -203,7 +207,8 @@
                                                       HInstruction* user) const {
   // Determine if input and user come from the same dex instruction, so that we can move
   // the clinit check responsibility from one to the other, i.e. from HClinitCheck (user)
-  // to HLoadClass (input), or from HClinitCheck (input) to HInvokeStaticOrDirect (user).
+  // to HLoadClass (input), or from HClinitCheck (input) to HInvokeStaticOrDirect (user),
+  // or from HLoadClass (input) to HNewInstance (user).
 
   // Start with a quick dex pc check.
   if (user->GetDexPc() != input->GetDexPc()) {
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 24a4d58..c133980 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -22,6 +22,7 @@
 
 #include <fstream>
 #include <iostream>
+#include <limits>
 #include <sstream>
 #include <string>
 #include <unordered_set>
@@ -81,6 +82,9 @@
 
 namespace art {
 
+static constexpr size_t kDefaultMinDexFilesForSwap = 2;
+static constexpr size_t kDefaultMinDexFileCumulativeSizeForSwap = 20 * MB;
+
 static int original_argc;
 static char** original_argv;
 
@@ -351,6 +355,20 @@
   UsageError("  --swap-fd=<file-descriptor>:  specifies a file to use for swap (by descriptor).");
   UsageError("      Example: --swap-fd=10");
   UsageError("");
+  UsageError("  --swap-dex-size-threshold=<size>:  specifies the minimum total dex file size in");
+  UsageError("      bytes to allow the use of swap.");
+  UsageError("      Example: --swap-dex-size-threshold=1000000");
+  UsageError("      Default: %zu", kDefaultMinDexFileCumulativeSizeForSwap);
+  UsageError("");
+  UsageError("  --swap-dex-count-threshold=<count>:  specifies the minimum number of dex files to");
+  UsageError("      allow the use of swap.");
+  UsageError("      Example: --swap-dex-count-threshold=10");
+  UsageError("      Default: %zu", kDefaultMinDexFilesForSwap);
+  UsageError("");
+  UsageError("  --very-large-app-threshold=<size>:  specifies the minimum total dex file size in");
+  UsageError("      bytes to consider the input \"very large\" and punt on the compilation.");
+  UsageError("      Example: --very-large-app-threshold=100000000");
+  UsageError("");
   UsageError("  --app-image-fd=<file-descriptor>: specify output file descriptor for app image.");
   UsageError("      Example: --app-image-fd=10");
   UsageError("");
@@ -473,25 +491,6 @@
   pthread_t pthread_;
 };
 
-static constexpr size_t kMinDexFilesForSwap = 2;
-static constexpr size_t kMinDexFileCumulativeSizeForSwap = 20 * MB;
-
-static bool UseSwap(bool is_image, std::vector<const DexFile*>& dex_files) {
-  if (is_image) {
-    // Don't use swap, we know generation should succeed, and we don't want to slow it down.
-    return false;
-  }
-  if (dex_files.size() < kMinDexFilesForSwap) {
-    // If there are less dex files than the threshold, assume it's gonna be fine.
-    return false;
-  }
-  size_t dex_files_size = 0;
-  for (const auto* dex_file : dex_files) {
-    dex_files_size += dex_file->GetHeader().file_size_;
-  }
-  return dex_files_size >= kMinDexFileCumulativeSizeForSwap;
-}
-
 class Dex2Oat FINAL {
  public:
   explicit Dex2Oat(TimingLogger* timings) :
@@ -1132,6 +1131,21 @@
         swap_file_name_ = option.substr(strlen("--swap-file=")).data();
       } else if (option.starts_with("--swap-fd=")) {
         ParseUintOption(option, "--swap-fd", &swap_fd_, Usage);
+      } else if (option.starts_with("--swap-dex-size-threshold=")) {
+        ParseUintOption(option,
+                        "--swap-dex-size-threshold",
+                        &min_dex_file_cumulative_size_for_swap_,
+                        Usage);
+      } else if (option.starts_with("--swap-dex-count-threshold=")) {
+        ParseUintOption(option,
+                        "--swap-dex-count-threshold",
+                        &min_dex_files_for_swap_,
+                        Usage);
+      } else if (option.starts_with("--very-large-app-threshold=")) {
+        ParseUintOption(option,
+                        "--very-large-app-threshold",
+                        &very_large_threshold_,
+                        Usage);
       } else if (option.starts_with("--app-image-file=")) {
         app_image_file_name_ = option.substr(strlen("--app-image-file=")).data();
       } else if (option.starts_with("--app-image-fd=")) {
@@ -1414,6 +1428,19 @@
     }
     // Note that dex2oat won't close the swap_fd_. The compiler driver's swap space will do that.
 
+    // If we need to downgrade the compiler-filter for size reasons, do that check now.
+    if (!IsBootImage() && IsVeryLarge(dex_files_)) {
+      if (!CompilerFilter::IsAsGoodAs(CompilerFilter::kVerifyAtRuntime,
+                                      compiler_options_->GetCompilerFilter())) {
+        LOG(INFO) << "Very large app, downgrading to verify-at-runtime.";
+        // Note: this change won't be reflected in the key-value store, as that had to be
+        //       finalized before loading the dex files. This setup is currently required
+        //       to get the size from the DexFile objects.
+        // TODO: refactor. b/29790079
+        compiler_options_->SetCompilerFilter(CompilerFilter::kVerifyAtRuntime);
+      }
+    }
+
     if (IsBootImage()) {
       // For boot image, pass opened dex files to the Runtime::Create().
       // Note: Runtime acquires ownership of these dex files.
@@ -1842,10 +1869,6 @@
     }
   }
 
-  CompilerOptions* GetCompilerOptions() const {
-    return compiler_options_.get();
-  }
-
   bool IsImage() const {
     return IsAppImage() || IsBootImage();
   }
@@ -1897,6 +1920,30 @@
   }
 
  private:
+  bool UseSwap(bool is_image, const std::vector<const DexFile*>& dex_files) {
+    if (is_image) {
+      // Don't use swap, we know generation should succeed, and we don't want to slow it down.
+      return false;
+    }
+    if (dex_files.size() < min_dex_files_for_swap_) {
+      // If there are less dex files than the threshold, assume it's gonna be fine.
+      return false;
+    }
+    size_t dex_files_size = 0;
+    for (const auto* dex_file : dex_files) {
+      dex_files_size += dex_file->GetHeader().file_size_;
+    }
+    return dex_files_size >= min_dex_file_cumulative_size_for_swap_;
+  }
+
+  bool IsVeryLarge(std::vector<const DexFile*>& dex_files) {
+    size_t dex_files_size = 0;
+    for (const auto* dex_file : dex_files) {
+      dex_files_size += dex_file->GetHeader().file_size_;
+    }
+    return dex_files_size >= very_large_threshold_;
+  }
+
   template <typename T>
   static std::vector<T*> MakeNonOwningPointerVector(const std::vector<std::unique_ptr<T>>& src) {
     std::vector<T*> result;
@@ -2486,6 +2533,9 @@
   bool dump_slow_timing_;
   std::string swap_file_name_;
   int swap_fd_;
+  size_t min_dex_files_for_swap_ = kDefaultMinDexFilesForSwap;
+  size_t min_dex_file_cumulative_size_for_swap_ = kDefaultMinDexFileCumulativeSizeForSwap;
+  size_t very_large_threshold_ = std::numeric_limits<size_t>::max();
   std::string app_image_file_name_;
   int app_image_fd_;
   std::string profile_file_;
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
new file mode 100644
index 0000000..6188883
--- /dev/null
+++ b/dex2oat/dex2oat_test.cc
@@ -0,0 +1,419 @@
+/*
+ * 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
+ *
+ * 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.
+ */
+
+#include <string>
+#include <vector>
+#include <sstream>
+
+#include "common_runtime_test.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/stringprintf.h"
+#include "dex2oat_environment_test.h"
+#include "oat.h"
+#include "oat_file.h"
+#include "utils.h"
+
+#include <sys/wait.h>
+#include <unistd.h>
+
+namespace art {
+
+class Dex2oatTest : public Dex2oatEnvironmentTest {
+ public:
+  virtual void TearDown() OVERRIDE {
+    Dex2oatEnvironmentTest::TearDown();
+
+    output_ = "";
+    error_msg_ = "";
+    success_ = false;
+  }
+
+ protected:
+  void GenerateOdexForTest(const std::string& dex_location,
+                           const std::string& odex_location,
+                           CompilerFilter::Filter filter,
+                           const std::vector<std::string>& extra_args = {},
+                           bool expect_success = true) {
+    std::vector<std::string> args;
+    args.push_back("--dex-file=" + dex_location);
+    args.push_back("--oat-file=" + odex_location);
+    args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
+    args.push_back("--runtime-arg");
+    args.push_back("-Xnorelocate");
+
+    args.insert(args.end(), extra_args.begin(), extra_args.end());
+
+    std::string error_msg;
+    bool success = Dex2Oat(args, &error_msg);
+
+    if (expect_success) {
+      ASSERT_TRUE(success) << error_msg;
+
+      // Verify the odex file was generated as expected.
+      std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+                                                       odex_location.c_str(),
+                                                       nullptr,
+                                                       nullptr,
+                                                       false,
+                                                       /*low_4gb*/false,
+                                                       dex_location.c_str(),
+                                                       &error_msg));
+      ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
+
+      CheckFilter(filter, odex_file->GetCompilerFilter());
+    } else {
+      ASSERT_FALSE(success) << output_;
+
+      error_msg_ = error_msg;
+
+      // Verify there's no loadable odex file.
+      std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+                                                       odex_location.c_str(),
+                                                       nullptr,
+                                                       nullptr,
+                                                       false,
+                                                       /*low_4gb*/false,
+                                                       dex_location.c_str(),
+                                                       &error_msg));
+      ASSERT_TRUE(odex_file.get() == nullptr);
+    }
+  }
+
+  // Check the input compiler filter against the generated oat file's filter. Mayb be overridden
+  // in subclasses when equality is not expected.
+  virtual void CheckFilter(CompilerFilter::Filter expected, CompilerFilter::Filter actual) {
+    EXPECT_EQ(expected, actual);
+  }
+
+  bool Dex2Oat(const std::vector<std::string>& dex2oat_args, std::string* error_msg) {
+    Runtime* runtime = Runtime::Current();
+
+    const std::vector<gc::space::ImageSpace*>& image_spaces =
+        runtime->GetHeap()->GetBootImageSpaces();
+    if (image_spaces.empty()) {
+      *error_msg = "No image location found for Dex2Oat.";
+      return false;
+    }
+    std::string image_location = image_spaces[0]->GetImageLocation();
+
+    std::vector<std::string> argv;
+    argv.push_back(runtime->GetCompilerExecutable());
+    argv.push_back("--runtime-arg");
+    argv.push_back("-classpath");
+    argv.push_back("--runtime-arg");
+    std::string class_path = runtime->GetClassPathString();
+    if (class_path == "") {
+      class_path = OatFile::kSpecialSharedLibrary;
+    }
+    argv.push_back(class_path);
+    if (runtime->IsDebuggable()) {
+      argv.push_back("--debuggable");
+    }
+    runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);
+
+    if (!runtime->IsVerificationEnabled()) {
+      argv.push_back("--compiler-filter=verify-none");
+    }
+
+    if (runtime->MustRelocateIfPossible()) {
+      argv.push_back("--runtime-arg");
+      argv.push_back("-Xrelocate");
+    } else {
+      argv.push_back("--runtime-arg");
+      argv.push_back("-Xnorelocate");
+    }
+
+    if (!kIsTargetBuild) {
+      argv.push_back("--host");
+    }
+
+    argv.push_back("--boot-image=" + image_location);
+
+    std::vector<std::string> compiler_options = runtime->GetCompilerOptions();
+    argv.insert(argv.end(), compiler_options.begin(), compiler_options.end());
+
+    argv.insert(argv.end(), dex2oat_args.begin(), dex2oat_args.end());
+
+    // We must set --android-root.
+    const char* android_root = getenv("ANDROID_ROOT");
+    CHECK(android_root != nullptr);
+    argv.push_back("--android-root=" + std::string(android_root));
+
+    std::string command_line(Join(argv, ' '));
+
+    // We need to fix up the '&' being used for "do not check classpath."
+    size_t ampersand = command_line.find(" &");
+    CHECK_NE(ampersand, std::string::npos);
+    command_line = command_line.replace(ampersand, 2, " \\&");
+
+    command_line += " 2>&1";
+
+    // We need dex2oat to actually log things.
+    setenv("ANDROID_LOG_TAGS", "*:d", 1);
+
+    FILE* pipe = popen(command_line.c_str(), "r");
+
+    setenv("ANDROID_LOG_TAGS", "*:e", 1);
+
+    if (pipe == nullptr) {
+      success_ = false;
+    } else {
+      char buffer[128];
+
+      while (fgets(buffer, 128, pipe) != nullptr) {
+        output_ += buffer;
+      }
+
+      int result = pclose(pipe);
+      success_ = result == 0;
+    }
+    return success_;
+  }
+
+  std::string output_ = "";
+  std::string error_msg_ = "";
+  bool success_ = false;
+};
+
+class Dex2oatSwapTest : public Dex2oatTest {
+ protected:
+  void RunTest(bool use_fd, bool expect_use, const std::vector<std::string>& extra_args = {}) {
+    std::string dex_location = GetScratchDir() + "/Dex2OatSwapTest.jar";
+    std::string odex_location = GetOdexDir() + "/Dex2OatSwapTest.odex";
+
+    Copy(GetDexSrc1(), dex_location);
+
+    std::vector<std::string> copy(extra_args);
+
+    std::unique_ptr<ScratchFile> sf;
+    if (use_fd) {
+      sf.reset(new ScratchFile());
+      copy.push_back(StringPrintf("--swap-fd=%d", sf->GetFd()));
+    } else {
+      std::string swap_location = GetOdexDir() + "/Dex2OatSwapTest.odex.swap";
+      copy.push_back("--swap-file=" + swap_location);
+    }
+    GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, copy);
+
+    CheckValidity();
+    ASSERT_TRUE(success_);
+    CheckResult(expect_use);
+  }
+
+  void CheckResult(bool expect_use) {
+    if (kIsTargetBuild) {
+      CheckTargetResult(expect_use);
+    } else {
+      CheckHostResult(expect_use);
+    }
+  }
+
+  void CheckTargetResult(bool expect_use ATTRIBUTE_UNUSED) {
+    // TODO: Ignore for now, as we won't capture any output (it goes to the logcat). We may do
+    //       something for variants with file descriptor where we can control the lifetime of
+    //       the swap file and thus take a look at it.
+  }
+
+  void CheckHostResult(bool expect_use) {
+    if (!kIsTargetBuild) {
+      if (expect_use) {
+        EXPECT_NE(output_.find("Large app, accepted running with swap."), std::string::npos)
+            << output_;
+      } else {
+        EXPECT_EQ(output_.find("Large app, accepted running with swap."), std::string::npos)
+            << output_;
+      }
+    }
+  }
+
+  // Check whether the dex2oat run was really successful.
+  void CheckValidity() {
+    if (kIsTargetBuild) {
+      CheckTargetValidity();
+    } else {
+      CheckHostValidity();
+    }
+  }
+
+  void CheckTargetValidity() {
+    // TODO: Ignore for now, as we won't capture any output (it goes to the logcat). We may do
+    //       something for variants with file descriptor where we can control the lifetime of
+    //       the swap file and thus take a look at it.
+  }
+
+  // On the host, we can get the dex2oat output. Here, look for "dex2oat took."
+  void CheckHostValidity() {
+    EXPECT_NE(output_.find("dex2oat took"), std::string::npos) << output_;
+  }
+};
+
+TEST_F(Dex2oatSwapTest, DoNotUseSwapDefaultSingleSmall) {
+  RunTest(false /* use_fd */, false /* expect_use */);
+  RunTest(true /* use_fd */, false /* expect_use */);
+}
+
+TEST_F(Dex2oatSwapTest, DoNotUseSwapSingle) {
+  RunTest(false /* use_fd */, false /* expect_use */, { "--swap-dex-size-threshold=0" });
+  RunTest(true /* use_fd */, false /* expect_use */, { "--swap-dex-size-threshold=0" });
+}
+
+TEST_F(Dex2oatSwapTest, DoNotUseSwapSmall) {
+  RunTest(false /* use_fd */, false /* expect_use */, { "--swap-dex-count-threshold=0" });
+  RunTest(true /* use_fd */, false /* expect_use */, { "--swap-dex-count-threshold=0" });
+}
+
+TEST_F(Dex2oatSwapTest, DoUseSwapSingleSmall) {
+  RunTest(false /* use_fd */,
+          true /* expect_use */,
+          { "--swap-dex-size-threshold=0", "--swap-dex-count-threshold=0" });
+  RunTest(true /* use_fd */,
+          true /* expect_use */,
+          { "--swap-dex-size-threshold=0", "--swap-dex-count-threshold=0" });
+}
+
+class Dex2oatVeryLargeTest : public Dex2oatTest {
+ protected:
+  void CheckFilter(CompilerFilter::Filter input ATTRIBUTE_UNUSED,
+                   CompilerFilter::Filter result ATTRIBUTE_UNUSED) OVERRIDE {
+    // Ignore, we'll do our own checks.
+  }
+
+  void RunTest(CompilerFilter::Filter filter,
+               bool expect_large,
+               const std::vector<std::string>& extra_args = {}) {
+    std::string dex_location = GetScratchDir() + "/DexNoOat.jar";
+    std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex";
+
+    Copy(GetDexSrc1(), dex_location);
+
+    std::vector<std::string> copy(extra_args);
+
+    GenerateOdexForTest(dex_location, odex_location, filter, copy);
+
+    CheckValidity();
+    ASSERT_TRUE(success_);
+    CheckResult(dex_location, odex_location, filter, expect_large);
+  }
+
+  void CheckResult(const std::string& dex_location,
+                   const std::string& odex_location,
+                   CompilerFilter::Filter filter,
+                   bool expect_large) {
+    // Host/target independent checks.
+    std::string error_msg;
+    std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+                                                     odex_location.c_str(),
+                                                     nullptr,
+                                                     nullptr,
+                                                     false,
+                                                     /*low_4gb*/false,
+                                                     dex_location.c_str(),
+                                                     &error_msg));
+    ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
+    if (expect_large) {
+      // Note: we cannot check the following:
+      //   EXPECT_TRUE(CompilerFilter::IsAsGoodAs(CompilerFilter::kVerifyAtRuntime,
+      //                                          odex_file->GetCompilerFilter()));
+      // The reason is that the filter override currently happens when the dex files are
+      // loaded in dex2oat, which is after the oat file has been started. Thus, the header
+      // store cannot be changed, and the original filter is set in stone.
+
+      for (const OatDexFile* oat_dex_file : odex_file->GetOatDexFiles()) {
+        std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
+        ASSERT_TRUE(dex_file != nullptr);
+        uint32_t class_def_count = dex_file->NumClassDefs();
+        ASSERT_LT(class_def_count, std::numeric_limits<uint16_t>::max());
+        for (uint16_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) {
+          OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index);
+          EXPECT_EQ(oat_class.GetType(), OatClassType::kOatClassNoneCompiled);
+        }
+      }
+
+      // If the input filter was "below," it should have been used.
+      if (!CompilerFilter::IsAsGoodAs(CompilerFilter::kVerifyAtRuntime, filter)) {
+        EXPECT_EQ(odex_file->GetCompilerFilter(), filter);
+      }
+    } else {
+      EXPECT_EQ(odex_file->GetCompilerFilter(), filter);
+    }
+
+    // Host/target dependent checks.
+    if (kIsTargetBuild) {
+      CheckTargetResult(expect_large);
+    } else {
+      CheckHostResult(expect_large);
+    }
+  }
+
+  void CheckTargetResult(bool expect_large ATTRIBUTE_UNUSED) {
+    // TODO: Ignore for now. May do something for fd things.
+  }
+
+  void CheckHostResult(bool expect_large) {
+    if (!kIsTargetBuild) {
+      if (expect_large) {
+        EXPECT_NE(output_.find("Very large app, downgrading to verify-at-runtime."),
+                  std::string::npos)
+            << output_;
+      } else {
+        EXPECT_EQ(output_.find("Very large app, downgrading to verify-at-runtime."),
+                  std::string::npos)
+            << output_;
+      }
+    }
+  }
+
+  // Check whether the dex2oat run was really successful.
+  void CheckValidity() {
+    if (kIsTargetBuild) {
+      CheckTargetValidity();
+    } else {
+      CheckHostValidity();
+    }
+  }
+
+  void CheckTargetValidity() {
+    // TODO: Ignore for now.
+  }
+
+  // On the host, we can get the dex2oat output. Here, look for "dex2oat took."
+  void CheckHostValidity() {
+    EXPECT_NE(output_.find("dex2oat took"), std::string::npos) << output_;
+  }
+};
+
+TEST_F(Dex2oatVeryLargeTest, DontUseVeryLarge) {
+  RunTest(CompilerFilter::kVerifyNone, false);
+  RunTest(CompilerFilter::kVerifyAtRuntime, false);
+  RunTest(CompilerFilter::kInterpretOnly, false);
+  RunTest(CompilerFilter::kSpeed, false);
+
+  RunTest(CompilerFilter::kVerifyNone, false, { "--very-large-app-threshold=1000000" });
+  RunTest(CompilerFilter::kVerifyAtRuntime, false, { "--very-large-app-threshold=1000000" });
+  RunTest(CompilerFilter::kInterpretOnly, false, { "--very-large-app-threshold=1000000" });
+  RunTest(CompilerFilter::kSpeed, false, { "--very-large-app-threshold=1000000" });
+}
+
+TEST_F(Dex2oatVeryLargeTest, UseVeryLarge) {
+  RunTest(CompilerFilter::kVerifyNone, false, { "--very-large-app-threshold=100" });
+  RunTest(CompilerFilter::kVerifyAtRuntime, false, { "--very-large-app-threshold=100" });
+  RunTest(CompilerFilter::kInterpretOnly, true, { "--very-large-app-threshold=100" });
+  RunTest(CompilerFilter::kSpeed, true, { "--very-large-app-threshold=100" });
+}
+
+}  // namespace art
diff --git a/runtime/Android.mk b/runtime/Android.mk
index bf4e8f1..1c442fc 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -368,7 +368,6 @@
   oat.h \
   object_callbacks.h \
   process_state.h \
-  profiler_options.h \
   quick/inline_method_analyser.h \
   runtime.h \
   stack.h \
diff --git a/runtime/arch/x86/fault_handler_x86.cc b/runtime/arch/x86/fault_handler_x86.cc
index 667d200..24e3a0d 100644
--- a/runtime/arch/x86/fault_handler_x86.cc
+++ b/runtime/arch/x86/fault_handler_x86.cc
@@ -36,6 +36,7 @@
 #define CTX_EIP uc_mcontext->__ss.__rip
 #define CTX_EAX uc_mcontext->__ss.__rax
 #define CTX_METHOD uc_mcontext->__ss.__rdi
+#define CTX_RDI uc_mcontext->__ss.__rdi
 #define CTX_JMP_BUF uc_mcontext->__ss.__rdi
 #else
 // 32 bit mac build.
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 3509d9a..741b682 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -463,7 +463,7 @@
 #define ART_TARGET_NATIVETEST_DIR_STRING ""
 #endif
 
-std::string CommonRuntimeTestImpl::GetTestDexFileName(const char* name) {
+std::string CommonRuntimeTestImpl::GetTestDexFileName(const char* name) const {
   CHECK(name != nullptr);
   std::string filename;
   if (IsHost()) {
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 0ce40e8..b68eb19 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -111,7 +111,7 @@
 
   std::string GetTestAndroidRoot();
 
-  std::string GetTestDexFileName(const char* name);
+  std::string GetTestDexFileName(const char* name) const;
 
   std::vector<std::unique_ptr<const DexFile>> OpenTestDexFiles(const char* name)
       SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 19387fb..912a74a 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -438,11 +438,11 @@
     case Instruction::IPUT_BYTE:
     case Instruction::IPUT_CHAR:
     case Instruction::IPUT_SHORT: {
-      // Check that the fault address is at the offset of the field or null. The compiler
-      // can generate both.
       ArtField* field =
           Runtime::Current()->GetClassLinker()->ResolveField(instr.VRegC_22c(), method, false);
-      return (addr == 0) || (addr == field->GetOffset().Uint32Value());
+      return (addr == 0) ||
+          (addr == field->GetOffset().Uint32Value()) ||
+          (kEmitCompilerReadBarrier && (addr == mirror::Object::MonitorOffset().Uint32Value()));
     }
 
     case Instruction::IGET_QUICK:
@@ -459,9 +459,9 @@
     case Instruction::IPUT_SHORT_QUICK:
     case Instruction::IPUT_WIDE_QUICK:
     case Instruction::IPUT_OBJECT_QUICK: {
-      // Check that the fault address is at the offset in the quickened instruction or null.
-      // The compiler can generate both.
-      return (addr == 0u) || (addr == instr.VRegC_22c());
+      return (addr == 0u) ||
+          (addr == instr.VRegC_22c()) ||
+          (kEmitCompilerReadBarrier && (addr == mirror::Object::MonitorOffset().Uint32Value()));
     }
 
     case Instruction::AGET:
@@ -477,21 +477,14 @@
     case Instruction::APUT_BOOLEAN:
     case Instruction::APUT_BYTE:
     case Instruction::APUT_CHAR:
-    case Instruction::APUT_SHORT: {
-      // The length access should crash. We currently do not do implicit checks on
-      // the array access itself.
-      return (addr == 0u) || (addr == mirror::Array::LengthOffset().Uint32Value());
-    }
-
-    case Instruction::FILL_ARRAY_DATA: {
-      // The length access should crash. We currently do not do implicit checks on
-      // the array access itself.
-      return (addr == 0u) || (addr == mirror::Array::LengthOffset().Uint32Value());
-    }
-
+    case Instruction::APUT_SHORT:
+    case Instruction::FILL_ARRAY_DATA:
     case Instruction::ARRAY_LENGTH: {
-      // The length access should crash.
-      return (addr == 0u) || (addr == mirror::Array::LengthOffset().Uint32Value());
+      // The length access should crash. We currently do not do implicit checks on
+      // the array access itself.
+      return (addr == 0u) ||
+          (addr == mirror::Array::LengthOffset().Uint32Value()) ||
+          (kEmitCompilerReadBarrier && (addr == mirror::Object::MonitorOffset().Uint32Value()));
     }
 
     default: {
@@ -655,6 +648,12 @@
                      "Using a null lambda");
       break;
     }
+    case Instruction::MONITOR_ENTER:
+    case Instruction::MONITOR_EXIT: {
+      ThrowException("Ljava/lang/NullPointerException;", nullptr,
+                     "Attempt to do a synchronize operation on a null object");
+      break;
+    }
     default: {
       const DexFile* dex_file =
           method->GetDeclaringClass()->GetDexCache()->GetDexFile();
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
new file mode 100644
index 0000000..743fbb9
--- /dev/null
+++ b/runtime/dex2oat_environment_test.h
@@ -0,0 +1,188 @@
+/*
+ * 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_DEX2OAT_ENVIRONMENT_TEST_H_
+#define ART_RUNTIME_DEX2OAT_ENVIRONMENT_TEST_H_
+
+#include <fstream>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "common_runtime_test.h"
+#include "compiler_callbacks.h"
+#include "gc/heap.h"
+#include "gc/space/image_space.h"
+#include "oat_file_assistant.h"
+#include "os.h"
+#include "runtime.h"
+#include "utils.h"
+
+namespace art {
+
+// Test class that provides some helpers to set a test up for compilation using dex2oat.
+class Dex2oatEnvironmentTest : public CommonRuntimeTest {
+ public:
+  virtual void SetUp() OVERRIDE {
+    CommonRuntimeTest::SetUp();
+
+    // Create a scratch directory to work from.
+    scratch_dir_ = android_data_ + "/Dex2oatEnvironmentTest";
+    ASSERT_EQ(0, mkdir(scratch_dir_.c_str(), 0700));
+
+    // Create a subdirectory in scratch for odex files.
+    odex_oat_dir_ = scratch_dir_ + "/oat";
+    ASSERT_EQ(0, mkdir(odex_oat_dir_.c_str(), 0700));
+
+    odex_dir_ = odex_oat_dir_ + "/" + std::string(GetInstructionSetString(kRuntimeISA));
+    ASSERT_EQ(0, mkdir(odex_dir_.c_str(), 0700));
+
+    // Verify the environment is as we expect
+    uint32_t checksum;
+    std::string error_msg;
+    ASSERT_TRUE(OS::FileExists(GetSystemImageFile().c_str()))
+      << "Expected pre-compiled boot image to be at: " << GetSystemImageFile();
+    ASSERT_TRUE(OS::FileExists(GetDexSrc1().c_str()))
+      << "Expected dex file to be at: " << GetDexSrc1();
+    ASSERT_TRUE(OS::FileExists(GetStrippedDexSrc1().c_str()))
+      << "Expected stripped dex file to be at: " << GetStrippedDexSrc1();
+    ASSERT_FALSE(DexFile::GetChecksum(GetStrippedDexSrc1().c_str(), &checksum, &error_msg))
+      << "Expected stripped dex file to be stripped: " << GetStrippedDexSrc1();
+    ASSERT_TRUE(OS::FileExists(GetDexSrc2().c_str()))
+      << "Expected dex file to be at: " << GetDexSrc2();
+
+    // GetMultiDexSrc2 should have the same primary dex checksum as
+    // GetMultiDexSrc1, but a different secondary dex checksum.
+    static constexpr bool kVerifyChecksum = true;
+    std::vector<std::unique_ptr<const DexFile>> multi1;
+    ASSERT_TRUE(DexFile::Open(GetMultiDexSrc1().c_str(),
+          GetMultiDexSrc1().c_str(), kVerifyChecksum, &error_msg, &multi1)) << error_msg;
+    ASSERT_GT(multi1.size(), 1u);
+
+    std::vector<std::unique_ptr<const DexFile>> multi2;
+    ASSERT_TRUE(DexFile::Open(GetMultiDexSrc2().c_str(),
+          GetMultiDexSrc2().c_str(), kVerifyChecksum, &error_msg, &multi2)) << error_msg;
+    ASSERT_GT(multi2.size(), 1u);
+
+    ASSERT_EQ(multi1[0]->GetLocationChecksum(), multi2[0]->GetLocationChecksum());
+    ASSERT_NE(multi1[1]->GetLocationChecksum(), multi2[1]->GetLocationChecksum());
+  }
+
+  virtual void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE {
+    // options->push_back(std::make_pair("-verbose:oat", nullptr));
+
+    // Set up the image location.
+    options->push_back(std::make_pair("-Ximage:" + GetImageLocation(),
+          nullptr));
+    // Make sure compilercallbacks are not set so that relocation will be
+    // enabled.
+    callbacks_.reset();
+  }
+
+  virtual void TearDown() OVERRIDE {
+    ClearDirectory(odex_dir_.c_str());
+    ASSERT_EQ(0, rmdir(odex_dir_.c_str()));
+
+    ClearDirectory(odex_oat_dir_.c_str());
+    ASSERT_EQ(0, rmdir(odex_oat_dir_.c_str()));
+
+    ClearDirectory(scratch_dir_.c_str());
+    ASSERT_EQ(0, rmdir(scratch_dir_.c_str()));
+
+    CommonRuntimeTest::TearDown();
+  }
+
+  static void Copy(const std::string& src, const std::string& dst) {
+    std::ifstream  src_stream(src, std::ios::binary);
+    std::ofstream  dst_stream(dst, std::ios::binary);
+
+    dst_stream << src_stream.rdbuf();
+  }
+
+  // Returns the directory where the pre-compiled core.art can be found.
+  // TODO: We should factor out this into common tests somewhere rather than
+  // re-hardcoding it here (This was copied originally from the elf writer
+  // test).
+  std::string GetImageDirectory() const {
+    if (IsHost()) {
+      const char* host_dir = getenv("ANDROID_HOST_OUT");
+      CHECK(host_dir != nullptr);
+      return std::string(host_dir) + "/framework";
+    } else {
+      return std::string("/data/art-test");
+    }
+  }
+
+  std::string GetImageLocation() const {
+    return GetImageDirectory() + "/core.art";
+  }
+
+  std::string GetSystemImageFile() const {
+    return GetImageDirectory() + "/" + GetInstructionSetString(kRuntimeISA)
+      + "/core.art";
+  }
+
+  bool GetCachedImageFile(/*out*/std::string* image, std::string* error_msg) const {
+    std::string cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA), true);
+    return GetDalvikCacheFilename(GetImageLocation().c_str(), cache.c_str(), image, error_msg);
+  }
+
+  std::string GetDexSrc1() const {
+    return GetTestDexFileName("Main");
+  }
+
+  // Returns the path to a dex file equivalent to GetDexSrc1, but with the dex
+  // file stripped.
+  std::string GetStrippedDexSrc1() const {
+    return GetTestDexFileName("MainStripped");
+  }
+
+  std::string GetMultiDexSrc1() const {
+    return GetTestDexFileName("MultiDex");
+  }
+
+  // Returns the path to a multidex file equivalent to GetMultiDexSrc2, but
+  // with the contents of the secondary dex file changed.
+  std::string GetMultiDexSrc2() const {
+    return GetTestDexFileName("MultiDexModifiedSecondary");
+  }
+
+  std::string GetDexSrc2() const {
+    return GetTestDexFileName("Nested");
+  }
+
+  // Scratch directory, for dex and odex files (oat files will go in the
+  // dalvik cache).
+  const std::string& GetScratchDir() const {
+    return scratch_dir_;
+  }
+
+  // Odex directory is the subdirectory in the scratch directory where odex
+  // files should be located.
+  const std::string& GetOdexDir() const {
+    return odex_dir_;
+  }
+
+ private:
+  std::string scratch_dir_;
+  std::string odex_oat_dir_;
+  std::string odex_dir_;
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_DEX2OAT_ENVIRONMENT_TEST_H_
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index ae5a0f6..cfe6cd1 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -59,8 +59,8 @@
       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()) {
@@ -144,11 +144,10 @@
              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)) {
@@ -163,12 +162,12 @@
     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();
@@ -310,13 +309,18 @@
                             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_);
   }
 }
@@ -330,7 +334,7 @@
 }
 
 Jit::~Jit() {
-  DCHECK(!save_profiling_info_ || !ProfileSaver::IsStarted());
+  DCHECK(!profile_saver_options_.IsEnabled() || !ProfileSaver::IsStarted());
   if (dump_info_on_shutdown_) {
     DumpInfo(LOG(INFO));
   }
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index f3a6240..2aa6f3d 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -24,6 +24,7 @@
 #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 {
@@ -92,8 +93,8 @@
     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.
@@ -189,7 +190,7 @@
   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_;
@@ -228,8 +229,11 @@
   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_;
@@ -237,8 +241,8 @@
   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;
@@ -255,15 +259,14 @@
   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);
 };
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 9822f6e..4d4d1ea 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -30,25 +30,11 @@
 
 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,
@@ -72,7 +58,9 @@
       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.
@@ -93,14 +81,13 @@
   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) {
@@ -108,7 +95,7 @@
       }
       period_condition_.TimedWait(self, NsToMs(end_time - current_time), 0);
     }
-    total_ms_of_sleep_ += kSaveResolvedClassesDelayMs;
+    total_ms_of_sleep_ += options_.GetSaveResolvedClassesDelayMs();
   }
   FetchAndCacheResolvedClassesAndMethods();
 
@@ -130,10 +117,11 @@
       // We might have been woken up by a huge number of notifications to guarantee saving.
       // If we didn't meet the minimum saving period go back to sleep (only if missed by
       // a reasonable margin).
-      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.
@@ -183,12 +171,12 @@
   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.
@@ -210,7 +198,9 @@
 // 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)) {
@@ -218,7 +208,7 @@
     }
     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();
@@ -231,6 +221,7 @@
 
  private:
   std::vector<MethodReference>* const methods_;
+  uint32_t startup_method_samples_;
 };
 
 void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() {
@@ -242,11 +233,11 @@
   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;
@@ -315,11 +306,11 @@
         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;
     }
@@ -398,12 +389,14 @@
   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);
 
@@ -433,7 +426,8 @@
   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,
diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h
index 9c6d0fa..59e2c94 100644
--- a/runtime/jit/profile_saver.h
+++ b/runtime/jit/profile_saver.h
@@ -20,6 +20,7 @@
 #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 {
@@ -28,7 +29,8 @@
  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,
@@ -61,7 +63,8 @@
                             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,
@@ -155,6 +158,7 @@
   uint64_t total_number_of_hot_spikes_;
   uint64_t total_number_of_wake_ups_;
 
+  const ProfileSaverOptions options_;
   DISALLOW_COPY_AND_ASSIGN(ProfileSaver);
 };
 
diff --git a/runtime/jit/profile_saver_options.h b/runtime/jit/profile_saver_options.h
new file mode 100644
index 0000000..a6385d7
--- /dev/null
+++ b/runtime/jit/profile_saver_options.h
@@ -0,0 +1,113 @@
+/*
+ * 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_
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index e3cc77f..a1d3ed9 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#include "oat_file_assistant.h"
-
 #include <algorithm>
 #include <fstream>
 #include <string>
@@ -29,8 +27,10 @@
 #include "class_linker-inl.h"
 #include "common_runtime_test.h"
 #include "compiler_callbacks.h"
+#include "dex2oat_environment_test.h"
 #include "gc/space/image_space.h"
 #include "mem_map.h"
+#include "oat_file_assistant.h"
 #include "oat_file_manager.h"
 #include "os.h"
 #include "scoped_thread_state_change.h"
@@ -39,52 +39,11 @@
 
 namespace art {
 
-class OatFileAssistantTest : public CommonRuntimeTest {
+class OatFileAssistantTest : public Dex2oatEnvironmentTest {
  public:
-  virtual void SetUp() {
+  virtual void SetUp() OVERRIDE {
     ReserveImageSpace();
-    CommonRuntimeTest::SetUp();
-
-    // Create a scratch directory to work from.
-    scratch_dir_ = android_data_ + "/OatFileAssistantTest";
-    ASSERT_EQ(0, mkdir(scratch_dir_.c_str(), 0700));
-
-    // Create a subdirectory in scratch for odex files.
-    odex_oat_dir_ = scratch_dir_ + "/oat";
-    ASSERT_EQ(0, mkdir(odex_oat_dir_.c_str(), 0700));
-
-    odex_dir_ = odex_oat_dir_ + "/" + std::string(GetInstructionSetString(kRuntimeISA));
-    ASSERT_EQ(0, mkdir(odex_dir_.c_str(), 0700));
-
-    // Verify the environment is as we expect
-    uint32_t checksum;
-    std::string error_msg;
-    ASSERT_TRUE(OS::FileExists(GetSystemImageFile().c_str()))
-      << "Expected pre-compiled boot image to be at: " << GetSystemImageFile();
-    ASSERT_TRUE(OS::FileExists(GetDexSrc1().c_str()))
-      << "Expected dex file to be at: " << GetDexSrc1();
-    ASSERT_TRUE(OS::FileExists(GetStrippedDexSrc1().c_str()))
-      << "Expected stripped dex file to be at: " << GetStrippedDexSrc1();
-    ASSERT_FALSE(DexFile::GetChecksum(GetStrippedDexSrc1().c_str(), &checksum, &error_msg))
-      << "Expected stripped dex file to be stripped: " << GetStrippedDexSrc1();
-    ASSERT_TRUE(OS::FileExists(GetDexSrc2().c_str()))
-      << "Expected dex file to be at: " << GetDexSrc2();
-
-    // GetMultiDexSrc2 should have the same primary dex checksum as
-    // GetMultiDexSrc1, but a different secondary dex checksum.
-    static constexpr bool kVerifyChecksum = true;
-    std::vector<std::unique_ptr<const DexFile>> multi1;
-    ASSERT_TRUE(DexFile::Open(GetMultiDexSrc1().c_str(),
-          GetMultiDexSrc1().c_str(), kVerifyChecksum, &error_msg, &multi1)) << error_msg;
-    ASSERT_GT(multi1.size(), 1u);
-
-    std::vector<std::unique_ptr<const DexFile>> multi2;
-    ASSERT_TRUE(DexFile::Open(GetMultiDexSrc2().c_str(),
-          GetMultiDexSrc2().c_str(), kVerifyChecksum, &error_msg, &multi2)) << error_msg;
-    ASSERT_GT(multi2.size(), 1u);
-
-    ASSERT_EQ(multi1[0]->GetLocationChecksum(), multi2[0]->GetLocationChecksum());
-    ASSERT_NE(multi1[1]->GetLocationChecksum(), multi2[1]->GetLocationChecksum());
+    Dex2oatEnvironmentTest::SetUp();
   }
 
   // Pre-Relocate the image to a known non-zero offset so we don't have to
@@ -108,17 +67,6 @@
     return Exec(argv, error_msg);
   }
 
-  virtual void SetUpRuntimeOptions(RuntimeOptions* options) {
-    // options->push_back(std::make_pair("-verbose:oat", nullptr));
-
-    // Set up the image location.
-    options->push_back(std::make_pair("-Ximage:" + GetImageLocation(),
-          nullptr));
-    // Make sure compilercallbacks are not set so that relocation will be
-    // enabled.
-    callbacks_.reset();
-  }
-
   virtual void PreRuntimeCreate() {
     std::string error_msg;
     ASSERT_TRUE(PreRelocateImage(&error_msg)) << error_msg;
@@ -126,94 +74,10 @@
     UnreserveImageSpace();
   }
 
-  virtual void PostRuntimeCreate() {
+  virtual void PostRuntimeCreate() OVERRIDE {
     ReserveImageSpace();
   }
 
-  virtual void TearDown() {
-    ClearDirectory(odex_dir_.c_str());
-    ASSERT_EQ(0, rmdir(odex_dir_.c_str()));
-
-    ClearDirectory(odex_oat_dir_.c_str());
-    ASSERT_EQ(0, rmdir(odex_oat_dir_.c_str()));
-
-    ClearDirectory(scratch_dir_.c_str());
-    ASSERT_EQ(0, rmdir(scratch_dir_.c_str()));
-
-    CommonRuntimeTest::TearDown();
-  }
-
-  void Copy(std::string src, std::string dst) {
-    std::ifstream  src_stream(src, std::ios::binary);
-    std::ofstream  dst_stream(dst, std::ios::binary);
-
-    dst_stream << src_stream.rdbuf();
-  }
-
-  // Returns the directory where the pre-compiled core.art can be found.
-  // TODO: We should factor out this into common tests somewhere rather than
-  // re-hardcoding it here (This was copied originally from the elf writer
-  // test).
-  std::string GetImageDirectory() {
-    if (IsHost()) {
-      const char* host_dir = getenv("ANDROID_HOST_OUT");
-      CHECK(host_dir != nullptr);
-      return std::string(host_dir) + "/framework";
-    } else {
-      return std::string("/data/art-test");
-    }
-  }
-
-  std::string GetImageLocation() {
-    return GetImageDirectory() + "/core.art";
-  }
-
-  std::string GetSystemImageFile() {
-    return GetImageDirectory() + "/" + GetInstructionSetString(kRuntimeISA)
-      + "/core.art";
-  }
-
-  bool GetCachedImageFile(/*out*/std::string* image, std::string* error_msg) {
-    std::string cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA), true);
-    return GetDalvikCacheFilename(GetImageLocation().c_str(), cache.c_str(), image, error_msg);
-  }
-
-  std::string GetDexSrc1() {
-    return GetTestDexFileName("Main");
-  }
-
-  // Returns the path to a dex file equivalent to GetDexSrc1, but with the dex
-  // file stripped.
-  std::string GetStrippedDexSrc1() {
-    return GetTestDexFileName("MainStripped");
-  }
-
-  std::string GetMultiDexSrc1() {
-    return GetTestDexFileName("MultiDex");
-  }
-
-  // Returns the path to a multidex file equivalent to GetMultiDexSrc2, but
-  // with the contents of the secondary dex file changed.
-  std::string GetMultiDexSrc2() {
-    return GetTestDexFileName("MultiDexModifiedSecondary");
-  }
-
-  std::string GetDexSrc2() {
-    return GetTestDexFileName("Nested");
-  }
-
-  // Scratch directory, for dex and odex files (oat files will go in the
-  // dalvik cache).
-  std::string GetScratchDir() {
-    return scratch_dir_;
-  }
-
-  // Odex directory is the subdirectory in the scratch directory where odex
-  // files should be located.
-  std::string GetOdexDir() {
-    return odex_dir_;
-  }
-
   // Generate a non-PIC odex file for the purposes of test.
   // The generated odex file will be un-relocated.
   void GenerateOdexForTest(const std::string& dex_location,
@@ -334,9 +198,6 @@
     image_reservation_.clear();
   }
 
-  std::string scratch_dir_;
-  std::string odex_oat_dir_;
-  std::string odex_dir_;
   std::vector<std::unique_ptr<MemMap>> image_reservation_;
 };
 
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index eac5b43..595a47b 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -176,8 +176,13 @@
           .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)
@@ -244,14 +249,6 @@
                          {"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)
@@ -690,17 +687,13 @@
   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");
diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h
index 5974fb6..1f5beb9 100644
--- a/runtime/parsed_options.h
+++ b/runtime/parsed_options.h
@@ -26,7 +26,7 @@
 #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 {
diff --git a/runtime/profiler_options.h b/runtime/profiler_options.h
deleted file mode 100644
index 1db2f05..0000000
--- a/runtime/profiler_options.h
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * 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_
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index af70270..21cd2aa 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -627,17 +627,6 @@
   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(),
@@ -1196,26 +1185,6 @@
         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));
 
@@ -1758,7 +1727,6 @@
     return;
   }
 
-  profile_output_filename_ = profile_output_filename;
   jit_->StartProfileSaver(profile_output_filename,
                           code_paths,
                           foreign_dex_profile_path,
@@ -2009,9 +1977,4 @@
   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
diff --git a/runtime/runtime.h b/runtime/runtime.h
index b7f377d..afa8e48 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -36,7 +36,6 @@
 #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"
@@ -192,10 +191,6 @@
     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_);
 
@@ -455,8 +450,6 @@
 
   // 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();
@@ -782,9 +775,6 @@
 
   const bool is_running_on_memory_tool_;
 
-  std::string profile_output_filename_;
-  ProfilerOptions profiler_options_;
-
   std::unique_ptr<TraceConfig> trace_config_;
 
   instrumentation::Instrumentation instrumentation_;
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 635ff51..31206b5 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -75,7 +75,6 @@
 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
@@ -105,7 +104,7 @@
 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 ...
diff --git a/runtime/runtime_options.h b/runtime/runtime_options.h
index ab69d4f..5fcb86e 100644
--- a/runtime/runtime_options.h
+++ b/runtime/runtime_options.h
@@ -29,8 +29,8 @@
 #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>
@@ -41,7 +41,6 @@
 class DexFile;
 struct XGcOption;
 struct BackgroundGcOption;
-struct TestProfilerOptions;
 
 #define DECLARE_KEY(Type, Name) static const Key<Type> Name
 
diff --git a/test/478-checker-clinit-check-pruning/expected.txt b/test/478-checker-clinit-check-pruning/expected.txt
index 7de097f..6f73b65 100644
--- a/test/478-checker-clinit-check-pruning/expected.txt
+++ b/test/478-checker-clinit-check-pruning/expected.txt
@@ -10,3 +10,4 @@
 Main$ClassWithClinit10's static initializer
 Main$ClassWithClinit11's static initializer
 Main$ClassWithClinit12's static initializer
+Main$ClassWithClinit13's static initializer
diff --git a/test/478-checker-clinit-check-pruning/src/Main.java b/test/478-checker-clinit-check-pruning/src/Main.java
index 7993513..6fc12f1 100644
--- a/test/478-checker-clinit-check-pruning/src/Main.java
+++ b/test/478-checker-clinit-check-pruning/src/Main.java
@@ -16,6 +16,8 @@
 
 public class Main {
 
+  static boolean doThrow = false;
+
   /*
    * Ensure an inlined static invoke explicitly triggers the
    * initialization check of the called method's declaring class, and
@@ -310,12 +312,12 @@
   /// CHECK-START: void Main.constClassAndInvokeStatic(java.lang.Iterable) liveness (before)
   /// CHECK-NOT:                           ClinitCheck
 
-  static void constClassAndInvokeStatic(Iterable it) {
+  static void constClassAndInvokeStatic(Iterable<?> it) {
     $opt$inline$ignoreClass(ClassWithClinit7.class);
     ClassWithClinit7.someStaticMethod(it);
   }
 
-  static void $opt$inline$ignoreClass(Class c) {
+  static void $opt$inline$ignoreClass(Class<?> c) {
   }
 
   static class ClassWithClinit7 {
@@ -324,7 +326,7 @@
     }
 
     // Note: not inlined from constClassAndInvokeStatic() but fully inlined from main().
-    static void someStaticMethod(Iterable it) {
+    static void someStaticMethod(Iterable<?> it) {
       // We're not inlining invoke-interface at the moment.
       it.iterator();
     }
@@ -341,7 +343,7 @@
   /// CHECK-START: void Main.sgetAndInvokeStatic(java.lang.Iterable) liveness (before)
   /// CHECK-NOT:                           ClinitCheck
 
-  static void sgetAndInvokeStatic(Iterable it) {
+  static void sgetAndInvokeStatic(Iterable<?> it) {
     $opt$inline$ignoreInt(ClassWithClinit8.value);
     ClassWithClinit8.someStaticMethod(it);
   }
@@ -356,7 +358,7 @@
     }
 
     // Note: not inlined from sgetAndInvokeStatic() but fully inlined from main().
-    static void someStaticMethod(Iterable it) {
+    static void someStaticMethod(Iterable<?> it) {
       // We're not inlining invoke-interface at the moment.
       it.iterator();
     }
@@ -372,7 +374,7 @@
   /// CHECK:                               ClinitCheck
   /// CHECK:                               InvokeStaticOrDirect clinit_check:none
 
-  static void constClassSgetAndInvokeStatic(Iterable it) {
+  static void constClassSgetAndInvokeStatic(Iterable<?> it) {
     $opt$inline$ignoreClass(ClassWithClinit9.class);
     $opt$inline$ignoreInt(ClassWithClinit9.value);
     ClassWithClinit9.someStaticMethod(it);
@@ -385,7 +387,7 @@
     }
 
     // Note: not inlined from constClassSgetAndInvokeStatic() but fully inlined from main().
-    static void someStaticMethod(Iterable it) {
+    static void someStaticMethod(Iterable<?> it) {
       // We're not inlining invoke-interface at the moment.
       it.iterator();
     }
@@ -403,12 +405,12 @@
   /// CHECK-START: void Main.inlinedInvokeStaticViaNonStatic(java.lang.Iterable) liveness (before)
   /// CHECK-NOT:                           ClinitCheck
 
-  static void inlinedInvokeStaticViaNonStatic(Iterable it) {
+  static void inlinedInvokeStaticViaNonStatic(Iterable<?> it) {
     inlinedInvokeStaticViaNonStaticHelper(null);
     inlinedInvokeStaticViaNonStaticHelper(it);
   }
 
-  static void inlinedInvokeStaticViaNonStaticHelper(Iterable it) {
+  static void inlinedInvokeStaticViaNonStaticHelper(Iterable<?> it) {
     ClassWithClinit10.inlinedForNull(it);
   }
 
@@ -418,7 +420,7 @@
       System.out.println("Main$ClassWithClinit10's static initializer");
     }
 
-    static void inlinedForNull(Iterable it) {
+    static void inlinedForNull(Iterable<?> it) {
       if (it != null) {
         // We're not inlining invoke-interface at the moment.
         it.iterator();
@@ -443,7 +445,7 @@
   /// CHECK-START: void Main.inlinedInvokeStaticViaStatic(java.lang.Iterable) liveness (before)
   /// CHECK-NOT:                           ClinitCheck
 
-  static void inlinedInvokeStaticViaStatic(Iterable it) {
+  static void inlinedInvokeStaticViaStatic(Iterable<?> it) {
     ClassWithClinit11.callInlinedForNull(it);
   }
 
@@ -453,11 +455,11 @@
       System.out.println("Main$ClassWithClinit11's static initializer");
     }
 
-    static void callInlinedForNull(Iterable it) {
+    static void callInlinedForNull(Iterable<?> it) {
       inlinedForNull(it);
     }
 
-    static void inlinedForNull(Iterable it) {
+    static void inlinedForNull(Iterable<?> it) {
       // We're not inlining invoke-interface at the moment.
       it.iterator();
     }
@@ -475,7 +477,7 @@
   /// CHECK-START: void Main.inlinedInvokeStaticViaStaticTwice(java.lang.Iterable) liveness (before)
   /// CHECK-NOT:                           ClinitCheck
 
-  static void inlinedInvokeStaticViaStaticTwice(Iterable it) {
+  static void inlinedInvokeStaticViaStaticTwice(Iterable<?> it) {
     ClassWithClinit12.callInlinedForNull(null);
     ClassWithClinit12.callInlinedForNull(it);
   }
@@ -486,11 +488,11 @@
       System.out.println("Main$ClassWithClinit12's static initializer");
     }
 
-    static void callInlinedForNull(Iterable it) {
+    static void callInlinedForNull(Iterable<?> it) {
       inlinedForNull(it);
     }
 
-    static void inlinedForNull(Iterable it) {
+    static void inlinedForNull(Iterable<?> it) {
       if (it != null) {
         // We're not inlining invoke-interface at the moment.
         it.iterator();
@@ -498,6 +500,28 @@
     }
   }
 
+  static class ClassWithClinit13 {
+    static {
+      System.out.println("Main$ClassWithClinit13's static initializer");
+    }
+
+    public static void $inline$forwardToGetIterator(Iterable<?> it) {
+      $noinline$getIterator(it);
+    }
+
+    public static void $noinline$getIterator(Iterable<?> it) {
+      // We're not inlining invoke-interface at the moment.
+      it.iterator();
+    }
+  }
+
+  // TODO: Write checker statements.
+  static Object $noinline$testInliningAndNewInstance(Iterable<?> it) {
+    if (doThrow) { throw new Error(); }
+    ClassWithClinit13.$inline$forwardToGetIterator(it);
+    return new ClassWithClinit13();
+  }
+
   // TODO: Add a test for the case of a static method whose declaring
   // class type index is not available (i.e. when `storage_index`
   // equals `DexFile::kDexNoIndex` in
@@ -517,5 +541,6 @@
     inlinedInvokeStaticViaNonStatic(it);
     inlinedInvokeStaticViaStatic(it);
     inlinedInvokeStaticViaStaticTwice(it);
+    $noinline$testInliningAndNewInstance(it);
   }
 }