Merge "ART: Refactor dex2oat"
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 448888f..cf2cddb 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -103,13 +103,13 @@
std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename.c_str()));
if (oat_file.get() == NULL) {
- LOG(ERROR) << "Failed to open oat file " << oat_filename << " for " << oat_location;
+ PLOG(ERROR) << "Failed to open oat file " << oat_filename << " for " << oat_location;
return false;
}
std::string error_msg;
oat_file_ = OatFile::OpenReadable(oat_file.get(), oat_location, &error_msg);
if (oat_file_ == nullptr) {
- LOG(ERROR) << "Failed to open writable oat file " << oat_filename << " for " << oat_location
+ PLOG(ERROR) << "Failed to open writable oat file " << oat_filename << " for " << oat_location
<< ": " << error_msg;
return false;
}
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 7770588..d87faeb 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -48,6 +48,7 @@
#include "dex/quick/dex_file_to_method_inliner_map.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
+#include "elf_file.h"
#include "elf_writer.h"
#include "gc/space/image_space.h"
#include "gc/space/space-inl.h"
@@ -251,367 +252,6 @@
exit(EXIT_FAILURE);
}
-class Dex2Oat {
- public:
- static bool Create(Dex2Oat** p_dex2oat,
- const RuntimeOptions& runtime_options,
- const CompilerOptions& compiler_options,
- Compiler::Kind compiler_kind,
- InstructionSet instruction_set,
- const InstructionSetFeatures* instruction_set_features,
- VerificationResults* verification_results,
- DexFileToMethodInlinerMap* method_inliner_map,
- size_t thread_count)
- SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) {
- CHECK(verification_results != nullptr);
- CHECK(method_inliner_map != nullptr);
- if (instruction_set == kRuntimeISA) {
- std::unique_ptr<const InstructionSetFeatures> runtime_features(
- InstructionSetFeatures::FromCppDefines());
- if (!instruction_set_features->Equals(runtime_features.get())) {
- LOG(WARNING) << "Mismatch between dex2oat instruction set features ("
- << *instruction_set_features << ") and those of dex2oat executable ("
- << *runtime_features <<") for the command line:\n"
- << CommandLine();
- }
- }
- std::unique_ptr<Dex2Oat> dex2oat(new Dex2Oat(&compiler_options,
- compiler_kind,
- instruction_set,
- instruction_set_features,
- verification_results,
- method_inliner_map,
- thread_count));
- if (!dex2oat->CreateRuntime(runtime_options, instruction_set)) {
- *p_dex2oat = nullptr;
- return false;
- }
- *p_dex2oat = dex2oat.release();
- return true;
- }
-
- ~Dex2Oat() {
- delete runtime_;
- LogCompletionTime();
- }
-
- void LogCompletionTime() {
- LOG(INFO) << "dex2oat took " << PrettyDuration(NanoTime() - start_ns_)
- << " (threads: " << thread_count_ << ")";
- }
-
-
- // Reads the class names (java.lang.Object) and returns a set of descriptors (Ljava/lang/Object;)
- std::set<std::string>* ReadImageClassesFromFile(const char* image_classes_filename) {
- std::unique_ptr<std::ifstream> image_classes_file(new std::ifstream(image_classes_filename,
- std::ifstream::in));
- if (image_classes_file.get() == nullptr) {
- LOG(ERROR) << "Failed to open image classes file " << image_classes_filename;
- return nullptr;
- }
- std::unique_ptr<std::set<std::string>> result(ReadImageClasses(*image_classes_file));
- image_classes_file->close();
- return result.release();
- }
-
- std::set<std::string>* ReadImageClasses(std::istream& image_classes_stream) {
- std::unique_ptr<std::set<std::string>> image_classes(new std::set<std::string>);
- while (image_classes_stream.good()) {
- std::string dot;
- std::getline(image_classes_stream, dot);
- if (StartsWith(dot, "#") || dot.empty()) {
- continue;
- }
- std::string descriptor(DotToDescriptor(dot.c_str()));
- image_classes->insert(descriptor);
- }
- return image_classes.release();
- }
-
- // Reads the class names (java.lang.Object) and returns a set of descriptors (Ljava/lang/Object;)
- std::set<std::string>* ReadImageClassesFromZip(const char* zip_filename,
- const char* image_classes_filename,
- std::string* error_msg) {
- std::unique_ptr<ZipArchive> zip_archive(ZipArchive::Open(zip_filename, error_msg));
- if (zip_archive.get() == nullptr) {
- return nullptr;
- }
- std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(image_classes_filename, error_msg));
- if (zip_entry.get() == nullptr) {
- *error_msg = StringPrintf("Failed to find '%s' within '%s': %s", image_classes_filename,
- zip_filename, error_msg->c_str());
- return nullptr;
- }
- std::unique_ptr<MemMap> image_classes_file(zip_entry->ExtractToMemMap(zip_filename,
- image_classes_filename,
- error_msg));
- if (image_classes_file.get() == nullptr) {
- *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", image_classes_filename,
- zip_filename, error_msg->c_str());
- return nullptr;
- }
- const std::string image_classes_string(reinterpret_cast<char*>(image_classes_file->Begin()),
- image_classes_file->Size());
- std::istringstream image_classes_stream(image_classes_string);
- return ReadImageClasses(image_classes_stream);
- }
-
- void Compile(const std::string& boot_image_option,
- const std::vector<const DexFile*>& dex_files,
- const std::string& bitcode_filename,
- bool image,
- std::unique_ptr<std::set<std::string>>& image_classes,
- bool dump_stats,
- bool dump_passes,
- TimingLogger* timings,
- CumulativeLogger* compiler_phases_timings,
- const std::string& profile_file) {
- // Handle and ClassLoader creation needs to come after Runtime::Create
- jobject class_loader = nullptr;
- Thread* self = Thread::Current();
- if (!boot_image_option.empty()) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- std::vector<const DexFile*> class_path_files(dex_files);
- OpenClassPathFiles(runtime_->GetClassPathString(), class_path_files);
- ScopedObjectAccess soa(self);
- for (size_t i = 0; i < class_path_files.size(); i++) {
- class_linker->RegisterDexFile(*class_path_files[i]);
- }
- soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader);
- ScopedLocalRef<jobject> class_loader_local(soa.Env(),
- soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader));
- class_loader = soa.Env()->NewGlobalRef(class_loader_local.get());
- Runtime::Current()->SetCompileTimeClassPath(class_loader, class_path_files);
- }
-
- driver_.reset(new CompilerDriver(compiler_options_,
- verification_results_,
- method_inliner_map_,
- compiler_kind_,
- instruction_set_,
- instruction_set_features_,
- image,
- image_classes.release(),
- thread_count_,
- dump_stats,
- dump_passes,
- compiler_phases_timings,
- profile_file));
-
- driver_->GetCompiler()->SetBitcodeFileName(*driver_, bitcode_filename);
-
- driver_->CompileAll(class_loader, dex_files, timings);
- }
-
- void PrepareImageWriter(uintptr_t image_base) {
- image_writer_.reset(new ImageWriter(*driver_, image_base, compiler_options_->GetCompilePic()));
- }
-
- bool CreateOatFile(const std::vector<const DexFile*>& dex_files,
- const std::string& android_root,
- bool is_host,
- File* oat_file,
- TimingLogger* timings,
- SafeMap<std::string, std::string>* key_value_store) {
- CHECK(key_value_store != nullptr);
-
- TimingLogger::ScopedTiming t2("dex2oat OatWriter", timings);
- std::string image_file_location;
- uint32_t image_file_location_oat_checksum = 0;
- uintptr_t image_file_location_oat_data_begin = 0;
- int32_t image_patch_delta = 0;
- if (!driver_->IsImage()) {
- TimingLogger::ScopedTiming t3("Loading image checksum", timings);
- gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace();
- image_file_location_oat_checksum = image_space->GetImageHeader().GetOatChecksum();
- image_file_location_oat_data_begin =
- reinterpret_cast<uintptr_t>(image_space->GetImageHeader().GetOatDataBegin());
- image_file_location = image_space->GetImageFilename();
- image_patch_delta = image_space->GetImageHeader().GetPatchDelta();
- }
-
- if (!image_file_location.empty()) {
- key_value_store->Put(OatHeader::kImageLocationKey, image_file_location);
- }
-
- OatWriter oat_writer(dex_files, image_file_location_oat_checksum,
- image_file_location_oat_data_begin,
- image_patch_delta,
- driver_.get(),
- image_writer_.get(),
- timings,
- key_value_store);
-
- if (driver_->IsImage()) {
- // The OatWriter constructor has already updated offsets in methods and we need to
- // prepare method offsets in the image address space for direct method patching.
- t2.NewTiming("Preparing image address space");
- if (!image_writer_->PrepareImageAddressSpace()) {
- LOG(ERROR) << "Failed to prepare image address space.";
- return false;
- }
- }
-
- t2.NewTiming("Writing ELF");
- if (!driver_->WriteElf(android_root, is_host, dex_files, &oat_writer, oat_file)) {
- LOG(ERROR) << "Failed to write ELF file " << oat_file->GetPath();
- return false;
- }
-
- // Flush result to disk.
- t2.NewTiming("Flushing ELF");
- if (oat_file->Flush() != 0) {
- LOG(ERROR) << "Failed to flush ELF file " << oat_file->GetPath();
- return false;
- }
-
- return true;
- }
-
- bool CreateImageFile(const std::string& image_filename,
- const std::string& oat_filename,
- const std::string& oat_location)
- LOCKS_EXCLUDED(Locks::mutator_lock_) {
- CHECK(image_writer_ != nullptr);
- if (!image_writer_->Write(image_filename, oat_filename, oat_location)) {
- LOG(ERROR) << "Failed to create image file " << image_filename;
- return false;
- }
- uintptr_t oat_data_begin = image_writer_->GetOatDataBegin();
-
- // Destroy ImageWriter before doing FixupElf.
- image_writer_.reset();
-
- std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename.c_str()));
- if (oat_file.get() == nullptr) {
- PLOG(ERROR) << "Failed to open ELF file: " << oat_filename;
- return false;
- }
-
- // Do not fix up the ELF file if we are --compile-pic
- if (!compiler_options_->GetCompilePic()) {
- if (!ElfWriter::Fixup(oat_file.get(), oat_data_begin)) {
- LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath();
- return false;
- }
- }
-
- return true;
- }
-
- private:
- explicit Dex2Oat(const CompilerOptions* compiler_options,
- Compiler::Kind compiler_kind,
- InstructionSet instruction_set,
- const InstructionSetFeatures* instruction_set_features,
- VerificationResults* verification_results,
- DexFileToMethodInlinerMap* method_inliner_map,
- size_t thread_count)
- : compiler_options_(compiler_options),
- compiler_kind_(compiler_kind),
- instruction_set_(instruction_set),
- instruction_set_features_(instruction_set_features),
- verification_results_(verification_results),
- method_inliner_map_(method_inliner_map),
- runtime_(nullptr),
- thread_count_(thread_count),
- start_ns_(NanoTime()),
- driver_(nullptr),
- image_writer_(nullptr) {
- CHECK(compiler_options != nullptr);
- CHECK(verification_results != nullptr);
- CHECK(method_inliner_map != nullptr);
- }
-
- bool CreateRuntime(const RuntimeOptions& runtime_options, InstructionSet instruction_set)
- SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) {
- if (!Runtime::Create(runtime_options, false)) {
- LOG(ERROR) << "Failed to create runtime";
- return false;
- }
- Runtime* runtime = Runtime::Current();
- runtime->SetInstructionSet(instruction_set);
- for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
- Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i);
- if (!runtime->HasCalleeSaveMethod(type)) {
- runtime->SetCalleeSaveMethod(runtime->CreateCalleeSaveMethod(), type);
- }
- }
- runtime->GetClassLinker()->FixupDexCaches(runtime->GetResolutionMethod());
- runtime->GetClassLinker()->RunRootClinits();
- runtime_ = runtime;
- return true;
- }
-
- // Appends to dex_files any elements of class_path that it doesn't already
- // contain. This will open those dex files as necessary.
- static void OpenClassPathFiles(const std::string& class_path,
- std::vector<const DexFile*>& dex_files) {
- std::vector<std::string> parsed;
- Split(class_path, ':', &parsed);
- // Take Locks::mutator_lock_ so that lock ordering on the ClassLinker::dex_lock_ is maintained.
- ScopedObjectAccess soa(Thread::Current());
- for (size_t i = 0; i < parsed.size(); ++i) {
- if (DexFilesContains(dex_files, parsed[i])) {
- continue;
- }
- std::string error_msg;
- if (!DexFile::Open(parsed[i].c_str(), parsed[i].c_str(), &error_msg, &dex_files)) {
- LOG(WARNING) << "Failed to open dex file '" << parsed[i] << "': " << error_msg;
- }
- }
- }
-
- // Returns true if dex_files has a dex with the named location.
- static bool DexFilesContains(const std::vector<const DexFile*>& dex_files,
- const std::string& location) {
- for (size_t i = 0; i < dex_files.size(); ++i) {
- if (dex_files[i]->GetLocation() == location) {
- return true;
- }
- }
- return false;
- }
-
- const CompilerOptions* const compiler_options_;
- const Compiler::Kind compiler_kind_;
-
- const InstructionSet instruction_set_;
- const InstructionSetFeatures* const instruction_set_features_;
-
- VerificationResults* const verification_results_;
- DexFileToMethodInlinerMap* const method_inliner_map_;
- Runtime* runtime_;
- size_t thread_count_;
- uint64_t start_ns_;
- std::unique_ptr<CompilerDriver> driver_;
- std::unique_ptr<ImageWriter> image_writer_;
-
- DISALLOW_IMPLICIT_CONSTRUCTORS(Dex2Oat);
-};
-
-static size_t OpenDexFiles(const std::vector<const char*>& dex_filenames,
- const std::vector<const char*>& dex_locations,
- std::vector<const DexFile*>& dex_files) {
- size_t failure_count = 0;
- for (size_t i = 0; i < dex_filenames.size(); i++) {
- const char* dex_filename = dex_filenames[i];
- const char* dex_location = dex_locations[i];
- ATRACE_BEGIN(StringPrintf("Opening dex file '%s'", dex_filenames[i]).c_str());
- std::string error_msg;
- if (!OS::FileExists(dex_filename)) {
- LOG(WARNING) << "Skipping non-existent dex file '" << dex_filename << "'";
- continue;
- }
- if (!DexFile::Open(dex_filename, dex_location, &error_msg, &dex_files)) {
- LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "': " << error_msg;
- ++failure_count;
- }
- ATRACE_END();
- }
- return failure_count;
-}
-
// The primary goal of the watchdog is to prevent stuck build servers
// during development when fatal aborts lead to a cascade of failures
// that result in a deadlock.
@@ -726,17 +366,13 @@
// When setting timeouts, keep in mind that the build server may not be as fast as your desktop.
// Debug builds are slower so they have larger timeouts.
static const unsigned int kSlowdownFactor = kIsDebugBuild ? 5U : 1U;
-#if ART_USE_PORTABLE_COMPILER
- // 2 minutes scaled by kSlowdownFactor.
- static const unsigned int kWatchDogWarningSeconds = kSlowdownFactor * 2 * 60;
- // 30 minutes scaled by kSlowdownFactor.
- static const unsigned int kWatchDogTimeoutSeconds = kSlowdownFactor * 30 * 60;
-#else
- // 1 minutes scaled by kSlowdownFactor.
- static const unsigned int kWatchDogWarningSeconds = kSlowdownFactor * 1 * 60;
- // 6 minutes scaled by kSlowdownFactor.
- static const unsigned int kWatchDogTimeoutSeconds = kSlowdownFactor * 6 * 60;
-#endif
+
+ static const unsigned int kWatchDogWarningSeconds = kUsePortableCompiler ?
+ kSlowdownFactor * 2 * 60 : // 2 minutes scaled by kSlowdownFactor (portable).
+ kSlowdownFactor * 1 * 60; // 1 minute scaled by kSlowdownFactor (not-portable).
+ static const unsigned int kWatchDogTimeoutSeconds = kUsePortableCompiler ?
+ kSlowdownFactor * 30 * 60 : // 30 minutes scaled by kSlowdownFactor (portable).
+ kSlowdownFactor * 6 * 60; // 6 minutes scaled by kSlowdownFactor (not-portable).
bool is_watch_dog_enabled_;
bool shutting_down_;
@@ -746,10 +382,8 @@
pthread_attr_t attr_;
pthread_t pthread_;
};
-const unsigned int WatchDog::kWatchDogWarningSeconds;
-const unsigned int WatchDog::kWatchDogTimeoutSeconds;
-void ParseStringAfterChar(const std::string& s, char c, std::string* parsed_value) {
+static void ParseStringAfterChar(const std::string& s, char c, std::string* parsed_value) {
std::string::size_type colon = s.find(c);
if (colon == std::string::npos) {
Usage("Missing char %c in option %s\n", c, s.c_str());
@@ -758,8 +392,8 @@
*parsed_value = s.substr(colon + 1);
}
-void ParseDouble(const std::string& option, char after_char,
- double min, double max, double* parsed_value) {
+static void ParseDouble(const std::string& option, char after_char, double min, double max,
+ double* parsed_value) {
std::string substring;
ParseStringAfterChar(option, after_char, &substring);
bool sane_val = true;
@@ -781,692 +415,690 @@
*parsed_value = value;
}
-static void b13564922() {
-#if defined(__linux__) && defined(__arm__)
- int major, minor;
- struct utsname uts;
- if (uname(&uts) != -1 &&
- sscanf(uts.release, "%d.%d", &major, &minor) == 2 &&
- ((major < 3) || ((major == 3) && (minor < 4)))) {
- // Kernels before 3.4 don't handle the ASLR well and we can run out of address
- // space (http://b/13564922). Work around the issue by inhibiting further mmap() randomization.
- int old_personality = personality(0xffffffff);
- if ((old_personality & ADDR_NO_RANDOMIZE) == 0) {
- int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE);
- if (new_personality == -1) {
- LOG(WARNING) << "personality(. | ADDR_NO_RANDOMIZE) failed.";
- }
+class Dex2Oat FINAL {
+ public:
+ explicit Dex2Oat(TimingLogger* timings) :
+ compiler_kind_(kUsePortableCompiler ? Compiler::kPortable : Compiler::kQuick),
+ instruction_set_(kRuntimeISA),
+ // Take the default set of instruction features from the build.
+ method_inliner_map_(),
+ runtime_(nullptr),
+ thread_count_(sysconf(_SC_NPROCESSORS_CONF)),
+ start_ns_(NanoTime()),
+ oat_fd_(-1),
+ zip_fd_(-1),
+ image_base_(0U),
+ image_classes_zip_filename_(nullptr),
+ image_classes_filename_(nullptr),
+ image_(false),
+ is_host_(false),
+ dump_stats_(false),
+ dump_passes_(false),
+ dump_timing_(false),
+ dump_slow_timing_(kIsDebugBuild),
+ timings_(timings) {}
+
+ ~Dex2Oat() {
+ if (kIsDebugBuild || (RUNNING_ON_VALGRIND != 0)) {
+ delete runtime_; // See field declaration for why this is manual.
}
- }
-#endif
-}
-
-static int dex2oat(int argc, char** argv) {
- b13564922();
-
- original_argc = argc;
- original_argv = argv;
-
- TimingLogger timings("compiler", false, false);
- CumulativeLogger compiler_phases_timings("compilation times");
-
- InitLogging(argv);
-
- // Skip over argv[0].
- argv++;
- argc--;
-
- if (argc == 0) {
- Usage("No arguments specified");
+ LogCompletionTime();
}
- std::vector<const char*> dex_filenames;
- std::vector<const char*> dex_locations;
- int zip_fd = -1;
- std::string zip_location;
- std::string oat_filename;
- std::string oat_symbols;
- std::string oat_location;
- int oat_fd = -1;
- std::string bitcode_filename;
- const char* image_classes_zip_filename = nullptr;
- const char* image_classes_filename = nullptr;
- std::string image_filename;
- std::string boot_image_filename;
- uintptr_t image_base = 0;
- std::string android_root;
- std::vector<const char*> runtime_args;
- int thread_count = sysconf(_SC_NPROCESSORS_CONF);
- Compiler::Kind compiler_kind = kUsePortableCompiler
- ? Compiler::kPortable
- : Compiler::kQuick;
- const char* compiler_filter_string = nullptr;
- bool compile_pic = false;
- 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;
- std::vector<std::string> verbose_methods;
+ // Parse the arguments from the command line. In case of an unrecognized option or impossible
+ // values/combinations, a usage error will be displayed and exit() is called. Thus, if the method
+ // returns, arguments have been successfully parsed.
+ void ParseArgs(int argc, char** argv) {
+ original_argc = argc;
+ original_argv = argv;
- // Initialize ISA and ISA features to default values.
- InstructionSet instruction_set = kRuntimeISA;
- std::string error_msg;
- std::unique_ptr<const InstructionSetFeatures> instruction_set_features(
- InstructionSetFeatures::FromFeatureString(kNone, "default", &error_msg));
- CHECK(instruction_set_features.get() != nullptr) << error_msg;
+ InitLogging(argv);
- // Profile file to use
- std::string profile_file;
- double top_k_profile_threshold = CompilerOptions::kDefaultTopKProfileThreshold;
+ // Skip over argv[0].
+ argv++;
+ argc--;
- bool is_host = false;
- bool dump_stats = false;
- bool dump_timing = false;
- bool dump_passes = false;
- bool print_pass_options = false;
- bool include_patch_information = CompilerOptions::kDefaultIncludePatchInformation;
- bool include_debug_symbols = kIsDebugBuild;
- bool dump_slow_timing = kIsDebugBuild;
- bool watch_dog_enabled = true;
- bool generate_gdb_information = kIsDebugBuild;
-
- // Checks are all explicit until we know the architecture.
- bool implicit_null_checks = false;
- bool implicit_so_checks = false;
- bool implicit_suspend_checks = false;
-
- for (int i = 0; i < argc; i++) {
- const StringPiece option(argv[i]);
- const bool log_options = false;
- if (log_options) {
- LOG(INFO) << "dex2oat: option[" << i << "]=" << argv[i];
+ if (argc == 0) {
+ Usage("No arguments specified");
}
- if (option.starts_with("--dex-file=")) {
- dex_filenames.push_back(option.substr(strlen("--dex-file=")).data());
- } else if (option.starts_with("--dex-location=")) {
- dex_locations.push_back(option.substr(strlen("--dex-location=")).data());
- } else if (option.starts_with("--zip-fd=")) {
- const char* zip_fd_str = option.substr(strlen("--zip-fd=")).data();
- 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=")) {
- oat_filename = option.substr(strlen("--oat-file=")).data();
- } else if (option.starts_with("--oat-symbols=")) {
- oat_symbols = option.substr(strlen("--oat-symbols=")).data();
- } else if (option.starts_with("--oat-fd=")) {
- const char* oat_fd_str = option.substr(strlen("--oat-fd=")).data();
- 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") {
- watch_dog_enabled = false;
- } else if (option == "--gen-gdb-info") {
- generate_gdb_information = true;
- // Debug symbols are needed for gdb information.
- include_debug_symbols = true;
- } else if (option == "--no-gen-gdb-info") {
- generate_gdb_information = false;
- } else if (option.starts_with("-j")) {
- const char* thread_count_str = option.substr(strlen("-j")).data();
- if (!ParseInt(thread_count_str, &thread_count)) {
- Usage("Failed to parse -j argument '%s' as an integer", thread_count_str);
- }
- } else if (option.starts_with("--oat-location=")) {
- oat_location = option.substr(strlen("--oat-location=")).data();
- } else if (option.starts_with("--bitcode=")) {
- bitcode_filename = option.substr(strlen("--bitcode=")).data();
- } else if (option.starts_with("--image=")) {
- image_filename = option.substr(strlen("--image=")).data();
- } else if (option.starts_with("--image-classes=")) {
- image_classes_filename = option.substr(strlen("--image-classes=")).data();
- } else if (option.starts_with("--image-classes-zip=")) {
- image_classes_zip_filename = option.substr(strlen("--image-classes-zip=")).data();
- } else if (option.starts_with("--base=")) {
- const char* image_base_str = option.substr(strlen("--base=")).data();
- char* end;
- image_base = strtoul(image_base_str, &end, 16);
- if (end == image_base_str || *end != '\0') {
- Usage("Failed to parse hexadecimal value for option %s", option.data());
- }
- } else if (option.starts_with("--boot-image=")) {
- boot_image_filename = option.substr(strlen("--boot-image=")).data();
- } else if (option.starts_with("--android-root=")) {
- android_root = option.substr(strlen("--android-root=")).data();
- } else if (option.starts_with("--instruction-set=")) {
- StringPiece instruction_set_str = option.substr(strlen("--instruction-set=")).data();
- if (instruction_set_str == "arm") {
- instruction_set = kThumb2;
- } else if (instruction_set_str == "arm64") {
- instruction_set = kArm64;
- } else if (instruction_set_str == "mips") {
- instruction_set = kMips;
- } else if (instruction_set_str == "x86") {
- instruction_set = kX86;
- } else if (instruction_set_str == "x86_64") {
- instruction_set = kX86_64;
- }
- } else if (option.starts_with("--instruction-set-variant=")) {
- StringPiece str = option.substr(strlen("--instruction-set-variant=")).data();
- instruction_set_features.reset(
- InstructionSetFeatures::FromVariant(instruction_set, str.as_string(), &error_msg));
- if (instruction_set_features.get() == nullptr) {
- Usage("%s", error_msg.c_str());
- }
- } else if (option.starts_with("--instruction-set-features=")) {
- StringPiece str = option.substr(strlen("--instruction-set-features=")).data();
- instruction_set_features.reset(
- InstructionSetFeatures::FromFeatureString(instruction_set, str.as_string(), &error_msg));
- if (instruction_set_features.get() == nullptr) {
- Usage("%s", error_msg.c_str());
- }
- } else if (option.starts_with("--compiler-backend=")) {
- StringPiece backend_str = option.substr(strlen("--compiler-backend=")).data();
- if (backend_str == "Quick") {
- compiler_kind = Compiler::kQuick;
- } else if (backend_str == "Optimizing") {
- compiler_kind = Compiler::kOptimizing;
- } else if (backend_str == "Portable") {
- compiler_kind = Compiler::kPortable;
- } else {
- Usage("Unknown compiler backend: %s", backend_str.data());
- }
- } else if (option.starts_with("--compiler-filter=")) {
- compiler_filter_string = option.substr(strlen("--compiler-filter=")).data();
- } else if (option == "--compile-pic") {
- compile_pic = true;
- } 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") {
- if (++i >= argc) {
- Usage("Missing required argument for --runtime-arg");
- }
+
+ std::string oat_symbols;
+ std::string boot_image_filename;
+ const char* compiler_filter_string = nullptr;
+ bool compile_pic = false;
+ 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;
+
+ // Profile file to use
+ double top_k_profile_threshold = CompilerOptions::kDefaultTopKProfileThreshold;
+
+ bool print_pass_options = false;
+ bool include_patch_information = CompilerOptions::kDefaultIncludePatchInformation;
+ bool include_debug_symbols = kIsDebugBuild;
+ bool watch_dog_enabled = true;
+ bool generate_gdb_information = kIsDebugBuild;
+
+ std::string error_msg;
+
+ for (int i = 0; i < argc; i++) {
+ const StringPiece option(argv[i]);
+ const bool log_options = false;
if (log_options) {
LOG(INFO) << "dex2oat: option[" << i << "]=" << argv[i];
}
- runtime_args.push_back(argv[i]);
- } else if (option == "--dump-timing") {
- dump_timing = true;
- } else if (option == "--dump-passes") {
- dump_passes = true;
- } else if (option == "--dump-stats") {
- dump_stats = true;
- } else if (option == "--include-debug-symbols" || option == "--no-strip-symbols") {
- include_debug_symbols = true;
- } else if (option == "--no-include-debug-symbols" || option == "--strip-symbols") {
- include_debug_symbols = false;
- generate_gdb_information = false; // Depends on debug symbols, see above.
- } else if (option.starts_with("--profile-file=")) {
- profile_file = option.substr(strlen("--profile-file=")).data();
- VLOG(compiler) << "dex2oat: profile file is " << profile_file;
- } else if (option == "--no-profile-file") {
- // No profile
- } else if (option.starts_with("--top-k-profile-threshold=")) {
- ParseDouble(option.data(), '=', 0.0, 100.0, &top_k_profile_threshold);
- } else if (option == "--print-pass-names") {
- PassDriverMEOpts::PrintPassNames();
- } else if (option.starts_with("--disable-passes=")) {
- std::string disable_passes = option.substr(strlen("--disable-passes=")).data();
- PassDriverMEOpts::CreateDefaultPassList(disable_passes);
- } else if (option.starts_with("--print-passes=")) {
- std::string print_passes = option.substr(strlen("--print-passes=")).data();
- PassDriverMEOpts::SetPrintPassList(print_passes);
- } else if (option == "--print-all-passes") {
- PassDriverMEOpts::SetPrintAllPasses();
- } else if (option.starts_with("--dump-cfg-passes=")) {
- std::string dump_passes_string = option.substr(strlen("--dump-cfg-passes=")).data();
- PassDriverMEOpts::SetDumpPassList(dump_passes_string);
- } else if (option == "--print-pass-options") {
- print_pass_options = true;
- } else if (option.starts_with("--pass-options=")) {
- std::string options = option.substr(strlen("--pass-options=")).data();
- PassDriverMEOpts::SetOverriddenPassOptions(options);
- } else if (option == "--include-patch-information") {
- include_patch_information = true;
- } else if (option == "--no-include-patch-information") {
- include_patch_information = false;
- } else if (option.starts_with("--verbose-methods=")) {
- // TODO: rather than switch off compiler logging, make all VLOG(compiler) messages conditional
- // on having verbost methods.
- gLogVerbosity.compiler = false;
- Split(option.substr(strlen("--verbose-methods=")).ToString(), ',', &verbose_methods);
- } else {
- Usage("Unknown argument %s", option.data());
- }
- }
-
- if (oat_filename.empty() && oat_fd == -1) {
- Usage("Output must be supplied with either --oat-file or --oat-fd");
- }
-
- if (!oat_filename.empty() && oat_fd != -1) {
- Usage("--oat-file should not be used with --oat-fd");
- }
-
- if (!oat_symbols.empty() && oat_fd != -1) {
- Usage("--oat-symbols should not be used with --oat-fd");
- }
-
- if (!oat_symbols.empty() && is_host) {
- Usage("--oat-symbols should not be used with --host");
- }
-
- if (oat_fd != -1 && !image_filename.empty()) {
- Usage("--oat-fd should not be used with --image");
- }
-
- if (android_root.empty()) {
- const char* android_root_env_var = getenv("ANDROID_ROOT");
- if (android_root_env_var == nullptr) {
- Usage("--android-root unspecified and ANDROID_ROOT not set");
- }
- android_root += android_root_env_var;
- }
-
- bool image = (!image_filename.empty());
- if (!image && boot_image_filename.empty()) {
- boot_image_filename += android_root;
- boot_image_filename += "/framework/boot.art";
- }
- std::string boot_image_option;
- if (!boot_image_filename.empty()) {
- boot_image_option += "-Ximage:";
- boot_image_option += boot_image_filename;
- }
-
- if (image_classes_filename != nullptr && !image) {
- Usage("--image-classes should only be used with --image");
- }
-
- if (image_classes_filename != nullptr && !boot_image_option.empty()) {
- Usage("--image-classes should not be used with --boot-image");
- }
-
- if (image_classes_zip_filename != nullptr && image_classes_filename == nullptr) {
- Usage("--image-classes-zip should be used with --image-classes");
- }
-
- if (dex_filenames.empty() && zip_fd == -1) {
- Usage("Input must be supplied with either --dex-file or --zip-fd");
- }
-
- if (!dex_filenames.empty() && zip_fd != -1) {
- Usage("--dex-file should not be used with --zip-fd");
- }
-
- if (!dex_filenames.empty() && !zip_location.empty()) {
- Usage("--dex-file should not be used with --zip-location");
- }
-
- if (dex_locations.empty()) {
- for (size_t i = 0; i < dex_filenames.size(); i++) {
- dex_locations.push_back(dex_filenames[i]);
- }
- } else if (dex_locations.size() != dex_filenames.size()) {
- Usage("--dex-location arguments do not match --dex-file arguments");
- }
-
- if (zip_fd != -1 && zip_location.empty()) {
- Usage("--zip-location should be supplied with --zip-fd");
- }
-
- if (boot_image_option.empty()) {
- if (image_base == 0) {
- Usage("Non-zero --base not specified");
- }
- }
-
- std::string oat_stripped(oat_filename);
- std::string oat_unstripped;
- if (!oat_symbols.empty()) {
- oat_unstripped += oat_symbols;
- } else {
- oat_unstripped += oat_filename;
- }
-
- // If no instruction set feature was given, use the default one for the target
- // instruction set.
- if (instruction_set_features->GetInstructionSet() == kNone) {
- instruction_set_features.reset(
- InstructionSetFeatures::FromFeatureString(instruction_set, "default", &error_msg));
- }
-
- if (compiler_filter_string == nullptr) {
- if (instruction_set == kMips64) {
- // TODO: fix compiler for Mips64.
- compiler_filter_string = "interpret-only";
- } else if (image) {
- compiler_filter_string = "speed";
- } 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, "verify-none") == 0) {
- compiler_filter = CompilerOptions::kVerifyNone;
- } else 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 if (strcmp(compiler_filter_string, "time") == 0) {
- compiler_filter = CompilerOptions::kTime;
- } else {
- Usage("Unknown --compiler-filter value %s", compiler_filter_string);
- }
-
- // Set the compilation target's implicit checks options.
- switch (instruction_set) {
- case kArm:
- case kThumb2:
- case kArm64:
- case kX86:
- case kX86_64:
- implicit_null_checks = true;
- implicit_so_checks = true;
- break;
-
- default:
- // Defaults are correct.
- break;
- }
-
- if (print_pass_options) {
- PassDriverMEOpts::PrintPassOptions();
- }
-
- std::unique_ptr<CompilerOptions> compiler_options(
- new CompilerOptions(compiler_filter,
- huge_method_threshold,
- large_method_threshold,
- small_method_threshold,
- tiny_method_threshold,
- num_dex_methods_threshold,
- generate_gdb_information,
- include_patch_information,
- top_k_profile_threshold,
- include_debug_symbols,
- implicit_null_checks,
- implicit_so_checks,
- implicit_suspend_checks,
- compile_pic,
-#ifdef ART_SEA_IR_MODE
- true,
-#endif
- verbose_methods.empty() ? nullptr : &verbose_methods));
-
- // Done with usage checks, enable watchdog if requested
- WatchDog watch_dog(watch_dog_enabled);
-
- // Check early that the result of compilation can be written
- std::unique_ptr<File> oat_file;
- bool create_file = !oat_unstripped.empty(); // as opposed to using open file descriptor
- if (create_file) {
- oat_file.reset(OS::CreateEmptyFile(oat_unstripped.c_str()));
- if (oat_location.empty()) {
- oat_location = oat_filename;
- }
- } else {
- oat_file.reset(new File(oat_fd, oat_location));
- oat_file->DisableAutoClose();
- oat_file->SetLength(0);
- }
- if (oat_file.get() == nullptr) {
- PLOG(ERROR) << "Failed to create oat file: " << oat_location;
- return EXIT_FAILURE;
- }
- if (create_file && fchmod(oat_file->Fd(), 0644) != 0) {
- PLOG(ERROR) << "Failed to make oat file world readable: " << oat_location;
- return EXIT_FAILURE;
- }
-
- timings.StartTiming("dex2oat Setup");
- LOG(INFO) << CommandLine();
-
- RuntimeOptions runtime_options;
- std::vector<const DexFile*> boot_class_path;
- art::MemMap::Init(); // For ZipEntry::ExtractToMemMap.
- if (boot_image_option.empty()) {
- size_t failure_count = OpenDexFiles(dex_filenames, dex_locations, boot_class_path);
- if (failure_count > 0) {
- LOG(ERROR) << "Failed to open some dex files: " << failure_count;
- return EXIT_FAILURE;
- }
- runtime_options.push_back(std::make_pair("bootclasspath", &boot_class_path));
- } else {
- runtime_options.push_back(std::make_pair(boot_image_option.c_str(), nullptr));
- }
- for (size_t i = 0; i < runtime_args.size(); i++) {
- runtime_options.push_back(std::make_pair(runtime_args[i], nullptr));
- }
-
- std::unique_ptr<VerificationResults> verification_results(new VerificationResults(
- compiler_options.get()));
- DexFileToMethodInlinerMap method_inliner_map;
- QuickCompilerCallbacks callbacks(verification_results.get(), &method_inliner_map);
- runtime_options.push_back(std::make_pair("compilercallbacks", &callbacks));
- runtime_options.push_back(
- std::make_pair("imageinstructionset", GetInstructionSetString(instruction_set)));
-
- Dex2Oat* p_dex2oat;
- if (!Dex2Oat::Create(&p_dex2oat,
- runtime_options,
- *compiler_options,
- compiler_kind,
- instruction_set,
- instruction_set_features.get(),
- verification_results.get(),
- &method_inliner_map,
- thread_count)) {
- LOG(ERROR) << "Failed to create dex2oat";
- return EXIT_FAILURE;
- }
- std::unique_ptr<Dex2Oat> dex2oat(p_dex2oat);
-
- // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start,
- // give it away now so that we don't starve GC.
- Thread* self = Thread::Current();
- self->TransitionFromRunnableToSuspended(kNative);
- // If we're doing the image, override the compiler filter to force full compilation. Must be
- // done ahead of WellKnownClasses::Init that causes verification. Note: doesn't force
- // compilation of class initializers.
- // Whilst we're in native take the opportunity to initialize well known classes.
- WellKnownClasses::Init(self->GetJniEnv());
-
- // If --image-classes was specified, calculate the full list of classes to include in the image
- std::unique_ptr<std::set<std::string>> image_classes(nullptr);
- if (image_classes_filename != nullptr) {
- if (image_classes_zip_filename != nullptr) {
- image_classes.reset(dex2oat->ReadImageClassesFromZip(image_classes_zip_filename,
- image_classes_filename,
- &error_msg));
- } else {
- image_classes.reset(dex2oat->ReadImageClassesFromFile(image_classes_filename));
- }
- if (image_classes.get() == nullptr) {
- LOG(ERROR) << "Failed to create list of image classes from '" << image_classes_filename <<
- "': " << error_msg;
- return EXIT_FAILURE;
- }
- } else if (image) {
- image_classes.reset(new std::set<std::string>);
- }
-
- std::vector<const DexFile*> dex_files;
- if (boot_image_option.empty()) {
- dex_files = Runtime::Current()->GetClassLinker()->GetBootClassPath();
- } else {
- if (dex_filenames.empty()) {
- ATRACE_BEGIN("Opening zip archive from file descriptor");
- std::unique_ptr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(zip_fd, zip_location.c_str(),
- &error_msg));
- if (zip_archive.get() == nullptr) {
- LOG(ERROR) << "Failed to open zip from file descriptor for '" << zip_location << "': "
- << error_msg;
- return EXIT_FAILURE;
+ if (option.starts_with("--dex-file=")) {
+ dex_filenames_.push_back(option.substr(strlen("--dex-file=")).data());
+ } else if (option.starts_with("--dex-location=")) {
+ dex_locations_.push_back(option.substr(strlen("--dex-location=")).data());
+ } else if (option.starts_with("--zip-fd=")) {
+ const char* zip_fd_str = option.substr(strlen("--zip-fd=")).data();
+ 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=")) {
+ oat_filename_ = option.substr(strlen("--oat-file=")).data();
+ } else if (option.starts_with("--oat-symbols=")) {
+ oat_symbols = option.substr(strlen("--oat-symbols=")).data();
+ } else if (option.starts_with("--oat-fd=")) {
+ const char* oat_fd_str = option.substr(strlen("--oat-fd=")).data();
+ 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") {
+ watch_dog_enabled = false;
+ } else if (option == "--gen-gdb-info") {
+ generate_gdb_information = true;
+ // Debug symbols are needed for gdb information.
+ include_debug_symbols = true;
+ } else if (option == "--no-gen-gdb-info") {
+ generate_gdb_information = false;
+ } else if (option.starts_with("-j")) {
+ const char* thread_count_str = option.substr(strlen("-j")).data();
+ if (!ParseUint(thread_count_str, &thread_count_)) {
+ Usage("Failed to parse -j argument '%s' as an integer", thread_count_str);
+ }
+ } else if (option.starts_with("--oat-location=")) {
+ oat_location_ = option.substr(strlen("--oat-location=")).data();
+ } else if (option.starts_with("--bitcode=")) {
+ bitcode_filename_ = option.substr(strlen("--bitcode=")).data();
+ } else if (option.starts_with("--image=")) {
+ image_filename_ = option.substr(strlen("--image=")).data();
+ } else if (option.starts_with("--image-classes=")) {
+ image_classes_filename_ = option.substr(strlen("--image-classes=")).data();
+ } else if (option.starts_with("--image-classes-zip=")) {
+ image_classes_zip_filename_ = option.substr(strlen("--image-classes-zip=")).data();
+ } else if (option.starts_with("--base=")) {
+ const char* image_base_str = option.substr(strlen("--base=")).data();
+ char* end;
+ image_base_ = strtoul(image_base_str, &end, 16);
+ if (end == image_base_str || *end != '\0') {
+ Usage("Failed to parse hexadecimal value for option %s", option.data());
+ }
+ } else if (option.starts_with("--boot-image=")) {
+ boot_image_filename = option.substr(strlen("--boot-image=")).data();
+ } else if (option.starts_with("--android-root=")) {
+ android_root_ = option.substr(strlen("--android-root=")).data();
+ } else if (option.starts_with("--instruction-set=")) {
+ StringPiece instruction_set_str = option.substr(strlen("--instruction-set=")).data();
+ // StringPiece is not necessarily zero-terminated, so need to make a copy and ensure it.
+ std::unique_ptr<char> buf(new char[instruction_set_str.length() + 1]);
+ strncpy(buf.get(), instruction_set_str.data(), instruction_set_str.length());
+ buf.get()[instruction_set_str.length()] = 0;
+ instruction_set_ = GetInstructionSetFromString(buf.get());
+ // arm actually means thumb2.
+ if (instruction_set_ == InstructionSet::kArm) {
+ instruction_set_ = InstructionSet::kThumb2;
+ }
+ } else if (option.starts_with("--instruction-set-variant=")) {
+ StringPiece str = option.substr(strlen("--instruction-set-variant=")).data();
+ instruction_set_features_.reset(
+ InstructionSetFeatures::FromVariant(instruction_set_, str.as_string(), &error_msg));
+ if (instruction_set_features_.get() == nullptr) {
+ Usage("%s", error_msg.c_str());
+ }
+ } else if (option.starts_with("--instruction-set-features=")) {
+ StringPiece str = option.substr(strlen("--instruction-set-features=")).data();
+ instruction_set_features_.reset(
+ InstructionSetFeatures::FromFeatureString(instruction_set_, str.as_string(),
+ &error_msg));
+ if (instruction_set_features_.get() == nullptr) {
+ Usage("%s", error_msg.c_str());
+ }
+ } else if (option.starts_with("--compiler-backend=")) {
+ StringPiece backend_str = option.substr(strlen("--compiler-backend=")).data();
+ if (backend_str == "Quick") {
+ compiler_kind_ = Compiler::kQuick;
+ } else if (backend_str == "Optimizing") {
+ compiler_kind_ = Compiler::kOptimizing;
+ } else if (backend_str == "Portable") {
+ compiler_kind_ = Compiler::kPortable;
+ } else {
+ Usage("Unknown compiler backend: %s", backend_str.data());
+ }
+ } else if (option.starts_with("--compiler-filter=")) {
+ compiler_filter_string = option.substr(strlen("--compiler-filter=")).data();
+ } else if (option == "--compile-pic") {
+ compile_pic = true;
+ } 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") {
+ if (++i >= argc) {
+ Usage("Missing required argument for --runtime-arg");
+ }
+ if (log_options) {
+ LOG(INFO) << "dex2oat: option[" << i << "]=" << argv[i];
+ }
+ runtime_args_.push_back(argv[i]);
+ } else if (option == "--dump-timing") {
+ dump_timing_ = true;
+ } else if (option == "--dump-passes") {
+ dump_passes_ = true;
+ } else if (option == "--dump-stats") {
+ dump_stats_ = true;
+ } else if (option == "--include-debug-symbols" || option == "--no-strip-symbols") {
+ include_debug_symbols = true;
+ } else if (option == "--no-include-debug-symbols" || option == "--strip-symbols") {
+ include_debug_symbols = false;
+ generate_gdb_information = false; // Depends on debug symbols, see above.
+ } else if (option.starts_with("--profile-file=")) {
+ profile_file_ = option.substr(strlen("--profile-file=")).data();
+ VLOG(compiler) << "dex2oat: profile file is " << profile_file_;
+ } else if (option == "--no-profile-file") {
+ // No profile
+ } else if (option.starts_with("--top-k-profile-threshold=")) {
+ ParseDouble(option.data(), '=', 0.0, 100.0, &top_k_profile_threshold);
+ } else if (option == "--print-pass-names") {
+ PassDriverMEOpts::PrintPassNames();
+ } else if (option.starts_with("--disable-passes=")) {
+ std::string disable_passes = option.substr(strlen("--disable-passes=")).data();
+ PassDriverMEOpts::CreateDefaultPassList(disable_passes);
+ } else if (option.starts_with("--print-passes=")) {
+ std::string print_passes = option.substr(strlen("--print-passes=")).data();
+ PassDriverMEOpts::SetPrintPassList(print_passes);
+ } else if (option == "--print-all-passes") {
+ PassDriverMEOpts::SetPrintAllPasses();
+ } else if (option.starts_with("--dump-cfg-passes=")) {
+ std::string dump_passes_string = option.substr(strlen("--dump-cfg-passes=")).data();
+ PassDriverMEOpts::SetDumpPassList(dump_passes_string);
+ } else if (option == "--print-pass-options") {
+ print_pass_options = true;
+ } else if (option.starts_with("--pass-options=")) {
+ std::string options = option.substr(strlen("--pass-options=")).data();
+ PassDriverMEOpts::SetOverriddenPassOptions(options);
+ } else if (option == "--include-patch-information") {
+ include_patch_information = true;
+ } else if (option == "--no-include-patch-information") {
+ include_patch_information = false;
+ } else if (option.starts_with("--verbose-methods=")) {
+ // TODO: rather than switch off compiler logging, make all VLOG(compiler) messages conditional
+ // on having verbost methods.
+ gLogVerbosity.compiler = false;
+ Split(option.substr(strlen("--verbose-methods=")).ToString(), ',', &verbose_methods_);
+ } else {
+ Usage("Unknown argument %s", option.data());
}
- if (!DexFile::OpenFromZip(*zip_archive.get(), zip_location, &error_msg, &dex_files)) {
- LOG(ERROR) << "Failed to open dex from file descriptor for zip file '" << zip_location
- << "': " << error_msg;
- return EXIT_FAILURE;
+ }
+
+ if (oat_filename_.empty() && oat_fd_ == -1) {
+ Usage("Output must be supplied with either --oat-file or --oat-fd");
+ }
+
+ if (!oat_filename_.empty() && oat_fd_ != -1) {
+ Usage("--oat-file should not be used with --oat-fd");
+ }
+
+ if (!oat_symbols.empty() && oat_fd_ != -1) {
+ Usage("--oat-symbols should not be used with --oat-fd");
+ }
+
+ if (!oat_symbols.empty() && is_host_) {
+ Usage("--oat-symbols should not be used with --host");
+ }
+
+ if (oat_fd_ != -1 && !image_filename_.empty()) {
+ Usage("--oat-fd should not be used with --image");
+ }
+
+ if (android_root_.empty()) {
+ const char* android_root_env_var = getenv("ANDROID_ROOT");
+ if (android_root_env_var == nullptr) {
+ Usage("--android-root unspecified and ANDROID_ROOT not set");
}
- ATRACE_END();
+ android_root_ += android_root_env_var;
+ }
+
+ image_ = (!image_filename_.empty());
+ if (!image_ && boot_image_filename.empty()) {
+ boot_image_filename += android_root_;
+ boot_image_filename += "/framework/boot.art";
+ }
+ if (!boot_image_filename.empty()) {
+ boot_image_option_ += "-Ximage:";
+ boot_image_option_ += boot_image_filename;
+ }
+
+ if (image_classes_filename_ != nullptr && !image_) {
+ Usage("--image-classes should only be used with --image");
+ }
+
+ if (image_classes_filename_ != nullptr && !boot_image_option_.empty()) {
+ Usage("--image-classes should not be used with --boot-image");
+ }
+
+ if (image_classes_zip_filename_ != nullptr && image_classes_filename_ == nullptr) {
+ Usage("--image-classes-zip should be used with --image-classes");
+ }
+
+ if (dex_filenames_.empty() && zip_fd_ == -1) {
+ Usage("Input must be supplied with either --dex-file or --zip-fd");
+ }
+
+ if (!dex_filenames_.empty() && zip_fd_ != -1) {
+ Usage("--dex-file should not be used with --zip-fd");
+ }
+
+ if (!dex_filenames_.empty() && !zip_location_.empty()) {
+ Usage("--dex-file should not be used with --zip-location");
+ }
+
+ if (dex_locations_.empty()) {
+ for (const char* dex_file_name : dex_filenames_) {
+ dex_locations_.push_back(dex_file_name);
+ }
+ } else if (dex_locations_.size() != dex_filenames_.size()) {
+ Usage("--dex-location arguments do not match --dex-file arguments");
+ }
+
+ if (zip_fd_ != -1 && zip_location_.empty()) {
+ Usage("--zip-location should be supplied with --zip-fd");
+ }
+
+ if (boot_image_option_.empty()) {
+ if (image_base_ == 0) {
+ Usage("Non-zero --base not specified");
+ }
+ }
+
+ oat_stripped_ = oat_filename_;
+ if (!oat_symbols.empty()) {
+ oat_unstripped_ = oat_symbols;
} else {
- size_t failure_count = OpenDexFiles(dex_filenames, dex_locations, dex_files);
+ oat_unstripped_ = oat_filename_;
+ }
+
+ // If no instruction set feature was given, use the default one for the target
+ // instruction set.
+ if (instruction_set_features_.get() == nullptr) {
+ instruction_set_features_.reset(
+ InstructionSetFeatures::FromFeatureString(instruction_set_, "default", &error_msg));
+ }
+
+ if (instruction_set_ == kRuntimeISA) {
+ std::unique_ptr<const InstructionSetFeatures> runtime_features(
+ InstructionSetFeatures::FromCppDefines());
+ if (!instruction_set_features_->Equals(runtime_features.get())) {
+ LOG(WARNING) << "Mismatch between dex2oat instruction set features ("
+ << *instruction_set_features_ << ") and those of dex2oat executable ("
+ << *runtime_features <<") for the command line:\n"
+ << CommandLine();
+ }
+ }
+
+ if (compiler_filter_string == nullptr) {
+ if (instruction_set_ == kMips64) {
+ // TODO: fix compiler for Mips64.
+ compiler_filter_string = "interpret-only";
+ } else if (image_) {
+ compiler_filter_string = "speed";
+ } else {
+ // TODO: Migrate SMALL mode to command line option.
+ #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, "verify-none") == 0) {
+ compiler_filter = CompilerOptions::kVerifyNone;
+ } else 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 if (strcmp(compiler_filter_string, "time") == 0) {
+ compiler_filter = CompilerOptions::kTime;
+ } else {
+ Usage("Unknown --compiler-filter value %s", compiler_filter_string);
+ }
+
+ // Checks are all explicit until we know the architecture.
+ bool implicit_null_checks = false;
+ bool implicit_so_checks = false;
+ bool implicit_suspend_checks = false;
+ // Set the compilation target's implicit checks options.
+ switch (instruction_set_) {
+ case kArm:
+ case kThumb2:
+ case kArm64:
+ case kX86:
+ case kX86_64:
+ implicit_null_checks = true;
+ implicit_so_checks = true;
+ break;
+
+ default:
+ // Defaults are correct.
+ break;
+ }
+
+ if (print_pass_options) {
+ PassDriverMEOpts::PrintPassOptions();
+ }
+
+ compiler_options_.reset(new CompilerOptions(compiler_filter,
+ huge_method_threshold,
+ large_method_threshold,
+ small_method_threshold,
+ tiny_method_threshold,
+ num_dex_methods_threshold,
+ generate_gdb_information,
+ include_patch_information,
+ top_k_profile_threshold,
+ include_debug_symbols,
+ implicit_null_checks,
+ implicit_so_checks,
+ implicit_suspend_checks,
+ compile_pic,
+ #ifdef ART_SEA_IR_MODE
+ true,
+ #endif
+ verbose_methods_.empty() ?
+ nullptr :
+ &verbose_methods_));
+
+ // Done with usage checks, enable watchdog if requested
+ if (watch_dog_enabled) {
+ watchdog_.reset(new WatchDog(true));
+ }
+
+ // Fill some values into the key-value store for the oat header.
+ key_value_store_.reset(new SafeMap<std::string, std::string>());
+
+ // Insert some compiler things.
+ {
+ std::ostringstream oss;
+ for (int i = 0; i < argc; ++i) {
+ if (i > 0) {
+ oss << ' ';
+ }
+ oss << argv[i];
+ }
+ key_value_store_->Put(OatHeader::kDex2OatCmdLineKey, oss.str());
+ oss.str(""); // Reset.
+ oss << kRuntimeISA;
+ key_value_store_->Put(OatHeader::kDex2OatHostKey, oss.str());
+ key_value_store_->Put(OatHeader::kPicKey, compile_pic ? "true" : "false");
+ }
+ }
+
+ // Check whether the oat output file is writable, and open it for later.
+ bool OpenFile() {
+ bool create_file = !oat_unstripped_.empty(); // as opposed to using open file descriptor
+ if (create_file) {
+ oat_file_.reset(OS::CreateEmptyFile(oat_unstripped_.c_str()));
+ if (oat_location_.empty()) {
+ oat_location_ = oat_filename_;
+ }
+ } else {
+ oat_file_.reset(new File(oat_fd_, oat_location_));
+ oat_file_->DisableAutoClose();
+ oat_file_->SetLength(0);
+ }
+ if (oat_file_.get() == nullptr) {
+ PLOG(ERROR) << "Failed to create oat file: " << oat_location_;
+ return false;
+ }
+ if (create_file && fchmod(oat_file_->Fd(), 0644) != 0) {
+ PLOG(ERROR) << "Failed to make oat file world readable: " << oat_location_;
+ return false;
+ }
+ return true;
+ }
+
+ // Set up the environment for compilation. Includes starting the runtime and loading/opening the
+ // boot class path.
+ bool Setup() {
+ TimingLogger::ScopedTiming t("dex2oat Setup", timings_);
+ RuntimeOptions runtime_options;
+ std::vector<const DexFile*> boot_class_path;
+ art::MemMap::Init(); // For ZipEntry::ExtractToMemMap.
+ if (boot_image_option_.empty()) {
+ size_t failure_count = OpenDexFiles(dex_filenames_, dex_locations_, boot_class_path);
if (failure_count > 0) {
LOG(ERROR) << "Failed to open some dex files: " << failure_count;
- return EXIT_FAILURE;
+ return false;
}
+ runtime_options.push_back(std::make_pair("bootclasspath", &boot_class_path));
+ } else {
+ runtime_options.push_back(std::make_pair(boot_image_option_.c_str(), nullptr));
+ }
+ for (size_t i = 0; i < runtime_args_.size(); i++) {
+ runtime_options.push_back(std::make_pair(runtime_args_[i], nullptr));
}
- const bool kSaveDexInput = false;
- if (kSaveDexInput) {
- for (size_t i = 0; i < dex_files.size(); ++i) {
- const DexFile* dex_file = dex_files[i];
- std::string tmp_file_name(StringPrintf("/data/local/tmp/dex2oat.%d.%zd.dex", getpid(), i));
- std::unique_ptr<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";
- continue;
+ verification_results_.reset(new VerificationResults(compiler_options_.get()));
+ callbacks_.reset(new QuickCompilerCallbacks(verification_results_.get(), &method_inliner_map_));
+ runtime_options.push_back(std::make_pair("compilercallbacks", callbacks_.get()));
+ runtime_options.push_back(
+ std::make_pair("imageinstructionset", GetInstructionSetString(instruction_set_)));
+
+ if (!CreateRuntime(runtime_options)) {
+ return false;
+ }
+
+ // Runtime::Create acquired the mutator_lock_ that is normally given away when we
+ // Runtime::Start, give it away now so that we don't starve GC.
+ Thread* self = Thread::Current();
+ self->TransitionFromRunnableToSuspended(kNative);
+ // If we're doing the image, override the compiler filter to force full compilation. Must be
+ // done ahead of WellKnownClasses::Init that causes verification. Note: doesn't force
+ // compilation of class initializers.
+ // Whilst we're in native take the opportunity to initialize well known classes.
+ WellKnownClasses::Init(self->GetJniEnv());
+
+ // If --image-classes was specified, calculate the full list of classes to include in the image
+ if (image_classes_filename_ != nullptr) {
+ std::string error_msg;
+ if (image_classes_zip_filename_ != nullptr) {
+ image_classes_.reset(ReadImageClassesFromZip(image_classes_zip_filename_,
+ image_classes_filename_,
+ &error_msg));
+ } else {
+ image_classes_.reset(ReadImageClassesFromFile(image_classes_filename_));
+ }
+ if (image_classes_.get() == nullptr) {
+ LOG(ERROR) << "Failed to create list of image classes from '" << image_classes_filename_ <<
+ "': " << error_msg;
+ return false;
+ }
+ } else if (image_) {
+ image_classes_.reset(new std::set<std::string>);
+ }
+
+ if (boot_image_option_.empty()) {
+ dex_files_ = Runtime::Current()->GetClassLinker()->GetBootClassPath();
+ } else {
+ if (dex_filenames_.empty()) {
+ ATRACE_BEGIN("Opening zip archive from file descriptor");
+ std::string error_msg;
+ std::unique_ptr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(zip_fd_,
+ zip_location_.c_str(),
+ &error_msg));
+ if (zip_archive.get() == nullptr) {
+ LOG(ERROR) << "Failed to open zip from file descriptor for '" << zip_location_ << "': "
+ << error_msg;
+ return false;
}
- tmp_file->WriteFully(dex_file->Begin(), dex_file->Size());
- LOG(INFO) << "Wrote input to " << tmp_file_name;
+ if (!DexFile::OpenFromZip(*zip_archive.get(), zip_location_, &error_msg, &dex_files_)) {
+ LOG(ERROR) << "Failed to open dex from file descriptor for zip file '" << zip_location_
+ << "': " << error_msg;
+ return false;
+ }
+ ATRACE_END();
+ } else {
+ size_t failure_count = OpenDexFiles(dex_filenames_, dex_locations_, dex_files_);
+ if (failure_count > 0) {
+ LOG(ERROR) << "Failed to open some dex files: " << failure_count;
+ return false;
+ }
+ }
+
+ constexpr bool kSaveDexInput = false;
+ if (kSaveDexInput) {
+ for (size_t i = 0; i < dex_files_.size(); ++i) {
+ const DexFile* dex_file = dex_files_[i];
+ std::string tmp_file_name(StringPrintf("/data/local/tmp/dex2oat.%d.%zd.dex", getpid(), i));
+ std::unique_ptr<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";
+ continue;
+ }
+ tmp_file->WriteFully(dex_file->Begin(), dex_file->Size());
+ LOG(INFO) << "Wrote input to " << tmp_file_name;
+ }
}
}
- }
- // Ensure opened dex files are writable for dex-to-dex transformations.
- for (const auto& dex_file : dex_files) {
- if (!dex_file->EnableWrite()) {
- PLOG(ERROR) << "Failed to make .dex file writeable '" << dex_file->GetLocation() << "'\n";
- }
- }
-
- /*
- * If we're not in interpret-only or verify-none mode, go ahead and compile small applications.
- * Don't bother to check if we're doing the image.
- */
- if (!image && compiler_options->IsCompilationEnabled() && compiler_kind == Compiler::kQuick) {
- 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 != nullptr);
- num_methods += dex_file->NumMethodIds();
- }
- if (num_methods <= compiler_options->GetNumDexMethodsThreshold()) {
- compiler_options->SetCompilerFilter(CompilerOptions::kSpeed);
- VLOG(compiler) << "Below method threshold, compiling anyways";
- }
- }
-
- // Fill some values into the key-value store for the oat header.
- std::unique_ptr<SafeMap<std::string, std::string> > key_value_store(
- new SafeMap<std::string, std::string>());
-
- // Insert some compiler things.
- {
- std::ostringstream oss;
- for (int i = 0; i < argc; ++i) {
- if (i > 0) {
- oss << ' ';
+ // Ensure opened dex files are writable for dex-to-dex transformations.
+ for (const auto& dex_file : dex_files_) {
+ if (!dex_file->EnableWrite()) {
+ PLOG(ERROR) << "Failed to make .dex file writeable '" << dex_file->GetLocation() << "'\n";
}
- oss << argv[i];
}
- key_value_store->Put(OatHeader::kDex2OatCmdLineKey, oss.str());
- oss.str(""); // Reset.
- oss << kRuntimeISA;
- key_value_store->Put(OatHeader::kDex2OatHostKey, oss.str());
- key_value_store->Put(OatHeader::kPicKey, compile_pic ? "true" : "false");
+
+ /*
+ * If we're not in interpret-only or verify-none mode, go ahead and compile small applications.
+ * Don't bother to check if we're doing the image.
+ */
+ if (!image_ && compiler_options_->IsCompilationEnabled() && compiler_kind_ == Compiler::kQuick) {
+ 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 != nullptr);
+ num_methods += dex_file->NumMethodIds();
+ }
+ if (num_methods <= compiler_options_->GetNumDexMethodsThreshold()) {
+ compiler_options_->SetCompilerFilter(CompilerOptions::kSpeed);
+ VLOG(compiler) << "Below method threshold, compiling anyways";
+ }
+ }
+
+ return true;
}
- dex2oat->Compile(boot_image_option,
- dex_files,
- bitcode_filename,
- image,
- image_classes,
- dump_stats,
- dump_passes,
- &timings,
- &compiler_phases_timings,
- profile_file);
+ // Create and invoke the compiler driver. This will compile all the dex files.
+ void Compile() {
+ TimingLogger::ScopedTiming t("dex2oat Compile", timings_);
+ compiler_phases_timings_.reset(new CumulativeLogger("compilation times"));
- if (image) {
- dex2oat->PrepareImageWriter(image_base);
+ // Handle and ClassLoader creation needs to come after Runtime::Create
+ jobject class_loader = nullptr;
+ Thread* self = Thread::Current();
+ if (!boot_image_option_.empty()) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ std::vector<const DexFile*> class_path_files(dex_files_);
+ OpenClassPathFiles(runtime_->GetClassPathString(), class_path_files);
+ ScopedObjectAccess soa(self);
+ for (size_t i = 0; i < class_path_files.size(); i++) {
+ class_linker->RegisterDexFile(*class_path_files[i]);
+ }
+ soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader);
+ ScopedLocalRef<jobject> class_loader_local(soa.Env(),
+ soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader));
+ class_loader = soa.Env()->NewGlobalRef(class_loader_local.get());
+ Runtime::Current()->SetCompileTimeClassPath(class_loader, class_path_files);
+ }
+
+ driver_.reset(new CompilerDriver(compiler_options_.get(),
+ verification_results_.get(),
+ &method_inliner_map_,
+ compiler_kind_,
+ instruction_set_,
+ instruction_set_features_.get(),
+ image_,
+ image_classes_.release(),
+ thread_count_,
+ dump_stats_,
+ dump_passes_,
+ compiler_phases_timings_.get(),
+ profile_file_));
+
+ driver_->GetCompiler()->SetBitcodeFileName(*driver_, bitcode_filename_);
+
+ driver_->CompileAll(class_loader, dex_files_, timings_);
}
- if (!dex2oat->CreateOatFile(dex_files,
- android_root,
- is_host,
- oat_file.get(),
- &timings,
- key_value_store.get())) {
- LOG(ERROR) << "Failed to create oat file: " << oat_location;
- return EXIT_FAILURE;
- }
-
- VLOG(compiler) << "Oat file written successfully (unstripped): " << oat_location;
-
// Notes on the interleaving of creating the image and oat file to
// ensure the references between the two are correct.
//
@@ -1527,87 +1159,454 @@
//
// Steps 1.-3. are done by the CreateOatFile() above, steps 4.-5.
// are done by the CreateImageFile() below.
- //
- if (image) {
- TimingLogger::ScopedTiming t("dex2oat ImageWriter", &timings);
- bool image_creation_success = dex2oat->CreateImageFile(image_filename,
- oat_unstripped,
- oat_location);
- if (!image_creation_success) {
- return EXIT_FAILURE;
+
+
+ // Write out the generated code part. Calls the OatWriter and ElfBuilder. Also prepares the
+ // ImageWriter, if necessary.
+ bool CreateOatFile() {
+ CHECK(key_value_store_.get() != nullptr);
+
+ TimingLogger::ScopedTiming t("dex2oat Oat", timings_);
+
+ std::unique_ptr<OatWriter> oat_writer;
+ {
+ TimingLogger::ScopedTiming t2("dex2oat OatWriter", timings_);
+ std::string image_file_location;
+ uint32_t image_file_location_oat_checksum = 0;
+ uintptr_t image_file_location_oat_data_begin = 0;
+ int32_t image_patch_delta = 0;
+ if (image_) {
+ PrepareImageWriter(image_base_);
+ } else {
+ TimingLogger::ScopedTiming t3("Loading image checksum", timings_);
+ gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace();
+ image_file_location_oat_checksum = image_space->GetImageHeader().GetOatChecksum();
+ image_file_location_oat_data_begin =
+ reinterpret_cast<uintptr_t>(image_space->GetImageHeader().GetOatDataBegin());
+ image_file_location = image_space->GetImageFilename();
+ image_patch_delta = image_space->GetImageHeader().GetPatchDelta();
+ }
+
+ if (!image_file_location.empty()) {
+ key_value_store_->Put(OatHeader::kImageLocationKey, image_file_location);
+ }
+
+ oat_writer.reset(new OatWriter(dex_files_, image_file_location_oat_checksum,
+ image_file_location_oat_data_begin,
+ image_patch_delta,
+ driver_.get(),
+ image_writer_.get(),
+ timings_,
+ key_value_store_.get()));
}
- VLOG(compiler) << "Image written successfully: " << image_filename;
+
+ if (image_) {
+ // The OatWriter constructor has already updated offsets in methods and we need to
+ // prepare method offsets in the image address space for direct method patching.
+ TimingLogger::ScopedTiming t2("dex2oat Prepare image address space", timings_);
+ if (!image_writer_->PrepareImageAddressSpace()) {
+ LOG(ERROR) << "Failed to prepare image address space.";
+ return false;
+ }
+ }
+
+ {
+ TimingLogger::ScopedTiming t2("dex2oat Write ELF", timings_);
+ if (!driver_->WriteElf(android_root_, is_host_, dex_files_, oat_writer.get(),
+ oat_file_.get())) {
+ LOG(ERROR) << "Failed to write ELF file " << oat_file_->GetPath();
+ return false;
+ }
+ }
+
+ // Flush result to disk.
+ {
+ TimingLogger::ScopedTiming t2("dex2oat Flush ELF", timings_);
+ if (oat_file_->Flush() != 0) {
+ LOG(ERROR) << "Failed to flush ELF file " << oat_file_->GetPath();
+ return false;
+ }
+ }
+
+ VLOG(compiler) << "Oat file written successfully (unstripped): " << oat_location_;
+ return true;
}
- if (is_host) {
- timings.EndTiming();
- if (dump_timing || (dump_slow_timing && timings.GetTotalNs() > MsToNs(1000))) {
- LOG(INFO) << Dumpable<TimingLogger>(timings);
+ // If we are compiling an image, invoke the image creation routine. Else just skip.
+ bool HandleImage() {
+ if (image_) {
+ TimingLogger::ScopedTiming t("dex2oat ImageWriter", timings_);
+ if (!CreateImageFile()) {
+ return false;
+ }
+ VLOG(compiler) << "Image written successfully: " << image_filename_;
}
- if (dump_passes) {
- LOG(INFO) << Dumpable<CumulativeLogger>(compiler_phases_timings);
+ return true;
+ }
+
+ // Strip the oat file, if requested. This first creates a copy from unstripped to stripped, and
+ // then runs the ElfStripper. Currently only relevant for the portable compiler.
+ bool Strip() {
+ // If we don't want to strip in place, copy from unstripped location to stripped location.
+ // We need to strip after image creation because FixupElf needs to use .strtab.
+ if (oat_unstripped_ != oat_stripped_) {
+ TimingLogger::ScopedTiming t("dex2oat OatFile copy", timings_);
+ oat_file_.reset();
+ std::unique_ptr<File> in(OS::OpenFileForReading(oat_unstripped_.c_str()));
+ std::unique_ptr<File> out(OS::CreateEmptyFile(oat_stripped_.c_str()));
+ size_t buffer_size = 8192;
+ std::unique_ptr<uint8_t> buffer(new uint8_t[buffer_size]);
+ while (true) {
+ int bytes_read = TEMP_FAILURE_RETRY(read(in->Fd(), buffer.get(), buffer_size));
+ if (bytes_read <= 0) {
+ break;
+ }
+ bool write_ok = out->WriteFully(buffer.get(), bytes_read);
+ CHECK(write_ok);
+ }
+ oat_file_.reset(out.release());
+ VLOG(compiler) << "Oat file copied successfully (stripped): " << oat_stripped_;
}
+
+ if (kUsePortableCompiler) {
+ // Portable includes debug symbols unconditionally. If we are not supposed to create them,
+ // strip them now. Quick generates debug symbols only when the flag(s) are set.
+ if (!compiler_options_->GetIncludeDebugSymbols()) {
+ TimingLogger::ScopedTiming t("dex2oat ElfStripper", timings_);
+ // Strip unneeded sections for target
+ off_t seek_actual = lseek(oat_file_->Fd(), 0, SEEK_SET);
+ CHECK_EQ(0, seek_actual);
+ std::string error_msg;
+ if (!ElfFile::Strip(oat_file_.get(), &error_msg)) {
+ LOG(ERROR) << "Failed to strip elf file: " << error_msg;
+ return false;
+ }
+
+ // We wrote the oat file successfully, and want to keep it.
+ VLOG(compiler) << "Oat file written successfully (stripped): " << oat_location_;
+ } else {
+ VLOG(compiler) << "Oat file written successfully without stripping: " << oat_location_;
+ }
+ }
+
+ return true;
+ }
+
+ void DumpTiming() {
+ if (dump_timing_ || (dump_slow_timing_ && timings_->GetTotalNs() > MsToNs(1000))) {
+ LOG(INFO) << Dumpable<TimingLogger>(*timings_);
+ }
+ if (dump_passes_) {
+ LOG(INFO) << Dumpable<CumulativeLogger>(*driver_->GetTimingsLogger());
+ }
+ }
+
+ CompilerOptions* GetCompilerOptions() const {
+ return compiler_options_.get();
+ }
+
+ bool IsHost() const {
+ return is_host_;
+ }
+
+ private:
+ static size_t OpenDexFiles(const std::vector<const char*>& dex_filenames,
+ const std::vector<const char*>& dex_locations,
+ std::vector<const DexFile*>& dex_files) {
+ size_t failure_count = 0;
+ for (size_t i = 0; i < dex_filenames.size(); i++) {
+ const char* dex_filename = dex_filenames[i];
+ const char* dex_location = dex_locations[i];
+ ATRACE_BEGIN(StringPrintf("Opening dex file '%s'", dex_filenames[i]).c_str());
+ std::string error_msg;
+ if (!OS::FileExists(dex_filename)) {
+ LOG(WARNING) << "Skipping non-existent dex file '" << dex_filename << "'";
+ continue;
+ }
+ if (!DexFile::Open(dex_filename, dex_location, &error_msg, &dex_files)) {
+ LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "': " << error_msg;
+ ++failure_count;
+ }
+ ATRACE_END();
+ }
+ return failure_count;
+ }
+
+ // Returns true if dex_files has a dex with the named location.
+ static bool DexFilesContains(const std::vector<const DexFile*>& dex_files,
+ const std::string& location) {
+ for (size_t i = 0; i < dex_files.size(); ++i) {
+ if (dex_files[i]->GetLocation() == location) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Appends to dex_files any elements of class_path that it doesn't already
+ // contain. This will open those dex files as necessary.
+ static void OpenClassPathFiles(const std::string& class_path,
+ std::vector<const DexFile*>& dex_files) {
+ std::vector<std::string> parsed;
+ Split(class_path, ':', &parsed);
+ // Take Locks::mutator_lock_ so that lock ordering on the ClassLinker::dex_lock_ is maintained.
+ ScopedObjectAccess soa(Thread::Current());
+ for (size_t i = 0; i < parsed.size(); ++i) {
+ if (DexFilesContains(dex_files, parsed[i])) {
+ continue;
+ }
+ std::string error_msg;
+ if (!DexFile::Open(parsed[i].c_str(), parsed[i].c_str(), &error_msg, &dex_files)) {
+ LOG(WARNING) << "Failed to open dex file '" << parsed[i] << "': " << error_msg;
+ }
+ }
+ }
+
+ // Create a runtime necessary for compilation.
+ bool CreateRuntime(const RuntimeOptions& runtime_options)
+ SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) {
+ if (!Runtime::Create(runtime_options, false)) {
+ LOG(ERROR) << "Failed to create runtime";
+ return false;
+ }
+ Runtime* runtime = Runtime::Current();
+ runtime->SetInstructionSet(instruction_set_);
+ for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
+ Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i);
+ if (!runtime->HasCalleeSaveMethod(type)) {
+ runtime->SetCalleeSaveMethod(runtime->CreateCalleeSaveMethod(), type);
+ }
+ }
+ runtime->GetClassLinker()->FixupDexCaches(runtime->GetResolutionMethod());
+ runtime->GetClassLinker()->RunRootClinits();
+ runtime_ = runtime;
+ return true;
+ }
+
+ void PrepareImageWriter(uintptr_t image_base) {
+ image_writer_.reset(new ImageWriter(*driver_, image_base, compiler_options_->GetCompilePic()));
+ }
+
+ // Let the ImageWriter write the image file. If we do not compile PIC, also fix up the oat file.
+ bool CreateImageFile()
+ LOCKS_EXCLUDED(Locks::mutator_lock_) {
+ CHECK(image_writer_ != nullptr);
+ if (!image_writer_->Write(image_filename_, oat_unstripped_, oat_location_)) {
+ LOG(ERROR) << "Failed to create image file " << image_filename_;
+ return false;
+ }
+ uintptr_t oat_data_begin = image_writer_->GetOatDataBegin();
+
+ // Destroy ImageWriter before doing FixupElf.
+ image_writer_.reset();
+
+ std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_unstripped_.c_str()));
+ if (oat_file.get() == nullptr) {
+ PLOG(ERROR) << "Failed to open ELF file: " << oat_unstripped_;
+ return false;
+ }
+
+ // Do not fix up the ELF file if we are --compile-pic
+ if (!compiler_options_->GetCompilePic()) {
+ if (!ElfWriter::Fixup(oat_file.get(), oat_data_begin)) {
+ LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath();
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ // Reads the class names (java.lang.Object) and returns a set of descriptors (Ljava/lang/Object;)
+ static std::set<std::string>* ReadImageClassesFromFile(const char* image_classes_filename) {
+ std::unique_ptr<std::ifstream> image_classes_file(new std::ifstream(image_classes_filename,
+ std::ifstream::in));
+ if (image_classes_file.get() == nullptr) {
+ LOG(ERROR) << "Failed to open image classes file " << image_classes_filename;
+ return nullptr;
+ }
+ std::unique_ptr<std::set<std::string>> result(ReadImageClasses(*image_classes_file));
+ image_classes_file->close();
+ return result.release();
+ }
+
+ static std::set<std::string>* ReadImageClasses(std::istream& image_classes_stream) {
+ std::unique_ptr<std::set<std::string>> image_classes(new std::set<std::string>);
+ while (image_classes_stream.good()) {
+ std::string dot;
+ std::getline(image_classes_stream, dot);
+ if (StartsWith(dot, "#") || dot.empty()) {
+ continue;
+ }
+ std::string descriptor(DotToDescriptor(dot.c_str()));
+ image_classes->insert(descriptor);
+ }
+ return image_classes.release();
+ }
+
+ // Reads the class names (java.lang.Object) and returns a set of descriptors (Ljava/lang/Object;)
+ static std::set<std::string>* ReadImageClassesFromZip(const char* zip_filename,
+ const char* image_classes_filename,
+ std::string* error_msg) {
+ std::unique_ptr<ZipArchive> zip_archive(ZipArchive::Open(zip_filename, error_msg));
+ if (zip_archive.get() == nullptr) {
+ return nullptr;
+ }
+ std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(image_classes_filename, error_msg));
+ if (zip_entry.get() == nullptr) {
+ *error_msg = StringPrintf("Failed to find '%s' within '%s': %s", image_classes_filename,
+ zip_filename, error_msg->c_str());
+ return nullptr;
+ }
+ std::unique_ptr<MemMap> image_classes_file(zip_entry->ExtractToMemMap(zip_filename,
+ image_classes_filename,
+ error_msg));
+ if (image_classes_file.get() == nullptr) {
+ *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", image_classes_filename,
+ zip_filename, error_msg->c_str());
+ return nullptr;
+ }
+ const std::string image_classes_string(reinterpret_cast<char*>(image_classes_file->Begin()),
+ image_classes_file->Size());
+ std::istringstream image_classes_stream(image_classes_string);
+ return ReadImageClasses(image_classes_stream);
+ }
+
+ void LogCompletionTime() const {
+ LOG(INFO) << "dex2oat took " << PrettyDuration(NanoTime() - start_ns_)
+ << " (threads: " << thread_count_ << ")";
+ }
+
+ std::unique_ptr<CompilerOptions> compiler_options_;
+ Compiler::Kind compiler_kind_;
+
+ InstructionSet instruction_set_;
+ std::unique_ptr<const InstructionSetFeatures> instruction_set_features_;
+
+ std::unique_ptr<SafeMap<std::string, std::string> > key_value_store_;
+
+ std::unique_ptr<VerificationResults> verification_results_;
+ DexFileToMethodInlinerMap method_inliner_map_;
+ std::unique_ptr<QuickCompilerCallbacks> callbacks_;
+
+ // Not a unique_ptr as we want to just exit on non-debug builds, not bringing the runtime down
+ // in an orderly fashion. The destructor takes care of deleting this.
+ Runtime* runtime_;
+
+ size_t thread_count_;
+ uint64_t start_ns_;
+ std::unique_ptr<WatchDog> watchdog_;
+ std::unique_ptr<File> oat_file_;
+ std::string oat_stripped_;
+ std::string oat_unstripped_;
+ std::string oat_location_;
+ std::string oat_filename_;
+ int oat_fd_;
+ std::string bitcode_filename_;
+ std::vector<const char*> dex_filenames_;
+ std::vector<const char*> dex_locations_;
+ int zip_fd_;
+ std::string zip_location_;
+ std::string boot_image_option_;
+ std::vector<const char*> runtime_args_;
+ std::string image_filename_;
+ uintptr_t image_base_;
+ const char* image_classes_zip_filename_;
+ const char* image_classes_filename_;
+ std::unique_ptr<std::set<std::string>> image_classes_;
+ bool image_;
+ std::unique_ptr<ImageWriter> image_writer_;
+ bool is_host_;
+ std::string android_root_;
+ std::vector<const DexFile*> dex_files_;
+ std::unique_ptr<CompilerDriver> driver_;
+ std::vector<std::string> verbose_methods_;
+ bool dump_stats_;
+ bool dump_passes_;
+ bool dump_timing_;
+ bool dump_slow_timing_;
+ std::string profile_file_; // Profile file to use
+ TimingLogger* timings_;
+ std::unique_ptr<CumulativeLogger> compiler_phases_timings_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Dex2Oat);
+};
+
+const unsigned int WatchDog::kWatchDogWarningSeconds;
+const unsigned int WatchDog::kWatchDogTimeoutSeconds;
+
+static void b13564922() {
+#if defined(__linux__) && defined(__arm__)
+ int major, minor;
+ struct utsname uts;
+ if (uname(&uts) != -1 &&
+ sscanf(uts.release, "%d.%d", &major, &minor) == 2 &&
+ ((major < 3) || ((major == 3) && (minor < 4)))) {
+ // Kernels before 3.4 don't handle the ASLR well and we can run out of address
+ // space (http://b/13564922). Work around the issue by inhibiting further mmap() randomization.
+ int old_personality = personality(0xffffffff);
+ if ((old_personality & ADDR_NO_RANDOMIZE) == 0) {
+ int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE);
+ if (new_personality == -1) {
+ LOG(WARNING) << "personality(. | ADDR_NO_RANDOMIZE) failed.";
+ }
+ }
+ }
+#endif
+}
+
+static int dex2oat(int argc, char** argv) {
+ b13564922();
+
+ TimingLogger timings("compiler", false, false);
+
+ Dex2Oat dex2oat(&timings);
+
+ // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError.
+ dex2oat.ParseArgs(argc, argv);
+
+ // Check early that the result of compilation can be written
+ if (!dex2oat.OpenFile()) {
+ return EXIT_FAILURE;
+ }
+
+ LOG(INFO) << CommandLine();
+
+ if (!dex2oat.Setup()) {
+ return EXIT_FAILURE;
+ }
+
+ dex2oat.Compile();
+
+ if (!dex2oat.CreateOatFile()) {
+ return EXIT_FAILURE;
+ }
+
+ if (!dex2oat.HandleImage()) {
+ return EXIT_FAILURE;
+ }
+
+ if (dex2oat.IsHost()) {
+ dex2oat.DumpTiming();
return EXIT_SUCCESS;
}
- // If we don't want to strip in place, copy from unstripped location to stripped location.
- // We need to strip after image creation because FixupElf needs to use .strtab.
- if (oat_unstripped != oat_stripped) {
- TimingLogger::ScopedTiming t("dex2oat OatFile copy", &timings);
- oat_file.reset();
- std::unique_ptr<File> in(OS::OpenFileForReading(oat_unstripped.c_str()));
- std::unique_ptr<File> out(OS::CreateEmptyFile(oat_stripped.c_str()));
- size_t buffer_size = 8192;
- std::unique_ptr<uint8_t> buffer(new uint8_t[buffer_size]);
- while (true) {
- int bytes_read = TEMP_FAILURE_RETRY(read(in->Fd(), buffer.get(), buffer_size));
- if (bytes_read <= 0) {
- break;
- }
- bool write_ok = out->WriteFully(buffer.get(), bytes_read);
- CHECK(write_ok);
- }
- oat_file.reset(out.release());
- VLOG(compiler) << "Oat file copied successfully (stripped): " << oat_stripped;
+ if (!dex2oat.Strip()) {
+ return EXIT_FAILURE;
}
-#if ART_USE_PORTABLE_COMPILER // We currently only generate symbols on Portable
- if (!compiler_options.GetIncludeDebugSymbols()) {
- timings.NewSplit("dex2oat ElfStripper");
- // Strip unneeded sections for target
- off_t seek_actual = lseek(oat_file->Fd(), 0, SEEK_SET);
- CHECK_EQ(0, seek_actual);
- std::string error_msg;
- CHECK(ElfStripper::Strip(oat_file.get(), &error_msg)) << error_msg;
-
-
- // We wrote the oat file successfully, and want to keep it.
- VLOG(compiler) << "Oat file written successfully (stripped): " << oat_location;
- } else {
- VLOG(compiler) << "Oat file written successfully without stripping: " << oat_location;
- }
-#endif // ART_USE_PORTABLE_COMPILER
-
- timings.EndTiming();
-
- if (dump_timing || (dump_slow_timing && timings.GetTotalNs() > MsToNs(1000))) {
- LOG(INFO) << Dumpable<TimingLogger>(timings);
- }
- if (dump_passes) {
- LOG(INFO) << Dumpable<CumulativeLogger>(compiler_phases_timings);
- }
-
- // 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)) {
- dex2oat->LogCompletionTime();
- exit(EXIT_SUCCESS);
- }
-
+ dex2oat.DumpTiming();
return EXIT_SUCCESS;
-} // NOLINT(readability/fn_size)
+}
} // namespace art
int main(int argc, char** argv) {
- return art::dex2oat(argc, argv);
+ int result = art::dex2oat(argc, argv);
+ // Everything was done, 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. Note: The Dex2Oat class
+ // should not destruct the runtime in this case.
+ if (!art::kIsDebugBuild && (RUNNING_ON_VALGRIND == 0)) {
+ exit(result);
+ }
+ return result;
}