ART: Add abort-on-soft-verifier-error

Add an option to abort compilation if any class fails compile-time
verification.

Bug: 65318848
Bug: 67358823
Test: m test-art-host-gtest-dex2oat_test
Change-Id: I5d2a7cd1d2ed048ab39d6f787ecc9eb2f41d3d77
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 7573367..547ffbc 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -296,6 +296,7 @@
       image_classes_(image_classes),
       classes_to_compile_(compiled_classes),
       methods_to_compile_(compiled_methods),
+      number_of_soft_verifier_failures_(0),
       had_hard_verifier_failure_(false),
       parallel_thread_count_(thread_count),
       stats_(new AOTCompilationStats),
@@ -923,6 +924,12 @@
     LOG(FATAL_WITHOUT_ABORT) << "Had a hard failure verifying all classes, and was asked to abort "
                              << "in such situations. Please check the log.";
     abort();
+  } else if (number_of_soft_verifier_failures_ > 0 &&
+             GetCompilerOptions().AbortOnSoftVerifierFailure()) {
+    LOG(FATAL_WITHOUT_ABORT) << "Had " << number_of_soft_verifier_failures_ << " soft failure(s) "
+                             << "verifying all classes, and was asked to abort in such situations. "
+                             << "Please check the log.";
+    abort();
   }
 
   if (compiler_options_->IsAnyCompilationEnabled()) {
@@ -2069,13 +2076,13 @@
         LOG(ERROR) << "Verification failed on class " << PrettyDescriptor(descriptor)
                    << " because: " << error_msg;
         manager_->GetCompiler()->SetHadHardVerifierFailure();
+      } else if (failure_kind == verifier::FailureKind::kSoftFailure) {
+        manager_->GetCompiler()->AddSoftVerifierFailure();
       } else {
         // Force a soft failure for the VerifierDeps. This is a sanity measure, as
         // the vdex file already records that the class hasn't been resolved. It avoids
         // trying to do future verification optimizations when processing the vdex file.
-        DCHECK(failure_kind == verifier::FailureKind::kSoftFailure ||
-               failure_kind == verifier::FailureKind::kNoFailure)
-            << failure_kind;
+        DCHECK(failure_kind == verifier::FailureKind::kNoFailure) << failure_kind;
         failure_kind = verifier::FailureKind::kSoftFailure;
       }
     } else if (!SkipClass(jclass_loader, dex_file, klass.Get())) {
@@ -2087,6 +2094,8 @@
         CHECK(soa.Self()->IsExceptionPending());
         soa.Self()->ClearException();
         manager_->GetCompiler()->SetHadHardVerifierFailure();
+      } else if (failure_kind == verifier::FailureKind::kSoftFailure) {
+        manager_->GetCompiler()->AddSoftVerifierFailure();
       }
 
       CHECK(klass->ShouldVerifyAtRuntime() || klass->IsVerified() || klass->IsErroneous())
@@ -2152,7 +2161,9 @@
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, dex_files,
                                      thread_pool);
-  verifier::HardFailLogMode log_level = GetCompilerOptions().AbortOnHardVerifierFailure()
+  bool abort_on_verifier_failures = GetCompilerOptions().AbortOnHardVerifierFailure()
+                                    || GetCompilerOptions().AbortOnSoftVerifierFailure();
+  verifier::HardFailLogMode log_level = abort_on_verifier_failures
                               ? verifier::HardFailLogMode::kLogInternalFatal
                               : verifier::HardFailLogMode::kLogWarning;
   VerifyClassVisitor visitor(&context, log_level);
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index f16e2ed..da4a580 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -17,6 +17,7 @@
 #ifndef ART_COMPILER_DRIVER_COMPILER_DRIVER_H_
 #define ART_COMPILER_DRIVER_COMPILER_DRIVER_H_
 
+#include <atomic>
 #include <set>
 #include <string>
 #include <unordered_set>
@@ -352,6 +353,9 @@
   void SetHadHardVerifierFailure() {
     had_hard_verifier_failure_ = true;
   }
