Merge "Pass methods array to GetMethodsSliceRangeUnchecked"
diff --git a/profman/profile_assistant.cc b/profman/profile_assistant.cc
index c238f0d..ff02b5d 100644
--- a/profman/profile_assistant.cc
+++ b/profman/profile_assistant.cc
@@ -23,8 +23,11 @@
 
 // Minimum number of new methods/classes that profiles
 // must contain to enable recompilation.
-static constexpr const uint32_t kMinNewMethodsForCompilation = 10;
-static constexpr const uint32_t kMinNewClassesForCompilation = 10;
+static constexpr const uint32_t kMinNewMethodsForCompilation = 100;
+static constexpr const uint32_t kMinNewMethodsPercentChangeForCompilation = 2;
+static constexpr const uint32_t kMinNewClassesForCompilation = 50;
+static constexpr const uint32_t kMinNewClassesPercentChangeForCompilation = 2;
+
 
 ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfilesInternal(
         const std::vector<ScopedFlock>& profile_files,
@@ -55,9 +58,16 @@
     }
   }
 
+  uint32_t min_change_in_methods_for_compilation = std::max(
+      (kMinNewMethodsPercentChangeForCompilation * number_of_methods) / 100,
+      kMinNewMethodsForCompilation);
+  uint32_t min_change_in_classes_for_compilation = std::max(
+      (kMinNewClassesPercentChangeForCompilation * number_of_classes) / 100,
+      kMinNewClassesForCompilation);
   // Check if there is enough new information added by the current profiles.
-  if (((info.GetNumberOfMethods() - number_of_methods) < kMinNewMethodsForCompilation) &&
-      ((info.GetNumberOfResolvedClasses() - number_of_classes) < kMinNewClassesForCompilation)) {
+  if (((info.GetNumberOfMethods() - number_of_methods) < min_change_in_methods_for_compilation) &&
+      ((info.GetNumberOfResolvedClasses() - number_of_classes)
+          < min_change_in_classes_for_compilation)) {
     return kSkipCompilation;
   }
 
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index 8cbf8c3..2f38535 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -335,6 +335,22 @@
     ASSERT_EQ(expected_clases.size(), found);
   }
 
+  int CheckCompilationPercentChange(int methods_in_cur_profile, int classes_in_cur_profile,
+                                    int methods_in_ref_profile, int classes_in_ref_profile) {
+    ScratchFile profile;
+    ScratchFile reference_profile;
+
+    std::vector<int> profile_fds({ GetFd(profile)});
+    int reference_profile_fd = GetFd(reference_profile);
+
+    ProfileCompilationInfo info1;
+    SetupProfile("p1", 1, methods_in_cur_profile, classes_in_cur_profile, profile,  &info1);
+    ProfileCompilationInfo info2;
+    SetupProfile("p1", 1, methods_in_ref_profile, classes_in_ref_profile, reference_profile,
+        &info2);
+    return ProcessProfiles(profile_fds, reference_profile_fd);
+  }
+
   std::unique_ptr<ArenaAllocator> arena_;
 
   // Cache of inline caches generated during tests.
@@ -460,7 +476,7 @@
       GetFd(profile2)});
   int reference_profile_fd = GetFd(reference_profile);
 
-  const uint16_t kNumberOfMethodsToSkipCompilation = 1;
+  const uint16_t kNumberOfMethodsToSkipCompilation = 24;  // Threshold is 100.
   ProfileCompilationInfo info1;
   SetupProfile("p1", 1, kNumberOfMethodsToSkipCompilation, 0, profile1, &info1);
   ProfileCompilationInfo info2;
@@ -489,6 +505,50 @@
   CheckProfileInfo(profile2, info2);
 }
 
