Add ability to store invocation information in an external file.

It is sometimes useful to save the invocation that generated an odex
file seprately from the file itself. This adds a
--write-invocation-to=<file> flag to dex2oat that lets one do this.

Test: m droid
Test: ./test.py --host -j72
Bug: 119332327
Change-Id: I3fe07e7daba3d27b49fd5b5aed7621e5cc62ab46
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 872fab3..97abde2 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -382,6 +382,9 @@
   UsageError("  --avoid-storing-invocation: Avoid storing the invocation args in the key value");
   UsageError("      store. Used to test determinism with different args.");
   UsageError("");
+  UsageError("  --write-invocation-to=<file>: Write the invocation commandline to the given file");
+  UsageError("      for later use. Used to test determinism with different host architectures.");
+  UsageError("");
   UsageError("  --runtime-arg <argument>: used to specify various arguments for the runtime,");
   UsageError("      such as initial heap size, maximum heap size, and verbose output.");
   UsageError("      Use a separate --runtime-arg switch for each argument.");
@@ -1003,6 +1006,21 @@
         CompilerFilter::NameOfFilter(compiler_options_->GetCompilerFilter()));
     key_value_store_->Put(OatHeader::kConcurrentCopying,
                           kUseReadBarrier ? OatHeader::kTrueValue : OatHeader::kFalseValue);
+    if (invocation_file_.get() != -1) {
+      std::ostringstream oss;
+      for (int i = 0; i < argc; ++i) {
+        if (i > 0) {
+          oss << std::endl;
+        }
+        oss << argv[i];
+      }
+      std::string invocation(oss.str());
+      if (TEMP_FAILURE_RETRY(write(invocation_file_.get(),
+                                   invocation.c_str(),
+                                   invocation.size())) == -1) {
+        Usage("Unable to write invocation file");
+      }
+    }
   }
 
   // This simple forward is here so the string specializations below don't look out of place.
@@ -1118,6 +1136,16 @@
 
     AssignTrueIfExists(args, M::Host, &is_host_);
     AssignTrueIfExists(args, M::AvoidStoringInvocation, &avoid_storing_invocation_);
+    if (args.Exists(M::InvocationFile)) {
+      invocation_file_.reset(open(args.Get(M::InvocationFile)->c_str(),
+                                  O_CREAT|O_WRONLY|O_TRUNC|O_CLOEXEC,
+                                  S_IRUSR|S_IWUSR));
+      if (invocation_file_.get() == -1) {
+        int err = errno;
+        Usage("Unable to open invocation file '%s' for writing due to %s.",
+              args.Get(M::InvocationFile)->c_str(), strerror(err));
+      }
+    }
     AssignIfExists(args, M::CopyDexFiles, &copy_dex_files_);
 
     if (args.Exists(M::ForceDeterminism)) {
@@ -2714,6 +2742,7 @@
   std::vector<std::unique_ptr<const DexFile>> opened_dex_files_;
 
   bool avoid_storing_invocation_;
+  android::base::unique_fd invocation_file_;
   std::string swap_file_name_;
   int swap_fd_;
   size_t min_dex_files_for_swap_ = kDefaultMinDexFilesForSwap;
diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc
index 236c1fc..783b326 100644
--- a/dex2oat/dex2oat_options.cc
+++ b/dex2oat/dex2oat_options.cc
@@ -229,6 +229,9 @@
                          {"false", linker::CopyOption::kNever},
                          {"always", linker::CopyOption::kAlways}})
           .IntoKey(M::CopyDexFiles)
+      .Define("--write-invocation-to=_")
+          .WithType<std::string>()
+          .IntoKey(M::InvocationFile)
       .Define("--classpath-dir=_")
           .WithType<std::string>()
           .IntoKey(M::ClasspathDir)
diff --git a/dex2oat/dex2oat_options.def b/dex2oat/dex2oat_options.def
index c8cb7e7..0b77859 100644
--- a/dex2oat/dex2oat_options.def
+++ b/dex2oat/dex2oat_options.def
@@ -84,6 +84,7 @@
 DEX2OAT_OPTIONS_KEY (std::string,                    NoInlineFrom)
 DEX2OAT_OPTIONS_KEY (Unit,                           ForceDeterminism)
 DEX2OAT_OPTIONS_KEY (std::string,                    ClasspathDir)
+DEX2OAT_OPTIONS_KEY (std::string,                    InvocationFile)
 DEX2OAT_OPTIONS_KEY (std::string,                    ClassLoaderContext)
 DEX2OAT_OPTIONS_KEY (std::string,                    StoredClassLoaderContext)
 DEX2OAT_OPTIONS_KEY (std::string,                    DirtyImageObjects)