diff options
author | 2020-07-13 16:10:03 -0700 | |
---|---|---|
committer | 2020-07-17 00:42:41 +0000 | |
commit | eb9da9eb2319dbac58683e01940de4171ce01027 (patch) | |
tree | 5ce09a7cca640400395860071a17e00f10358813 | |
parent | c607092fda93d8d9a52ec8ee2fddd6a685a6785a (diff) |
Add support for generating --help messages from cmdline definition
Previously our --help messages were always simply hard-coded text
blocks. This makes it easy for them to get out-of-sync with the actual
flags the programs take. To fix this teach the cmdline system how to
generate these types of help text.
Convert over `dalvikvm` which is one of the worst when it comes to
usefulness of the -help text.
```
% dalvikvm -help
dalvikvm: [options] class [argument ...]
The following standard arguments are supported:
-classpath {string value}
-cp {string value}
The classpath, separated by ':'
-D{string value} [-D{string value}...]
-verbose:{class|collector|compiler|deopt|gc|heap|interpreter|jdwp|jit|jni|monitor|oat|profiler|signals|simulator|startup|third-party-jni|threads|verifier|verifier-debug|image|systrace-locks|plugin|agents|dex}
Switches for advanced logging. Multiple categories can be enabled separated by ','. Eg: -verbose:class,deopt
-help
-h
Print this help text.
-showversion
-agentpath:{/path/to/libagent.so=options} [-agentpath:{/path/to/libagent.so=options}...]
Load native agents.
The following extended arguments are supported:
-Xbootclasspath:{list separated by ':'}
-Xcheck:jni
-Xms{Memory with granularity of 1024 bytes}
-Xmx{Memory with granularity of 1024 bytes}
-Xss{Memory with granularity of 1 bytes}
-Xint
The following Dalvik arguments are supported:
-Xzygote
Start as zygote
-Xjnitrace:{string value}
...
```
Test: dalvikvm -help
Bug: 160933150
Change-Id: Id96bc8340b799636d322450cb95c88f814a77c35
-rw-r--r-- | cmdline/cmdline_parser.h | 156 | ||||
-rw-r--r-- | cmdline/cmdline_type_parser.h | 1 | ||||
-rw-r--r-- | cmdline/cmdline_types.h | 41 | ||||
-rw-r--r-- | cmdline/detail/cmdline_parse_argument_detail.h | 62 | ||||
-rw-r--r-- | runtime/parsed_options.cc | 311 | ||||
-rw-r--r-- | runtime/parsed_options.h | 1 |
6 files changed, 354 insertions, 218 deletions
diff --git a/cmdline/cmdline_parser.h b/cmdline/cmdline_parser.h index 952be44b0e..01806d48b2 100644 --- a/cmdline/cmdline_parser.h +++ b/cmdline/cmdline_parser.h @@ -19,21 +19,26 @@ #define CMDLINE_NDEBUG 1 // Do not output any debugging information for parsing. -#include "detail/cmdline_debug_detail.h" -#include "detail/cmdline_parse_argument_detail.h" -#include "detail/cmdline_parser_detail.h" +#include <memory> +#include <optional> +#include <string> +#include <string_view> +#include <tuple> +#include <unordered_map> +#include <unordered_set> +#include <vector> +#include "base/indenter.h" +#include "base/variant_map.h" #include "cmdline_parse_result.h" #include "cmdline_result.h" #include "cmdline_type_parser.h" #include "cmdline_types.h" +#include "detail/cmdline_debug_detail.h" +#include "detail/cmdline_parse_argument_detail.h" +#include "detail/cmdline_parser_detail.h" #include "token_range.h" -#include "base/variant_map.h" - -#include <memory> -#include <vector> - namespace art { // Build a parser for command line arguments with a small domain specific language. // Each parsed type must have a specialized CmdlineType<T> in order to do the string->T parsing. @@ -165,6 +170,16 @@ struct CmdlineParser { return *this; } + ArgumentBuilder<TArg>& WithMetavar(const char* sv) { + argument_info_.metavar_ = sv; + return *this; + } + + ArgumentBuilder<TArg>& WithHelp(const char* sv) { + argument_info_.help_ = sv; + return *this; + } + // Convenience type alias for the variant map key type definition. using MapKey = TVariantMapKey<TArg>; @@ -231,6 +246,17 @@ struct CmdlineParser { argument_info_.names_ = names; } + void SetHelp(std::optional<const char*>&& val) { + argument_info_.help_ = val; + } + + void SetCategory(std::optional<const char*>&& val) { + argument_info_.category_ = val; + } + void SetMetavar(std::optional<const char*>&& val) { + argument_info_.metavar_ = val; + } + private: // Copying is bad. Move only. ArgumentBuilder(const ArgumentBuilder&) = delete; @@ -291,6 +317,21 @@ struct CmdlineParser { return CreateTypedBuilder<TArg>(); } + UntypedArgumentBuilder& WithHelp(const char* sv) { + SetHelp(sv); + return *this; + } + + UntypedArgumentBuilder& WithCategory(const char* sv) { + SetCategory(sv); + return *this; + } + + UntypedArgumentBuilder& WithMetavar(const char* sv) { + SetMetavar(sv); + return *this; + } + // When used with multiple aliases, map the position of the alias to the value position. template <typename TArg> ArgumentBuilder<TArg> WithValues(std::initializer_list<TArg> values) { @@ -324,6 +365,18 @@ struct CmdlineParser { names_ = names; } + void SetHelp(std::optional<const char*> sv) { + help_.swap(sv); + } + + void SetMetavar(std::optional<const char*> sv) { + metavar_.swap(sv); + } + + void SetCategory(std::optional<const char*> sv) { + category_.swap(sv); + } + private: // No copying. Move instead. UntypedArgumentBuilder(const UntypedArgumentBuilder&) = delete; @@ -333,6 +386,9 @@ struct CmdlineParser { auto&& b = CreateArgumentBuilder<TArg>(parent_); InitializeTypedBuilder(&b); // Type-specific initialization b.SetNames(std::move(names_)); + b.SetHelp(std::move(help_)); + b.SetCategory(std::move(category_)); + b.SetMetavar(std::move(metavar_)); return std::move(b); } @@ -356,6 +412,9 @@ struct CmdlineParser { CmdlineParser::Builder& parent_; std::vector<const char*> names_; + std::optional<const char*> category_; + std::optional<const char*> help_; + std::optional<const char*> metavar_; }; // Build a new parser given a chain of calls to define arguments. @@ -367,10 +426,26 @@ struct CmdlineParser { return Define({name}); } + Builder& ClearCategory() { + default_category_.reset(); + return *this; + } + + Builder& SetCategory(const char* sv) { + default_category_ = sv; + return *this; + } + + Builder& OrderCategories(std::vector<const char*> categories) { + category_order_.swap(categories); + return *this; + } + // Define a single argument with multiple aliases. UntypedArgumentBuilder Define(std::initializer_list<const char*> names) { auto&& b = UntypedArgumentBuilder(*this); b.SetNames(names); + b.SetCategory(default_category_); return std::move(b); } @@ -382,6 +457,8 @@ struct CmdlineParser { // Provide a list of arguments to ignore for backwards compatibility. Builder& Ignore(std::initializer_list<const char*> ignore_list) { + auto current_cat = default_category_; + default_category_ = "Ignored"; for (auto&& ignore_name : ignore_list) { std::string ign = ignore_name; @@ -403,6 +480,7 @@ struct CmdlineParser { } } ignore_list_ = ignore_list; + default_category_ = current_cat; return *this; } @@ -415,7 +493,8 @@ struct CmdlineParser { auto&& p = CmdlineParser(ignore_unrecognized_, std::move(ignore_list_), save_destination_, - std::move(completed_arguments_)); + std::move(completed_arguments_), + std::move(category_order_)); return std::move(p); } @@ -439,10 +518,14 @@ struct CmdlineParser { bool ignore_unrecognized_ = false; std::vector<const char*> ignore_list_; std::shared_ptr<SaveDestination> save_destination_; + std::optional<const char*> default_category_; + std::vector<const char*> category_order_; std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_; }; + void DumpHelp(VariableIndentationOutputStream& vios); + CmdlineResult Parse(const std::string& argv) { std::vector<std::string> tokenized; Split(argv, ' ', &tokenized); @@ -500,11 +583,13 @@ struct CmdlineParser { CmdlineParser(bool ignore_unrecognized, std::vector<const char*>&& ignore_list, std::shared_ptr<SaveDestination> save_destination, - std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>>&& completed_arguments) + std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>>&& completed_arguments, + std::vector<const char*>&& category_order) : ignore_unrecognized_(ignore_unrecognized), ignore_list_(std::move(ignore_list)), save_destination_(save_destination), - completed_arguments_(std::move(completed_arguments)) { + completed_arguments_(std::move(completed_arguments)), + category_order_(category_order) { assert(save_destination != nullptr); } @@ -606,6 +691,7 @@ struct CmdlineParser { std::vector<const char*> ignore_list_; std::shared_ptr<SaveDestination> save_destination_; std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_; + std::vector<const char*> category_order_; }; // This has to be defined after everything else, since we want the builders to call this. @@ -628,6 +714,54 @@ void CmdlineParser<TVariantMap, TVariantMapKey>::AppendCompletedArgument( builder.AppendCompletedArgument(arg); } +template <typename TVariantMap, + template <typename TKeyValue> class TVariantMapKey> +void CmdlineParser<TVariantMap, TVariantMapKey>::DumpHelp(VariableIndentationOutputStream& vios) { + std::vector<detail::CmdlineParseArgumentAny*> uncat; + std::unordered_map<std::string, std::vector<detail::CmdlineParseArgumentAny*>> args; + for (const std::unique_ptr<detail::CmdlineParseArgumentAny>& it : completed_arguments_) { + auto cat = it->GetCategory(); + if (cat) { + if (args.find(cat.value()) == args.end()) { + args[cat.value()] = {}; + } + args.at(cat.value()).push_back(it.get()); + } else { + uncat.push_back(it.get()); + } + } + args.erase("Ignored"); + for (auto arg : uncat) { + arg->DumpHelp(vios); + vios.Stream(); + } + for (auto it : category_order_) { + auto cur = args.find(it); + if (cur != args.end() && !cur->second.empty()) { + vios.Stream() << "The following " << it << " arguments are supported:" << std::endl; + ScopedIndentation si(&vios); + for (detail::CmdlineParseArgumentAny* arg : cur->second) { + arg->DumpHelp(vios); + vios.Stream(); + } + args.erase(cur->first); + } + } + for (auto [cat, lst] : args) { + vios.Stream() << "The following " << cat << " arguments are supported:" << std::endl; + ScopedIndentation si(&vios); + for (auto& arg : completed_arguments_) { + arg->DumpHelp(vios); + vios.Stream(); + } + } + vios.Stream() << "The following arguments are ignored for compatibility:" << std::endl; + ScopedIndentation si(&vios); + for (auto ign : ignore_list_) { + vios.Stream() << ign << std::endl; + } +} + } // namespace art #endif // ART_CMDLINE_CMDLINE_PARSER_H_ diff --git a/cmdline/cmdline_type_parser.h b/cmdline/cmdline_type_parser.h index fa5cdaf906..82a76f4ad9 100644 --- a/cmdline/cmdline_type_parser.h +++ b/cmdline/cmdline_type_parser.h @@ -54,6 +54,7 @@ struct CmdlineTypeParser { // Runtime type name of T, so that we can print more useful error messages. static const char* Name() { assert(false); return "UnspecializedType"; } + static const char* DescribeType() { assert(false); return "UnspecializedType"; } // Whether or not your type can parse argument definitions defined without a "_" // e.g. -Xenable-profiler just mutates the existing profiler struct in-place diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h index 25902f1809..2d7d5f1b78 100644 --- a/cmdline/cmdline_types.h +++ b/cmdline/cmdline_types.h @@ -19,7 +19,9 @@ #define CMDLINE_NDEBUG 1 // Do not output any debugging information for parsing. #include <list> +#include <ostream> +#include "android-base/stringprintf.h" #include "cmdline_type_parser.h" #include "detail/cmdline_debug_detail.h" #include "memory_representation.h" @@ -87,6 +89,7 @@ struct CmdlineType<JdwpProvider> : CmdlineTypeParser<JdwpProvider> { } } static const char* Name() { return "JdwpProvider"; } + static const char* DescribeType() { return "none|adbconnection|default"; } }; template <size_t Divisor> @@ -173,6 +176,13 @@ struct CmdlineType<Memory<Divisor>> : CmdlineTypeParser<Memory<Divisor>> { } static const char* Name() { return Memory<Divisor>::Name(); } + static const char* DescribeType() { + static std::string str; + if (str.empty()) { + str = "Memory with granularity of " + std::to_string(Divisor) + " bytes"; + } + return str.c_str(); + } }; template <> @@ -194,6 +204,7 @@ struct CmdlineType<double> : CmdlineTypeParser<double> { } static const char* Name() { return "double"; } + static const char* DescribeType() { return "double value"; } }; template <typename T> @@ -226,6 +237,7 @@ struct CmdlineType<unsigned int> : CmdlineTypeParser<unsigned int> { } static const char* Name() { return "unsigned integer"; } + static const char* DescribeType() { return "unsigned integer value"; } }; template <> @@ -235,6 +247,7 @@ struct CmdlineType<int> : CmdlineTypeParser<int> { } static const char* Name() { return "integer"; } + static const char* DescribeType() { return "integer value"; } }; // Lightweight nanosecond value type. Allows parser to convert user-input from milliseconds @@ -289,6 +302,7 @@ struct CmdlineType<MillisecondsToNanoseconds> : CmdlineTypeParser<MillisecondsTo } static const char* Name() { return "MillisecondsToNanoseconds"; } + static const char* DescribeType() { return "millisecond value"; } }; template <> @@ -307,6 +321,7 @@ struct CmdlineType<std::string> : CmdlineTypeParser<std::string> { } return Result::SuccessNoValue(); } + static const char* DescribeType() { return "string value"; } }; template <> @@ -323,6 +338,7 @@ struct CmdlineType<std::vector<Plugin>> : CmdlineTypeParser<std::vector<Plugin>> } static const char* Name() { return "std::vector<Plugin>"; } + static const char* DescribeType() { return "/path/to/libplugin.so"; } }; template <> @@ -339,6 +355,7 @@ struct CmdlineType<std::list<ti::AgentSpec>> : CmdlineTypeParser<std::list<ti::A } static const char* Name() { return "std::list<ti::AgentSpec>"; } + static const char* DescribeType() { return "/path/to/libagent.so=options"; } }; template <> @@ -355,6 +372,7 @@ struct CmdlineType<std::vector<std::string>> : CmdlineTypeParser<std::vector<std } static const char* Name() { return "std::vector<std::string>"; } + static const char* DescribeType() { return "string value"; } }; template <char Separator> @@ -400,6 +418,13 @@ struct CmdlineType<ParseStringList<Separator>> : CmdlineTypeParser<ParseStringLi } static const char* Name() { return "ParseStringList<Separator>"; } + static const char* DescribeType() { + static std::string str; + if (str.empty()) { + str = android::base::StringPrintf("list separated by '%c'", Separator); + } + return str.c_str(); + } }; template <> @@ -433,6 +458,7 @@ struct CmdlineType<std::vector<int32_t>> : CmdlineTypeParser<std::vector<int32_t } static const char* Name() { return "std::vector<int32_t>"; } + static const char* DescribeType() { return "unsigned integer value"; } }; static gc::CollectorType ParseCollectorType(const std::string& option) { @@ -534,6 +560,11 @@ struct CmdlineType<XGcOption> : CmdlineTypeParser<XGcOption> { } static const char* Name() { return "XgcOption"; } + static const char* DescribeType() { + return "MS|nonconccurent|concurrent|CMS|SS|CC|[no]preverify[_rosalloc]|" + "[no]presweepingverify[_rosalloc]|[no]generation_cc|[no]postverify[_rosalloc]|" + "[no]gcstress|measure|[no]precisce|[no]verifycardtable"; + } }; struct BackgroundGcOption { @@ -573,6 +604,9 @@ struct CmdlineType<BackgroundGcOption> } static const char* Name() { return "BackgroundGcOption"; } + static const char* DescribeType() { + return "HSpaceCompact|MS|nonconccurent|CMS|concurrent|SS|CC"; + } }; template <> @@ -642,6 +676,11 @@ struct CmdlineType<LogVerbosity> : CmdlineTypeParser<LogVerbosity> { } static const char* Name() { return "LogVerbosity"; } + static const char* DescribeType() { + return "class|collector|compiler|deopt|gc|heap|interpreter|jdwp|jit|jni|monitor|oat|profiler|" + "signals|simulator|startup|third-party-jni|threads|verifier|verifier-debug|image|" + "systrace-locks|plugin|agents|dex"; + } }; template <> @@ -754,6 +793,7 @@ struct CmdlineType<ProfileSaverOptions> : CmdlineTypeParser<ProfileSaverOptions> } static const char* Name() { return "ProfileSaverOptions"; } + static const char* DescribeType() { return "string|unsigned integer"; } static constexpr bool kCanParseBlankless = true; }; @@ -769,6 +809,7 @@ struct CmdlineType<ExperimentalFlags> : CmdlineTypeParser<ExperimentalFlags> { } static const char* Name() { return "ExperimentalFlags"; } + static const char* DescribeType() { return "none"; } }; } // namespace art #endif // ART_CMDLINE_CMDLINE_TYPES_H_ diff --git a/cmdline/detail/cmdline_parse_argument_detail.h b/cmdline/detail/cmdline_parse_argument_detail.h index 215543586a..4591d0b11c 100644 --- a/cmdline/detail/cmdline_parse_argument_detail.h +++ b/cmdline/detail/cmdline_parse_argument_detail.h @@ -22,11 +22,13 @@ #include <functional> #include <memory> #include <numeric> +#include <string_view> #include <type_traits> #include <vector> #include "android-base/strings.h" +#include "base/indenter.h" #include "cmdline_parse_result.h" #include "cmdline_types.h" #include "token_range.h" @@ -35,6 +37,7 @@ namespace art { // Implementation details for the parser. Do not look inside if you hate templates. namespace detail { + // A non-templated base class for argument parsers. Used by the general parser // to parse arguments, without needing to know the argument type at compile time. // @@ -77,6 +80,10 @@ struct CmdlineParseArgumentAny { // Returns how many tokens were either matched (or ignored because there was a // wildcard present). 0 means no match. If the Size() tokens are returned. virtual size_t MaybeMatches(const TokenRange& tokens) = 0; + + virtual void DumpHelp(VariableIndentationOutputStream& os) = 0; + + virtual const std::optional<const char*>& GetCategory() = 0; }; template <typename T> @@ -136,6 +143,49 @@ struct CmdlineParserArgumentInfo { return std::make_pair(best_match_ptr, best_match); } + template <typename T = TArg> // Necessary to get SFINAE to kick in. + void DumpHelp(VariableIndentationOutputStream& vios) { + for (auto cname : names_) { + std::string_view name = cname; + auto& os = vios.Stream(); + std::function<void()> print_once; + if (using_blanks_) { + std::string_view nblank = name.substr(0, name.find("_")); + print_once = [&]() { + os << nblank; + if (has_value_map_) { + bool first = true; + for (auto [val, unused] : value_map_) { + os << (first ? "{" : "|") << val; + first = false; + } + os << "}"; + } else if (metavar_) { + os << metavar_.value(); + } else { + os << "{" << CmdlineType<T>::DescribeType() << "}"; + } + }; + } else { + print_once = [&]() { + os << name; + }; + } + print_once(); + if (appending_values_) { + os << " ["; + print_once(); + os << "...]"; + } + os << std::endl; + } + if (help_) { + ScopedIndentation si(&vios); + vios.Stream() << help_.value() << std::endl; + } + } + + // Mark the argument definition as completed, do not mutate the object anymore after this // call is done. // @@ -279,6 +329,10 @@ struct CmdlineParserArgumentInfo { bool has_value_list_ = false; std::vector<TArg> value_list_; + std::optional<const char*> help_; + std::optional<const char*> category_; + std::optional<const char*> metavar_; + // Make sure there's a default constructor. CmdlineParserArgumentInfo() = default; @@ -380,6 +434,14 @@ struct CmdlineParseArgument : CmdlineParseArgumentAny { return ParseArgumentSingle(blank_value); } + virtual void DumpHelp(VariableIndentationOutputStream& os) { + argument_info_.DumpHelp(os); + } + + virtual const std::optional<const char*>& GetCategory() { + return argument_info_.category_; + } + private: virtual CmdlineResult ParseArgumentSingle(const std::string& argument) { // TODO: refactor to use LookupValue for the value lists/maps diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 5bb9ed6698..0a52e7e861 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -23,6 +23,7 @@ #include <android-base/strings.h> #include "base/file_utils.h" +#include "base/indenter.h" #include "base/macros.h" #include "base/utils.h" #include "debugger.h" @@ -81,54 +82,59 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize static_cast<size_t>(hiddenapi::EnforcementPolicy::kMax) + 1); parser_builder-> - Define("-Xzygote") - .IntoKey(M::Zygote) - .Define("-Xprimaryzygote") - .IntoKey(M::PrimaryZygote) - .Define("-help") - .IntoKey(M::Help) - .Define("-showversion") - .IntoKey(M::ShowVersion) - .Define("-Xbootclasspath:_") - .WithType<ParseStringList<':'>>() // std::vector<std::string>, split by : - .IntoKey(M::BootClassPath) - .Define("-Xbootclasspath-locations:_") - .WithType<ParseStringList<':'>>() // std::vector<std::string>, split by : - .IntoKey(M::BootClassPathLocations) + SetCategory("standard") .Define({"-classpath _", "-cp _"}) + .WithHelp("The classpath, separated by ':'") .WithType<std::string>() .IntoKey(M::ClassPath) - .Define("-Ximage:_") - .WithType<std::string>() - .IntoKey(M::Image) - .Define("-Ximage-load-order:_") - .WithType<gc::space::ImageSpaceLoadingOrder>() - .WithValueMap({{"system", gc::space::ImageSpaceLoadingOrder::kSystemFirst}, - {"data", gc::space::ImageSpaceLoadingOrder::kDataFirst}}) - .IntoKey(M::ImageSpaceLoadingOrder) - .Define("-Xcheck:jni") - .IntoKey(M::CheckJni) - .Define("-Xjniopts:forcecopy") - .IntoKey(M::JniOptsForceCopy) - .Define("-XjdwpProvider:_") - .WithType<JdwpProvider>() - .IntoKey(M::JdwpProvider) - .Define("-XjdwpOptions:_") - .WithType<std::string>() - .IntoKey(M::JdwpOptions) + .Define("-D_") + .WithType<std::vector<std::string>>().AppendValues() + .IntoKey(M::PropertiesList) + .Define("-verbose:_") + .WithHelp("Switches for advanced logging. Multiple categories can be enabled separated by ','. Eg: -verbose:class,deopt") + .WithType<LogVerbosity>() + .IntoKey(M::Verbose) + .Define({"-help", "-h"}) + .WithHelp("Print this help text.") + .IntoKey(M::Help) + .Define("-showversion") + .IntoKey(M::ShowVersion) // TODO Re-enable -agentlib: once I have a good way to transform the values. // .Define("-agentlib:_") // .WithType<std::vector<ti::Agent>>().AppendValues() // .IntoKey(M::AgentLib) .Define("-agentpath:_") + .WithHelp("Load native agents.") .WithType<std::list<ti::AgentSpec>>().AppendValues() .IntoKey(M::AgentPath) + .SetCategory("extended") + .Define("-Xbootclasspath:_") + .WithType<ParseStringList<':'>>() // std::vector<std::string>, split by : + .IntoKey(M::BootClassPath) + .Define("-Xcheck:jni") + .IntoKey(M::CheckJni) .Define("-Xms_") .WithType<MemoryKiB>() .IntoKey(M::MemoryInitialSize) .Define("-Xmx_") .WithType<MemoryKiB>() .IntoKey(M::MemoryMaximumSize) + .Define("-Xss_") + .WithType<Memory<1>>() + .IntoKey(M::StackSize) + .Define("-Xint") + .WithValue(true) + .IntoKey(M::Interpret) + .SetCategory("Dalvik") + .Define("-Xzygote") + .WithHelp("Start as zygote") + .IntoKey(M::Zygote) + .Define("-Xjnitrace:_") + .WithType<std::string>() + .IntoKey(M::JniTrace) + .Define("-Xgc:_") + .WithType<XGcOption>() + .IntoKey(M::GcOption) .Define("-XX:HeapGrowthLimit=_") .WithType<MemoryKiB>() .IntoKey(M::HeapGrowthLimit) @@ -141,15 +147,50 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize .Define("-XX:NonMovingSpaceCapacity=_") .WithType<MemoryKiB>() .IntoKey(M::NonMovingSpaceCapacity) - .Define("-XX:StopForNativeAllocs=_") - .WithType<MemoryKiB>() - .IntoKey(M::StopForNativeAllocs) .Define("-XX:HeapTargetUtilization=_") .WithType<double>().WithRange(0.1, 0.9) .IntoKey(M::HeapTargetUtilization) .Define("-XX:ForegroundHeapGrowthMultiplier=_") .WithType<double>().WithRange(0.1, 5.0) .IntoKey(M::ForegroundHeapGrowthMultiplier) + .Define("-XX:LowMemoryMode") + .IntoKey(M::LowMemoryMode) + .Define("-Xprofile:_") + .WithType<TraceClockSource>() + .WithValueMap({{"threadcpuclock", TraceClockSource::kThreadCpu}, + {"wallclock", TraceClockSource::kWall}, + {"dualclock", TraceClockSource::kDual}}) + .IntoKey(M::ProfileClock) + .Define("-Xjitthreshold:_") + .WithType<unsigned int>() + .IntoKey(M::JITCompileThreshold) + .SetCategory("ART") + .Define("-Ximage:_") + .WithType<std::string>() + .IntoKey(M::Image) + .Define("-Xprimaryzygote") + .IntoKey(M::PrimaryZygote) + .Define("-Xbootclasspath-locations:_") + .WithType<ParseStringList<':'>>() // std::vector<std::string>, split by : + .IntoKey(M::BootClassPathLocations) + .Define("-Ximage-load-order:_") + .WithType<gc::space::ImageSpaceLoadingOrder>() + .WithValueMap({{"system", gc::space::ImageSpaceLoadingOrder::kSystemFirst}, + {"data", gc::space::ImageSpaceLoadingOrder::kDataFirst}}) + .IntoKey(M::ImageSpaceLoadingOrder) + .Define("-Xjniopts:forcecopy") + .IntoKey(M::JniOptsForceCopy) + .Define("-XjdwpProvider:_") + .WithType<JdwpProvider>() + .IntoKey(M::JdwpProvider) + .Define("-XjdwpOptions:_") + .WithMetavar("OPTION[,OPTION...]") + .WithHelp("JDWP options. Eg suspend=n,server=y.") + .WithType<std::string>() + .IntoKey(M::JdwpOptions) + .Define("-XX:StopForNativeAllocs=_") + .WithType<MemoryKiB>() + .IntoKey(M::StopForNativeAllocs) .Define("-XX:ParallelGCThreads=_") .WithType<unsigned int>() .IntoKey(M::ParallelGCThreads) @@ -159,9 +200,6 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize .Define("-XX:FinalizerTimeoutMs=_") .WithType<unsigned int>() .IntoKey(M::FinalizerTimeoutMs) - .Define("-Xss_") - .WithType<Memory<1>>() - .IntoKey(M::StackSize) .Define("-XX:MaxSpinsBeforeThinLockInflation=_") .WithType<unsigned int>() .IntoKey(M::MaxSpinsBeforeThinLockInflation) @@ -181,8 +219,6 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize .IntoKey(M::DumpJITInfoOnShutdown) .Define("-XX:IgnoreMaxFootprint") .IntoKey(M::IgnoreMaxFootprint) - .Define("-XX:LowMemoryMode") - .IntoKey(M::LowMemoryMode) .Define("-XX:UseTLAB") .WithValue(true) .IntoKey(M::UseTLAB) @@ -211,9 +247,6 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize .Define("-Xjitmaxsize:_") .WithType<MemoryKiB>() .IntoKey(M::JITCodeCacheMaxCapacity) - .Define("-Xjitthreshold:_") - .WithType<unsigned int>() - .IntoKey(M::JITCompileThreshold) .Define("-Xjitwarmupthreshold:_") .WithType<unsigned int>() .IntoKey(M::JITWarmupThreshold) @@ -233,31 +266,36 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize .WithType<ProfileSaverOptions>() .AppendValues() .IntoKey(M::ProfileSaverOpts) - .Define("-Xps-_") // profile saver options -Xps-<key>:<value> + // .Define("-Xps-_") // profile saver options -Xps-<key>:<value> + // .WithType<ProfileSaverOptions>() + // .AppendValues() + // .IntoKey(M::ProfileSaverOpts) // NOTE: Appends into same key as -Xjitsaveprofilinginfo + // profile saver options -Xps-<key>:<value> but are split-out for better help messages. + // The order of these is important. We want the wildcard one to be the + // only one actually matched so it needs to be first. + // TODO This should be redone. + .Define({"-Xps-_", + "-Xps-min-save-period-ms:_", + "-Xps-save-resolved-classes-delayed-ms:_", + "-Xps-hot-startup-method-samples:_", + "-Xps-min-methods-to-save:_", + "-Xps-min-classes-to-save:_", + "-Xps-min-notification-before-wake:_", + "-Xps-max-notification-before-wake:_", + "-Xps-profile-path:_"}) + .WithHelp("profile-saver options -Xps-<key>:<value>") .WithType<ProfileSaverOptions>() .AppendValues() .IntoKey(M::ProfileSaverOpts) // NOTE: Appends into same key as -Xjitsaveprofilinginfo .Define("-XX:HspaceCompactForOOMMinIntervalMs=_") // in ms .WithType<MillisecondsToNanoseconds>() // store as ns .IntoKey(M::HSpaceCompactForOOMMinIntervalsMs) - .Define("-D_") - .WithType<std::vector<std::string>>().AppendValues() - .IntoKey(M::PropertiesList) - .Define("-Xjnitrace:_") - .WithType<std::string>() - .IntoKey(M::JniTrace) .Define({"-Xrelocate", "-Xnorelocate"}) .WithValues({true, false}) .IntoKey(M::Relocate) .Define({"-Ximage-dex2oat", "-Xnoimage-dex2oat"}) .WithValues({true, false}) .IntoKey(M::ImageDex2Oat) - .Define("-Xint") - .WithValue(true) - .IntoKey(M::Interpret) - .Define("-Xgc:_") - .WithType<XGcOption>() - .IntoKey(M::GcOption) .Define("-XX:LargeObjectSpace=_") .WithType<gc::space::LargeObjectSpaceType>() .WithValueMap({{"disabled", gc::space::LargeObjectSpaceType::kDisabled}, @@ -272,9 +310,6 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize .IntoKey(M::BackgroundGc) .Define("-XX:+DisableExplicitGC") .IntoKey(M::DisableExplicitGC) - .Define("-verbose:_") - .WithType<LogVerbosity>() - .IntoKey(M::Verbose) .Define("-Xlockprofthreshold:_") .WithType<unsigned int>() .IntoKey(M::LockProfThreshold) @@ -291,12 +326,6 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize .IntoKey(M::MethodTraceFileSize) .Define("-Xmethod-trace-stream") .IntoKey(M::MethodTraceStreaming) - .Define("-Xprofile:_") - .WithType<TraceClockSource>() - .WithValueMap({{"threadcpuclock", TraceClockSource::kThreadCpu}, - {"wallclock", TraceClockSource::kWall}, - {"dualclock", TraceClockSource::kDual}}) - .IntoKey(M::ProfileClock) .Define("-Xcompiler:_") .WithType<std::string>() .IntoKey(M::Compiler) @@ -336,6 +365,7 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize .Define("-Xforce-nb-testing") .IntoKey(M::ForceNativeBridge) .Define("-Xplugin:_") + .WithHelp("Load and initialize the specified art-plugin.") .WithType<std::vector<Plugin>>().AppendValues() .IntoKey(M::Plugins) .Define("-XX:ThreadSuspendTimeout=_") // in ms @@ -371,6 +401,7 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize .WithValueMap({{"false", false}, {"true", true}}) .IntoKey(M::FastClassNotFoundException) .Define("-Xopaque-jni-ids:_") + .WithHelp("Control the representation of jmethodID and jfieldID values") .WithType<JniIdType>() .WithValueMap({{"true", JniIdType::kIndices}, {"false", JniIdType::kPointer}, @@ -396,11 +427,12 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize "-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_", "-Xdexopt:_", "-Xnoquithandler", "-Xjnigreflimit:_", "-Xgenregmap", "-Xnogenregmap", "-Xverifyopt:_", "-Xcheckdexsum", "-Xincludeselectedop", "-Xjitop:_", - "-Xincludeselectedmethod", "-Xjitthreshold:_", + "-Xincludeselectedmethod", "-Xjitblocking", "-Xjitmethod:_", "-Xjitclass:_", "-Xjitoffset:_", "-Xjitconfig:_", "-Xjitcheckcg", "-Xjitverbose", "-Xjitprofile", "-Xjitdisableopt", "-Xjitsuspendpoll", "-XX:mainThreadStackSize=_"}) - .IgnoreUnrecognized(ignore_unrecognized); + .IgnoreUnrecognized(ignore_unrecognized) + .OrderCategories({"standard", "extended", "Dalvik", "ART"}); // TODO: Move Usage information into this DSL. @@ -705,147 +737,12 @@ void ParsedOptions::Usage(const char* fmt, ...) { const char* program = "dalvikvm"; UsageMessage(stream, "%s: [options] class [argument ...]\n", program); UsageMessage(stream, "\n"); - UsageMessage(stream, "The following standard options are supported:\n"); - UsageMessage(stream, " -classpath classpath (-cp classpath)\n"); - UsageMessage(stream, " -Dproperty=value\n"); - UsageMessage(stream, " -verbose:tag[,tag...] (currently valid tags: 'agents', 'class',\n" - " 'collector', 'compiler', 'deopt', 'dex', 'gc', 'heap', 'image',\n" - " 'interpreter', 'jdwp', 'jit', 'jni', 'monitor', 'oat', 'profiler',\n" - " 'signals', 'simulator', 'startup', 'systrace-locks',\n" - " 'third-party-jni', 'threads', 'verifier', 'verifier-debug')\n"); - UsageMessage(stream, " -showversion\n"); - UsageMessage(stream, " -help\n"); - // TODO add back in once -agentlib actually does something. - // UsageMessage(stream, " -agentlib:library=options (Experimental feature, " - // "requires -Xexperimental:agent, some features might not be supported)\n"); - UsageMessage(stream, " -agentpath:library_path=options (Experimental feature, requires\n" - " -Xexperimental:agent, some features might not be supported)\n"); - UsageMessage(stream, "\n"); - - UsageMessage(stream, "The following extended options are supported:\n"); - UsageMessage(stream, " -Xbootclasspath:bootclasspath\n"); - UsageMessage(stream, " -Xcheck:tag (e.g. 'jni')\n"); - UsageMessage(stream, " -XmsN (min heap, must be multiple of 1K, >= 1MB)\n"); - UsageMessage(stream, " -XmxN (max heap, must be multiple of 1K, >= 2MB)\n"); - UsageMessage(stream, " -XssN (stack size)\n"); - UsageMessage(stream, " -Xint\n"); - UsageMessage(stream, "\n"); - - UsageMessage(stream, "The following Dalvik options are supported:\n"); - UsageMessage(stream, " -Xzygote\n"); - UsageMessage(stream, " -Xjnitrace:substring (eg NativeClass or nativeMethod)\n"); - UsageMessage(stream, " -Xgc:[no]preverify\n"); - UsageMessage(stream, " -Xgc:[no]postverify\n"); - UsageMessage(stream, " -XX:HeapGrowthLimit=N\n"); - UsageMessage(stream, " -XX:HeapMinFree=N\n"); - UsageMessage(stream, " -XX:HeapMaxFree=N\n"); - UsageMessage(stream, " -XX:NonMovingSpaceCapacity=N\n"); - UsageMessage(stream, " -XX:HeapTargetUtilization=doublevalue\n"); - UsageMessage(stream, " -XX:ForegroundHeapGrowthMultiplier=doublevalue\n"); - UsageMessage(stream, " -XX:LowMemoryMode\n"); - UsageMessage(stream, " -Xprofile:{threadcpuclock,wallclock,dualclock}\n"); - UsageMessage(stream, " -Xjitthreshold:integervalue\n"); - UsageMessage(stream, "\n"); - - UsageMessage(stream, "The following unique to ART options are supported:\n"); - UsageMessage(stream, " -Xgc:[no]preverify_rosalloc\n"); - UsageMessage(stream, " -Xgc:[no]postsweepingverify_rosalloc\n"); - UsageMessage(stream, " -Xgc:[no]postverify_rosalloc\n"); - UsageMessage(stream, " -Xgc:[no]presweepingverify\n"); - UsageMessage(stream, " -Xgc:[no]generational_cc\n"); - UsageMessage(stream, " -Ximage:filename\n"); - UsageMessage(stream, " -Xbootclasspath-locations:bootclasspath\n" - " (override the dex locations of the -Xbootclasspath files)\n"); - UsageMessage(stream, " -XX:+DisableExplicitGC\n"); - UsageMessage(stream, " -XX:ParallelGCThreads=integervalue\n"); - UsageMessage(stream, " -XX:ConcGCThreads=integervalue\n"); - UsageMessage(stream, " -XX:FinalizerTimeoutMs=integervalue\n"); - UsageMessage(stream, " -XX:MaxSpinsBeforeThinLockInflation=integervalue\n"); - UsageMessage(stream, " -XX:LongPauseLogThreshold=integervalue\n"); - UsageMessage(stream, " -XX:LongGCLogThreshold=integervalue\n"); - UsageMessage(stream, " -XX:ThreadSuspendTimeout=integervalue\n"); - UsageMessage(stream, " -XX:DumpGCPerformanceOnShutdown\n"); - UsageMessage(stream, " -XX:DumpJITInfoOnShutdown\n"); - UsageMessage(stream, " -XX:IgnoreMaxFootprint\n"); - UsageMessage(stream, " -XX:UseTLAB\n"); - UsageMessage(stream, " -XX:BackgroundGC=none\n"); - UsageMessage(stream, " -XX:LargeObjectSpace={disabled,map,freelist}\n"); - UsageMessage(stream, " -XX:LargeObjectThreshold=N\n"); - UsageMessage(stream, " -XX:StopForNativeAllocs=N\n"); - UsageMessage(stream, " -XX:DumpNativeStackOnSigQuit=booleanvalue\n"); - UsageMessage(stream, " -XX:MadviseRandomAccess:booleanvalue\n"); - UsageMessage(stream, " -XX:SlowDebug={false,true}\n"); - UsageMessage(stream, " -Xmethod-trace\n"); - UsageMessage(stream, " -Xmethod-trace-file:filename\n"); - UsageMessage(stream, " -Xmethod-trace-file-size:integervalue\n"); - UsageMessage(stream, " -Xps-min-save-period-ms:integervalue\n"); - UsageMessage(stream, " -Xps-save-resolved-classes-delay-ms:integervalue\n"); - UsageMessage(stream, " -Xps-hot-startup-method-samples:integervalue\n"); - UsageMessage(stream, " -Xps-min-methods-to-save:integervalue\n"); - UsageMessage(stream, " -Xps-min-classes-to-save:integervalue\n"); - UsageMessage(stream, " -Xps-min-notification-before-wake:integervalue\n"); - UsageMessage(stream, " -Xps-max-notification-before-wake:integervalue\n"); - UsageMessage(stream, " -Xps-profile-path:file-path\n"); - UsageMessage(stream, " -Xcompiler:filename\n"); - UsageMessage(stream, " -Xcompiler-option dex2oat-option\n"); - UsageMessage(stream, " -Ximage-compiler-option dex2oat-option\n"); - UsageMessage(stream, " -Xusejit:booleanvalue\n"); - UsageMessage(stream, " -Xjitinitialsize:N\n"); - UsageMessage(stream, " -Xjitmaxsize:N\n"); - UsageMessage(stream, " -Xjitwarmupthreshold:integervalue\n"); - UsageMessage(stream, " -Xjitosrthreshold:integervalue\n"); - UsageMessage(stream, " -Xjitprithreadweight:integervalue\n"); - UsageMessage(stream, " -X[no]relocate\n"); - UsageMessage(stream, " -X[no]dex2oat (Whether to invoke dex2oat on the application)\n"); - UsageMessage(stream, " -X[no]image-dex2oat (Whether to create and use a boot image)\n"); - UsageMessage(stream, " -Xno-dex-file-fallback " - "(Don't fall back to dex files without oat files)\n"); - UsageMessage(stream, " -Xplugin:<library.so> " - "(Load a runtime plugin, requires -Xexperimental:runtime-plugins)\n"); - UsageMessage(stream, " -Xexperimental:runtime-plugins" - "(Enable new and experimental agent support)\n"); - UsageMessage(stream, " -Xexperimental:agents" - "(Enable new and experimental agent support)\n"); - UsageMessage(stream, " -Xopaque-jni-ids:{true,false,swapable}"); - UsageMessage(stream, "(Use opauque integers for jni ids, yes, no or punt for later)\n"); - UsageMessage(stream, "\n"); - - UsageMessage(stream, "The following previously supported Dalvik options are ignored:\n"); - UsageMessage(stream, " -ea[:<package name>... |:<class name>]\n"); - UsageMessage(stream, " -da[:<package name>... |:<class name>]\n"); - UsageMessage(stream, " (-enableassertions, -disableassertions)\n"); - UsageMessage(stream, " -esa\n"); - UsageMessage(stream, " -dsa\n"); - UsageMessage(stream, " (-enablesystemassertions, -disablesystemassertions)\n"); - UsageMessage(stream, " -Xverify:{none,remote,all,softfail}\n"); - UsageMessage(stream, " -Xrs\n"); - UsageMessage(stream, " -Xint:portable, -Xint:fast, -Xint:jit\n"); - UsageMessage(stream, " -Xdexopt:{none,verified,all,full}\n"); - UsageMessage(stream, " -Xnoquithandler\n"); - UsageMessage(stream, " -Xjniopts:{warnonly,forcecopy}\n"); - UsageMessage(stream, " -Xjnigreflimit:integervalue\n"); - UsageMessage(stream, " -Xgc:[no]precise\n"); - UsageMessage(stream, " -Xgc:[no]verifycardtable\n"); - UsageMessage(stream, " -X[no]genregmap\n"); - UsageMessage(stream, " -Xverifyopt:[no]checkmon\n"); - UsageMessage(stream, " -Xcheckdexsum\n"); - UsageMessage(stream, " -Xincludeselectedop\n"); - UsageMessage(stream, " -Xjitop:hexopvalue[-endvalue][,hexopvalue[-endvalue]]*\n"); - UsageMessage(stream, " -Xincludeselectedmethod\n"); - UsageMessage(stream, " -Xjitblocking\n"); - UsageMessage(stream, " -Xjitmethod:signature[,signature]* (eg Ljava/lang/String\\;replace)\n"); - UsageMessage(stream, " -Xjitclass:classname[,classname]*\n"); - UsageMessage(stream, " -Xjitcodecachesize:N\n"); - UsageMessage(stream, " -Xjitoffset:offset[,offset]\n"); - UsageMessage(stream, " -Xjitconfig:filename\n"); - UsageMessage(stream, " -Xjitcheckcg\n"); - UsageMessage(stream, " -Xjitverbose\n"); - UsageMessage(stream, " -Xjitprofile\n"); - UsageMessage(stream, " -Xjitdisableopt\n"); - UsageMessage(stream, " -Xjitsuspendpoll\n"); - UsageMessage(stream, " -XX:mainThreadStackSize=N\n"); - UsageMessage(stream, "\n"); + std::stringstream oss; + VariableIndentationOutputStream vios(&oss); + auto parser = MakeParser(false); + parser->DumpHelp(vios); + UsageMessage(stream, oss.str().c_str()); Exit((error) ? 1 : 0); } diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h index 39ce4cebfa..f489b07ae8 100644 --- a/runtime/parsed_options.h +++ b/runtime/parsed_options.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_PARSED_OPTIONS_H_ #include <string> +#include <string_view> #include <vector> #include <jni.h> |