+TEST_F(ProfileAssistantTest, DoNotAdviseCompilationMethodPercentage) {
+  const uint16_t kNumberOfMethodsInRefProfile = 6000;
+  const uint16_t kNumberOfMethodsInCurProfile = 6110;  // Threshold is 2%.
+  // We should not advise compilation.
+  ASSERT_EQ(ProfileAssistant::kSkipCompilation,
+            CheckCompilationPercentChange(kNumberOfMethodsInCurProfile,
+                                          0,
+                                          kNumberOfMethodsInRefProfile,
+                                          0));
+}
+
+TEST_F(ProfileAssistantTest, ShouldAdviseCompilationMethodPercentage) {
+  const uint16_t kNumberOfMethodsInRefProfile = 6000;
+  const uint16_t kNumberOfMethodsInCurProfile = 6120;  // Threshold is 2%.
+  // We should advise compilation.
+  ASSERT_EQ(ProfileAssistant::kCompile,
+            CheckCompilationPercentChange(kNumberOfMethodsInCurProfile,
+                                          0,
+                                          kNumberOfMethodsInRefProfile,
+                                          0));
+}
+
+TEST_F(ProfileAssistantTest, DoNotdviseCompilationClassPercentage) {
+  const uint16_t kNumberOfClassesInRefProfile = 6000;
+  const uint16_t kNumberOfClassesInCurProfile = 6110;  // Threshold is 2%.
+  // We should not advise compilation.
+  ASSERT_EQ(ProfileAssistant::kSkipCompilation,
+            CheckCompilationPercentChange(0,
+                                          kNumberOfClassesInCurProfile,
+                                          0,
+                                          kNumberOfClassesInRefProfile));
+}
+
+TEST_F(ProfileAssistantTest, ShouldAdviseCompilationClassPercentage) {
+  const uint16_t kNumberOfClassesInRefProfile = 6000;
+  const uint16_t kNumberOfClassesInCurProfile = 6120;  // Threshold is 2%.
+  // We should advise compilation.
+  ASSERT_EQ(ProfileAssistant::kCompile,
+            CheckCompilationPercentChange(0,
+                                          kNumberOfClassesInCurProfile,
+                                          0,
+                                          kNumberOfClassesInRefProfile));
+}
+
 TEST_F(ProfileAssistantTest, FailProcessingBecauseOfProfiles) {
   ScratchFile profile1;
   ScratchFile profile2;
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index cff3ea7..2dd4db3 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -237,7 +237,8 @@
 }
 
 IndirectRef IndirectReferenceTable::Add(IRTSegmentState previous_state,
-                                        ObjPtr<mirror::Object> obj) {
+                                        ObjPtr<mirror::Object> obj,
+                                        std::string* error_msg) {
   if (kDebugIRT) {
     LOG(INFO) << "+++ Add: previous_state=" << previous_state.top_index
               << " top_index=" << segment_state_.top_index
@@ -253,28 +254,34 @@
 
   if (top_index == max_entries_) {
     if (resizable_ == ResizableCapacity::kNo) {
-      LOG(FATAL) << "JNI ERROR (app bug): " << kind_ << " table overflow "
-                 << "(max=" << max_entries_ << ")\n"
-                 << MutatorLockedDumpable<IndirectReferenceTable>(*this);
-      UNREACHABLE();
+      std::ostringstream oss;
+      oss << "JNI ERROR (app bug): " << kind_ << " table overflow "
+          << "(max=" << max_entries_ << ")"
+          << MutatorLockedDumpable<IndirectReferenceTable>(*this);
+      *error_msg = oss.str();
+      return nullptr;
     }
 
     // Try to double space.
     if (std::numeric_limits<size_t>::max() / 2 < max_entries_) {
-      LOG(FATAL) << "JNI ERROR (app bug): " << kind_ << " table overflow "
-                 << "(max=" << max_entries_ << ")" << std::endl
-                 << MutatorLockedDumpable<IndirectReferenceTable>(*this)
-                << " Resizing failed: exceeds size_t";
-      UNREACHABLE();
+      std::ostringstream oss;
+      oss << "JNI ERROR (app bug): " << kind_ << " table overflow "
+          << "(max=" << max_entries_ << ")" << std::endl
+          << MutatorLockedDumpable<IndirectReferenceTable>(*this)
+          << " Resizing failed: exceeds size_t";
+      *error_msg = oss.str();
+      return nullptr;
     }
 
-    std::string error_msg;
-    if (!Resize(max_entries_ * 2, &error_msg)) {
-      LOG(FATAL) << "JNI ERROR (app bug): " << kind_ << " table overflow "
-                 << "(max=" << max_entries_ << ")" << std::endl
-                 << MutatorLockedDumpable<IndirectReferenceTable>(*this)
-                 << " Resizing failed: " << error_msg;
-      UNREACHABLE();
+    std::string inner_error_msg;
+    if (!Resize(max_entries_ * 2, &inner_error_msg)) {
+      std::ostringstream oss;
+      oss << "JNI ERROR (app bug): " << kind_ << " table overflow "
+          << "(max=" << max_entries_ << ")" << std::endl
+          << MutatorLockedDumpable<IndirectReferenceTable>(*this)
+          << " Resizing failed: " << inner_error_msg;
+      *error_msg = oss.str();
+      return nullptr;
     }
   }
 
diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h
index 6d52d95..bf287b1 100644
--- a/runtime/indirect_reference_table.h
+++ b/runtime/indirect_reference_table.h
@@ -244,8 +244,10 @@
   bool IsValid() const;
 
   // Add a new entry. "obj" must be a valid non-null object reference. This function will
-  // abort if the table is full (max entries reached, or expansion failed).
-  IndirectRef Add(IRTSegmentState previous_state, ObjPtr<mirror::Object> obj)
+  // return null if an error happened (with an appropriate error message set).
+  IndirectRef Add(IRTSegmentState previous_state,
+                  ObjPtr<mirror::Object> obj,
+                  std::string* error_msg)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Given an IndirectRef in the table, return the Object it refers to.
diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc
index 6aefe23..9278509 100644
--- a/runtime/indirect_reference_table_test.cc
+++ b/runtime/indirect_reference_table_test.cc
@@ -80,13 +80,13 @@
   EXPECT_FALSE(irt.Remove(cookie, iref0)) << "unexpectedly successful removal";
 
   // Add three, check, remove in the order in which they were added.
-  iref0 = irt.Add(cookie, obj0.Get());
+  iref0 = irt.Add(cookie, obj0.Get(), &error_msg);
   EXPECT_TRUE(iref0 != nullptr);
   CheckDump(&irt, 1, 1);
-  IndirectRef iref1 = irt.Add(cookie, obj1.Get());
+  IndirectRef iref1 = irt.Add(cookie, obj1.Get(), &error_msg);
   EXPECT_TRUE(iref1 != nullptr);
   CheckDump(&irt, 2, 2);
-  IndirectRef iref2 = irt.Add(cookie, obj2.Get());
+  IndirectRef iref2 = irt.Add(cookie, obj2.Get(), &error_msg);
   EXPECT_TRUE(iref2 != nullptr);
   CheckDump(&irt, 3, 3);
 
@@ -108,11 +108,11 @@
   EXPECT_TRUE(irt.Get(iref0) == nullptr);
 
   // Add three, remove in the opposite order.
-  iref0 = irt.Add(cookie, obj0.Get());
+  iref0 = irt.Add(cookie, obj0.Get(), &error_msg);
   EXPECT_TRUE(iref0 != nullptr);
-  iref1 = irt.Add(cookie, obj1.Get());
+  iref1 = irt.Add(cookie, obj1.Get(), &error_msg);
   EXPECT_TRUE(iref1 != nullptr);
-  iref2 = irt.Add(cookie, obj2.Get());
+  iref2 = irt.Add(cookie, obj2.Get(), &error_msg);
   EXPECT_TRUE(iref2 != nullptr);
   CheckDump(&irt, 3, 3);
 
@@ -128,11 +128,11 @@
 
   // Add three, remove middle / middle / bottom / top.  (Second attempt
   // to remove middle should fail.)
-  iref0 = irt.Add(cookie, obj0.Get());
+  iref0 = irt.Add(cookie, obj0.Get(), &error_msg);
   EXPECT_TRUE(iref0 != nullptr);
-  iref1 = irt.Add(cookie, obj1.Get());
+  iref1 = irt.Add(cookie, obj1.Get(), &error_msg);
   EXPECT_TRUE(iref1 != nullptr);
-  iref2 = irt.Add(cookie, obj2.Get());
+  iref2 = irt.Add(cookie, obj2.Get(), &error_msg);
   EXPECT_TRUE(iref2 != nullptr);
   CheckDump(&irt, 3, 3);
 
@@ -157,20 +157,20 @@
   // Add four entries.  Remove #1, add new entry, verify that table size
   // is still 4 (i.e. holes are getting filled).  Remove #1 and #3, verify
   // that we delete one and don't hole-compact the other.
-  iref0 = irt.Add(cookie, obj0.Get());
+  iref0 = irt.Add(cookie, obj0.Get(), &error_msg);
   EXPECT_TRUE(iref0 != nullptr);
-  iref1 = irt.Add(cookie, obj1.Get());
+  iref1 = irt.Add(cookie, obj1.Get(), &error_msg);
   EXPECT_TRUE(iref1 != nullptr);
-  iref2 = irt.Add(cookie, obj2.Get());
+  iref2 = irt.Add(cookie, obj2.Get(), &error_msg);
   EXPECT_TRUE(iref2 != nullptr);
-  IndirectRef iref3 = irt.Add(cookie, obj3.Get());
+  IndirectRef iref3 = irt.Add(cookie, obj3.Get(), &error_msg);
   EXPECT_TRUE(iref3 != nullptr);
   CheckDump(&irt, 4, 4);
 
   ASSERT_TRUE(irt.Remove(cookie, iref1));
   CheckDump(&irt, 3, 3);
 
-  iref1 = irt.Add(cookie, obj1.Get());
+  iref1 = irt.Add(cookie, obj1.Get(), &error_msg);
   EXPECT_TRUE(iref1 != nullptr);
 
   ASSERT_EQ(4U, irt.Capacity()) << "hole not filled";
@@ -193,12 +193,12 @@
   // Add an entry, remove it, add a new entry, and try to use the original
   // iref.  They have the same slot number but are for different objects.
   // With the extended checks in place, this should fail.
-  iref0 = irt.Add(cookie, obj0.Get());
+  iref0 = irt.Add(cookie, obj0.Get(), &error_msg);
   EXPECT_TRUE(iref0 != nullptr);
   CheckDump(&irt, 1, 1);
   ASSERT_TRUE(irt.Remove(cookie, iref0));
   CheckDump(&irt, 0, 0);
-  iref1 = irt.Add(cookie, obj1.Get());
+  iref1 = irt.Add(cookie, obj1.Get(), &error_msg);
   EXPECT_TRUE(iref1 != nullptr);
   CheckDump(&irt, 1, 1);
   ASSERT_FALSE(irt.Remove(cookie, iref0)) << "mismatched del succeeded";
@@ -209,12 +209,12 @@
 
   // Same as above, but with the same object.  A more rigorous checker
   // (e.g. with slot serialization) will catch this.
-  iref0 = irt.Add(cookie, obj0.Get());
+  iref0 = irt.Add(cookie, obj0.Get(), &error_msg);
   EXPECT_TRUE(iref0 != nullptr);
   CheckDump(&irt, 1, 1);
   ASSERT_TRUE(irt.Remove(cookie, iref0));
   CheckDump(&irt, 0, 0);
-  iref1 = irt.Add(cookie, obj0.Get());
+  iref1 = irt.Add(cookie, obj0.Get(), &error_msg);
   EXPECT_TRUE(iref1 != nullptr);
   CheckDump(&irt, 1, 1);
   if (iref0 != iref1) {
@@ -229,7 +229,7 @@
   ASSERT_TRUE(irt.Get(nullptr) == nullptr);
 
   // Stale lookup.
-  iref0 = irt.Add(cookie, obj0.Get());
+  iref0 = irt.Add(cookie, obj0.Get(), &error_msg);
   EXPECT_TRUE(iref0 != nullptr);
   CheckDump(&irt, 1, 1);
   ASSERT_TRUE(irt.Remove(cookie, iref0));
@@ -241,12 +241,12 @@
   static const size_t kTableInitial = kTableMax / 2;
   IndirectRef manyRefs[kTableInitial];
   for (size_t i = 0; i < kTableInitial; i++) {
-    manyRefs[i] = irt.Add(cookie, obj0.Get());
+    manyRefs[i] = irt.Add(cookie, obj0.Get(), &error_msg);
     ASSERT_TRUE(manyRefs[i] != nullptr) << "Failed adding " << i;
     CheckDump(&irt, i + 1, 1);
   }
   // ...this one causes overflow.
-  iref0 = irt.Add(cookie, obj0.Get());
+  iref0 = irt.Add(cookie, obj0.Get(), &error_msg);
   ASSERT_TRUE(iref0 != nullptr);
   ASSERT_EQ(kTableInitial + 1, irt.Capacity());
   CheckDump(&irt, kTableInitial + 1, 1);
@@ -306,16 +306,16 @@
 
     CheckDump(&irt, 0, 0);
 
-    IndirectRef iref0 = irt.Add(cookie0, obj0.Get());
-    IndirectRef iref1 = irt.Add(cookie0, obj1.Get());
-    IndirectRef iref2 = irt.Add(cookie0, obj2.Get());
+    IndirectRef iref0 = irt.Add(cookie0, obj0.Get(), &error_msg);
+    IndirectRef iref1 = irt.Add(cookie0, obj1.Get(), &error_msg);
+    IndirectRef iref2 = irt.Add(cookie0, obj2.Get(), &error_msg);
 
     EXPECT_TRUE(irt.Remove(cookie0, iref1));
 
     // New segment.
     const IRTSegmentState cookie1 = irt.GetSegmentState();
 
-    IndirectRef iref3 = irt.Add(cookie1, obj3.Get());
+    IndirectRef iref3 = irt.Add(cookie1, obj3.Get(), &error_msg);
 
     // Must not have filled the previous hole.
     EXPECT_EQ(irt.Capacity(), 4u);
@@ -337,21 +337,21 @@
 
     CheckDump(&irt, 0, 0);
 
-    IndirectRef iref0 = irt.Add(cookie0, obj0.Get());
+    IndirectRef iref0 = irt.Add(cookie0, obj0.Get(), &error_msg);
 
     // New segment.
     const IRTSegmentState cookie1 = irt.GetSegmentState();
 
-    IndirectRef iref1 = irt.Add(cookie1, obj1.Get());
-    IndirectRef iref2 = irt.Add(cookie1, obj2.Get());
-    IndirectRef iref3 = irt.Add(cookie1, obj3.Get());
+    IndirectRef iref1 = irt.Add(cookie1, obj1.Get(), &error_msg);
+    IndirectRef iref2 = irt.Add(cookie1, obj2.Get(), &error_msg);
+    IndirectRef iref3 = irt.Add(cookie1, obj3.Get(), &error_msg);
 
     EXPECT_TRUE(irt.Remove(cookie1, iref2));
 
     // Pop segment.
     irt.SetSegmentState(cookie1);
 
-    IndirectRef iref4 = irt.Add(cookie1, obj4.Get());
+    IndirectRef iref4 = irt.Add(cookie1, obj4.Get(), &error_msg);
 
     EXPECT_EQ(irt.Capacity(), 2u);
     EXPECT_TRUE(irt.Get(iref2) == nullptr);
@@ -373,25 +373,25 @@
 
     CheckDump(&irt, 0, 0);
 
-    IndirectRef iref0 = irt.Add(cookie0, obj0.Get());
+    IndirectRef iref0 = irt.Add(cookie0, obj0.Get(), &error_msg);
 
     // New segment.
     const IRTSegmentState cookie1 = irt.GetSegmentState();
 
-    IndirectRef iref1 = irt.Add(cookie1, obj1.Get());
-    IndirectRef iref2 = irt.Add(cookie1, obj2.Get());
+    IndirectRef iref1 = irt.Add(cookie1, obj1.Get(), &error_msg);
+    IndirectRef iref2 = irt.Add(cookie1, obj2.Get(), &error_msg);
 
     EXPECT_TRUE(irt.Remove(cookie1, iref1));
 
     // New segment.
     const IRTSegmentState cookie2 = irt.GetSegmentState();
 
-    IndirectRef iref3 = irt.Add(cookie2, obj3.Get());
+    IndirectRef iref3 = irt.Add(cookie2, obj3.Get(), &error_msg);
 
     // Pop segment.
     irt.SetSegmentState(cookie2);
 
-    IndirectRef iref4 = irt.Add(cookie1, obj4.Get());
+    IndirectRef iref4 = irt.Add(cookie1, obj4.Get(), &error_msg);
 
     EXPECT_EQ(irt.Capacity(), 3u);
     EXPECT_TRUE(irt.Get(iref1) == nullptr);
@@ -412,20 +412,20 @@
 
     CheckDump(&irt, 0, 0);
 
-    IndirectRef iref0 = irt.Add(cookie0, obj0.Get());
+    IndirectRef iref0 = irt.Add(cookie0, obj0.Get(), &error_msg);
 
     // New segment.
     const IRTSegmentState cookie1 = irt.GetSegmentState();
 
-    IndirectRef iref1 = irt.Add(cookie1, obj1.Get());
+    IndirectRef iref1 = irt.Add(cookie1, obj1.Get(), &error_msg);
     EXPECT_TRUE(irt.Remove(cookie1, iref1));
 
     // Emptied segment, push new one.
     const IRTSegmentState cookie2 = irt.GetSegmentState();
 
-    IndirectRef iref2 = irt.Add(cookie1, obj1.Get());
-    IndirectRef iref3 = irt.Add(cookie1, obj2.Get());
-    IndirectRef iref4 = irt.Add(cookie1, obj3.Get());
+    IndirectRef iref2 = irt.Add(cookie1, obj1.Get(), &error_msg);
+    IndirectRef iref3 = irt.Add(cookie1, obj2.Get(), &error_msg);
+    IndirectRef iref4 = irt.Add(cookie1, obj3.Get(), &error_msg);
 
     EXPECT_TRUE(irt.Remove(cookie1, iref3));
 
@@ -433,7 +433,7 @@
     UNUSED(cookie2);
     irt.SetSegmentState(cookie1);
 
-    IndirectRef iref5 = irt.Add(cookie1, obj4.Get());
+    IndirectRef iref5 = irt.Add(cookie1, obj4.Get(), &error_msg);
 
     EXPECT_EQ(irt.Capacity(), 2u);
     EXPECT_TRUE(irt.Get(iref3) == nullptr);
@@ -455,14 +455,14 @@
 
     CheckDump(&irt, 0, 0);
 
-    IndirectRef iref0 = irt.Add(cookie0, obj0.Get());
+    IndirectRef iref0 = irt.Add(cookie0, obj0.Get(), &error_msg);
 
     // New segment.
     const IRTSegmentState cookie1 = irt.GetSegmentState();
 
-    IndirectRef iref1 = irt.Add(cookie1, obj1.Get());
-    IndirectRef iref2 = irt.Add(cookie1, obj1.Get());
-    IndirectRef iref3 = irt.Add(cookie1, obj2.Get());
+    IndirectRef iref1 = irt.Add(cookie1, obj1.Get(), &error_msg);
+    IndirectRef iref2 = irt.Add(cookie1, obj1.Get(), &error_msg);
+    IndirectRef iref3 = irt.Add(cookie1, obj2.Get(), &error_msg);
 
     EXPECT_TRUE(irt.Remove(cookie1, iref2));
 
@@ -473,7 +473,7 @@
     const IRTSegmentState cookie1_second = irt.GetSegmentState();
     UNUSED(cookie1_second);
 
-    IndirectRef iref4 = irt.Add(cookie1, obj3.Get());
+    IndirectRef iref4 = irt.Add(cookie1, obj3.Get(), &error_msg);
 
     EXPECT_EQ(irt.Capacity(), 2u);
     EXPECT_TRUE(irt.Get(iref3) == nullptr);
@@ -504,7 +504,7 @@
   const IRTSegmentState cookie = kIRTFirstSegment;
 
   for (size_t i = 0; i != kTableMax + 1; ++i) {
-    irt.Add(cookie, obj0.Get());
+    irt.Add(cookie, obj0.Get(), &error_msg);
   }
 
   EXPECT_EQ(irt.Capacity(), kTableMax + 1);
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index c0d1861..5a16053 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -589,7 +589,12 @@
     return nullptr;
   }
   WriterMutexLock mu(self, *Locks::jni_globals_lock_);
-  IndirectRef ref = globals_.Add(kIRTFirstSegment, obj);
+  std::string error_msg;
+  IndirectRef ref = globals_.Add(kIRTFirstSegment, obj, &error_msg);
+  if (UNLIKELY(ref == nullptr)) {
+    LOG(FATAL) << error_msg;
+    UNREACHABLE();
+  }
   return reinterpret_cast<jobject>(ref);
 }
 
@@ -607,7 +612,12 @@
     self->CheckEmptyCheckpointFromWeakRefAccess(Locks::jni_weak_globals_lock_);
     weak_globals_add_condition_.WaitHoldingLocks(self);
   }
-  IndirectRef ref = weak_globals_.Add(kIRTFirstSegment, obj);
+  std::string error_msg;
+  IndirectRef ref = weak_globals_.Add(kIRTFirstSegment, obj, &error_msg);
+  if (UNLIKELY(ref == nullptr)) {
+    LOG(FATAL) << error_msg;
+    UNREACHABLE();
+  }
   return reinterpret_cast<jweak>(ref);
 }
 
