ART: Use CmdlineParser in dex2oat
Refactor dex2oat and the compiler to use the cmdline parser. In
an effort to standardize command-line parsing and make it easier
to add new properties in a principled manner.
Test: m test-art-host
Change-Id: Ia077a56234dbf579d7ca3430fef325b57aa06333
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 7b46531..45d0f17 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -20,11 +20,13 @@
#include <sys/stat.h>
#include "base/memory_tool.h"
+#include <forward_list>
#include <fstream>
#include <iostream>
#include <limits>
#include <sstream>
#include <string>
+#include <type_traits>
#include <unordered_set>
#include <vector>
@@ -50,16 +52,19 @@
#include "base/unix_file/fd_file.h"
#include "class_linker.h"
#include "class_loader_context.h"
+#include "cmdline_parser.h"
#include "compiler.h"
#include "compiler_callbacks.h"
#include "debug/elf_debug_writer.h"
#include "debug/method_debug_info.h"
#include "dex/quick_compiler_callbacks.h"
#include "dex/verification_results.h"
+#include "dex2oat_options.h"
#include "dex2oat_return_codes.h"
#include "dex_file-inl.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
+#include "driver/compiler_options_map-inl.h"
#include "elf_file.h"
#include "gc/space/image_space.h"
#include "gc/space/space-inl.h"
@@ -659,76 +664,27 @@
std::string error_msg;
};
- void ParseZipFd(const StringPiece& option) {
- ParseUintOption(option, "--zip-fd", &zip_fd_, Usage);
- }
-
- void ParseInputVdexFd(const StringPiece& option) {
- // Note that the input vdex fd might be -1.
- ParseIntOption(option, "--input-vdex-fd", &input_vdex_fd_, Usage);
- }
-
- void ParseOutputVdexFd(const StringPiece& option) {
- ParseUintOption(option, "--output-vdex-fd", &output_vdex_fd_, Usage);
- }
-
- void ParseOatFd(const StringPiece& option) {
- ParseUintOption(option, "--oat-fd", &oat_fd_, Usage);
- }
-
- void ParseFdForCollection(const StringPiece& option,
- const char* arg_name,
- std::vector<uint32_t>* fds) {
- uint32_t fd;
- ParseUintOption(option, arg_name, &fd, Usage);
- fds->push_back(fd);
- }
-
- void ParseJ(const StringPiece& option) {
- ParseUintOption(option, "-j", &thread_count_, Usage, /* is_long_option */ false);
- }
-
- void ParseBase(const StringPiece& option) {
- DCHECK(option.starts_with("--base="));
- const char* image_base_str = option.substr(strlen("--base=")).data();
+ void ParseBase(const std::string& option) {
char* end;
- image_base_ = strtoul(image_base_str, &end, 16);
- if (end == image_base_str || *end != '\0') {
+ image_base_ = strtoul(option.c_str(), &end, 16);
+ if (end == option.c_str() || *end != '\0') {
Usage("Failed to parse hexadecimal value for option %s", option.data());
}
}
- void ParseInstructionSet(const StringPiece& option) {
- DCHECK(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;
- }
- }
-
bool VerifyProfileData() {
return profile_compilation_info_->VerifyProfileData(dex_files_);
}
- void ParseInstructionSetVariant(const StringPiece& option, ParserOptions* parser_options) {
- DCHECK(option.starts_with("--instruction-set-variant="));
- StringPiece str = option.substr(strlen("--instruction-set-variant=")).data();
+ void ParseInstructionSetVariant(const std::string& option, ParserOptions* parser_options) {
instruction_set_features_ = InstructionSetFeatures::FromVariant(
- instruction_set_, str.as_string(), &parser_options->error_msg);
+ instruction_set_, option, &parser_options->error_msg);
if (instruction_set_features_.get() == nullptr) {
Usage("%s", parser_options->error_msg.c_str());
}
}
- void ParseInstructionSetFeatures(const StringPiece& option, ParserOptions* parser_options) {
- DCHECK(option.starts_with("--instruction-set-features="));
- StringPiece str = option.substr(strlen("--instruction-set-features=")).data();
+ void ParseInstructionSetFeatures(const std::string& option, ParserOptions* parser_options) {
if (instruction_set_features_ == nullptr) {
instruction_set_features_ = InstructionSetFeatures::FromVariant(
instruction_set_, "default", &parser_options->error_msg);
@@ -738,38 +694,9 @@
}
}
instruction_set_features_ =
- instruction_set_features_->AddFeaturesFromString(str.as_string(),
- &parser_options->error_msg);
+ instruction_set_features_->AddFeaturesFromString(option, &parser_options->error_msg);
if (instruction_set_features_ == nullptr) {
- Usage("Error parsing '%s': %s", option.data(), parser_options->error_msg.c_str());
- }
- }
-
- void ParseCompilerBackend(const StringPiece& option, ParserOptions* parser_options) {
- DCHECK(option.starts_with("--compiler-backend="));
- parser_options->requested_specific_compiler = true;
- 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 {
- Usage("Unknown compiler backend: %s", backend_str.data());
- }
- }
-
- void ParseImageFormat(const StringPiece& option) {
- const StringPiece substr("--image-format=");
- DCHECK(option.starts_with(substr));
- const StringPiece format_str = option.substr(substr.length());
- if (format_str == "lz4") {
- image_storage_mode_ = ImageHeader::kStorageModeLZ4;
- } else if (format_str == "lz4hc") {
- image_storage_mode_ = ImageHeader::kStorageModeLZ4HC;
- } else if (format_str == "uncompressed") {
- image_storage_mode_ = ImageHeader::kStorageModeUncompressed;
- } else {
- Usage("Unknown image format: %s", format_str.data());
+ Usage("Error parsing '%s': %s", option.c_str(), parser_options->error_msg.c_str());
}
}
@@ -1092,23 +1019,20 @@
base_symbol_oat = base_symbol_oat.substr(0, last_symbol_oat_slash + 1);
}
- const size_t num_expanded_files = 2 + (base_symbol_oat.empty() ? 0 : 1);
- char_backing_storage_.reserve((dex_locations_.size() - 1) * num_expanded_files);
-
// Now create the other names. Use a counted loop to skip the first one.
for (size_t i = 1; i < dex_locations_.size(); ++i) {
// TODO: Make everything properly std::string.
std::string image_name = CreateMultiImageName(dex_locations_[i], prefix, infix, ".art");
- char_backing_storage_.push_back(base_img + image_name);
- image_filenames_.push_back((char_backing_storage_.end() - 1)->c_str());
+ char_backing_storage_.push_front(base_img + image_name);
+ image_filenames_.push_back(char_backing_storage_.front().c_str());
std::string oat_name = CreateMultiImageName(dex_locations_[i], prefix, infix, ".oat");
- char_backing_storage_.push_back(base_oat + oat_name);
- oat_filenames_.push_back((char_backing_storage_.end() - 1)->c_str());
+ char_backing_storage_.push_front(base_oat + oat_name);
+ oat_filenames_.push_back(char_backing_storage_.front().c_str());
if (!base_symbol_oat.empty()) {
- char_backing_storage_.push_back(base_symbol_oat + oat_name);
- oat_unstripped_.push_back((char_backing_storage_.end() - 1)->c_str());
+ char_backing_storage_.push_front(base_symbol_oat + oat_name);
+ oat_unstripped_.push_back(char_backing_storage_.front().c_str());
}
}
}
@@ -1173,6 +1097,43 @@
kUseReadBarrier ? OatHeader::kTrueValue : OatHeader::kFalseValue);
}
+ // This simple forward is here so the string specializations below don't look out of place.
+ template <typename T, typename U>
+ void AssignIfExists(Dex2oatArgumentMap& map,
+ const Dex2oatArgumentMap::Key<T>& key,
+ U* out) {
+ map.AssignIfExists(key, out);
+ }
+
+ // Specializations to handle const char* vs std::string.
+ void AssignIfExists(Dex2oatArgumentMap& map,
+ const Dex2oatArgumentMap::Key<std::string>& key,
+ const char** out) {
+ if (map.Exists(key)) {
+ char_backing_storage_.push_front(std::move(*map.Get(key)));
+ *out = char_backing_storage_.front().c_str();
+ }
+ }
+ void AssignIfExists(Dex2oatArgumentMap& map,
+ const Dex2oatArgumentMap::Key<std::vector<std::string>>& key,
+ std::vector<const char*>* out) {
+ if (map.Exists(key)) {
+ for (auto& val : *map.Get(key)) {
+ char_backing_storage_.push_front(std::move(val));
+ out->push_back(char_backing_storage_.front().c_str());
+ }
+ }
+ }
+
+ template <typename T>
+ void AssignTrueIfExists(Dex2oatArgumentMap& map,
+ const Dex2oatArgumentMap::Key<T>& key,
+ bool* out) {
+ if (map.Exists(key)) {
+ *out = true;
+ }
+ }
+
// 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.
@@ -1182,159 +1143,104 @@
InitLogging(argv, Runtime::Abort);
- // Skip over argv[0].
- argv++;
- argc--;
-
- if (argc == 0) {
- Usage("No arguments specified");
- }
-
- std::unique_ptr<ParserOptions> parser_options(new ParserOptions());
compiler_options_.reset(new CompilerOptions());
- 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];
+ using M = Dex2oatArgumentMap;
+ std::string error_msg;
+ std::unique_ptr<M> args_uptr = M::Parse(argc, const_cast<const char**>(argv), &error_msg);
+ if (args_uptr == nullptr) {
+ Usage("Failed to parse command line: %s", error_msg.c_str());
+ UNREACHABLE();
+ }
+
+ M& args = *args_uptr;
+
+ std::unique_ptr<ParserOptions> parser_options(new ParserOptions());
+
+ AssignIfExists(args, M::DexFiles, &dex_filenames_);
+ AssignIfExists(args, M::DexLocations, &dex_locations_);
+ AssignIfExists(args, M::OatFiles, &oat_filenames_);
+ AssignIfExists(args, M::OatSymbols, &parser_options->oat_symbols);
+ AssignIfExists(args, M::ImageFilenames, &image_filenames_);
+ AssignIfExists(args, M::ZipFd, &zip_fd_);
+ AssignIfExists(args, M::ZipLocation, &zip_location_);
+ AssignIfExists(args, M::InputVdexFd, &input_vdex_fd_);
+ AssignIfExists(args, M::OutputVdexFd, &output_vdex_fd_);
+ AssignIfExists(args, M::InputVdex, &input_vdex_);
+ AssignIfExists(args, M::OutputVdex, &output_vdex_);
+ AssignIfExists(args, M::OatFd, &oat_fd_);
+ AssignIfExists(args, M::OatLocation, &oat_location_);
+ AssignIfExists(args, M::Watchdog, &parser_options->watch_dog_enabled);
+ AssignIfExists(args, M::WatchdogTimeout, &parser_options->watch_dog_timeout_in_ms);
+ AssignIfExists(args, M::Threads, &thread_count_);
+ AssignIfExists(args, M::ImageClasses, &image_classes_filename_);
+ AssignIfExists(args, M::ImageClassesZip, &image_classes_zip_filename_);
+ AssignIfExists(args, M::CompiledClasses, &compiled_classes_filename_);
+ AssignIfExists(args, M::CompiledClassesZip, &compiled_classes_zip_filename_);
+ AssignIfExists(args, M::CompiledMethods, &compiled_methods_filename_);
+ AssignIfExists(args, M::CompiledMethodsZip, &compiled_methods_zip_filename_);
+ AssignIfExists(args, M::Passes, &passes_to_run_filename_);
+ AssignIfExists(args, M::BootImage, &parser_options->boot_image_filename);
+ AssignIfExists(args, M::AndroidRoot, &android_root_);
+ AssignIfExists(args, M::Profile, &profile_file_);
+ AssignIfExists(args, M::ProfileFd, &profile_file_fd_);
+ AssignIfExists(args, M::RuntimeOptions, &runtime_args_);
+ AssignIfExists(args, M::SwapFile, &swap_file_name_);
+ AssignIfExists(args, M::SwapFileFd, &swap_fd_);
+ AssignIfExists(args, M::SwapDexSizeThreshold, &min_dex_file_cumulative_size_for_swap_);
+ AssignIfExists(args, M::SwapDexCountThreshold, &min_dex_files_for_swap_);
+ AssignIfExists(args, M::VeryLargeAppThreshold, &very_large_threshold_);
+ AssignIfExists(args, M::AppImageFile, &app_image_file_name_);
+ AssignIfExists(args, M::AppImageFileFd, &app_image_fd_);
+ AssignIfExists(args, M::NoInlineFrom, &no_inline_from_string_);
+ AssignIfExists(args, M::ClasspathDir, &classpath_dir_);
+ AssignIfExists(args, M::DirtyImageObjects, &dirty_image_objects_filename_);
+ AssignIfExists(args, M::ImageFormat, &image_storage_mode_);
+
+ AssignIfExists(args, M::Backend, &compiler_kind_);
+ parser_options->requested_specific_compiler = args.Exists(M::Backend);
+
+ AssignIfExists(args, M::TargetInstructionSet, &instruction_set_);
+ // arm actually means thumb2.
+ if (instruction_set_ == InstructionSet::kArm) {
+ instruction_set_ = InstructionSet::kThumb2;
+ }
+
+ AssignTrueIfExists(args, M::Host, &is_host_);
+ AssignTrueIfExists(args, M::DumpTiming, &dump_timing_);
+ AssignTrueIfExists(args, M::DumpPasses, &dump_passes_);
+ AssignTrueIfExists(args, M::DumpStats, &dump_stats_);
+ AssignTrueIfExists(args, M::AvoidStoringInvocation, &avoid_storing_invocation_);
+ AssignTrueIfExists(args, M::MultiImage, &multi_image_);
+
+ if (args.Exists(M::ForceDeterminism)) {
+ if (!SupportsDeterministicCompilation()) {
+ Usage("Option --force-determinism requires read barriers or a CMS/MS garbage collector");
}
- 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=")) {
- ParseZipFd(option);
- } else if (option.starts_with("--zip-location=")) {
- zip_location_ = option.substr(strlen("--zip-location=")).data();
- } else if (option.starts_with("--input-vdex-fd=")) {
- ParseInputVdexFd(option);
- } else if (option.starts_with("--input-vdex=")) {
- input_vdex_ = option.substr(strlen("--input-vdex=")).data();
- } else if (option.starts_with("--output-vdex=")) {
- output_vdex_ = option.substr(strlen("--output-vdex=")).data();
- } else if (option.starts_with("--output-vdex-fd=")) {
- ParseOutputVdexFd(option);
- } else if (option.starts_with("--oat-file=")) {
- oat_filenames_.push_back(option.substr(strlen("--oat-file=")).data());
- } else if (option.starts_with("--oat-symbols=")) {
- parser_options->oat_symbols.push_back(option.substr(strlen("--oat-symbols=")).data());
- } else if (option.starts_with("--oat-fd=")) {
- ParseOatFd(option);
- } else if (option.starts_with("--oat-location=")) {
- oat_location_ = option.substr(strlen("--oat-location=")).data();
- } else if (option == "--watch-dog") {
- parser_options->watch_dog_enabled = true;
- } else if (option == "--no-watch-dog") {
- parser_options->watch_dog_enabled = false;
- } else if (option.starts_with("--watchdog-timeout=")) {
- ParseIntOption(option,
- "--watchdog-timeout",
- &parser_options->watch_dog_timeout_in_ms,
- Usage);
- } else if (option.starts_with("-j")) {
- ParseJ(option);
- } else if (option.starts_with("--image=")) {
- image_filenames_.push_back(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("--image-format=")) {
- ParseImageFormat(option);
- } else if (option.starts_with("--compiled-classes=")) {
- compiled_classes_filename_ = option.substr(strlen("--compiled-classes=")).data();
- } else if (option.starts_with("--compiled-classes-zip=")) {
- compiled_classes_zip_filename_ = option.substr(strlen("--compiled-classes-zip=")).data();
- } else if (option.starts_with("--compiled-methods=")) {
- compiled_methods_filename_ = option.substr(strlen("--compiled-methods=")).data();
- } else if (option.starts_with("--compiled-methods-zip=")) {
- compiled_methods_zip_filename_ = option.substr(strlen("--compiled-methods-zip=")).data();
- } else if (option.starts_with("--run-passes=")) {
- passes_to_run_filename_ = option.substr(strlen("--run-passes=")).data();
- } else if (option.starts_with("--base=")) {
- ParseBase(option);
- } else if (option.starts_with("--boot-image=")) {
- parser_options->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=")) {
- ParseInstructionSet(option);
- } else if (option.starts_with("--instruction-set-variant=")) {
- ParseInstructionSetVariant(option, parser_options.get());
- } else if (option.starts_with("--instruction-set-features=")) {
- ParseInstructionSetFeatures(option, parser_options.get());
- } else if (option.starts_with("--compiler-backend=")) {
- ParseCompilerBackend(option, parser_options.get());
- } else if (option.starts_with("--profile-file=")) {
- profile_file_ = option.substr(strlen("--profile-file=")).ToString();
- } else if (option.starts_with("--profile-file-fd=")) {
- ParseUintOption(option, "--profile-file-fd", &profile_file_fd_, Usage);
- } 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 == "--avoid-storing-invocation") {
- avoid_storing_invocation_ = true;
- } else if (option.starts_with("--swap-file=")) {
- swap_file_name_ = option.substr(strlen("--swap-file=")).data();
- } else if (option.starts_with("--swap-fd=")) {
- ParseUintOption(option, "--swap-fd", &swap_fd_, Usage);
- } else if (option.starts_with("--swap-dex-size-threshold=")) {
- ParseUintOption(option,
- "--swap-dex-size-threshold",
- &min_dex_file_cumulative_size_for_swap_,
- Usage);
- } else if (option.starts_with("--swap-dex-count-threshold=")) {
- ParseUintOption(option,
- "--swap-dex-count-threshold",
- &min_dex_files_for_swap_,
- Usage);
- } else if (option.starts_with("--very-large-app-threshold=")) {
- ParseUintOption(option,
- "--very-large-app-threshold",
- &very_large_threshold_,
- Usage);
- } else if (option.starts_with("--app-image-file=")) {
- app_image_file_name_ = option.substr(strlen("--app-image-file=")).data();
- } else if (option.starts_with("--app-image-fd=")) {
- ParseUintOption(option, "--app-image-fd", &app_image_fd_, Usage);
- } else if (option == "--multi-image") {
- multi_image_ = true;
- } else if (option.starts_with("--no-inline-from=")) {
- no_inline_from_string_ = option.substr(strlen("--no-inline-from=")).data();
- } else if (option == "--force-determinism") {
- if (!SupportsDeterministicCompilation()) {
- Usage("Option --force-determinism requires read barriers or a CMS/MS garbage collector");
- }
- force_determinism_ = true;
- } else if (option.starts_with("--classpath-dir=")) {
- classpath_dir_ = option.substr(strlen("--classpath-dir=")).data();
- } else if (option.starts_with("--class-loader-context=")) {
- class_loader_context_ = ClassLoaderContext::Create(
- option.substr(strlen("--class-loader-context=")).data());
- if (class_loader_context_ == nullptr) {
- Usage("Option --class-loader-context has an incorrect format: %s", option.data());
- }
- } else if (option.starts_with("--dirty-image-objects=")) {
- dirty_image_objects_filename_ = option.substr(strlen("--dirty-image-objects=")).data();
- } else if (!compiler_options_->ParseCompilerOption(option, Usage)) {
- Usage("Unknown argument %s", option.data());
+ force_determinism_ = true;
+ }
+
+ if (args.Exists(M::Base)) {
+ ParseBase(*args.Get(M::Base));
+ }
+ if (args.Exists(M::TargetInstructionSetVariant)) {
+ ParseInstructionSetVariant(*args.Get(M::TargetInstructionSetVariant), parser_options.get());
+ }
+ if (args.Exists(M::TargetInstructionSetFeatures)) {
+ ParseInstructionSetFeatures(*args.Get(M::TargetInstructionSetFeatures), parser_options.get());
+ }
+ if (args.Exists(M::ClassLoaderContext)) {
+ class_loader_context_ = ClassLoaderContext::Create(*args.Get(M::ClassLoaderContext));
+ if (class_loader_context_ == nullptr) {
+ Usage("Option --class-loader-context has an incorrect format: %s",
+ args.Get(M::ClassLoaderContext)->c_str());
}
}
+ if (!ReadCompilerOptions(args, compiler_options_.get(), &error_msg)) {
+ Usage(error_msg.c_str());
+ }
+
ProcessOptions(parser_options.get());
// Insert some compiler things.
@@ -2931,7 +2837,7 @@
std::unordered_map<const DexFile*, size_t> dex_file_oat_index_map_;
// Backing storage.
- std::vector<std::string> char_backing_storage_;
+ std::forward_list<std::string> char_backing_storage_;
// See CompilerOptions.force_determinism_.
bool force_determinism_;