summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian Carlstrom <bdc@google.com>2014-02-10 23:48:36 -0800
committerBrian Carlstrom <bdc@google.com>2014-02-24 14:24:12 -0800
commit6449c62e40ef3a9bb75f664f922555affb532ee4 (patch)
tree2f1b2120bd648c95dea32b68c8e168e42c8e24fd
parent3fcf18e25241253f23efbeebe77b2a4c4a7c54d3 (diff)
downloadart-6449c62e40ef3a9bb75f664f922555affb532ee4.zip
art-6449c62e40ef3a9bb75f664f922555affb532ee4.tar.gz
art-6449c62e40ef3a9bb75f664f922555affb532ee4.tar.bz2
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
-rw-r--r--compiler/dex/frontend.cc2
-rw-r--r--compiler/dex/mir_analysis.cc39
-rw-r--r--compiler/dex/mir_graph.h2
-rw-r--r--compiler/dex/verification_results.cc20
-rw-r--r--compiler/dex/verification_results.h12
-rw-r--r--compiler/driver/compiler_callbacks_impl.h59
-rw-r--r--compiler/driver/compiler_driver.cc11
-rw-r--r--compiler/driver/compiler_driver.h10
-rw-r--r--compiler/driver/compiler_options.h136
-rw-r--r--compiler/oat_test.cc9
-rw-r--r--dex2oat/dex2oat.cc290
-rw-r--r--runtime/class_linker.cc130
-rw-r--r--runtime/common_test.h53
-rw-r--r--runtime/gc/space/image_space.cc50
-rw-r--r--runtime/oat_file.cc1
-rw-r--r--runtime/runtime.cc61
-rw-r--r--runtime/runtime.h77
-rw-r--r--runtime/thread_list.cc1
-rw-r--r--runtime/utils.cc53
-rw-r--r--runtime/utils.h3
-rw-r--r--runtime/utils_test.cc22
21 files changed, 632 insertions, 409 deletions
diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc
index 591d92a..6800f7b 100644
--- a/compiler/dex/frontend.cc
+++ b/compiler/dex/frontend.cc
@@ -211,7 +211,7 @@ static CompiledMethod* CompileMethod(CompilerDriver& compiler,
class_loader, dex_file);
cu.NewTimingSplit("MIROpt:CheckFilters");
- if (cu.mir_graph->SkipCompilation(Runtime::Current()->GetCompilerFilter())) {
+ if (cu.mir_graph->SkipCompilation()) {
return NULL;
}
diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc
index ab55333..7ce8f69 100644
--- a/compiler/dex/mir_analysis.cc
+++ b/compiler/dex/mir_analysis.cc
@@ -18,6 +18,7 @@
#include "dataflow_iterator-inl.h"
#include "dex/quick/dex_file_method_inliner.h"
#include "dex/quick/dex_file_to_method_inliner_map.h"
+#include "driver/compiler_options.h"
namespace art {
@@ -958,7 +959,7 @@ bool MIRGraph::ComputeSkipCompilation(MethodStats* stats, bool skip_default) {
}
// Complex, logic-intensive?
- if ((GetNumDalvikInsns() > Runtime::Current()->GetSmallMethodThreshold()) &&
+ if (cu_->compiler_driver->GetCompilerOptions().IsSmallMethod(GetNumDalvikInsns()) &&
stats->branch_ratio > 0.3) {
return false;
}
@@ -984,7 +985,7 @@ bool MIRGraph::ComputeSkipCompilation(MethodStats* stats, bool skip_default) {
}
// If significant in size and high proportion of expensive operations, skip.
- if ((GetNumDalvikInsns() > Runtime::Current()->GetSmallMethodThreshold()) &&
+ if (cu_->compiler_driver->GetCompilerOptions().IsSmallMethod(GetNumDalvikInsns()) &&
(stats->heavyweight_ratio > 0.3)) {
return true;
}
@@ -996,12 +997,14 @@ bool MIRGraph::ComputeSkipCompilation(MethodStats* stats, bool skip_default) {
* Will eventually want this to be a bit more sophisticated and happen at verification time.
* Ultimate goal is to drive with profile data.
*/
-bool MIRGraph::SkipCompilation(Runtime::CompilerFilter compiler_filter) {
- if (compiler_filter == Runtime::kEverything) {
+bool MIRGraph::SkipCompilation() {
+ const CompilerOptions& compiler_options = cu_->compiler_driver->GetCompilerOptions();
+ CompilerOptions::CompilerFilter compiler_filter = compiler_options.GetCompilerFilter();
+ if (compiler_filter == CompilerOptions::kEverything) {
return false;
}
- if (compiler_filter == Runtime::kInterpretOnly) {
+ if (compiler_filter == CompilerOptions::kInterpretOnly) {
LOG(WARNING) << "InterpretOnly should ideally be filtered out prior to parsing.";
return true;
}
@@ -1010,17 +1013,17 @@ bool MIRGraph::SkipCompilation(Runtime::CompilerFilter compiler_filter) {
size_t small_cutoff = 0;
size_t default_cutoff = 0;
switch (compiler_filter) {
- case Runtime::kBalanced:
- small_cutoff = Runtime::Current()->GetSmallMethodThreshold();
- default_cutoff = Runtime::Current()->GetLargeMethodThreshold();
+ case CompilerOptions::kBalanced:
+ small_cutoff = compiler_options.GetSmallMethodThreshold();
+ default_cutoff = compiler_options.GetLargeMethodThreshold();
break;
- case Runtime::kSpace:
- small_cutoff = Runtime::Current()->GetTinyMethodThreshold();
- default_cutoff = Runtime::Current()->GetSmallMethodThreshold();
+ case CompilerOptions::kSpace:
+ small_cutoff = compiler_options.GetTinyMethodThreshold();
+ default_cutoff = compiler_options.GetSmallMethodThreshold();
break;
- case Runtime::kSpeed:
- small_cutoff = Runtime::Current()->GetHugeMethodThreshold();
- default_cutoff = Runtime::Current()->GetHugeMethodThreshold();
+ case CompilerOptions::kSpeed:
+ small_cutoff = compiler_options.GetHugeMethodThreshold();
+ default_cutoff = compiler_options.GetHugeMethodThreshold();
break;
default:
LOG(FATAL) << "Unexpected compiler_filter_: " << compiler_filter;
@@ -1033,17 +1036,17 @@ bool MIRGraph::SkipCompilation(Runtime::CompilerFilter compiler_filter) {
* Filter 1: Huge methods are likely to be machine generated, but some aren't.
* If huge, assume we won't compile, but allow futher analysis to turn it back on.
*/
- if (GetNumDalvikInsns() > Runtime::Current()->GetHugeMethodThreshold()) {
+ if (compiler_options.IsHugeMethod(GetNumDalvikInsns())) {
skip_compilation = true;
// If we're got a huge number of basic blocks, don't bother with further analysis.
- if (static_cast<size_t>(num_blocks_) > (Runtime::Current()->GetHugeMethodThreshold() / 2)) {
+ if (static_cast<size_t>(num_blocks_) > (compiler_options.GetHugeMethodThreshold() / 2)) {
return true;
}
- } else if (GetNumDalvikInsns() > Runtime::Current()->GetLargeMethodThreshold() &&
+ } else if (compiler_options.IsLargeMethod(GetNumDalvikInsns()) &&
/* If it's large and contains no branches, it's likely to be machine generated initialization */
(GetBranchCount() == 0)) {
return true;
- } else if (compiler_filter == Runtime::kSpeed) {
+ } else if (compiler_filter == CompilerOptions::kSpeed) {
// If not huge, compile.
return false;
}
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index d844aac..2174f67 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -372,7 +372,7 @@ class MIRGraph {
* Examine the graph to determine whether it's worthwile to spend the time compiling
* this method.
*/
- bool SkipCompilation(Runtime::CompilerFilter compiler_filter);
+ bool SkipCompilation();
/*
* Parse dex method and add MIR at current insert point. Returns id (which is
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index edccec5..947c22d 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -19,6 +19,8 @@
#include "base/stl_util.h"
#include "base/mutex.h"
#include "base/mutex-inl.h"
+#include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
#include "thread.h"
#include "thread-inl.h"
#include "verified_method.h"
@@ -27,8 +29,9 @@
namespace art {
-VerificationResults::VerificationResults()
- : verified_methods_lock_("compiler verified methods lock"),
+VerificationResults::VerificationResults(const CompilerOptions* compiler_options)
+ : compiler_options_(compiler_options),
+ verified_methods_lock_("compiler verified methods lock"),
verified_methods_(),
rejected_classes_lock_("compiler rejected classes lock"),
rejected_classes_() {
@@ -43,6 +46,7 @@ VerificationResults::~VerificationResults() {
}
bool VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) {
+ DCHECK(method_verifier != NULL);
MethodReference ref = method_verifier->GetMethodReference();
bool compile = IsCandidateForCompilation(ref, method_verifier->GetAccessFlags());
// TODO: Check also for virtual/interface invokes when DEX-to-DEX supports devirtualization.
@@ -95,16 +99,18 @@ bool VerificationResults::IsClassRejected(ClassReference ref) {
bool VerificationResults::IsCandidateForCompilation(MethodReference& method_ref,
const uint32_t access_flags) {
#ifdef ART_SEA_IR_MODE
- bool use_sea = Runtime::Current()->IsSeaIRMode();
- use_sea = use_sea && (std::string::npos != PrettyMethod(
- method_ref.dex_method_index, *(method_ref.dex_file)).find("fibonacci"));
- if (use_sea) return true;
+ bool use_sea = compiler_options_->GetSeaIrMode();
+ use_sea = use_sea && (std::string::npos != PrettyMethod(
+ method_ref.dex_method_index, *(method_ref.dex_file)).find("fibonacci"));
+ if (use_sea) {
+ return true;
+ }
#endif
// Don't compile class initializers, ever.
if (((access_flags & kAccConstructor) != 0) && ((access_flags & kAccStatic) != 0)) {
return false;
}
- return (Runtime::Current()->GetCompilerFilter() != Runtime::kInterpretOnly);
+ return (compiler_options_->GetCompilerFilter() != CompilerOptions::kInterpretOnly);
}
} // namespace art
diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h
index 2eb0713..278182f 100644
--- a/compiler/dex/verification_results.h
+++ b/compiler/dex/verification_results.h
@@ -33,11 +33,13 @@ namespace verifier {
class MethodVerifier;
} // namespace verifier
+class CompilerOptions;
class VerifiedMethod;
+// Used by CompilerCallbacks to track verification information from the Runtime.
class VerificationResults {
public:
- VerificationResults();
+ explicit VerificationResults(const CompilerOptions* compiler_options);
~VerificationResults();
bool ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier)
@@ -50,15 +52,17 @@ class VerificationResults {
void AddRejectedClass(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_);
bool IsClassRejected(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_);
- static bool IsCandidateForCompilation(MethodReference& method_ref,
- const uint32_t access_flags);
+ bool IsCandidateForCompilation(MethodReference& method_ref,
+ const uint32_t access_flags);
private:
+ const CompilerOptions* compiler_options_;
+
// Verified methods.
typedef SafeMap<MethodReference, const VerifiedMethod*,
MethodReferenceComparator> VerifiedMethodMap;
ReaderWriterMutex verified_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- VerifiedMethodMap verified_methods_;
+ VerifiedMethodMap verified_methods_ GUARDED_BY(verified_methods_lock_);
// Rejected classes.
ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
diff --git a/compiler/driver/compiler_callbacks_impl.h b/compiler/driver/compiler_callbacks_impl.h
new file mode 100644
index 0000000..ab57832
--- /dev/null
+++ b/compiler/driver/compiler_callbacks_impl.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_DRIVER_COMPILER_CALLBACKS_IMPL_H_
+#define ART_COMPILER_DRIVER_COMPILER_CALLBACKS_IMPL_H_
+
+#include "compiler_callbacks.h"
+#include "dex/quick/dex_file_to_method_inliner_map.h"
+#include "verifier/method_verifier-inl.h"
+
+namespace art {
+
+class CompilerCallbacksImpl : public CompilerCallbacks {
+ public:
+ CompilerCallbacksImpl(VerificationResults* verification_results,
+ DexFileToMethodInlinerMap* method_inliner_map)
+ : verification_results_(verification_results),
+ method_inliner_map_(method_inliner_map) {
+ CHECK(verification_results != nullptr);
+ CHECK(method_inliner_map != nullptr);
+ }
+
+ virtual ~CompilerCallbacksImpl() { }
+
+ virtual bool MethodVerified(verifier::MethodVerifier* verifier)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ bool result = verification_results_->ProcessVerifiedMethod(verifier);
+ if (result) {
+ 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_;
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_DRIVER_COMPILER_CALLBACKS_IMPL_H_
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 5adb792..530abc8 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -293,14 +293,16 @@ extern "C" art::CompiledMethod* ArtCompileDEX(art::CompilerDriver& compiler,
jobject class_loader,
const art::DexFile& dex_file);
-CompilerDriver::CompilerDriver(VerificationResults* verification_results,
+CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options,
+ VerificationResults* verification_results,
DexFileToMethodInlinerMap* method_inliner_map,
CompilerBackend::Kind compiler_backend_kind,
InstructionSet instruction_set,
InstructionSetFeatures instruction_set_features,
bool image, DescriptorSet* image_classes, size_t thread_count,
bool dump_stats, bool dump_passes, CumulativeLogger* timer)
- : verification_results_(verification_results),
+ : compiler_options_(compiler_options),
+ verification_results_(verification_results),
method_inliner_map_(method_inliner_map),
compiler_backend_(CompilerBackend::Create(compiler_backend_kind)),
instruction_set_(instruction_set),
@@ -325,6 +327,9 @@ CompilerDriver::CompilerDriver(VerificationResults* verification_results,
dedupe_mapping_table_("dedupe mapping table"),
dedupe_vmap_table_("dedupe vmap table"),
dedupe_gc_map_("dedupe gc map") {
+ DCHECK(compiler_options_ != nullptr);
+ DCHECK(verification_results_ != nullptr);
+ DCHECK(method_inliner_map_ != nullptr);
CHECK_PTHREAD_CALL(pthread_key_create, (&tls_key_, NULL), "compiler tls key");
@@ -1929,7 +1934,7 @@ void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t
} else if ((access_flags & kAccAbstract) != 0) {
} else {
MethodReference method_ref(&dex_file, method_idx);
- bool compile = VerificationResults::IsCandidateForCompilation(method_ref, access_flags);
+ bool compile = verification_results_->IsCandidateForCompilation(method_ref, access_flags);
if (compile) {
// NOTE: if compiler declines to compile this method, it will return NULL.
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 092fe52..5009779 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -45,6 +45,7 @@ class MethodVerifier;
} // namespace verifier
class AOTCompilationStats;
+class CompilerOptions;
class DexCompilationUnit;
class DexFileToMethodInlinerMap;
class InlineIGetIPutData;
@@ -94,7 +95,8 @@ class CompilerDriver {
// enabled. "image_classes" lets the compiler know what classes it
// can assume will be in the image, with NULL implying all available
// classes.
- explicit CompilerDriver(VerificationResults* verification_results,
+ explicit CompilerDriver(const CompilerOptions* compiler_options,
+ VerificationResults* verification_results,
DexFileToMethodInlinerMap* method_inliner_map,
CompilerBackend::Kind compiler_backend_kind,
InstructionSet instruction_set,
@@ -129,6 +131,11 @@ class CompilerDriver {
return instruction_set_features_;
}
+ const CompilerOptions& GetCompilerOptions() const {
+ DCHECK(compiler_options_ != nullptr);
+ return *compiler_options_;
+ }
+
CompilerBackend* GetCompilerBackend() const {
return compiler_backend_.get();
}
@@ -551,6 +558,7 @@ class CompilerDriver {
std::vector<const CallPatchInformation*> methods_to_patch_;
std::vector<const TypePatchInformation*> classes_to_patch_;
+ const CompilerOptions* compiler_options_;
VerificationResults* verification_results_;
DexFileToMethodInlinerMap* method_inliner_map_;
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
new file mode 100644
index 0000000..9f6745b
--- /dev/null
+++ b/compiler/driver/compiler_options.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_DRIVER_COMPILER_OPTIONS_H_
+#define ART_COMPILER_DRIVER_COMPILER_OPTIONS_H_
+
+namespace art {
+
+class CompilerOptions {
+ public:
+ 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.
+ static const CompilerFilter kDefaultCompilerFilter = kSpeed;
+ 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;
+
+ CompilerOptions() :
+ compiler_filter_(kDefaultCompilerFilter),
+ huge_method_threshold_(kDefaultHugeMethodThreshold),
+ large_method_threshold_(kDefaultLargeMethodThreshold),
+ small_method_threshold_(kDefaultSmallMethodThreshold),
+ tiny_method_threshold_(kDefaultTinyMethodThreshold),
+ num_dex_methods_threshold_(kDefaultNumDexMethodsThreshold)
+#ifdef ART_SEA_IR_MODE
+ , sea_ir_mode_(false)
+#endif
+ {}
+
+ CompilerOptions(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
+#ifdef ART_SEA_IR_MODE
+ , bool sea_ir_mode
+#endif
+ ) : // NOLINT(whitespace/parens)
+ compiler_filter_(compiler_filter),
+ huge_method_threshold_(huge_method_threshold),
+ large_method_threshold_(large_method_threshold),
+ small_method_threshold_(small_method_threshold),
+ tiny_method_threshold_(tiny_method_threshold),
+ num_dex_methods_threshold_(num_dex_methods_threshold)
+#ifdef ART_SEA_IR_MODE
+ , sea_ir_mode_(sea_ir_mode)
+#endif
+ {}
+
+ 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_;
+ }
+
+ bool IsHugeMethod(size_t num_dalvik_instructions) const {
+ return num_dalvik_instructions > huge_method_threshold_;
+ }
+
+ bool IsLargeMethod(size_t num_dalvik_instructions) const {
+ return num_dalvik_instructions > large_method_threshold_;
+ }
+
+ bool IsSmallMethod(size_t num_dalvik_instructions) const {
+ return num_dalvik_instructions > small_method_threshold_;
+ }
+
+ bool IsTinyMethod(size_t num_dalvik_instructions) const {
+ return num_dalvik_instructions > tiny_method_threshold_;
+ }
+
+ size_t GetNumDexMethodsThreshold() const {
+ return num_dex_methods_threshold_;
+ }
+
+#ifdef ART_SEA_IR_MODE
+ bool GetSeaIrMode();
+#endif
+
+ private:
+ 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_;
+
+#ifdef ART_SEA_IR_MODE
+ bool sea_ir_mode_;
+#endif
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_DRIVER_COMPILER_OPTIONS_H_
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 10d2c5c..29acc86 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -91,11 +91,14 @@ TEST_F(OatTest, WriteRead) {
InstructionSet insn_set = kIsTargetBuild ? kThumb2 : kX86;
InstructionSetFeatures insn_features;
- 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()));
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, insn_set,
insn_features, false, NULL, 2, true, true,
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index bfda17d..6c879d0 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -34,7 +34,9 @@
#include "compiler_callbacks.h"
#include "dex_file-inl.h"
#include "dex/verification_results.h"
+#include "driver/compiler_callbacks_impl.h"
#include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
#include "elf_fixup.h"
#include "elf_stripper.h"
#include "gc/space/image_space.h"
@@ -54,15 +56,22 @@
#include "scoped_thread_state_change.h"
#include "sirt_ref.h"
#include "vector_output_stream.h"
-#include "verifier/method_verifier.h"
-#include "verifier/method_verifier-inl.h"
#include "well_known_classes.h"
#include "zip_archive.h"
-#include "dex/quick/dex_file_to_method_inliner_map.h"
-
namespace art {
+static int original_argc;
+static char** original_argv;
+
+static std::string CommandLine() {
+ std::vector<std::string> command;
+ for (int i = 0; i < original_argc; ++i) {
+ command.push_back(original_argv[i]);
+ }
+ return Join(command, ' ');
+}
+
static void UsageErrorV(const char* fmt, va_list ap) {
std::string error;
StringAppendV(&error, fmt, ap);
@@ -82,6 +91,8 @@ static void Usage(const char* fmt, ...) {
UsageErrorV(fmt, ap);
va_end(ap);
+ UsageError("Command: %s", CommandLine().c_str());
+
UsageError("Usage: dex2oat [options]...");
UsageError("");
UsageError(" --dex-file=<dex-file>: specifies a .dex file to compile.");
@@ -147,6 +158,46 @@ static void Usage(const char* fmt, ...) {
UsageError(" Example: --compiler-backend=Portable");
UsageError(" Default: Quick");
UsageError("");
+ UsageError(" --compiler-filter=(interpret-only|space|balanced|speed|everything): select");
+ UsageError(" compiler filter.");
+ UsageError(" Example: --compiler-filter=everything");
+#if ART_SMALL_MODE
+ UsageError(" Default: interpret-only");
+#else
+ UsageError(" Default: speed");
+#endif
+ UsageError("");
+ UsageError(" --huge-method-max=<method-instruction-count>: the threshold size for a huge");
+ UsageError(" method for compiler filter tuning.");
+ UsageError(" Example: --huge-method-max=%d", CompilerOptions::kDefaultHugeMethodThreshold);
+ UsageError(" Default: %d", CompilerOptions::kDefaultHugeMethodThreshold);
+ UsageError("");
+ UsageError(" --huge-method-max=<method-instruction-count>: threshold size for a huge");
+ UsageError(" method for compiler filter tuning.");
+ UsageError(" Example: --huge-method-max=%d", CompilerOptions::kDefaultHugeMethodThreshold);
+ UsageError(" Default: %d", CompilerOptions::kDefaultHugeMethodThreshold);
+ UsageError("");
+ UsageError(" --large-method-max=<method-instruction-count>: threshold size for a large");
+ UsageError(" method for compiler filter tuning.");
+ UsageError(" Example: --large-method-max=%d", CompilerOptions::kDefaultLargeMethodThreshold);
+ UsageError(" Default: %d", CompilerOptions::kDefaultLargeMethodThreshold);
+ UsageError("");
+ UsageError(" --small-method-max=<method-instruction-count>: threshold size for a small");
+ UsageError(" method for compiler filter tuning.");
+ UsageError(" Example: --small-method-max=%d", CompilerOptions::kDefaultSmallMethodThreshold);
+ UsageError(" Default: %d", CompilerOptions::kDefaultSmallMethodThreshold);
+ UsageError("");
+ UsageError(" --tiny-method-max=<method-instruction-count>: threshold size for a tiny");
+ UsageError(" method for compiler filter tuning.");
+ UsageError(" Example: --tiny-method-max=%d", CompilerOptions::kDefaultTinyMethodThreshold);
+ UsageError(" Default: %d", CompilerOptions::kDefaultTinyMethodThreshold);
+ UsageError("");
+ UsageError(" --num-dex-methods=<method-count>: threshold size for a small dex file for");
+ UsageError(" compiler filter tuning. If the input has fewer than this many methods");
+ UsageError(" and the filter is not interpret-only, overrides the filter to use speed");
+ UsageError(" Example: --num-dex-method=%d", CompilerOptions::kDefaultNumDexMethodsThreshold);
+ UsageError(" Default: %d", CompilerOptions::kDefaultNumDexMethodsThreshold);
+ UsageError("");
UsageError(" --host: used with Portable backend to link against host runtime libraries");
UsageError("");
UsageError(" --dump-timing: display a breakdown of where time was spent");
@@ -163,15 +214,25 @@ static void Usage(const char* fmt, ...) {
class Dex2Oat {
public:
static bool Create(Dex2Oat** p_dex2oat,
- Runtime::Options& options,
+ const Runtime::Options& runtime_options,
+ const CompilerOptions& compiler_options,
CompilerBackend::Kind compiler_backend,
InstructionSet instruction_set,
InstructionSetFeatures instruction_set_features,
+ VerificationResults* verification_results,
+ DexFileToMethodInlinerMap* method_inliner_map,
size_t thread_count)
SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) {
- UniquePtr<Dex2Oat> dex2oat(new Dex2Oat(compiler_backend, instruction_set,
- instruction_set_features, thread_count));
- if (!dex2oat->CreateRuntime(options, instruction_set)) {
+ CHECK(verification_results != nullptr);
+ CHECK(method_inliner_map != nullptr);
+ UniquePtr<Dex2Oat> dex2oat(new Dex2Oat(&compiler_options,
+ compiler_backend,
+ instruction_set,
+ instruction_set_features,
+ verification_results,
+ method_inliner_map,
+ thread_count));
+ if (!dex2oat->CreateRuntime(runtime_options, instruction_set)) {
*p_dex2oat = NULL;
return false;
}
@@ -275,8 +336,9 @@ class Dex2Oat {
Runtime::Current()->SetCompileTimeClassPath(class_loader, class_path_files);
}
- UniquePtr<CompilerDriver> driver(new CompilerDriver(verification_results_.get(),
- method_inliner_map_.get(),
+ UniquePtr<CompilerDriver> driver(new CompilerDriver(compiler_options_,
+ verification_results_,
+ method_inliner_map_,
compiler_backend_,
instruction_set_,
instruction_set_features_,
@@ -353,53 +415,30 @@ class Dex2Oat {
}
private:
- class Dex2OatCompilerCallbacks : public CompilerCallbacks {
- public:
- Dex2OatCompilerCallbacks(VerificationResults* verification_results,
- DexFileToMethodInlinerMap* method_inliner_map)
- : verification_results_(verification_results),
- method_inliner_map_(method_inliner_map) { }
- virtual ~Dex2OatCompilerCallbacks() { }
-
- virtual bool MethodVerified(verifier::MethodVerifier* verifier)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- bool result = verification_results_->ProcessVerifiedMethod(verifier);
- if (result) {
- 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_;
- };
-
- explicit Dex2Oat(CompilerBackend::Kind compiler_backend,
+ explicit Dex2Oat(const CompilerOptions* compiler_options,
+ CompilerBackend::Kind compiler_backend,
InstructionSet instruction_set,
InstructionSetFeatures instruction_set_features,
+ VerificationResults* verification_results,
+ DexFileToMethodInlinerMap* method_inliner_map,
size_t thread_count)
- : compiler_backend_(compiler_backend),
+ : compiler_options_(compiler_options),
+ compiler_backend_(compiler_backend),
instruction_set_(instruction_set),
instruction_set_features_(instruction_set_features),
- verification_results_(new VerificationResults),
- method_inliner_map_(new DexFileToMethodInlinerMap),
- callbacks_(verification_results_.get(), method_inliner_map_.get()),
+ verification_results_(verification_results),
+ method_inliner_map_(method_inliner_map),
runtime_(nullptr),
thread_count_(thread_count),
start_ns_(NanoTime()) {
+ CHECK(compiler_options != nullptr);
+ CHECK(verification_results != nullptr);
+ CHECK(method_inliner_map != nullptr);
}
- bool CreateRuntime(Runtime::Options& options, InstructionSet instruction_set)
+ bool CreateRuntime(const Runtime::Options& runtime_options, InstructionSet instruction_set)
SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) {
- options.push_back(
- std::make_pair("compilercallbacks", static_cast<CompilerCallbacks*>(&callbacks_)));
- if (!Runtime::Create(options, false)) {
+ if (!Runtime::Create(runtime_options, false)) {
LOG(ERROR) << "Failed to create runtime";
return false;
}
@@ -448,14 +487,14 @@ class Dex2Oat {
return false;
}
+ const CompilerOptions* compiler_options_;
const CompilerBackend::Kind compiler_backend_;
const InstructionSet instruction_set_;
const InstructionSetFeatures instruction_set_features_;
- UniquePtr<VerificationResults> verification_results_;
- UniquePtr<DexFileToMethodInlinerMap> method_inliner_map_;
- Dex2OatCompilerCallbacks callbacks_;
+ VerificationResults* verification_results_;
+ DexFileToMethodInlinerMap* method_inliner_map_;
Runtime* runtime_;
size_t thread_count_;
uint64_t start_ns_;
@@ -656,6 +695,9 @@ static InstructionSetFeatures ParseFeatureList(std::string str) {
}
static int dex2oat(int argc, char** argv) {
+ original_argc = argc;
+ original_argv = argv;
+
TimingLogger timings("compiler", false, false);
CumulativeLogger compiler_phases_timings("compilation times");
@@ -690,6 +732,12 @@ static int dex2oat(int argc, char** argv) {
CompilerBackend::Kind compiler_backend = kUsePortableCompiler
? CompilerBackend::kPortable
: CompilerBackend::kQuick;
+ const char* compiler_filter_string = NULL;
+ int huge_method_threshold = CompilerOptions::kDefaultHugeMethodThreshold;
+ int large_method_threshold = CompilerOptions::kDefaultLargeMethodThreshold;
+ int small_method_threshold = CompilerOptions::kDefaultSmallMethodThreshold;
+ int tiny_method_threshold = CompilerOptions::kDefaultTinyMethodThreshold;
+ int num_dex_methods_threshold = CompilerOptions::kDefaultNumDexMethodsThreshold;
// Take the default set of instruction features from the build.
InstructionSetFeatures instruction_set_features =
@@ -713,7 +761,6 @@ static int dex2oat(int argc, char** argv) {
bool dump_slow_timing = kIsDebugBuild;
bool watch_dog_enabled = !kIsTargetBuild;
-
for (int i = 0; i < argc; i++) {
const StringPiece option(argv[i]);
bool log_options = false;
@@ -729,6 +776,9 @@ static int dex2oat(int argc, char** argv) {
if (!ParseInt(zip_fd_str, &zip_fd)) {
Usage("Failed to parse --zip-fd argument '%s' as an integer", zip_fd_str);
}
+ if (zip_fd < 0) {
+ Usage("--zip-fd passed a negative value %d", zip_fd);
+ }
} else if (option.starts_with("--zip-location=")) {
zip_location = option.substr(strlen("--zip-location=")).data();
} else if (option.starts_with("--oat-file=")) {
@@ -740,6 +790,9 @@ static int dex2oat(int argc, char** argv) {
if (!ParseInt(oat_fd_str, &oat_fd)) {
Usage("Failed to parse --oat-fd argument '%s' as an integer", oat_fd_str);
}
+ if (oat_fd < 0) {
+ Usage("--oat-fd passed a negative value %d", oat_fd);
+ }
} else if (option == "--watch-dog") {
watch_dog_enabled = true;
} else if (option == "--no-watch-dog") {
@@ -793,6 +846,48 @@ static int dex2oat(int argc, char** argv) {
} else if (backend_str == "Portable") {
compiler_backend = CompilerBackend::kPortable;
}
+ } else if (option.starts_with("--compiler-filter=")) {
+ compiler_filter_string = option.substr(strlen("--compiler-filter=")).data();
+ } else if (option.starts_with("--huge-method-max=")) {
+ const char* threshold = option.substr(strlen("--huge-method-max=")).data();
+ if (!ParseInt(threshold, &huge_method_threshold)) {
+ Usage("Failed to parse --huge-method-max '%s' as an integer", threshold);
+ }
+ if (huge_method_threshold < 0) {
+ Usage("--huge-method-max passed a negative value %s", huge_method_threshold);
+ }
+ } else if (option.starts_with("--large-method-max=")) {
+ const char* threshold = option.substr(strlen("--large-method-max=")).data();
+ if (!ParseInt(threshold, &large_method_threshold)) {
+ Usage("Failed to parse --large-method-max '%s' as an integer", threshold);
+ }
+ if (large_method_threshold < 0) {
+ Usage("--large-method-max passed a negative value %s", large_method_threshold);
+ }
+ } else if (option.starts_with("--small-method-max=")) {
+ const char* threshold = option.substr(strlen("--small-method-max=")).data();
+ if (!ParseInt(threshold, &small_method_threshold)) {
+ Usage("Failed to parse --small-method-max '%s' as an integer", threshold);
+ }
+ if (small_method_threshold < 0) {
+ Usage("--small-method-max passed a negative value %s", small_method_threshold);
+ }
+ } else if (option.starts_with("--tiny-method-max=")) {
+ const char* threshold = option.substr(strlen("--tiny-method-max=")).data();
+ if (!ParseInt(threshold, &tiny_method_threshold)) {
+ Usage("Failed to parse --tiny-method-max '%s' as an integer", threshold);
+ }
+ if (tiny_method_threshold < 0) {
+ Usage("--tiny-method-max passed a negative value %s", tiny_method_threshold);
+ }
+ } else if (option.starts_with("--num-dex-methods=")) {
+ const char* threshold = option.substr(strlen("--num-dex-methods=")).data();
+ if (!ParseInt(threshold, &num_dex_methods_threshold)) {
+ Usage("Failed to parse --num-dex-methods '%s' as an integer", threshold);
+ }
+ if (num_dex_methods_threshold < 0) {
+ Usage("--num-dex-methods passed a negative value %s", num_dex_methods_threshold);
+ }
} else if (option == "--host") {
is_host = true;
} else if (option == "--runtime-arg") {
@@ -915,6 +1010,44 @@ static int dex2oat(int argc, char** argv) {
oat_unstripped += oat_filename;
}
+ if (compiler_filter_string == NULL) {
+ if (image) {
+ compiler_filter_string = "everything";
+ } else {
+#if ART_SMALL_MODE
+ compiler_filter_string = "interpret-only";
+#else
+ compiler_filter_string = "speed";
+#endif
+ }
+ }
+ CHECK(compiler_filter_string != nullptr);
+ CompilerOptions::CompilerFilter compiler_filter = CompilerOptions::kDefaultCompilerFilter;
+ if (strcmp(compiler_filter_string, "interpret-only") == 0) {
+ compiler_filter = CompilerOptions::kInterpretOnly;
+ } else if (strcmp(compiler_filter_string, "space") == 0) {
+ compiler_filter = CompilerOptions::kSpace;
+ } else if (strcmp(compiler_filter_string, "balanced") == 0) {
+ compiler_filter = CompilerOptions::kBalanced;
+ } else if (strcmp(compiler_filter_string, "speed") == 0) {
+ compiler_filter = CompilerOptions::kSpeed;
+ } else if (strcmp(compiler_filter_string, "everything") == 0) {
+ compiler_filter = CompilerOptions::kEverything;
+ } else {
+ Usage("Unknown --compiler-filter value %s", compiler_filter_string);
+ }
+
+ CompilerOptions compiler_options(compiler_filter,
+ huge_method_threshold,
+ large_method_threshold,
+ small_method_threshold,
+ tiny_method_threshold,
+ num_dex_methods_threshold
+#ifdef ART_SEA_IR_MODE
+ , compiler_options.sea_ir_ = true;
+#endif
+ ); // NOLINT(whitespace/parens)
+
// Done with usage checks, enable watchdog if requested
WatchDog watch_dog(watch_dog_enabled);
@@ -940,22 +1073,9 @@ static int dex2oat(int argc, char** argv) {
}
timings.StartSplit("dex2oat Setup");
- LOG(INFO) << "dex2oat: " << oat_location;
+ LOG(INFO) << "dex2oat: " << CommandLine();
- if (image) {
- bool has_compiler_filter = false;
- for (const char* r : runtime_args) {
- if (strncmp(r, "-compiler-filter:", 17) == 0) {
- has_compiler_filter = true;
- break;
- }
- }
- if (!has_compiler_filter) {
- runtime_args.push_back("-compiler-filter:everything");
- }
- }
-
- Runtime::Options options;
+ Runtime::Options runtime_options;
std::vector<const DexFile*> boot_class_path;
if (boot_image_option.empty()) {
size_t failure_count = OpenDexFiles(dex_filenames, dex_locations, boot_class_path);
@@ -963,24 +1083,33 @@ static int dex2oat(int argc, char** argv) {
LOG(ERROR) << "Failed to open some dex files: " << failure_count;
return EXIT_FAILURE;
}
- options.push_back(std::make_pair("bootclasspath", &boot_class_path));
+ runtime_options.push_back(std::make_pair("bootclasspath", &boot_class_path));
} else {
- options.push_back(std::make_pair(boot_image_option.c_str(), reinterpret_cast<void*>(NULL)));
+ runtime_options.push_back(std::make_pair(boot_image_option.c_str(),
+ reinterpret_cast<void*>(NULL)));
}
if (host_prefix.get() != NULL) {
- options.push_back(std::make_pair("host-prefix", host_prefix->c_str()));
+ runtime_options.push_back(std::make_pair("host-prefix", host_prefix->c_str()));
}
for (size_t i = 0; i < runtime_args.size(); i++) {
- options.push_back(std::make_pair(runtime_args[i], reinterpret_cast<void*>(NULL)));
+ runtime_options.push_back(std::make_pair(runtime_args[i], reinterpret_cast<void*>(NULL)));
}
-#ifdef ART_SEA_IR_MODE
- options.push_back(std::make_pair("-sea_ir", reinterpret_cast<void*>(NULL)));
-#endif
+ VerificationResults verification_results(&compiler_options);
+ DexFileToMethodInlinerMap method_inliner_map;
+ CompilerCallbacksImpl callbacks(&verification_results, &method_inliner_map);
+ runtime_options.push_back(std::make_pair("compilercallbacks", &callbacks));
Dex2Oat* p_dex2oat;
- if (!Dex2Oat::Create(&p_dex2oat, options, compiler_backend, instruction_set,
- instruction_set_features, thread_count)) {
+ if (!Dex2Oat::Create(&p_dex2oat,
+ runtime_options,
+ compiler_options,
+ compiler_backend,
+ instruction_set,
+ instruction_set_features,
+ &verification_results,
+ &method_inliner_map,
+ thread_count)) {
LOG(ERROR) << "Failed to create dex2oat";
return EXIT_FAILURE;
}
@@ -1050,7 +1179,8 @@ static int dex2oat(int argc, char** argv) {
std::string tmp_file_name(StringPrintf("/data/local/tmp/dex2oat.%d.%zd.dex", getpid(), i));
UniquePtr<File> tmp_file(OS::CreateEmptyFile(tmp_file_name.c_str()));
if (tmp_file.get() == nullptr) {
- PLOG(ERROR) << "Failed to open file " << tmp_file_name << ". Try: adb shell chmod 777 /data/local/tmp";
+ PLOG(ERROR) << "Failed to open file " << tmp_file_name
+ << ". Try: adb shell chmod 777 /data/local/tmp";
continue;
}
tmp_file->WriteFully(dex_file->Begin(), dex_file->Size());
@@ -1070,15 +1200,15 @@ static int dex2oat(int argc, char** argv) {
* If we're not in interpret-only mode, go ahead and compile small applications. Don't
* bother to check if we're doing the image.
*/
- if (!image && (Runtime::Current()->GetCompilerFilter() != Runtime::kInterpretOnly)) {
+ if (!image && (compiler_options.GetCompilerFilter() != CompilerOptions::kInterpretOnly)) {
size_t num_methods = 0;
for (size_t i = 0; i != dex_files.size(); ++i) {
const DexFile* dex_file = dex_files[i];
CHECK(dex_file != NULL);
num_methods += dex_file->NumMethodIds();
}
- if (num_methods <= Runtime::Current()->GetNumDexMethodsThreshold()) {
- Runtime::Current()->SetCompilerFilter(Runtime::kSpeed);
+ if (num_methods <= compiler_options.GetNumDexMethodsThreshold()) {
+ compiler_options.SetCompilerFilter(CompilerOptions::kSpeed);
VLOG(compiler) << "Below method threshold, compiling anyways";
}
}
@@ -1222,13 +1352,13 @@ static int dex2oat(int argc, char** argv) {
// Everything was successfully written, do an explicit exit here to avoid running Runtime
// destructors that take time (bug 10645725) unless we're a debug build or running on valgrind.
- if (!kIsDebugBuild || (RUNNING_ON_VALGRIND == 0)) {
+ if (!kIsDebugBuild && (RUNNING_ON_VALGRIND == 0)) {
dex2oat->LogCompletionTime();
exit(EXIT_SUCCESS);
}
return EXIT_SUCCESS;
-}
+} // NOLINT(readability/fn_size)
} // namespace art
int main(int argc, char** argv) {
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 @@ bool ClassLinker::GenerateOatFile(const char* dex_filename,
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 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 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_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_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;
- }
- 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;
- }
- }
- return true;
+ std::string boot_image_option("--boot-image=");
+ boot_image_option += heap->GetImageSpace()->GetImageFilename();
+
+ std::string dex_file_option("--dex-file=");
+ dex_file_option += dex_filename;
+
+ std::string oat_fd_option("--oat-fd=");
+ StringAppendF(&oat_fd_option, "%d", oat_fd);
+
+ std::string oat_location_option("--oat-location=");
+ oat_location_option += oat_cache_filename;
+
+ 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");
+ }
+ 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 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 @@ class CommonTest : public testing::Test {
? 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 @@ class CommonTest : public testing::Test {
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 CommonTest : public testing::Test {
}
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 @@ class CommonTest : public testing::Test {
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 @@ class CommonTest : public testing::Test {
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 @@ class CommonTest : public testing::Test {
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 @@ static bool GenerateImage(const std::string& image_file_name, std::string* error
arg_vector.push_back("--host");
}
- 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()));
+ 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());
}
- 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;
+ std::string command_line(Join(arg_vector, ' '));
+ LOG(INFO) << "GenerateImage: " << command_line;
+ 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 @@ OatFile* OatFile::Open(const std::string& filename,
// 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 @@ Runtime::Runtime()
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 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b
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 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b
} 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 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) {
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 @@ class Runtime {
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 @@ class Runtime {
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 @@ class Runtime {
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;
- }
-
- CompilerFilter GetCompilerFilter() const {
- return compiler_filter_;
- }
-
- void SetCompilerFilter(CompilerFilter compiler_filter) {
- compiler_filter_ = compiler_filter;
+ const std::vector<std::string>& GetCompilerOptions() const {
+ return compiler_options_;
}
- 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 @@ class Runtime {
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 @@ size_t ThreadList::RunCheckpoint(Closure* checkpoint_function) {
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 @@ bool IsOatMagic(uint32_t magic) {
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 IsZipMagic(uint32_t magic);
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 @@ TEST_F(UtilsTest, GetDalvikCacheFilenameOrDie) {
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