diff --git a/runtime/jni_env_ext-inl.h b/runtime/jni_env_ext-inl.h
index 25893b7..d66df08 100644
--- a/runtime/jni_env_ext-inl.h
+++ b/runtime/jni_env_ext-inl.h
@@ -25,7 +25,13 @@
 
 template<typename T>
 inline T JNIEnvExt::AddLocalReference(ObjPtr<mirror::Object> obj) {
-  IndirectRef ref = locals.Add(local_ref_cookie, obj);
+  std::string error_msg;
+  IndirectRef ref = locals.Add(local_ref_cookie, obj, &error_msg);
+  if (UNLIKELY(ref == nullptr)) {
+    // This is really unexpected if we allow resizing local IRTs...
+    LOG(FATAL) << error_msg;
+    UNREACHABLE();
+  }
 
   // TODO: fix this to understand PushLocalFrame, so we can turn it on.
   if (false) {
diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc
index 3ff94f9..8352657 100644
--- a/runtime/jni_env_ext.cc
+++ b/runtime/jni_env_ext.cc
@@ -99,7 +99,14 @@
   if (obj == nullptr) {
     return nullptr;
   }
-  return reinterpret_cast<jobject>(locals.Add(local_ref_cookie, obj));
+  std::string error_msg;
+  jobject ref = reinterpret_cast<jobject>(locals.Add(local_ref_cookie, obj, &error_msg));
+  if (UNLIKELY(ref == nullptr)) {
+    // This is really unexpected if we allow resizing local IRTs...
+    LOG(FATAL) << error_msg;
+    UNREACHABLE();
+  }
+  return ref;
 }
 
 void JNIEnvExt::DeleteLocalRef(jobject obj) {
diff --git a/test/testrunner/env.py b/test/testrunner/env.py
index d45d009..cc19afc 100644
--- a/test/testrunner/env.py
+++ b/test/testrunner/env.py
@@ -115,82 +115,9 @@
 # Keep going after encountering a test failure?
 ART_TEST_KEEP_GOING = _getEnvBoolean('ART_TEST_KEEP_GOING', True)
 
-# Do you want all tests, even those that are time consuming?
-ART_TEST_FULL = _getEnvBoolean('ART_TEST_FULL', False)
-
-# Do you want interpreter tests run?
-ART_TEST_INTERPRETER = _getEnvBoolean('ART_TEST_INTERPRETER', ART_TEST_FULL)
-ART_TEST_INTERPRETER_ACCESS_CHECKS = _getEnvBoolean('ART_TEST_INTERPRETER_ACCESS_CHECKS',
-                                                   ART_TEST_FULL)
-
-# Do you want JIT tests run?
-ART_TEST_JIT = _getEnvBoolean('ART_TEST_JIT', ART_TEST_FULL)
-
-# Do you want optimizing compiler tests run?
-ART_TEST_OPTIMIZING = _getEnvBoolean('ART_TEST_OPTIMIZING', ART_TEST_FULL)
-
-# Do you want to test the optimizing compiler with graph coloring register allocation?
-ART_TEST_OPTIMIZING_GRAPH_COLOR = _getEnvBoolean('ART_TEST_OPTIMIZING_GRAPH_COLOR', ART_TEST_FULL)
-
-# Do you want to do run-tests with profiles?
-ART_TEST_SPEED_PROFILE = _getEnvBoolean('ART_TEST_SPEED_PROFILE', ART_TEST_FULL)
-
-# Do we want to test PIC-compiled tests ("apps")?
-ART_TEST_PIC_TEST = _getEnvBoolean('ART_TEST_PIC_TEST', ART_TEST_FULL)
-# Do you want tracing tests run?
-ART_TEST_TRACE = _getEnvBoolean('ART_TEST_TRACE', ART_TEST_FULL)
-
-# Do you want tracing tests (streaming mode) run?
-ART_TEST_TRACE_STREAM = _getEnvBoolean('ART_TEST_TRACE_STREAM', ART_TEST_FULL)
-
-# Do you want tests with GC verification enabled run?
-ART_TEST_GC_VERIFY = _getEnvBoolean('ART_TEST_GC_VERIFY', ART_TEST_FULL)
-
-# Do you want tests with the GC stress mode enabled run?
-ART_TEST_GC_STRESS = _getEnvBoolean('ART_TEST_GC_STRESS', ART_TEST_FULL)
-
-# Do you want tests with the JNI forcecopy mode enabled run?
-ART_TEST_JNI_FORCECOPY = _getEnvBoolean('ART_TEST_JNI_FORCECOPY', ART_TEST_FULL)
-
-# Do you want run-tests with relocation disabled run?
-ART_TEST_RUN_TEST_RELOCATE = _getEnvBoolean('ART_TEST_RUN_TEST_RELOCATE', ART_TEST_FULL)
-
-# Do you want run-tests with prebuilding?
-ART_TEST_RUN_TEST_PREBUILD = _getEnvBoolean('ART_TEST_RUN_TEST_PREBUILD', ART_TEST_FULL)
-
-# Do you want run-tests with no prebuilding enabled run?
-ART_TEST_RUN_TEST_NO_PREBUILD = _getEnvBoolean('ART_TEST_RUN_TEST_NO_PREBUILD', ART_TEST_FULL)
-
-# Do you want run-tests with a pregenerated core.art?
-ART_TEST_RUN_TEST_IMAGE = _getEnvBoolean('ART_TEST_RUN_TEST_IMAGE', ART_TEST_FULL)
-
-# Do you want run-tests without a pregenerated core.art?
-ART_TEST_RUN_TEST_NO_IMAGE = _getEnvBoolean('ART_TEST_RUN_TEST_NO_IMAGE', ART_TEST_FULL)
-
-# Do you want run-tests with relocation enabled but patchoat failing?
-ART_TEST_RUN_TEST_RELOCATE_NO_PATCHOAT = _getEnvBoolean('ART_TEST_RUN_TEST_RELOCATE_NO_PATCHOAT',
-                                                       ART_TEST_FULL)
-
-# Do you want run-tests without a dex2oat?
-ART_TEST_RUN_TEST_NO_DEX2OAT = _getEnvBoolean('ART_TEST_RUN_TEST_NO_DEX2OAT', ART_TEST_FULL)
-
-# Do you want run-tests with libartd.so?
-ART_TEST_RUN_TEST_DEBUG = _getEnvBoolean('ART_TEST_RUN_TEST_DEBUG', ART_TEST_FULL)
-
-# Do you want run-tests with libart.so?
-ART_TEST_RUN_TEST_NDEBUG = _getEnvBoolean('ART_TEST_RUN_TEST_NDEBUG', ART_TEST_FULL)
-
 # Do you want failed tests to have their artifacts cleaned up?
 ART_TEST_RUN_TEST_ALWAYS_CLEAN = _getEnvBoolean('ART_TEST_RUN_TEST_ALWAYS_CLEAN', True)
 
-# Do you want run-tests with the --debuggable flag
-ART_TEST_RUN_TEST_DEBUGGABLE = _getEnvBoolean('ART_TEST_RUN_TEST_DEBUGGABLE', ART_TEST_FULL)
-
-# Do you want to test multi-part boot-image functionality?
-ART_TEST_RUN_TEST_MULTI_IMAGE = _getEnvBoolean('ART_TEST_RUN_TEST_MULTI_IMAGE', ART_TEST_FULL)
-
-ART_TEST_DEBUG_GC = _getEnvBoolean('ART_TEST_DEBUG_GC', False)
-
 ART_TEST_BISECTION = _getEnvBoolean('ART_TEST_BISECTION', False)
 
 DEX2OAT_HOST_INSTRUCTION_SET_FEATURES = _env.get('DEX2OAT_HOST_INSTRUCTION_SET_FEATURES')
@@ -217,8 +144,6 @@
 # Note: ART_2ND_PHONY_TEST_HOST_SUFFIX is 2ND_ART_PHONY_HOST_TARGET_SUFFIX in .mk files
 # Python does not let us have variable names starting with a digit, so it has differ.
 
-ART_TEST_RUN_TEST_JVMTI_STRESS = _getEnvBoolean('ART_TEST_RUN_TEST_JVMTI_STRESS', ART_TEST_FULL)
-
 if TARGET_2ND_ARCH:
   if "64" in TARGET_ARCH:
     ART_PHONY_TEST_TARGET_SUFFIX = "64"
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index e07198b..2a772ff 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -45,6 +45,7 @@
 
 """
 import argparse
+import collections
 import fnmatch
 import itertools
 import json
@@ -60,21 +61,6 @@
 import env
 from target_config import target_config
 
-TARGET_TYPES = set()
-RUN_TYPES = set()
-PREBUILD_TYPES = set()
-COMPILER_TYPES = set()
-RELOCATE_TYPES = set()
-TRACE_TYPES = set()
-GC_TYPES = set()
-JNI_TYPES = set()
-IMAGE_TYPES = set()
-PICTEST_TYPES = set()
-DEBUGGABLE_TYPES = set()
-ADDRESS_SIZES = set()
-OPTIMIZING_COMPILER_TYPES = set()
-JVMTI_TYPES = set()
-ADDRESS_SIZES_TARGET = {'host': set(), 'target': set()}
 # timeout for individual tests.
 # TODO: make it adjustable per tests and for buildbots
 timeout = 3000 # 50 minutes
@@ -128,6 +114,12 @@
 gdb_arg = ''
 stop_testrunner = False
 dex2oat_jobs = -1   # -1 corresponds to default threads for dex2oat
+run_all_configs = False
+
+# Dict to store user requested test variants.
+# key: variant_type.
+# value: set of variants user wants to run of type <key>.
+_user_input_variants = collections.defaultdict(set)
 
 def gather_test_info():
   """The method gathers test information about the test to be run which includes
@@ -151,7 +143,7 @@
   VARIANT_TYPE_DICT['jvmti'] = {'no-jvmti', 'jvmti-stress', 'redefine-stress', 'trace-stress',
                                 'field-stress', 'step-stress'}
   VARIANT_TYPE_DICT['compiler'] = {'interp-ac', 'interpreter', 'jit', 'optimizing',
-                              'regalloc_gc', 'speed-profile'}
+                                   'regalloc_gc', 'speed-profile'}
 
   for v_type in VARIANT_TYPE_DICT:
     TOTAL_VARIANTS_SET = TOTAL_VARIANTS_SET.union(VARIANT_TYPE_DICT.get(v_type))
@@ -173,106 +165,75 @@
     # Bisection search writes to standard output.
     env.ART_TEST_QUIET = False
 
-  if not TARGET_TYPES:
-    TARGET_TYPES.add('host')
-    TARGET_TYPES.add('target')
+  global _user_input_variants
+  global run_all_configs
+  if run_all_configs:
+    target_types = _user_input_variants['target']
+    _user_input_variants = VARIANT_TYPE_DICT
+    _user_input_variants['target'] = target_types
 
-  if env.ART_TEST_RUN_TEST_NO_PREBUILD:
-    PREBUILD_TYPES.add('no-prebuild')
-  if env.ART_TEST_RUN_TEST_NO_DEX2OAT:
-    PREBUILD_TYPES.add('no-dex2oat')
-  if env.ART_TEST_RUN_TEST_PREBUILD or not PREBUILD_TYPES: # Default
-    PREBUILD_TYPES.add('prebuild')
+  if not _user_input_variants['target']:
+    _user_input_variants['target'].add('host')
+    _user_input_variants['target'].add('target')
 
-  if env.ART_TEST_INTERPRETER_ACCESS_CHECKS:
-    COMPILER_TYPES.add('interp-ac')
-  if env.ART_TEST_INTERPRETER:
-    COMPILER_TYPES.add('interpreter')
-  if env.ART_TEST_JIT:
-    COMPILER_TYPES.add('jit')
-  if env.ART_TEST_OPTIMIZING_GRAPH_COLOR:
-    COMPILER_TYPES.add('regalloc_gc')
-    OPTIMIZING_COMPILER_TYPES.add('regalloc_gc')
-  if env.ART_TEST_OPTIMIZING:
-    COMPILER_TYPES.add('optimizing')
-    OPTIMIZING_COMPILER_TYPES.add('optimizing')
-  if env.ART_TEST_SPEED_PROFILE:
-    COMPILER_TYPES.add('speed-profile')
+  if not _user_input_variants['prebuild']: # Default
+    _user_input_variants['prebuild'].add('prebuild')
 
   # By default only run without jvmti
-  if not JVMTI_TYPES:
-    JVMTI_TYPES.add('no-jvmti')
+  if not _user_input_variants['jvmti']:
+    _user_input_variants['jvmti'].add('no-jvmti')
 
   # By default we run all 'compiler' variants.
-  if not COMPILER_TYPES:
-    COMPILER_TYPES.add('optimizing')
-    COMPILER_TYPES.add('jit')
-    COMPILER_TYPES.add('interpreter')
-    COMPILER_TYPES.add('interp-ac')
-    COMPILER_TYPES.add('speed-profile')
-    OPTIMIZING_COMPILER_TYPES.add('optimizing')
+  if not _user_input_variants['compiler']:
+    _user_input_variants['compiler'].add('optimizing')
+    _user_input_variants['compiler'].add('jit')
+    _user_input_variants['compiler'].add('interpreter')
+    _user_input_variants['compiler'].add('interp-ac')
+    _user_input_variants['compiler'].add('speed-profile')
 
-  if env.ART_TEST_RUN_TEST_RELOCATE:
-    RELOCATE_TYPES.add('relocate')
-  if env.ART_TEST_RUN_TEST_RELOCATE_NO_PATCHOAT:
-    RELOCATE_TYPES.add('relocate-npatchoat')
-  if not RELOCATE_TYPES: # Default
-    RELOCATE_TYPES.add('no-relocate')
+  if not _user_input_variants['relocate']: # Default
+    _user_input_variants['relocate'].add('no-relocate')
 
-  if env.ART_TEST_TRACE:
-    TRACE_TYPES.add('trace')
-  if env.ART_TEST_TRACE_STREAM:
-    TRACE_TYPES.add('stream')
-  if not TRACE_TYPES: # Default
-    TRACE_TYPES.add('ntrace')
+  if not _user_input_variants['trace']: # Default
+    _user_input_variants['trace'].add('ntrace')
 
-  if env.ART_TEST_GC_STRESS:
-    GC_TYPES.add('gcstress')
-  if env.ART_TEST_GC_VERIFY:
-    GC_TYPES.add('gcverify')
-  if not GC_TYPES: # Default
-    GC_TYPES.add('cms')
+  if not _user_input_variants['gc']: # Default
+    _user_input_variants['gc'].add('cms')
 
-  if env.ART_TEST_JNI_FORCECOPY:
-    JNI_TYPES.add('forcecopy')
-  if not JNI_TYPES: # Default
-    JNI_TYPES.add('checkjni')
+  if not _user_input_variants['jni']: # Default
+    _user_input_variants['jni'].add('checkjni')
 
-  if env.ART_TEST_RUN_TEST_NO_IMAGE:
-    IMAGE_TYPES.add('no-image')
-  if env.ART_TEST_RUN_TEST_MULTI_IMAGE:
-    IMAGE_TYPES.add('multipicimage')
-  if env.ART_TEST_RUN_TEST_IMAGE or not IMAGE_TYPES: # Default
-    IMAGE_TYPES.add('picimage')
+  if not _user_input_variants['image']: # Default
+    _user_input_variants['image'].add('picimage')
 
-  if env.ART_TEST_PIC_TEST:
-    PICTEST_TYPES.add('pictest')
-  if not PICTEST_TYPES: # Default
-    PICTEST_TYPES.add('npictest')
 
-  if env.ART_TEST_RUN_TEST_NDEBUG:
-    RUN_TYPES.add('ndebug')
-  if env.ART_TEST_RUN_TEST_DEBUG or not RUN_TYPES: # Default
-    RUN_TYPES.add('debug')
+  if not _user_input_variants['pictest']: # Default
+    _user_input_variants['pictest'].add('npictest')
 
-  if env.ART_TEST_RUN_TEST_DEBUGGABLE:
-    DEBUGGABLE_TYPES.add('debuggable')
-  if not DEBUGGABLE_TYPES: # Default
-    DEBUGGABLE_TYPES.add('ndebuggable')
+  if not _user_input_variants['debuggable']: # Default
+    _user_input_variants['debuggable'].add('ndebuggable')
 
-  if not ADDRESS_SIZES:
-    ADDRESS_SIZES_TARGET['target'].add(env.ART_PHONY_TEST_TARGET_SUFFIX)
-    ADDRESS_SIZES_TARGET['host'].add(env.ART_PHONY_TEST_HOST_SUFFIX)
+  if not _user_input_variants['run']: # Default
+    _user_input_variants['run'].add('debug')
+
+  _user_input_variants['address_sizes_target'] = collections.defaultdict(set)
+  if not _user_input_variants['address_sizes']:
+    _user_input_variants['address_sizes_target']['target'].add(
+        env.ART_PHONY_TEST_TARGET_SUFFIX)
+    _user_input_variants['address_sizes_target']['host'].add(
+        env.ART_PHONY_TEST_HOST_SUFFIX)
     if env.ART_TEST_RUN_TEST_2ND_ARCH:
-      ADDRESS_SIZES_TARGET['host'].add(env.ART_2ND_PHONY_TEST_HOST_SUFFIX)
-      ADDRESS_SIZES_TARGET['target'].add(env.ART_2ND_PHONY_TEST_TARGET_SUFFIX)
+      _user_input_variants['address_sizes_target']['host'].add(
+          env.ART_2ND_PHONY_TEST_HOST_SUFFIX)
+      _user_input_variants['address_sizes_target']['target'].add(
+          env.ART_2ND_PHONY_TEST_TARGET_SUFFIX)
   else:
-    ADDRESS_SIZES_TARGET['host'] = ADDRESS_SIZES_TARGET['host'].union(ADDRESS_SIZES)
-    ADDRESS_SIZES_TARGET['target'] = ADDRESS_SIZES_TARGET['target'].union(ADDRESS_SIZES)
+    _user_input_variants['address_sizes_target']['host'] = _user_input_variants['address_sizes']
+    _user_input_variants['address_sizes_target']['target'] = _user_input_variants['address_sizes']
 
   global n_thread
   if n_thread is -1:
-    if 'target' in TARGET_TYPES:
+    if 'target' in _user_input_variants['target']:
       n_thread = get_default_threads('target')
     else:
       n_thread = get_default_threads('host')
@@ -308,20 +269,12 @@
   options_all = ''
   global total_test_count
   total_test_count = len(tests)
-  total_test_count *= len(RUN_TYPES)
-  total_test_count *= len(PREBUILD_TYPES)
-  total_test_count *= len(RELOCATE_TYPES)
-  total_test_count *= len(TRACE_TYPES)
-  total_test_count *= len(GC_TYPES)
-  total_test_count *= len(JNI_TYPES)
-  total_test_count *= len(IMAGE_TYPES)
-  total_test_count *= len(PICTEST_TYPES)
-  total_test_count *= len(DEBUGGABLE_TYPES)
-  total_test_count *= len(COMPILER_TYPES)
-  total_test_count *= len(JVMTI_TYPES)
+  for variant_type in VARIANT_TYPE_DICT:
+    if not (variant_type == 'target' or 'address_sizes' in variant_type):
+      total_test_count *= len(_user_input_variants[variant_type])
   target_address_combinations = 0
-  for target in TARGET_TYPES:
-    for address_size in ADDRESS_SIZES_TARGET[target]:
+  for target in _user_input_variants['target']:
+    for address_size in _user_input_variants['address_sizes_target'][target]:
       target_address_combinations += 1
   total_test_count *= target_address_combinations
 
@@ -345,14 +298,16 @@
   if dex2oat_jobs != -1:
     options_all += ' --dex2oat-jobs ' + str(dex2oat_jobs)
 
-  config = itertools.product(tests, TARGET_TYPES, RUN_TYPES, PREBUILD_TYPES,
-                             COMPILER_TYPES, RELOCATE_TYPES, TRACE_TYPES,
-                             GC_TYPES, JNI_TYPES, IMAGE_TYPES, PICTEST_TYPES,
-                             DEBUGGABLE_TYPES, JVMTI_TYPES)
+  config = itertools.product(tests, _user_input_variants['target'], _user_input_variants['run'],
+                             _user_input_variants['prebuild'], _user_input_variants['compiler'],
+                             _user_input_variants['relocate'], _user_input_variants['trace'],
+                             _user_input_variants['gc'], _user_input_variants['jni'],
+                             _user_input_variants['image'], _user_input_variants['pictest'],
+                             _user_input_variants['debuggable'], _user_input_variants['jvmti'])
 
   for test, target, run, prebuild, compiler, relocate, trace, gc, \
       jni, image, pictest, debuggable, jvmti in config:
-    for address_size in ADDRESS_SIZES_TARGET[target]:
+    for address_size in _user_input_variants['address_sizes_target'][target]:
       if stop_testrunner:
         # When ART_TEST_KEEP_GOING is set to false, then as soon as a test
         # fails, stop_testrunner is set to True. When this happens, the method
@@ -577,11 +532,10 @@
       total_test_count)
 
     if result == 'FAIL' or result == 'TIMEOUT':
