Create CompilerOptions

Package up most compiler related options in CompilerOptions. Details include:
- Includes compiler filter, method thresholds, SEA IR mode.
- Excludes those needed during Runtime::Init such as CompilerCallbacks and VerificationResults.
- Pass CompilerOptions to CompilerDriver.
- Remove CompilerOptions from Runtime.
- Add ability to pass options for app and image dex2oat to runtime via
  -Xcompiler-option and -Ximage-compiler-option respectively.

Other
- Replace 2x CompilerCallbacks implementations with one.
- Factor out execv code for use by both image and oat generation.
- More OatFile error_msg reporting.
- DCHECK for SuspendAll found trying to run valgrind.

Change-Id: Iecb57da907be0c856d00c3cd634b5042a229e620
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 48ec5ab..52dd541 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -550,101 +550,41 @@
   const char* class_path = Runtime::Current()->GetClassPathString().c_str();
 
   gc::Heap* heap = Runtime::Current()->GetHeap();
-  std::string boot_image_option_string("--boot-image=");
-  boot_image_option_string += heap->GetImageSpace()->GetImageFilename();
-  const char* boot_image_option = boot_image_option_string.c_str();
+  std::string boot_image_option("--boot-image=");
+  boot_image_option += heap->GetImageSpace()->GetImageFilename();
 
-  std::string dex_file_option_string("--dex-file=");
-  dex_file_option_string += dex_filename;
-  const char* dex_file_option = dex_file_option_string.c_str();
+  std::string dex_file_option("--dex-file=");
+  dex_file_option += dex_filename;
 
-  std::string oat_fd_option_string("--oat-fd=");
-  StringAppendF(&oat_fd_option_string, "%d", oat_fd);
-  const char* oat_fd_option = oat_fd_option_string.c_str();
+  std::string oat_fd_option("--oat-fd=");
+  StringAppendF(&oat_fd_option, "%d", oat_fd);
 
-  std::string oat_location_option_string("--oat-location=");
-  oat_location_option_string += oat_cache_filename;
-  const char* oat_location_option = oat_location_option_string.c_str();
+  std::string oat_location_option("--oat-location=");
+  oat_location_option += oat_cache_filename;
 
