ART: Add dex2oat return code for runtime failure

Add a return code for failure to create a runtime. Add a test.

Bug: 36336122
Test: m test-art-host-gtest-dex2oat_test
Change-Id: I17d48a6052ead7a138d77c87832968a7294c3593
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index 0eee4ab..048f36d 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -139,4 +139,5 @@
         "art_gtest_defaults",
     ],
     srcs: ["dex2oat_test.cc"],
+    header_libs: ["dex2oat_headers"],
 }
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 59b2724..e80be81 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1443,11 +1443,11 @@
 
   // Set up the environment for compilation. Includes starting the runtime and loading/opening the
   // boot class path.
-  bool Setup() {
+  dex2oat::ReturnCode Setup() {
     TimingLogger::ScopedTiming t("dex2oat Setup", timings_);
 
     if (!PrepareImageClasses() || !PrepareCompiledClasses() || !PrepareCompiledMethods()) {
-      return false;
+      return dex2oat::ReturnCode::kOther;
     }
 
     verification_results_.reset(new VerificationResults(compiler_options_.get()));
@@ -1459,12 +1459,12 @@
 
     RuntimeArgumentMap runtime_options;
     if (!PrepareRuntimeOptions(&runtime_options)) {
-      return false;
+      return dex2oat::ReturnCode::kOther;
     }
 
     CreateOatWriters();
     if (!AddDexFileSources()) {
-      return false;
+      return dex2oat::ReturnCode::kOther;
     }
 
     if (IsBootImage() && image_filenames_.size() > 1) {
@@ -1480,7 +1480,7 @@
       // When compiling an app, create the runtime early to retrieve
       // the image location key needed for the oat header.
       if (!CreateRuntime(std::move(runtime_options))) {
-        return false;
+        return dex2oat::ReturnCode::kCreateRuntime;
       }
 
       if (CompilerFilter::DependsOnImageChecksum(compiler_options_->GetCompilerFilter())) {
@@ -1551,7 +1551,7 @@
             update_input_vdex_,
             &opened_dex_files_map,
             &opened_dex_files)) {
-          return false;
+          return dex2oat::ReturnCode::kOther;
         }
         dex_files_per_oat_file_.push_back(MakeNonOwningPointerVector(opened_dex_files));
         if (opened_dex_files_map != nullptr) {
@@ -1603,7 +1603,7 @@
       // Note: Runtime acquires ownership of these dex files.
       runtime_options.Set(RuntimeArgumentMap::BootClassPathDexList, &opened_dex_files_);
       if (!CreateRuntime(std::move(runtime_options))) {
-        return false;
+        return dex2oat::ReturnCode::kOther;
       }
     }
 
@@ -1637,7 +1637,7 @@
     for (const std::unique_ptr<MemMap>& map : opened_dex_files_maps_) {
       if (!map->Protect(PROT_READ | PROT_WRITE)) {
         PLOG(ERROR) << "Failed to make .dex files writeable.";
-        return false;
+        return dex2oat::ReturnCode::kOther;
       }
     }
 
@@ -1652,14 +1652,14 @@
         soa.Self()->AssertPendingException();
         soa.Self()->ClearException();
         PLOG(ERROR) << "Failed to register dex file.";
-        return false;
+        return dex2oat::ReturnCode::kOther;
       }
       // Pre-register dex files so that we can access verification results without locks during
       // compilation and verification.
       verification_results_->AddDexFile(dex_file);
     }
 
-    return true;
+    return dex2oat::ReturnCode::kNoFailure;
   }
 
   // If we need to keep the oat file open for the image writer.
@@ -2924,9 +2924,10 @@
     LOG(INFO) << StrippedCommandLine();
   }
 
-  if (!dex2oat->Setup()) {
+  dex2oat::ReturnCode setup_code = dex2oat->Setup();
+  if (setup_code != dex2oat::ReturnCode::kNoFailure) {
     dex2oat->EraseOutputFiles();
-    return dex2oat::ReturnCode::kOther;
+    return setup_code;
   }
 
   // Helps debugging on device. Can be used to determine which dalvikvm instance invoked a dex2oat
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 289b8ab..8c14b50 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -30,6 +30,7 @@
 #include "base/macros.h"
 #include "dex_file-inl.h"
 #include "dex2oat_environment_test.h"
+#include "dex2oat_return_codes.h"
 #include "jit/profile_compilation_info.h"
 #include "oat.h"
 #include "oat_file.h"
@@ -50,12 +51,12 @@
   }
 
  protected:
-  void GenerateOdexForTest(const std::string& dex_location,
-                           const std::string& odex_location,
-                           CompilerFilter::Filter filter,
-                           const std::vector<std::string>& extra_args = {},
-                           bool expect_success = true,
-                           bool use_fd = false) {
+  int GenerateOdexForTestWithStatus(const std::string& dex_location,
+                                    const std::string& odex_location,
+                                    CompilerFilter::Filter filter,
+                                    std::string* error_msg,
+                                    const std::vector<std::string>& extra_args = {},
+                                    bool use_fd = false) {
     std::unique_ptr<File> oat_file;
     std::vector<std::string> args;
     args.push_back("--dex-file=" + dex_location);
@@ -73,12 +74,27 @@
 
     args.insert(args.end(), extra_args.begin(), extra_args.end());
 
-    std::string error_msg;
-    bool success = Dex2Oat(args, &error_msg);
+    int status = Dex2Oat(args, error_msg);
     if (oat_file != nullptr) {
-      ASSERT_EQ(oat_file->FlushClose(), 0) << "Could not flush and close oat file";
+      CHECK_EQ(oat_file->FlushClose(), 0) << "Could not flush and close oat file";
     }
+    return status;
+  }
 
+  void GenerateOdexForTest(const std::string& dex_location,
+                           const std::string& odex_location,
+                           CompilerFilter::Filter filter,
+                           const std::vector<std::string>& extra_args = {},
+                           bool expect_success = true,
+                           bool use_fd = false) {
+    std::string error_msg;
+    int status = GenerateOdexForTestWithStatus(dex_location,
+                                               odex_location,
+                                               filter,
+                                               &error_msg,
+                                               extra_args,
+                                               use_fd);
+    bool success = (status == 0);
     if (expect_success) {
       ASSERT_TRUE(success) << error_msg << std::endl << output_;
 
@@ -118,7 +134,7 @@
     EXPECT_EQ(expected, actual);
   }
 
-  bool Dex2Oat(const std::vector<std::string>& dex2oat_args, std::string* error_msg) {
+  int Dex2Oat(const std::vector<std::string>& dex2oat_args, std::string* error_msg) {
     Runtime* runtime = Runtime::Current();
 
     const std::vector<gc::space::ImageSpace*>& image_spaces =
@@ -196,6 +212,7 @@
       c_args.push_back(nullptr);
       execv(c_args[0], const_cast<char* const*>(c_args.data()));
       exit(1);
+      UNREACHABLE();
     } else {
       close(link[1]);
       char buffer[128];
@@ -206,12 +223,12 @@
         output_ += std::string(buffer, bytes_read);
       }
       close(link[0]);
-      int status = 0;
+      int status = -1;
       if (waitpid(pid, &status, 0) != -1) {
         success_ = (status == 0);
       }
+      return status;
     }
-    return success_;
   }
 
   std::string output_ = "";
@@ -845,4 +862,30 @@
   RunTest(false, { "--watchdog-timeout=10" });
 }
 
+class Dex2oatReturnCodeTest : public Dex2oatTest {
+ protected:
+  int RunTest(const std::vector<std::string>& extra_args = {}) {
+    std::string dex_location = GetScratchDir() + "/Dex2OatSwapTest.jar";
+    std::string odex_location = GetOdexDir() + "/Dex2OatSwapTest.odex";
+
+    Copy(GetTestDexFileName(), dex_location);
+
+    std::string error_msg;
+    return GenerateOdexForTestWithStatus(dex_location,
+                                         odex_location,
+                                         CompilerFilter::kSpeed,
+                                         &error_msg,
+                                         extra_args);
+  }
+
+  std::string GetTestDexFileName() {
+    return GetDexSrc1();
+  }
+};
+
+TEST_F(Dex2oatReturnCodeTest, TestCreateRuntime) {
+  int status = RunTest({ "--boot-image=/this/does/not/exist/yolo.oat" });
+  EXPECT_EQ(static_cast<int>(dex2oat::ReturnCode::kCreateRuntime), WEXITSTATUS(status)) << output_;
+}
+
 }  // namespace art
diff --git a/dex2oat/include/dex2oat_return_codes.h b/dex2oat/include/dex2oat_return_codes.h
index ba8799f..cc5400f 100644
--- a/dex2oat/include/dex2oat_return_codes.h
+++ b/dex2oat/include/dex2oat_return_codes.h
@@ -23,6 +23,7 @@
 enum class ReturnCode : int {
   kNoFailure = 0,
   kOther = 1,
+  kCreateRuntime = 2,
 };
 
 }  // namespace dex2oat