-      info += ('%s %s %s\n%s\n') % (
+      info += ('%s %s %s\n') % (
         progress_info,
         test_name,
-        COLOR_ERROR + result + COLOR_NORMAL,
-        failed_test_info)
+        COLOR_ERROR + result + COLOR_NORMAL)
     else:
       result_text = ''
       if result == 'PASS':
@@ -810,19 +764,19 @@
   regex += '(' + '|'.join(VARIANT_TYPE_DICT['address_sizes']) + ')$'
   match = re.match(regex, test_name)
   if match:
-    TARGET_TYPES.add(match.group(1))
-    RUN_TYPES.add(match.group(2))
-    PREBUILD_TYPES.add(match.group(3))
-    COMPILER_TYPES.add(match.group(4))
-    RELOCATE_TYPES.add(match.group(5))
-    TRACE_TYPES.add(match.group(6))
-    GC_TYPES.add(match.group(7))
-    JNI_TYPES.add(match.group(8))
-    IMAGE_TYPES.add(match.group(9))
-    PICTEST_TYPES.add(match.group(10))
-    DEBUGGABLE_TYPES.add(match.group(11))
-    JVMTI_TYPES.add(match.group(12))
-    ADDRESS_SIZES.add(match.group(14))
+    _user_input_variants['target'].add(match.group(1))
+    _user_input_variants['run'].add(match.group(2))
+    _user_input_variants['prebuild'].add(match.group(3))
+    _user_input_variants['compiler'].add(match.group(4))
+    _user_input_variants['relocate'].add(match.group(5))
+    _user_input_variants['trace'].add(match.group(6))
+    _user_input_variants['gc'].add(match.group(7))
+    _user_input_variants['jni'].add(match.group(8))
+    _user_input_variants['image'].add(match.group(9))
+    _user_input_variants['pictest'].add(match.group(10))
+    _user_input_variants['debuggable'].add(match.group(11))
+    _user_input_variants['jvmti'].add(match.group(12))
+    _user_input_variants['address_sizes'].add(match.group(14))
     return {match.group(13)}
   raise ValueError(test_name + " is not a valid test")
 
