summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Alex Light <allight@google.com> 2020-07-13 16:10:03 -0700
committer Treehugger Robot <treehugger-gerrit@google.com> 2020-07-17 00:42:41 +0000
commiteb9da9eb2319dbac58683e01940de4171ce01027 (patch)
tree5ce09a7cca640400395860071a17e00f10358813
parentc607092fda93d8d9a52ec8ee2fddd6a685a6785a (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.h156
-rw-r--r--cmdline/cmdline_type_parser.h1
-rw-r--r--cmdline/cmdline_types.h41
-rw-r--r--cmdline/detail/cmdline_parse_argument_detail.h62
-rw-r--r--runtime/parsed_options.cc311
-rw-r--r--runtime/parsed_options.h1
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>