ART: Add profile-compile-check support

Add --check-profiled-methods, which verifies that all methods mentioned
in a profile for a guided compilation are actually compiled instead of
being punted. As outcome it may log or abort dex2oat.

TODO: Extend dex2oat_test

Bug: 76145463
Test: mmma art
Test: m test-art-host
Change-Id: I956113b55796d0666db9dbfd387105a7d27b0868
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 89ac308..408cde2 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -24,6 +24,7 @@
 #include <malloc.h>  // For mallinfo
 #endif
 
+#include "android-base/logging.h"
 #include "android-base/strings.h"
 
 #include "art_field-inl.h"
@@ -609,6 +610,29 @@
                                                          class_loader,
                                                          dex_file,
                                                          dex_cache);
+        ProfileMethodsCheck check_type =
+            driver->GetCompilerOptions().CheckProfiledMethodsCompiled();
+        if (UNLIKELY(check_type != ProfileMethodsCheck::kNone)) {
+          bool violation = driver->ShouldCompileBasedOnProfile(method_ref) &&
+                               (compiled_method == nullptr);
+          if (violation) {
+            std::ostringstream oss;
+            oss << "Failed to compile "
+                << method_ref.dex_file->PrettyMethod(method_ref.index)
+                << "[" << method_ref.dex_file->GetLocation() << "]"
+                << " as expected by profile";
+            switch (check_type) {
+              case ProfileMethodsCheck::kNone:
+                break;
+              case ProfileMethodsCheck::kLog:
+                LOG(ERROR) << oss.str();
+                break;
+              case ProfileMethodsCheck::kAbort:
+                LOG(FATAL_WITHOUT_ABORT) << oss.str();
+                _exit(1);
+            }
+          }
+        }
       }
       if (compiled_method == nullptr &&
           dex_to_dex_compilation_level !=
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 6b0e456..1ae9e4e 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -70,6 +70,7 @@
       deduplicate_code_(true),
       count_hotness_in_compiled_code_(false),
       resolve_startup_const_strings_(false),
+      check_profiled_methods_(ProfileMethodsCheck::kNone),
       register_allocation_strategy_(RegisterAllocator::kRegisterAllocatorDefault),
       passes_to_run_(nullptr) {
 }
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 4a6bbfa..a760840 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -43,6 +43,13 @@
 enum class InstructionSet;
 class InstructionSetFeatures;
 
+// Enum for CheckProfileMethodsCompiled. Outside CompilerOptions so it can be forward-declared.
+enum class ProfileMethodsCheck : uint8_t {
+  kNone,
+  kLog,
+  kAbort,
+};
+
 class CompilerOptions final {
  public:
   // Guide heuristics to determine whether to compile method if profile data not available.
@@ -317,6 +324,10 @@
     return resolve_startup_const_strings_;
   }
 
+  ProfileMethodsCheck CheckProfiledMethodsCompiled() const {
+    return check_profiled_methods_;
+  }
+
  private:
   bool ParseDumpInitFailures(const std::string& option, std::string* error_msg);
   void ParseDumpCfgPasses(const StringPiece& option, UsageFn Usage);
@@ -400,6 +411,10 @@
   // profile.
   bool resolve_startup_const_strings_;
 
+  // When running profile-guided compilation, check that methods intended to be compiled end
+  // up compiled and are not punted.
+  ProfileMethodsCheck check_profiled_methods_;
+
   RegisterAllocator::Strategy register_allocation_strategy_;
 
   // If not null, specifies optimization passes which will be run instead of defaults.
diff --git a/compiler/driver/compiler_options_map-inl.h b/compiler/driver/compiler_options_map-inl.h
index 5a84495..c7334a7 100644
--- a/compiler/driver/compiler_options_map-inl.h
+++ b/compiler/driver/compiler_options_map-inl.h
@@ -81,6 +81,9 @@
     options->count_hotness_in_compiled_code_ = true;
   }
   map.AssignIfExists(Base::ResolveStartupConstStrings, &options->resolve_startup_const_strings_);
+  if (map.Exists(Base::CheckProfiledMethods)) {
+    options->check_profiled_methods_ = *map.Get(Base::CheckProfiledMethods);
+  }
 
   if (map.Exists(Base::DumpTimings)) {
     options->dump_timings_ = true;
@@ -145,6 +148,12 @@
       .Define({"--count-hotness-in-compiled-code"})
           .IntoKey(Map::CountHotnessInCompiledCode)
 
+      .Define({"--check-profiled-methods=_"})
+          .template WithType<ProfileMethodsCheck>()
+          .WithValueMap({{"log", ProfileMethodsCheck::kLog},
+                         {"abort", ProfileMethodsCheck::kAbort}})
+          .IntoKey(Map::CheckProfiledMethods)
+
       .Define({"--dump-timings"})
           .IntoKey(Map::DumpTimings)
 
diff --git a/compiler/driver/compiler_options_map.def b/compiler/driver/compiler_options_map.def
index a593240..c2fac5e 100644
--- a/compiler/driver/compiler_options_map.def
+++ b/compiler/driver/compiler_options_map.def
@@ -61,6 +61,7 @@
 COMPILER_OPTIONS_KEY (ParseStringList<','>,        VerboseMethods)
 COMPILER_OPTIONS_KEY (bool,                        DeduplicateCode,            true)
 COMPILER_OPTIONS_KEY (Unit,                        CountHotnessInCompiledCode)
+COMPILER_OPTIONS_KEY (ProfileMethodsCheck,         CheckProfiledMethods)
 COMPILER_OPTIONS_KEY (Unit,                        DumpTimings)
 COMPILER_OPTIONS_KEY (Unit,                        DumpPassTimings)
 COMPILER_OPTIONS_KEY (Unit,                        DumpStats)
diff --git a/compiler/driver/compiler_options_map.h b/compiler/driver/compiler_options_map.h
index b9bc8b6..af212d6 100644
--- a/compiler/driver/compiler_options_map.h
+++ b/compiler/driver/compiler_options_map.h
@@ -25,6 +25,8 @@
 
 namespace art {
 
+enum class ProfileMethodsCheck : uint8_t;
+
 // Defines a type-safe heterogeneous key->value map. This is to be used as the base for
 // an extended map.
 template <typename Base, template <typename TV> class KeyType>