-  std::string oat_compiler_filter_string("-compiler-filter:");
-  Runtime::CompilerFilter filter = Runtime::Current()->GetCompilerFilter();
-  switch (filter) {
-    case Runtime::kInterpretOnly:
-      oat_compiler_filter_string += "interpret-only";
-      break;
-    case Runtime::kSpace:
-      oat_compiler_filter_string += "space";
-      break;
-    case Runtime::kBalanced:
-      oat_compiler_filter_string += "balanced";
-      break;
-    case Runtime::kSpeed:
-      oat_compiler_filter_string += "speed";
-      break;
-    case Runtime::kEverything:
-      oat_compiler_filter_string += "everything";
-      break;
-    default:
-      LOG(FATAL) << "Unexpected case: " << filter;
+  std::vector<std::string> argv;
+  argv.push_back(dex2oat);
+  argv.push_back("--runtime-arg");
+  argv.push_back("-Xms64m");
+  argv.push_back("--runtime-arg");
+  argv.push_back("-Xmx64m");
+  argv.push_back("--runtime-arg");
+  argv.push_back("-classpath");
+  argv.push_back("--runtime-arg");
+  argv.push_back(class_path);
+  if (!kIsTargetBuild) {
+    argv.push_back("--host");
   }
-  const char* oat_compiler_filter_option = oat_compiler_filter_string.c_str();
-
-  // fork and exec dex2oat
-  pid_t pid = fork();
-  if (pid == 0) {
-    // no allocation allowed between fork and exec
-
-    // change process groups, so we don't get reaped by ProcessManager
-    setpgid(0, 0);
-
-    // gLogVerbosity.class_linker = true;
-    VLOG(class_linker) << dex2oat
-                       << " --runtime-arg -Xms64m"
-                       << " --runtime-arg -Xmx64m"
-                       << " --runtime-arg -classpath"
-                       << " --runtime-arg " << class_path
-                       << " --runtime-arg " << oat_compiler_filter_option
-#if !defined(ART_TARGET)
-                       << " --host"
-#endif
-                       << " " << boot_image_option
-                       << " " << dex_file_option
-                       << " " << oat_fd_option
-                       << " " << oat_location_option;
-
-    execl(dex2oat, dex2oat,
-          "--runtime-arg", "-Xms64m",
-          "--runtime-arg", "-Xmx64m",
-          "--runtime-arg", "-classpath",
-          "--runtime-arg", class_path,
-          "--runtime-arg", oat_compiler_filter_option,
-#if !defined(ART_TARGET)
-          "--host",
-#endif
-          boot_image_option,
-          dex_file_option,
-          oat_fd_option,
-          oat_location_option,
-          NULL);
-
-    PLOG(FATAL) << "execl(" << dex2oat << ") failed";
-    return false;
-  } else {
-    // wait for dex2oat to finish
-    int status;
-    pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
-    if (got_pid != pid) {
-      *error_msg = StringPrintf("Failed to create oat file. Waitpid failed: wanted %d, got %d",
-                                pid, got_pid);
-      return false;
-    }
-    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
-      *error_msg = StringPrintf("Failed to create oat file. %s failed with dex-file '%s'",
-                                dex2oat, dex_filename);
-      return false;
-    }
+  argv.push_back(boot_image_option);
+  argv.push_back(dex_file_option);
+  argv.push_back(oat_fd_option);
+  argv.push_back(oat_location_option);
+  const std::vector<std::string>& compiler_options = Runtime::Current()->GetCompilerOptions();
+  for (size_t i = 0; compiler_options.size(); ++i) {
+    argv.push_back(compiler_options[i].c_str());
   }
-  return true;
+
+  return Exec(argv, error_msg);
 }
 
 const OatFile* ClassLinker::RegisterOatFile(const OatFile* oat_file) {
diff --git a/runtime/common_test.h b/runtime/common_test.h
index 7f9b6b1..e3843be 100644
--- a/runtime/common_test.h
+++ b/runtime/common_test.h
@@ -28,7 +28,9 @@
 #include "../compiler/compiler_backend.h"
 #include "../compiler/dex/quick/dex_file_to_method_inliner_map.h"
 #include "../compiler/dex/verification_results.h"
+#include "../compiler/driver/compiler_callbacks_impl.h"
 #include "../compiler/driver/compiler_driver.h"
+#include "../compiler/driver/compiler_options.h"
 #include "base/macros.h"
 #include "base/stl_util.h"
 #include "base/stringprintf.h"
@@ -458,11 +460,13 @@
         ? CompilerBackend::kPortable
         : CompilerBackend::kQuick;
 
-    verification_results_.reset(new VerificationResults);
+    compiler_options_.reset(new CompilerOptions);
+    verification_results_.reset(new VerificationResults(compiler_options_.get()));
     method_inliner_map_.reset(new DexFileToMethodInlinerMap);
-    callbacks_.Reset(verification_results_.get(), method_inliner_map_.get());
+    callbacks_.reset(new CompilerCallbacksImpl(verification_results_.get(),
+                                               method_inliner_map_.get()));
     Runtime::Options options;
-    options.push_back(std::make_pair("compilercallbacks", static_cast<CompilerCallbacks*>(&callbacks_)));
+    options.push_back(std::make_pair("compilercallbacks", callbacks_.get()));
     options.push_back(std::make_pair("bootclasspath", &boot_class_path_));
     options.push_back(std::make_pair("-Xcheck:jni", reinterpret_cast<void*>(NULL)));
     options.push_back(std::make_pair(min_heap_string.c_str(), reinterpret_cast<void*>(NULL)));
@@ -472,8 +476,8 @@
       return;
     }
     runtime_.reset(Runtime::Current());
-    // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start,
-    // give it away now and then switch to a more managable ScopedObjectAccess.
+    // Runtime::Create acquired the mutator_lock_ that is normally given away when we
+    // Runtime::Start, give it away now and then switch to a more managable ScopedObjectAccess.
     Thread::Current()->TransitionFromRunnableToSuspended(kNative);
     {
       ScopedObjectAccess soa(Thread::Current());
@@ -512,7 +516,8 @@
       }
       class_linker_->FixupDexCaches(runtime_->GetResolutionMethod());
       timer_.reset(new CumulativeLogger("Compilation times"));