+  void AddSoftVerifierFailure() {
+    number_of_soft_verifier_failures_++;
+  }
 
   Compiler::Kind GetCompilerKind() {
     return compiler_kind_;
@@ -519,6 +523,7 @@
   // This option may be restricted to the boot image, depending on a flag in the implementation.
   std::unique_ptr<std::unordered_set<std::string>> methods_to_compile_;
 
+  std::atomic<uint32_t> number_of_soft_verifier_failures_;
   bool had_hard_verifier_failure_;
 
   // A thread pool that can (potentially) run tasks in parallel.
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index b6cedff..f789314 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -51,6 +51,7 @@
       compile_pic_(false),
       verbose_methods_(),
       abort_on_hard_verifier_failure_(false),
+      abort_on_soft_verifier_failure_(false),
       init_failure_output_(nullptr),
       dump_cfg_file_name_(""),
       dump_cfg_append_(false),
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 311dbd5..12de9be 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -226,6 +226,9 @@
   bool AbortOnHardVerifierFailure() const {
     return abort_on_hard_verifier_failure_;
   }
+  bool AbortOnSoftVerifierFailure() const {
+    return abort_on_soft_verifier_failure_;
+  }
 
   const std::vector<const DexFile*>* GetNoInlineFromDexFile() const {
     return no_inline_from_;
@@ -303,6 +306,8 @@
   // Abort compilation with an error if we find a class that fails verification with a hard
   // failure.
   bool abort_on_hard_verifier_failure_;
+  // Same for soft failures.
+  bool abort_on_soft_verifier_failure_;
 
   // Log initialization of initialization failures to this stream if not null.
   std::unique_ptr<std::ostream> init_failure_output_;
diff --git a/compiler/driver/compiler_options_map-inl.h b/compiler/driver/compiler_options_map-inl.h
index 9cb818a..772d1b4 100644
--- a/compiler/driver/compiler_options_map-inl.h
+++ b/compiler/driver/compiler_options_map-inl.h
@@ -60,6 +60,7 @@
   }
   map.AssignIfExists(Base::TopKProfileThreshold, &options->top_k_profile_threshold_);
   map.AssignIfExists(Base::AbortOnHardVerifierFailure, &options->abort_on_hard_verifier_failure_);
+  map.AssignIfExists(Base::AbortOnSoftVerifierFailure, &options->abort_on_soft_verifier_failure_);
   if (map.Exists(Base::DumpInitFailures)) {
     if (!options->ParseDumpInitFailures(*map.Get(Base::DumpInitFailures), error_msg)) {
       return false;
@@ -132,6 +133,9 @@
       .Define({"--abort-on-hard-verifier-error", "--no-abort-on-hard-verifier-error"})
           .WithValues({true, false})
           .IntoKey(Map::AbortOnHardVerifierFailure)
+      .Define({"--abort-on-soft-verifier-error", "--no-abort-on-soft-verifier-error"})
+          .WithValues({true, false})
+          .IntoKey(Map::AbortOnSoftVerifierFailure)
 
       .Define("--dump-init-failures=_")
           .template WithType<std::string>()
diff --git a/compiler/driver/compiler_options_map.def b/compiler/driver/compiler_options_map.def
index 570bc5a..cc75634 100644
--- a/compiler/driver/compiler_options_map.def
+++ b/compiler/driver/compiler_options_map.def
@@ -50,6 +50,7 @@
 COMPILER_OPTIONS_KEY (Unit,                        Debuggable)
 COMPILER_OPTIONS_KEY (double,                      TopKProfileThreshold)
 COMPILER_OPTIONS_KEY (bool,                        AbortOnHardVerifierFailure)
+COMPILER_OPTIONS_KEY (bool,                        AbortOnSoftVerifierFailure)
 COMPILER_OPTIONS_KEY (std::string,                 DumpInitFailures)
 COMPILER_OPTIONS_KEY (std::string,                 DumpCFG)
 COMPILER_OPTIONS_KEY (Unit,                        DumpCFGAppend)