Add --compilation-reason option to dex2oat

The compilation reason is an optional metadata specifying the reason for
compiling the apk. If specified, the string will be embedded verbatim in
the key value store of the oat file.

This will allow a more precise performance monitoring based on the actual
reason for compilation (e.g. install time vs background dexopt time).

Test: dex2oat_test
Bug: 73102540
Change-Id: I73c7fcc73e37a695f1684d9e282c7cc5be3030f8
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index c4e5398..64db7be 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -456,6 +456,12 @@
   UsageError("  --deduplicate-code=true|false: enable|disable code deduplication. Deduplicated");
   UsageError("      code will have an arbitrary symbol tagged with [DEDUPED].");
   UsageError("");
+  UsageError("  --compilation-reason=<string>: optional metadata specifying the reason for");
+  UsageError("      compiling the apk. If specified, the string will be embedded verbatim in");
+  UsageError("      the key value store of the oat file.");
+  UsageError("");
+  UsageError("      Example: --compilation-reason=install");
+  UsageError("");
   std::cerr << "See log for usage error information\n";
   exit(EXIT_FAILURE);
 }
@@ -1212,6 +1218,7 @@
     AssignIfExists(args, M::ClasspathDir, &classpath_dir_);
     AssignIfExists(args, M::DirtyImageObjects, &dirty_image_objects_filename_);
     AssignIfExists(args, M::ImageFormat, &image_storage_mode_);
+    AssignIfExists(args, M::CompilationReason, &compilation_reason_);
 
     AssignIfExists(args, M::Backend, &compiler_kind_);
     parser_options->requested_specific_compiler = args.Exists(M::Backend);
@@ -1512,6 +1519,10 @@
       return dex2oat::ReturnCode::kOther;
     }
 
+    if (!compilation_reason_.empty()) {
+      key_value_store_->Put(OatHeader::kCompilationReasonKey, compilation_reason_);
+    }
+
     if (IsBootImage() && image_filenames_.size() > 1) {
       // If we're compiling the boot image, store the boot classpath into the Key-Value store.
       // We need this for the multi-image case.
@@ -2907,6 +2918,9 @@
   // Whether the given input vdex is also the output.
   bool update_input_vdex_ = false;
 
+  // The reason for invoking the compiler.
+  std::string compilation_reason_;
+
   DISALLOW_IMPLICIT_CONSTRUCTORS(Dex2Oat);
 };
 
diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc
index 0eecc84..4b6f8a4 100644
--- a/dex2oat/dex2oat_options.cc
+++ b/dex2oat/dex2oat_options.cc
@@ -246,7 +246,10 @@
           .IntoKey(M::CompactDexLevel)
       .Define("--runtime-arg _")
           .WithType<std::vector<std::string>>().AppendValues()
-          .IntoKey(M::RuntimeOptions);
+          .IntoKey(M::RuntimeOptions)
+      .Define("--compilation-reason=_")
+          .WithType<std::string>()
+          .IntoKey(M::CompilationReason);
 
   AddCompilerOptionsArgumentParserOptions<Dex2oatArgumentMap>(*parser_builder);
 
diff --git a/dex2oat/dex2oat_options.def b/dex2oat/dex2oat_options.def
index 9a8bdf4..a1646aa 100644
--- a/dex2oat/dex2oat_options.def
+++ b/dex2oat/dex2oat_options.def
@@ -89,5 +89,6 @@
 DEX2OAT_OPTIONS_KEY (std::string,                    ClassLoaderContext)
 DEX2OAT_OPTIONS_KEY (std::string,                    DirtyImageObjects)
 DEX2OAT_OPTIONS_KEY (std::vector<std::string>,       RuntimeOptions)
+DEX2OAT_OPTIONS_KEY (std::string,                    CompilationReason)
 
 #undef DEX2OAT_OPTIONS_KEY
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 7948bca..4ac8e6a 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -1731,4 +1731,54 @@
   EXPECT_NE(std::string::npos, output_.find("dex2oat took"));
 }
 
+TEST_F(Dex2oatTest, VerifyCompilationReason) {
+  std::string dex_location = GetScratchDir() + "/Dex2OatCompilationReason.jar";
+  std::string odex_location = GetOdexDir() + "/Dex2OatCompilationReason.odex";
+
+  // Test file doesn't matter.
+  Copy(GetDexSrc1(), dex_location);
+
+  GenerateOdexForTest(dex_location,
+                      odex_location,
+                      CompilerFilter::kVerify,
+                      { "--compilation-reason=install" },
+                      true);
+  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 != nullptr);
+  ASSERT_STREQ("install", odex_file->GetCompilationReason());
+}
+
+TEST_F(Dex2oatTest, VerifyNoCompilationReason) {
+  std::string dex_location = GetScratchDir() + "/Dex2OatNoCompilationReason.jar";
+  std::string odex_location = GetOdexDir() + "/Dex2OatNoCompilationReason.odex";
+
+  // Test file doesn't matter.
+  Copy(GetDexSrc1(), dex_location);
+
+  GenerateOdexForTest(dex_location,
+                      odex_location,
+                      CompilerFilter::kVerify,
+                      {},
+                      true);
+  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 != nullptr);
+  ASSERT_EQ(nullptr, odex_file->GetCompilationReason());
+}
+
 }  // namespace art