-      compiler_driver_.reset(new CompilerDriver(verification_results_.get(),
+      compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
+                                                verification_results_.get(),
                                                 method_inliner_map_.get(),
                                                 compiler_backend, instruction_set,
                                                 instruction_set_features,
@@ -563,9 +568,10 @@
 
     compiler_driver_.reset();
     timer_.reset();
-    callbacks_.Reset(nullptr, nullptr);
+    callbacks_.reset();
     method_inliner_map_.reset();
     verification_results_.reset();
+    compiler_options_.reset();
     STLDeleteElements(&opened_dex_files_);
 
     Runtime::Current()->GetHeap()->VerifyHeap();  // Check for heap corruption after the test
@@ -693,36 +699,6 @@
     image_reservation_.reset();
   }
 
-  class TestCompilerCallbacks : public CompilerCallbacks {
-   public:
-    TestCompilerCallbacks() : verification_results_(nullptr), method_inliner_map_(nullptr) {}
-
-    void Reset(VerificationResults* verification_results,
-               DexFileToMethodInlinerMap* method_inliner_map) {
-        verification_results_ = verification_results;
-        method_inliner_map_ = method_inliner_map;
-    }
-
-    virtual bool MethodVerified(verifier::MethodVerifier* verifier)
-        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-      CHECK(verification_results_);
-      bool result = verification_results_->ProcessVerifiedMethod(verifier);
-      if (result && method_inliner_map_ != nullptr) {
-        MethodReference ref = verifier->GetMethodReference();
-        method_inliner_map_->GetMethodInliner(ref.dex_file)
-            ->AnalyseMethodCode(verifier);
-      }
-      return result;
-    }
-    virtual void ClassRejected(ClassReference ref) {
-      verification_results_->AddRejectedClass(ref);
-    }
-
-   private:
-    VerificationResults* verification_results_;
-    DexFileToMethodInlinerMap* method_inliner_map_;
-  };
-
   std::string android_data_;
   std::string dalvik_cache_;
   const DexFile* java_lang_dex_file_;  // owned by runtime_
@@ -730,9 +706,10 @@
   UniquePtr<Runtime> runtime_;
   // Owned by the runtime
   ClassLinker* class_linker_;
+  UniquePtr<CompilerOptions> compiler_options_;
   UniquePtr<VerificationResults> verification_results_;
   UniquePtr<DexFileToMethodInlinerMap> method_inliner_map_;
-  TestCompilerCallbacks callbacks_;
+  UniquePtr<CompilerCallbacksImpl> callbacks_;
   UniquePtr<CompilerDriver> compiler_driver_;
   UniquePtr<CumulativeLogger> timer_;
 
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index ebad8dd..86dee1d 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -89,52 +89,14 @@
     arg_vector.push_back("--host");
   }
 
+  const std::vector<std::string>& compiler_options = Runtime::Current()->GetImageCompilerOptions();
+  for (size_t i = 0; compiler_options.size(); ++i) {
+    arg_vector.push_back(compiler_options[i].c_str());
+  }
+
   std::string command_line(Join(arg_vector, ' '));
   LOG(INFO) << "GenerateImage: " << command_line;
