From: Andreas Gampe Date: Wed, 5 Nov 2014 18:18:32 +0000 (-0800) Subject: ART: Refactor dex2oat X-Git-Tag: android-x86-7.1-r1~889^2~2661^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=88ec7f45c0e2c3f659048fbe4b1dc78730fdaea8;p=android-x86%2Fart.git ART: Refactor dex2oat Take the huge dex2oat main function apart. Move to ScopedLogging. Bug: 18276913 Bug: 17444504 Change-Id: Iab3d7437d60508088cb16bf33da0c5defbf7ae03 --- diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 2fd5a5274..6584d53b1 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -103,13 +103,13 @@ bool ImageWriter::Write(const std::string& image_filename, std::unique_ptr 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 7770588ec..d87faebac 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 @@ static void UsageError(const char* fmt, ...) { 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 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(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* ReadImageClassesFromFile(const char* image_classes_filename) { - std::unique_ptr 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> result(ReadImageClasses(*image_classes_file)); - image_classes_file->close(); - return result.release(); - } - - std::set* ReadImageClasses(std::istream& image_classes_stream) { - std::unique_ptr> image_classes(new std::set); - 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* ReadImageClassesFromZip(const char* zip_filename, - const char* image_classes_filename, - std::string* error_msg) { - std::unique_ptr zip_archive(ZipArchive::Open(zip_filename, error_msg)); - if (zip_archive.get() == nullptr) { - return nullptr; - } - std::unique_ptr 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 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(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& dex_files, - const std::string& bitcode_filename, - bool image, - std::unique_ptr>& 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 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 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& dex_files, - const std::string& android_root, - bool is_host, - File* oat_file, - TimingLogger* timings, - SafeMap* 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(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 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& dex_files) { - std::vector 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& 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 driver_; - std::unique_ptr image_writer_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(Dex2Oat); -}; - -static size_t OpenDexFiles(const std::vector& dex_filenames, - const std::vector& dex_locations, - std::vector& 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 @@ class WatchDog { // 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 @@ class WatchDog { 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 @@ void ParseStringAfterChar(const std::string& s, char c, std::string* parsed_valu *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,691 +415,689 @@ void ParseDouble(const std::string& option, char after_char, *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. } + LogCompletionTime(); } -#endif -} -static int dex2oat(int argc, char** argv) { - b13564922(); + // 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; - original_argc = argc; - original_argv = argv; + InitLogging(argv); - TimingLogger timings("compiler", false, false); - CumulativeLogger compiler_phases_timings("compilation times"); + // Skip over argv[0]. + argv++; + argc--; - InitLogging(argv); + if (argc == 0) { + Usage("No arguments specified"); + } - // Skip over argv[0]. - argv++; - argc--; + 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; - if (argc == 0) { - Usage("No arguments specified"); - } + std::string error_msg; - std::vector dex_filenames; - std::vector 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 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 verbose_methods; - - // Initialize ISA and ISA features to default values. - InstructionSet instruction_set = kRuntimeISA; - std::string error_msg; - std::unique_ptr instruction_set_features( - InstructionSetFeatures::FromFeatureString(kNone, "default", &error_msg)); - CHECK(instruction_set_features.get() != nullptr) << error_msg; - - // Profile file to use - std::string profile_file; - double top_k_profile_threshold = CompilerOptions::kDefaultTopKProfileThreshold; - - 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 (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); + 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 (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]; + 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 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()); } - 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("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_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() && 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_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 (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"); + 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; } - 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; - } + 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 && !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_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 (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("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_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_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]); + 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"); } - } 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 (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"); + 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; - } + oat_stripped_ = oat_filename_; + 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 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 (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 + if (instruction_set_ == kRuntimeISA) { + std::unique_ptr 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(); + } } - } - 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 (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); + } - if (print_pass_options) { - PassDriverMEOpts::PrintPassOptions(); - } + // 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; - std::unique_ptr 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)); + default: + // Defaults are correct. + break; + } - // Done with usage checks, enable watchdog if requested - WatchDog watch_dog(watch_dog_enabled); + if (print_pass_options) { + PassDriverMEOpts::PrintPassOptions(); + } - // Check early that the result of compilation can be written - std::unique_ptr 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; + 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)); } - } 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(); + // Fill some values into the key-value store for the oat header. + key_value_store_.reset(new SafeMap()); - RuntimeOptions runtime_options; - std::vector 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; + // 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"); } - 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 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(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> 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)); + // 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 { - image_classes.reset(dex2oat->ReadImageClassesFromFile(image_classes_filename)); + oat_file_.reset(new File(oat_fd_, oat_location_)); + oat_file_->DisableAutoClose(); + oat_file_->SetLength(0); } - if (image_classes.get() == nullptr) { - LOG(ERROR) << "Failed to create list of image classes from '" << image_classes_filename << - "': " << error_msg; - return EXIT_FAILURE; + if (oat_file_.get() == nullptr) { + PLOG(ERROR) << "Failed to create oat file: " << oat_location_; + return false; } - } else if (image) { - image_classes.reset(new std::set); + if (create_file && fchmod(oat_file_->Fd(), 0644) != 0) { + PLOG(ERROR) << "Failed to make oat file world readable: " << oat_location_; + return false; + } + return true; } - std::vector 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 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 (!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; - } - ATRACE_END(); - } else { - size_t failure_count = OpenDexFiles(dex_filenames, dex_locations, dex_files); + // 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 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)); + } + + 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; } - 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 tmp_file(OS::CreateEmptyFile(tmp_file_name.c_str())); - if (tmp_file.get() == nullptr) { + // 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); + } + + 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 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; + } + 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 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"; + << ". 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; } - 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"; + // 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"; + /* + * 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; } - // Fill some values into the key-value store for the oat header. - std::unique_ptr > key_value_store( - new SafeMap()); + // 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")); - // Insert some compiler things. - { - std::ostringstream oss; - for (int i = 0; i < argc; ++i) { - if (i > 0) { - oss << ' '; + // 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 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]); } - oss << argv[i]; + soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader); + ScopedLocalRef 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); } - 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"); - } - dex2oat->Compile(boot_image_option, - dex_files, - bitcode_filename, - image, - image_classes, - dump_stats, - dump_passes, - &timings, - &compiler_phases_timings, - profile_file); - - if (image) { - dex2oat->PrepareImageWriter(image_base); - } + 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_)); - 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; - } + driver_->GetCompiler()->SetBitcodeFileName(*driver_, bitcode_filename_); - VLOG(compiler) << "Oat file written successfully (unstripped): " << oat_location; + driver_->CompileAll(class_loader, dex_files_, timings_); + } // Notes on the interleaving of creating the image and oat file to // ensure the references between the two are correct. @@ -1527,87 +1159,454 @@ static int dex2oat(int argc, char** argv) { // // 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; - } - VLOG(compiler) << "Image written successfully: " << image_filename; + + + // 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 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(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())); + } + + 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(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(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 in(OS::OpenFileForReading(oat_unstripped_.c_str())); + std::unique_ptr out(OS::CreateEmptyFile(oat_stripped_.c_str())); + size_t buffer_size = 8192; + std::unique_ptr 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_; } - return EXIT_SUCCESS; + + 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; } - // 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 in(OS::OpenFileForReading(oat_unstripped.c_str())); - std::unique_ptr out(OS::CreateEmptyFile(oat_stripped.c_str())); - size_t buffer_size = 8192; - std::unique_ptr 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; + void DumpTiming() { + if (dump_timing_ || (dump_slow_timing_ && timings_->GetTotalNs() > MsToNs(1000))) { + LOG(INFO) << Dumpable(*timings_); + } + if (dump_passes_) { + LOG(INFO) << Dumpable(*driver_->GetTimingsLogger()); + } + } + + CompilerOptions* GetCompilerOptions() const { + return compiler_options_.get(); + } + + bool IsHost() const { + return is_host_; + } + + private: + static size_t OpenDexFiles(const std::vector& dex_filenames, + const std::vector& dex_locations, + std::vector& 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; } - bool write_ok = out->WriteFully(buffer.get(), bytes_read); - CHECK(write_ok); + ATRACE_END(); } - oat_file.reset(out.release()); - VLOG(compiler) << "Oat file copied successfully (stripped): " << oat_stripped; + return failure_count; } -#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; + // Returns true if dex_files has a dex with the named location. + static bool DexFilesContains(const std::vector& 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& dex_files) { + std::vector 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; + } - // 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; + 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 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* ReadImageClassesFromFile(const char* image_classes_filename) { + std::unique_ptr 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> result(ReadImageClasses(*image_classes_file)); + image_classes_file->close(); + return result.release(); + } + + static std::set* ReadImageClasses(std::istream& image_classes_stream) { + std::unique_ptr> image_classes(new std::set); + 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* ReadImageClassesFromZip(const char* zip_filename, + const char* image_classes_filename, + std::string* error_msg) { + std::unique_ptr zip_archive(ZipArchive::Open(zip_filename, error_msg)); + if (zip_archive.get() == nullptr) { + return nullptr; + } + std::unique_ptr 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 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(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 compiler_options_; + Compiler::Kind compiler_kind_; + + InstructionSet instruction_set_; + std::unique_ptr instruction_set_features_; + + std::unique_ptr > key_value_store_; + + std::unique_ptr verification_results_; + DexFileToMethodInlinerMap method_inliner_map_; + std::unique_ptr 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_; + std::unique_ptr 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 dex_filenames_; + std::vector dex_locations_; + int zip_fd_; + std::string zip_location_; + std::string boot_image_option_; + std::vector runtime_args_; + std::string image_filename_; + uintptr_t image_base_; + const char* image_classes_zip_filename_; + const char* image_classes_filename_; + std::unique_ptr> image_classes_; + bool image_; + std::unique_ptr image_writer_; + bool is_host_; + std::string android_root_; + std::vector dex_files_; + std::unique_ptr driver_; + std::vector 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 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; } -#endif // ART_USE_PORTABLE_COMPILER - timings.EndTiming(); + LOG(INFO) << CommandLine(); - if (dump_timing || (dump_slow_timing && timings.GetTotalNs() > MsToNs(1000))) { - LOG(INFO) << Dumpable(timings); + if (!dex2oat.Setup()) { + return EXIT_FAILURE; } - if (dump_passes) { - LOG(INFO) << Dumpable(compiler_phases_timings); + + dex2oat.Compile(); + + if (!dex2oat.CreateOatFile()) { + return EXIT_FAILURE; } - // 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); + if (!dex2oat.HandleImage()) { + return EXIT_FAILURE; } + if (dex2oat.IsHost()) { + dex2oat.DumpTiming(); + return EXIT_SUCCESS; + } + + if (!dex2oat.Strip()) { + return EXIT_FAILURE; + } + + 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; }