@@ -871,6 +825,7 @@
   global gdb_arg
   global timeout
   global dex2oat_jobs
+  global run_all_configs
 
   parser = argparse.ArgumentParser(description="Runs all or a subset of the ART test suite.")
   parser.add_argument('-t', '--test', dest='test', help='name of the test')
@@ -878,10 +833,7 @@
   parser.add_argument('--timeout', default=timeout, type=int, dest='timeout')
   for variant in TOTAL_VARIANTS_SET:
     flag = '--' + variant
-    flag_dest = variant.replace('-', '_')
-    if variant == '32' or variant == '64':
-      flag_dest = 'n' + flag_dest
-    parser.add_argument(flag, action='store_true', dest=flag_dest)
+    parser.add_argument(flag, action='store_true', dest=variant)
   parser.add_argument('--verbose', '-v', action='store_true', dest='verbose')
   parser.add_argument('--dry-run', action='store_true', dest='dry_run')
   parser.add_argument("--skip", action="append", dest="skips", default=[],
@@ -900,6 +852,8 @@
   parser.add_argument('--gdb-arg', dest='gdb_arg')
   parser.add_argument('--dex2oat-jobs', type=int, dest='dex2oat_jobs',
                       help='Number of dex2oat jobs')
+  parser.add_argument('-a', '--all', action='store_true', dest='run_all',
+                      help="Run all the possible configurations for the input test set")
 
   options = vars(parser.parse_args())
   if options['build_target']:
@@ -910,82 +864,12 @@
   env.EXTRA_DISABLED_TESTS.update(set(options['skips']))
   if options['test']:
     test = parse_test_name(options['test'])
-  if options['pictest']:
-    PICTEST_TYPES.add('pictest')
-  if options['ndebug']:
-    RUN_TYPES.add('ndebug')
-  if options['interp_ac']:
-    COMPILER_TYPES.add('interp-ac')
-  if options['picimage']:
-    IMAGE_TYPES.add('picimage')
-  if options['n64']:
-    ADDRESS_SIZES.add('64')
-  if options['interpreter']:
-    COMPILER_TYPES.add('interpreter')
-  if options['jni']:
-    JNI_TYPES.add('jni')
-  if options['relocate_npatchoat']:
-    RELOCATE_TYPES.add('relocate-npatchoat')
-  if options['no_prebuild']:
-    PREBUILD_TYPES.add('no-prebuild')
-  if options['npictest']:
-    PICTEST_TYPES.add('npictest')
-  if options['no_dex2oat']:
-    PREBUILD_TYPES.add('no-dex2oat')
-  if options['jit']:
-    COMPILER_TYPES.add('jit')
-  if options['relocate']:
-    RELOCATE_TYPES.add('relocate')
-  if options['ndebuggable']:
-    DEBUGGABLE_TYPES.add('ndebuggable')
-  if options['no_image']:
-    IMAGE_TYPES.add('no-image')
-  if options['optimizing']:
-    COMPILER_TYPES.add('optimizing')
-  if options['speed_profile']:
-    COMPILER_TYPES.add('speed-profile')
-  if options['trace']:
-    TRACE_TYPES.add('trace')
-  if options['gcstress']:
-    GC_TYPES.add('gcstress')
-  if options['no_relocate']:
-    RELOCATE_TYPES.add('no-relocate')
-  if options['target']:
-    TARGET_TYPES.add('target')
-  if options['forcecopy']:
-    JNI_TYPES.add('forcecopy')
-  if options['n32']:
-    ADDRESS_SIZES.add('32')
-  if options['host']:
-    TARGET_TYPES.add('host')
-  if options['gcverify']:
-    GC_TYPES.add('gcverify')
-  if options['debuggable']:
-    DEBUGGABLE_TYPES.add('debuggable')
-  if options['prebuild']:
-    PREBUILD_TYPES.add('prebuild')
-  if options['debug']:
-    RUN_TYPES.add('debug')
-  if options['checkjni']:
-    JNI_TYPES.add('checkjni')
-  if options['ntrace']:
-    TRACE_TYPES.add('ntrace')
-  if options['cms']:
-    GC_TYPES.add('cms')
-  if options['multipicimage']:
-    IMAGE_TYPES.add('multipicimage')
-  if options['jvmti_stress']:
-    JVMTI_TYPES.add('jvmti-stress')
-  if options['redefine_stress']:
-    JVMTI_TYPES.add('redefine-stress')
-  if options['field_stress']:
-    JVMTI_TYPES.add('field-stress')
-  if options['step_stress']:
-    JVMTI_TYPES.add('step-stress')
-  if options['trace_stress']:
-    JVMTI_TYPES.add('trace-stress')
-  if options['no_jvmti']:
-    JVMTI_TYPES.add('no-jvmti')
+
+  for variant_type in VARIANT_TYPE_DICT:
+    for variant in VARIANT_TYPE_DICT[variant_type]:
+      if options.get(variant):
+        _user_input_variants[variant_type].add(variant)
+
   if options['verbose']:
     verbose = True
   if options['n_thread']:
@@ -1002,6 +886,8 @@
   timeout = options['timeout']
   if options['dex2oat_jobs']:
     dex2oat_jobs = options['dex2oat_jobs']
+  if options['run_all']:
+    run_all_configs = True
 
   return test
 
@@ -1011,9 +897,9 @@
   setup_test_env()
   if build:
     build_targets = ''
-    if 'host' in TARGET_TYPES:
+    if 'host' in _user_input_variants['target']:
       build_targets += 'test-art-host-run-test-dependencies'
-    if 'target' in TARGET_TYPES:
+    if 'target' in _user_input_variants['target']:
       build_targets += 'test-art-target-run-test-dependencies'
     build_command = 'make'
     build_command += ' -j'