-
-  // Convert the args to char pointers.
-  std::vector<char*> char_args;
-  for (std::vector<std::string>::iterator it = arg_vector.begin(); it != arg_vector.end();
-      ++it) {
-    char_args.push_back(const_cast<char*>(it->c_str()));
-  }
-  char_args.push_back(NULL);
-
-  // fork and exec dex2oat
-  pid_t pid = fork();
-  if (pid == 0) {
-    // no allocation allowed between fork and exec
-
-    // change process groups, so we don't get reaped by ProcessManager
-    setpgid(0, 0);
-
-    execv(dex2oat.c_str(), &char_args[0]);
-
-    PLOG(FATAL) << "execv(" << dex2oat << ") failed";
-    return false;
-  } else {
-    if (pid == -1) {
-      *error_msg = StringPrintf("Failed to generate image '%s' because fork failed: %s",
-                                image_file_name.c_str(), strerror(errno));
-      return false;
-    }
-
-    // wait for dex2oat to finish
-    int status;
-    pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
-    if (got_pid != pid) {
-      *error_msg = StringPrintf("Failed to generate image '%s' because waitpid failed: "
-                                "wanted %d, got %d: %s",
-                                image_file_name.c_str(), pid, got_pid, strerror(errno));
-      return false;
-    }
-    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
-      *error_msg = StringPrintf("Failed to generate image '%s' because dex2oat failed: %s",
-                                image_file_name.c_str(), command_line.c_str());
-      return false;
-    }
-  }
-  return true;
+  return Exec(arg_vector, error_msg);
 }
 
 ImageSpace* ImageSpace::Create(const char* original_image_file_name) {
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 00a8506..61f023c 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -84,6 +84,7 @@
   // This won't work for portable runtime execution because it doesn't process relocations.
   UniquePtr<File> file(OS::OpenFileForReading(filename.c_str()));
   if (file.get() == NULL) {
+    *error_msg = StringPrintf("Failed to open oat filename for reading: %s", strerror(errno));
     return NULL;
   }
   return OpenElfFile(file.get(), location, requested_base, false, executable, error_msg);
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 3ccea36..1ef15f7 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -77,13 +77,6 @@
       is_zygote_(false),
       is_concurrent_gc_enabled_(true),
       is_explicit_gc_disabled_(false),
-      compiler_filter_(kSpeed),
-      huge_method_threshold_(0),
-      large_method_threshold_(0),
-      small_method_threshold_(0),
-      tiny_method_threshold_(0),
-      num_dex_methods_threshold_(0),
-      sea_ir_mode_(false),
       default_stack_size_(0),
       heap_(nullptr),
       max_spins_before_thin_lock_inflation_(Monitor::kDefaultMaxSpinsBeforeThinLockInflation),
@@ -452,14 +445,6 @@
   parsed->hook_exit_ = exit;
   parsed->hook_abort_ = NULL;  // We don't call abort(3) by default; see Runtime::Abort.
 
-  parsed->compiler_filter_ = Runtime::kDefaultCompilerFilter;
-  parsed->huge_method_threshold_ = Runtime::kDefaultHugeMethodThreshold;
-  parsed->large_method_threshold_ = Runtime::kDefaultLargeMethodThreshold;
-  parsed->small_method_threshold_ = Runtime::kDefaultSmallMethodThreshold;
-  parsed->tiny_method_threshold_ = Runtime::kDefaultTinyMethodThreshold;
-  parsed->num_dex_methods_threshold_ = Runtime::kDefaultNumDexMethodsThreshold;
-
-  parsed->sea_ir_mode_ = false;
 //  gLogVerbosity.class_linker = true;  // TODO: don't check this in!
 //  gLogVerbosity.compiler = true;  // TODO: don't check this in!
 //  gLogVerbosity.verifier = true;  // TODO: don't check this in!
@@ -721,28 +706,22 @@
     } else if (StartsWith(option, "-Xprofile-backoff:")) {
       parsed->profile_backoff_coefficient_ = ParseDoubleOrDie(
           option, ':', 1.0, 10.0, ignore_unrecognized, parsed->profile_backoff_coefficient_);
-    } else if (option == "-compiler-filter:interpret-only") {
-      parsed->compiler_filter_ = kInterpretOnly;
-    } else if (option == "-compiler-filter:space") {
-      parsed->compiler_filter_ = kSpace;
-    } else if (option == "-compiler-filter:balanced") {
-      parsed->compiler_filter_ = kBalanced;
-    } else if (option == "-compiler-filter:speed") {
-      parsed->compiler_filter_ = kSpeed;
-    } else if (option == "-compiler-filter:everything") {
-      parsed->compiler_filter_ = kEverything;
-    } else if (option == "-sea_ir") {
-      parsed->sea_ir_mode_ = true;
-    } else if (StartsWith(option, "-huge-method-max:")) {
-      parsed->huge_method_threshold_ = ParseIntegerOrDie(option, ':');
-    } else if (StartsWith(option, "-large-method-max:")) {
-      parsed->large_method_threshold_ = ParseIntegerOrDie(option, ':');
-    } else if (StartsWith(option, "-small-method-max:")) {
-      parsed->small_method_threshold_ = ParseIntegerOrDie(option, ':');
-    } else if (StartsWith(option, "-tiny-method-max:")) {
-      parsed->tiny_method_threshold_ = ParseIntegerOrDie(option, ':');
-    } else if (StartsWith(option, "-num-dex-methods-max:")) {
-      parsed->num_dex_methods_threshold_ = ParseIntegerOrDie(option, ':');
+    } else if (option == "-Xcompiler-option") {
+      i++;
+      if (i == options.size()) {
+        // TODO: usage
+        LOG(FATAL) << "Missing required compiler option for " << option;
+        return NULL;
+      }
+      parsed->compiler_options_.push_back(options[i].first);
+    } else if (option == "-Ximage-compiler-option") {
+      i++;
+      if (i == options.size()) {
+        // TODO: usage
+        LOG(FATAL) << "Missing required compiler option for " << option;
+        return NULL;
+      }
+      parsed->image_compiler_options_.push_back(options[i].first);
     } else {
       if (!ignore_unrecognized) {
         // TODO: print usage via vfprintf
@@ -988,14 +967,6 @@
   is_zygote_ = options->is_zygote_;
   is_explicit_gc_disabled_ = options->is_explicit_gc_disabled_;
 
-  compiler_filter_ = options->compiler_filter_;
-  huge_method_threshold_ = options->huge_method_threshold_;
-  large_method_threshold_ = options->large_method_threshold_;
-  small_method_threshold_ = options->small_method_threshold_;
-  tiny_method_threshold_ = options->tiny_method_threshold_;
-  num_dex_methods_threshold_ = options->num_dex_methods_threshold_;
-
-  sea_ir_mode_ = options->sea_ir_mode_;
   vfprintf_ = options->hook_vfprintf_;
   exit_ = options->hook_exit_;
   abort_ = options->hook_abort_;
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 223b8d5..8924921 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -72,26 +72,6 @@
  public:
   typedef std::vector<std::pair<std::string, const void*> > Options;
 
-  enum CompilerFilter {
-    kInterpretOnly,       // Compile nothing.
-    kSpace,               // Maximize space savings.
-    kBalanced,            // Try to get the best performance return on compilation investment.
-    kSpeed,               // Maximize runtime performance.
-    kEverything           // Force compilation (Note: excludes compilaton of class initializers).
-  };
-
-  // Guide heuristics to determine whether to compile method if profile data not available.
-#if ART_SMALL_MODE
-  static const CompilerFilter kDefaultCompilerFilter = kInterpretOnly;
-#else
-  static const CompilerFilter kDefaultCompilerFilter = kSpeed;
-#endif
-  static const size_t kDefaultHugeMethodThreshold = 10000;
-  static const size_t kDefaultLargeMethodThreshold = 600;
-  static const size_t kDefaultSmallMethodThreshold = 60;
-  static const size_t kDefaultTinyMethodThreshold = 20;
-  static const size_t kDefaultNumDexMethodsThreshold = 900;
-
   class ParsedOptions {
    public:
     // returns null if problem parsing and ignore_unrecognized is false
@@ -140,13 +120,8 @@
     void (*hook_exit_)(jint status);
     void (*hook_abort_)();
     std::vector<std::string> properties_;
-    CompilerFilter compiler_filter_;
-    size_t huge_method_threshold_;
-    size_t large_method_threshold_;
-    size_t small_method_threshold_;
-    size_t tiny_method_threshold_;
-    size_t num_dex_methods_threshold_;
-    bool sea_ir_mode_;
+    std::vector<std::string> compiler_options_;
+    std::vector<std::string> image_compiler_options_;
     bool profile_;
     std::string profile_output_filename_;
     int profile_period_s_;
@@ -178,42 +153,12 @@
     return is_explicit_gc_disabled_;
   }
 
-#ifdef ART_SEA_IR_MODE
-  bool IsSeaIRMode() const {
-    return sea_ir_mode_;
-  }
-#endif
-
-  void SetSeaIRMode(bool sea_ir_mode) {
-    sea_ir_mode_ = sea_ir_mode;
+  const std::vector<std::string>& GetCompilerOptions() const {
+    return compiler_options_;
   }
 
-  CompilerFilter GetCompilerFilter() const {
-    return compiler_filter_;
-  }
-
-  void SetCompilerFilter(CompilerFilter compiler_filter) {
-    compiler_filter_ = compiler_filter;
-  }
-
-  size_t GetHugeMethodThreshold() const {
-    return huge_method_threshold_;
-  }
-
-  size_t GetLargeMethodThreshold() const {
-    return large_method_threshold_;
-  }
-
-  size_t GetSmallMethodThreshold() const {
-    return small_method_threshold_;
-  }
-
-  size_t GetTinyMethodThreshold() const {
-    return tiny_method_threshold_;
-  }
-
-  size_t GetNumDexMethodsThreshold() const {
-      return num_dex_methods_threshold_;
+  const std::vector<std::string>& GetImageCompilerOptions() const {
+    return image_compiler_options_;
   }
 
   const std::string& GetHostPrefix() const {
@@ -525,14 +470,8 @@
   bool is_concurrent_gc_enabled_;
   bool is_explicit_gc_disabled_;
 
-  CompilerFilter compiler_filter_;
-  size_t huge_method_threshold_;
-  size_t large_method_threshold_;
-  size_t small_method_threshold_;
-  size_t tiny_method_threshold_;
-  size_t num_dex_methods_threshold_;
-
-  bool sea_ir_mode_;
+  std::vector<std::string> compiler_options_;
+  std::vector<std::string> image_compiler_options_;
 
   // The host prefix is used during cross compilation. It is removed
   // from the start of host paths such as:
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 74e6f1c..d311945 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -269,6 +269,7 @@
 
 void ThreadList::SuspendAll() {
   Thread* self = Thread::Current();
+  DCHECK(self != nullptr);
 
   VLOG(threads) << *self << " SuspendAll starting...";
 
diff --git a/runtime/utils.cc b/runtime/utils.cc
index aad21bc..8e6ddaf 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -24,6 +24,7 @@
 #include <unistd.h>
 
 #include "UniquePtr.h"
+#include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
 #include "dex_file-inl.h"
 #include "mirror/art_field-inl.h"
@@ -1203,4 +1204,56 @@
                  sizeof(OatHeader::kOatMagic)) == 0);
 }
 
+bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg) {
+  const std::string command_line(Join(arg_vector, ' '));
+
+  CHECK_GE(arg_vector.size(), 1U) << command_line;
+
+  // Convert the args to char pointers.
+  const char* program = arg_vector[0].c_str();
+  std::vector<char*> args;
+  for (std::vector<std::string>::const_iterator it = arg_vector.begin(); it != arg_vector.end();
+      ++it) {
+    CHECK(*it != nullptr);
+    args.push_back(const_cast<char*>(it->c_str()));
+  }
+  args.push_back(NULL);
+
+  // fork and exec
+  pid_t pid = fork();
+  if (pid == 0) {
+    // no allocation allowed between fork and exec
+
+    // change process groups, so we don't get reaped by ProcessManager
+    setpgid(0, 0);
+
+    execv(program, &args[0]);
+
+    *error_msg = StringPrintf("Failed to execv(%s): %s", command_line.c_str(), strerror(errno));
+    return false;
+  } else {
+    if (pid == -1) {
+      *error_msg = StringPrintf("Failed to execv(%s) because fork failed: %s",
+                                command_line.c_str(), strerror(errno));
+      return false;
+    }
+
+    // wait for subprocess to finish
+    int status;
+    pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
+    if (got_pid != pid) {
+      *error_msg = StringPrintf("Failed after fork for execv(%s) because waitpid failed: "
+                                "wanted %d, got %d: %s",
+                                command_line.c_str(), pid, got_pid, strerror(errno));
+      return false;
+    }
+    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+      *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status",
+                                command_line.c_str());
+      return false;
+    }
+  }
+  return true;
+}
+
 }  // namespace art
diff --git a/runtime/utils.h b/runtime/utils.h
index e2d8966..0bb06de 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -396,6 +396,9 @@
 bool IsDexMagic(uint32_t magic);
 bool IsOatMagic(uint32_t magic);
 
+// Wrapper on fork/execv to run a command in a subprocess.
+bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg);
+
 class VoidFunctor {
  public:
   template <typename A>
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index b43177b..ff65e47 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -349,4 +349,26 @@
   CheckGetDalvikCacheFilenameOrDie("/system/framework/boot.art", "system@framework@boot.art");
 }
 
+TEST_F(UtilsTest, ExecSuccess) {
+  std::vector<std::string> command;
+  if (kIsTargetBuild) {
+    command.push_back("/system/bin/id");
+  } else {
+    command.push_back("/usr/bin/id");
+  }
+  std::string error_msg;
+  EXPECT_TRUE(Exec(command, &error_msg));
+  EXPECT_EQ(0U, error_msg.size()) << error_msg;
+}
+
+// TODO: Disabled due to hang tearing down CommonTest.
+// Renable after splitting into RuntimeTest and CompilerTest.
+TEST_F(UtilsTest, DISABLED_ExecError) {
+  std::vector<std::string> command;
+  command.push_back("bogus");
+  std::string error_msg;
+  EXPECT_FALSE(Exec(command, &error_msg));
+  EXPECT_NE(0U, error_msg.size());
+}
+
 }  // namespace art