summaryrefslogtreecommitdiffstats
path: root/cmdline/cmdline_types.h
diff options
context:
space:
mode:
Diffstat (limited to 'cmdline/cmdline_types.h')
-rw-r--r--cmdline/cmdline_types.h820
1 files changed, 820 insertions, 0 deletions
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
new file mode 100644
index 0000000000..92210235e8
--- /dev/null
+++ b/cmdline/cmdline_types.h
@@ -0,0 +1,820 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ART_CMDLINE_CMDLINE_TYPES_H_
+#define ART_CMDLINE_CMDLINE_TYPES_H_
+
+#define CMDLINE_NDEBUG 1 // Do not output any debugging information for parsing.
+
+#include "cmdline/memory_representation.h"
+#include "cmdline/detail/cmdline_debug_detail.h"
+#include "cmdline_type_parser.h"
+
+// Includes for the types that are being specialized
+#include <string>
+#include "unit.h"
+#include "jdwp/jdwp.h"
+#include "runtime/base/logging.h"
+#include "gc/collector_type.h"
+#include "gc/space/large_object_space.h"
+#include "profiler_options.h"
+
+namespace art {
+
+// The default specialization will always fail parsing the type from a string.
+// Provide your own specialization that inherits from CmdlineTypeParser<T>
+// and implements either Parse or ParseAndAppend
+// (only if the argument was defined with ::AppendValues()) but not both.
+template <typename T>
+struct CmdlineType : CmdlineTypeParser<T> {
+};
+
+// Specializations for CmdlineType<T> follow:
+
+// Parse argument definitions for Unit-typed arguments.
+template <>
+struct CmdlineType<Unit> : CmdlineTypeParser<Unit> {
+ Result Parse(const std::string& args) {
+ if (args == "") {
+ return Result::Success(Unit{}); // NOLINT [whitespace/braces] [5]
+ }
+ return Result::Failure("Unexpected extra characters " + args);
+ }
+};
+
+template <>
+struct CmdlineType<JDWP::JdwpOptions> : CmdlineTypeParser<JDWP::JdwpOptions> {
+ Result Parse(const std::string& options) {
+ VLOG(jdwp) << "ParseJdwpOptions: " << options;
+
+ if (options == "help") {
+ return Result::Usage(
+ "Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n"
+ "Example: -Xrunjdwp:transport=dt_socket,address=localhost:6500,server=n\n");
+ }
+
+ const std::string s;
+
+ std::vector<std::string> pairs;
+ Split(options, ',', &pairs);
+
+ JDWP::JdwpOptions jdwp_options = JDWP::JdwpOptions();
+ std::stringstream error_stream;
+
+ for (size_t i = 0; i < pairs.size(); ++i) {
+ std::string::size_type equals = pairs[i].find('=');
+ if (equals == std::string::npos) {
+ return Result::Failure(s +
+ "Can't parse JDWP option '" + pairs[i] + "' in '" + options + "'");
+ }
+
+ if (!ParseJdwpOption(pairs[i].substr(0, equals),
+ pairs[i].substr(equals + 1),
+ error_stream,
+ jdwp_options)) {
+ return Result::Failure(error_stream.str());
+ }
+ }
+
+ if (jdwp_options.transport == JDWP::kJdwpTransportUnknown) {
+ return Result::Failure(s + "Must specify JDWP transport: " + options);
+ }
+ if (!jdwp_options.server && (jdwp_options.host.empty() || jdwp_options.port == 0)) {
+ return Result::Failure(s + "Must specify JDWP host and port when server=n: " + options);
+ }
+
+ return Result::Success(std::move(jdwp_options));
+ }
+
+ bool ParseJdwpOption(const std::string& name, const std::string& value,
+ std::ostream& error_stream,
+ JDWP::JdwpOptions& jdwp_options) {
+ if (name == "transport") {
+ if (value == "dt_socket") {
+ jdwp_options.transport = JDWP::kJdwpTransportSocket;
+ } else if (value == "dt_android_adb") {
+ jdwp_options.transport = JDWP::kJdwpTransportAndroidAdb;
+ } else {
+ error_stream << "JDWP transport not supported: " << value;
+ return false;
+ }
+ } else if (name == "server") {
+ if (value == "n") {
+ jdwp_options.server = false;
+ } else if (value == "y") {
+ jdwp_options.server = true;
+ } else {
+ error_stream << "JDWP option 'server' must be 'y' or 'n'";
+ return false;
+ }
+ } else if (name == "suspend") {
+ if (value == "n") {
+ jdwp_options.suspend = false;
+ } else if (value == "y") {
+ jdwp_options.suspend = true;
+ } else {
+ error_stream << "JDWP option 'suspend' must be 'y' or 'n'";
+ return false;
+ }
+ } else if (name == "address") {
+ /* this is either <port> or <host>:<port> */
+ std::string port_string;
+ jdwp_options.host.clear();
+ std::string::size_type colon = value.find(':');
+ if (colon != std::string::npos) {
+ jdwp_options.host = value.substr(0, colon);
+ port_string = value.substr(colon + 1);
+ } else {
+ port_string = value;
+ }
+ if (port_string.empty()) {
+ error_stream << "JDWP address missing port: " << value;
+ return false;
+ }
+ char* end;
+ uint64_t port = strtoul(port_string.c_str(), &end, 10);
+ if (*end != '\0' || port > 0xffff) {
+ error_stream << "JDWP address has junk in port field: " << value;
+ return false;
+ }
+ jdwp_options.port = port;
+ } else if (name == "launch" || name == "onthrow" || name == "oncaught" || name == "timeout") {
+ /* valid but unsupported */
+ LOG(INFO) << "Ignoring JDWP option '" << name << "'='" << value << "'";
+ } else {
+ LOG(INFO) << "Ignoring unrecognized JDWP option '" << name << "'='" << value << "'";
+ }
+
+ return true;
+ }
+
+ static const char* Name() { return "JdwpOptions"; }
+};
+
+template <size_t Divisor>
+struct CmdlineType<Memory<Divisor>> : CmdlineTypeParser<Memory<Divisor>> {
+ using typename CmdlineTypeParser<Memory<Divisor>>::Result;
+
+ Result Parse(const std::string arg) {
+ CMDLINE_DEBUG_LOG << "Parsing memory: " << arg << std::endl;
+ size_t val = ParseMemoryOption(arg.c_str(), Divisor);
+ CMDLINE_DEBUG_LOG << "Memory parsed to size_t value: " << val << std::endl;
+
+ if (val == 0) {
+ return Result::Failure(std::string("not a valid memory value, or not divisible by ")
+ + std::to_string(Divisor));
+ }
+
+ return Result::Success(Memory<Divisor>(val));
+ }
+
+ // Parse a string of the form /[0-9]+[kKmMgG]?/, which is used to specify
+ // memory sizes. [kK] indicates kilobytes, [mM] megabytes, and
+ // [gG] gigabytes.
+ //
+ // "s" should point just past the "-Xm?" part of the string.
+ // "div" specifies a divisor, e.g. 1024 if the value must be a multiple
+ // of 1024.
+ //
+ // The spec says the -Xmx and -Xms options must be multiples of 1024. It
+ // doesn't say anything about -Xss.
+ //
+ // Returns 0 (a useless size) if "s" is malformed or specifies a low or
+ // non-evenly-divisible value.
+ //
+ static size_t ParseMemoryOption(const char* s, size_t div) {
+ // strtoul accepts a leading [+-], which we don't want,
+ // so make sure our string starts with a decimal digit.
+ if (isdigit(*s)) {
+ char* s2;
+ size_t val = strtoul(s, &s2, 10);
+ if (s2 != s) {
+ // s2 should be pointing just after the number.
+ // If this is the end of the string, the user
+ // has specified a number of bytes. Otherwise,
+ // there should be exactly one more character
+ // that specifies a multiplier.
+ if (*s2 != '\0') {
+ // The remainder of the string is either a single multiplier
+ // character, or nothing to indicate that the value is in
+ // bytes.
+ char c = *s2++;
+ if (*s2 == '\0') {
+ size_t mul;
+ if (c == '\0') {
+ mul = 1;
+ } else if (c == 'k' || c == 'K') {
+ mul = KB;
+ } else if (c == 'm' || c == 'M') {
+ mul = MB;
+ } else if (c == 'g' || c == 'G') {
+ mul = GB;
+ } else {
+ // Unknown multiplier character.
+ return 0;
+ }
+
+ if (val <= std::numeric_limits<size_t>::max() / mul) {
+ val *= mul;
+ } else {
+ // Clamp to a multiple of 1024.
+ val = std::numeric_limits<size_t>::max() & ~(1024-1);
+ }
+ } else {
+ // There's more than one character after the numeric part.
+ return 0;
+ }
+ }
+ // The man page says that a -Xm value must be a multiple of 1024.
+ if (val % div == 0) {
+ return val;
+ }
+ }
+ }
+ return 0;
+ }
+
+ static const char* Name() { return Memory<Divisor>::Name(); }
+};
+
+template <>
+struct CmdlineType<double> : CmdlineTypeParser<double> {
+ Result Parse(const std::string& str) {
+ char* end = nullptr;
+ errno = 0;
+ double value = strtod(str.c_str(), &end);
+
+ if (*end != '\0') {
+ return Result::Failure("Failed to parse double from " + str);
+ }
+ if (errno == ERANGE) {
+ return Result::OutOfRange(
+ "Failed to parse double from " + str + "; overflow/underflow occurred");
+ }
+
+ return Result::Success(value);
+ }
+
+ static const char* Name() { return "double"; }
+};
+
+template <>
+struct CmdlineType<unsigned int> : CmdlineTypeParser<unsigned int> {
+ Result Parse(const std::string& str) {
+ const char* begin = str.c_str();
+ char* end;
+
+ // Parse into a larger type (long long) because we can't use strtoul
+ // since it silently converts negative values into unsigned long and doesn't set errno.
+ errno = 0;
+ long long int result = strtoll(begin, &end, 10); // NOLINT [runtime/int] [4]
+ if (begin == end || *end != '\0' || errno == EINVAL) {
+ return Result::Failure("Failed to parse integer from " + str);
+ } else if ((errno == ERANGE) || // NOLINT [runtime/int] [4]
+ result < std::numeric_limits<int>::min()
+ || result > std::numeric_limits<unsigned int>::max() || result < 0) {
+ return Result::OutOfRange(
+ "Failed to parse integer from " + str + "; out of unsigned int range");
+ }
+
+ return Result::Success(static_cast<unsigned int>(result));
+ }
+
+ static const char* Name() { return "unsigned integer"; }
+};
+
+// Lightweight nanosecond value type. Allows parser to convert user-input from milliseconds
+// to nanoseconds automatically after parsing.
+//
+// All implicit conversion from uint64_t uses nanoseconds.
+struct MillisecondsToNanoseconds {
+ // Create from nanoseconds.
+ MillisecondsToNanoseconds(uint64_t nanoseconds) : nanoseconds_(nanoseconds) { // NOLINT [runtime/explicit] [5]
+ }
+
+ // Create from milliseconds.
+ static MillisecondsToNanoseconds FromMilliseconds(unsigned int milliseconds) {
+ return MillisecondsToNanoseconds(MsToNs(milliseconds));
+ }
+
+ // Get the underlying nanoseconds value.
+ uint64_t GetNanoseconds() const {
+ return nanoseconds_;
+ }
+
+ // Get the milliseconds value [via a conversion]. Loss of precision will occur.
+ uint64_t GetMilliseconds() const {
+ return NsToMs(nanoseconds_);
+ }
+
+ // Get the underlying nanoseconds value.
+ operator uint64_t() const {
+ return GetNanoseconds();
+ }
+
+ // Default constructors/copy-constructors.
+ MillisecondsToNanoseconds() : nanoseconds_(0ul) {}
+ MillisecondsToNanoseconds(const MillisecondsToNanoseconds& rhs) = default;
+ MillisecondsToNanoseconds(MillisecondsToNanoseconds&& rhs) = default;
+
+ private:
+ uint64_t nanoseconds_;
+};
+
+template <>
+struct CmdlineType<MillisecondsToNanoseconds> : CmdlineTypeParser<MillisecondsToNanoseconds> {
+ Result Parse(const std::string& str) {
+ CmdlineType<unsigned int> uint_parser;
+ CmdlineParseResult<unsigned int> res = uint_parser.Parse(str);
+
+ if (res.IsSuccess()) {
+ return Result::Success(MillisecondsToNanoseconds::FromMilliseconds(res.GetValue()));
+ } else {
+ return Result::CastError(res);
+ }
+ }
+
+ static const char* Name() { return "MillisecondsToNanoseconds"; }
+};
+
+template <>
+struct CmdlineType<std::string> : CmdlineTypeParser<std::string> {
+ Result Parse(const std::string& args) {
+ return Result::Success(args);
+ }
+
+ Result ParseAndAppend(const std::string& args,
+ std::string& existing_value) {
+ if (existing_value.empty()) {
+ existing_value = args;
+ } else {
+ existing_value += ' ';
+ existing_value += args;
+ }
+ return Result::SuccessNoValue();
+ }
+};
+
+template <>
+struct CmdlineType<std::vector<std::string>> : CmdlineTypeParser<std::vector<std::string>> {
+ Result Parse(const std::string& args) {
+ assert(false && "Use AppendValues() for a string vector type");
+ return Result::Failure("Unconditional failure: string vector must be appended: " + args);
+ }
+
+ Result ParseAndAppend(const std::string& args,
+ std::vector<std::string>& existing_value) {
+ existing_value.push_back(args);
+ return Result::SuccessNoValue();
+ }
+
+ static const char* Name() { return "std::vector<std::string>"; }
+};
+
+template <char Separator>
+struct ParseStringList {
+ explicit ParseStringList(std::vector<std::string>&& list) : list_(list) {}
+
+ operator std::vector<std::string>() const {
+ return list_;
+ }
+
+ operator std::vector<std::string>&&() && {
+ return std::move(list_);
+ }
+
+ size_t Size() const {
+ return list_.size();
+ }
+
+ std::string Join() const {
+ return art::Join(list_, Separator);
+ }
+
+ static ParseStringList<Separator> Split(const std::string& str) {
+ std::vector<std::string> list;
+ art::Split(str, Separator, &list);
+ return ParseStringList<Separator>(std::move(list));
+ }
+
+ ParseStringList() = default;
+ ParseStringList(const ParseStringList& rhs) = default;
+ ParseStringList(ParseStringList&& rhs) = default;
+
+ private:
+ std::vector<std::string> list_;
+};
+
+template <char Separator>
+struct CmdlineType<ParseStringList<Separator>> : CmdlineTypeParser<ParseStringList<Separator>> {
+ using Result = CmdlineParseResult<ParseStringList<Separator>>;
+
+ Result Parse(const std::string& args) {
+ return Result::Success(ParseStringList<Separator>::Split(args));
+ }
+
+ static const char* Name() { return "ParseStringList<Separator>"; }
+};
+
+static gc::CollectorType ParseCollectorType(const std::string& option) {
+ if (option == "MS" || option == "nonconcurrent") {
+ return gc::kCollectorTypeMS;
+ } else if (option == "CMS" || option == "concurrent") {
+ return gc::kCollectorTypeCMS;
+ } else if (option == "SS") {
+ return gc::kCollectorTypeSS;
+ } else if (option == "GSS") {
+ return gc::kCollectorTypeGSS;
+ } else if (option == "CC") {
+ return gc::kCollectorTypeCC;
+ } else if (option == "MC") {
+ return gc::kCollectorTypeMC;
+ } else {
+ return gc::kCollectorTypeNone;
+ }
+}
+
+struct XGcOption {
+ // These defaults are used when the command line arguments for -Xgc:
+ // are either omitted completely or partially.
+ gc::CollectorType collector_type_ = kUseReadBarrier ?
+ // If RB is enabled (currently a build-time decision),
+ // use CC as the default GC.
+ gc::kCollectorTypeCC :
+ gc::kCollectorTypeDefault;
+ bool verify_pre_gc_heap_ = false;
+ bool verify_pre_sweeping_heap_ = kIsDebugBuild;
+ bool verify_post_gc_heap_ = false;
+ bool verify_pre_gc_rosalloc_ = kIsDebugBuild;
+ bool verify_pre_sweeping_rosalloc_ = false;
+ bool verify_post_gc_rosalloc_ = false;
+};
+
+template <>
+struct CmdlineType<XGcOption> : CmdlineTypeParser<XGcOption> {
+ Result Parse(const std::string& option) { // -Xgc: already stripped
+ XGcOption xgc{}; // NOLINT [readability/braces] [4]
+
+ std::vector<std::string> gc_options;
+ Split(option, ',', &gc_options);
+ for (const std::string& gc_option : gc_options) {
+ gc::CollectorType collector_type = ParseCollectorType(gc_option);
+ if (collector_type != gc::kCollectorTypeNone) {
+ xgc.collector_type_ = collector_type;
+ } else if (gc_option == "preverify") {
+ xgc.verify_pre_gc_heap_ = true;
+ } else if (gc_option == "nopreverify") {
+ xgc.verify_pre_gc_heap_ = false;
+ } else if (gc_option == "presweepingverify") {
+ xgc.verify_pre_sweeping_heap_ = true;
+ } else if (gc_option == "nopresweepingverify") {
+ xgc.verify_pre_sweeping_heap_ = false;
+ } else if (gc_option == "postverify") {
+ xgc.verify_post_gc_heap_ = true;
+ } else if (gc_option == "nopostverify") {
+ xgc.verify_post_gc_heap_ = false;
+ } else if (gc_option == "preverify_rosalloc") {
+ xgc.verify_pre_gc_rosalloc_ = true;
+ } else if (gc_option == "nopreverify_rosalloc") {
+ xgc.verify_pre_gc_rosalloc_ = false;
+ } else if (gc_option == "presweepingverify_rosalloc") {
+ xgc.verify_pre_sweeping_rosalloc_ = true;
+ } else if (gc_option == "nopresweepingverify_rosalloc") {
+ xgc.verify_pre_sweeping_rosalloc_ = false;
+ } else if (gc_option == "postverify_rosalloc") {
+ xgc.verify_post_gc_rosalloc_ = true;
+ } else if (gc_option == "nopostverify_rosalloc") {
+ xgc.verify_post_gc_rosalloc_ = false;
+ } else if ((gc_option == "precise") ||
+ (gc_option == "noprecise") ||
+ (gc_option == "verifycardtable") ||
+ (gc_option == "noverifycardtable")) {
+ // Ignored for backwards compatibility.
+ } else {
+ return Result::Usage(std::string("Unknown -Xgc option ") + gc_option);
+ }
+ }
+
+ return Result::Success(std::move(xgc));
+ }
+
+ static const char* Name() { return "XgcOption"; }
+};
+
+struct BackgroundGcOption {
+ // If background_collector_type_ is kCollectorTypeNone, it defaults to the
+ // XGcOption::collector_type_ after parsing options. If you set this to
+ // kCollectorTypeHSpaceCompact then we will do an hspace compaction when
+ // we transition to background instead of a normal collector transition.
+ gc::CollectorType background_collector_type_;
+
+ BackgroundGcOption(gc::CollectorType background_collector_type) // NOLINT [runtime/explicit] [5]
+ : background_collector_type_(background_collector_type) {}
+ BackgroundGcOption()
+ : background_collector_type_(gc::kCollectorTypeNone) {
+
+ if (kUseReadBarrier) {
+ background_collector_type_ = gc::kCollectorTypeCC; // Disable background compaction for CC.
+ }
+ }
+
+ operator gc::CollectorType() const { return background_collector_type_; }
+};
+
+template<>
+struct CmdlineType<BackgroundGcOption>
+ : CmdlineTypeParser<BackgroundGcOption>, private BackgroundGcOption {
+ Result Parse(const std::string& substring) {
+ // Special handling for HSpaceCompact since this is only valid as a background GC type.
+ if (substring == "HSpaceCompact") {
+ background_collector_type_ = gc::kCollectorTypeHomogeneousSpaceCompact;
+ } else {
+ gc::CollectorType collector_type = ParseCollectorType(substring);
+ if (collector_type != gc::kCollectorTypeNone) {
+ background_collector_type_ = collector_type;
+ } else {
+ return Result::Failure();
+ }
+ }
+
+ BackgroundGcOption res = *this;
+ return Result::Success(res);
+ }
+
+ static const char* Name() { return "BackgroundGcOption"; }
+};
+
+template <>
+struct CmdlineType<LogVerbosity> : CmdlineTypeParser<LogVerbosity> {
+ Result Parse(const std::string& options) {
+ LogVerbosity log_verbosity = LogVerbosity();
+
+ std::vector<std::string> verbose_options;
+ Split(options, ',', &verbose_options);
+ for (size_t j = 0; j < verbose_options.size(); ++j) {
+ if (verbose_options[j] == "class") {
+ log_verbosity.class_linker = true;
+ } else if (verbose_options[j] == "compiler") {
+ log_verbosity.compiler = true;
+ } else if (verbose_options[j] == "gc") {
+ log_verbosity.gc = true;
+ } else if (verbose_options[j] == "heap") {
+ log_verbosity.heap = true;
+ } else if (verbose_options[j] == "jdwp") {
+ log_verbosity.jdwp = true;
+ } else if (verbose_options[j] == "jni") {
+ log_verbosity.jni = true;
+ } else if (verbose_options[j] == "monitor") {
+ log_verbosity.monitor = true;
+ } else if (verbose_options[j] == "profiler") {
+ log_verbosity.profiler = true;
+ } else if (verbose_options[j] == "signals") {
+ log_verbosity.signals = true;
+ } else if (verbose_options[j] == "startup") {
+ log_verbosity.startup = true;
+ } else if (verbose_options[j] == "third-party-jni") {
+ log_verbosity.third_party_jni = true;
+ } else if (verbose_options[j] == "threads") {
+ log_verbosity.threads = true;
+ } else if (verbose_options[j] == "verifier") {
+ log_verbosity.verifier = true;
+ } else {
+ return Result::Usage(std::string("Unknown -verbose option ") + verbose_options[j]);
+ }
+ }
+
+ return Result::Success(log_verbosity);
+ }
+
+ static const char* Name() { return "LogVerbosity"; }
+};
+
+// TODO: Replace with art::ProfilerOptions for the real thing.
+struct TestProfilerOptions {
+ // Whether or not the applications should be profiled.
+ bool enabled_;
+ // Destination file name where the profiling data will be saved into.
+ std::string output_file_name_;
+ // Generate profile every n seconds.
+ uint32_t period_s_;
+ // Run profile for n seconds.
+ uint32_t duration_s_;
+ // Microseconds between samples.
+ uint32_t interval_us_;
+ // Coefficient to exponential backoff.
+ double backoff_coefficient_;
+ // Whether the profile should start upon app startup or be delayed by some random offset.
+ bool start_immediately_;
+ // Top K% of samples that are considered relevant when deciding if the app should be recompiled.
+ double top_k_threshold_;
+ // How much the top K% samples needs to change in order for the app to be recompiled.
+ double top_k_change_threshold_;
+ // The type of profile data dumped to the disk.
+ ProfileDataType profile_type_;
+ // The max depth of the stack collected by the profiler
+ uint32_t max_stack_depth_;
+
+ TestProfilerOptions() :
+ enabled_(false),
+ output_file_name_(),
+ period_s_(0),
+ duration_s_(0),
+ interval_us_(0),
+ backoff_coefficient_(0),
+ start_immediately_(0),
+ top_k_threshold_(0),
+ top_k_change_threshold_(0),
+ profile_type_(ProfileDataType::kProfilerMethod),
+ max_stack_depth_(0) {
+ }
+
+ TestProfilerOptions(const TestProfilerOptions& other) = default;
+ TestProfilerOptions(TestProfilerOptions&& other) = default;
+};
+
+static inline std::ostream& operator<<(std::ostream& stream, const TestProfilerOptions& options) {
+ stream << "TestProfilerOptions {" << std::endl;
+
+#define PRINT_TO_STREAM(field) \
+ stream << #field << ": '" << options.field << "'" << std::endl;
+
+ PRINT_TO_STREAM(enabled_);
+ PRINT_TO_STREAM(output_file_name_);
+ PRINT_TO_STREAM(period_s_);
+ PRINT_TO_STREAM(duration_s_);
+ PRINT_TO_STREAM(interval_us_);
+ PRINT_TO_STREAM(backoff_coefficient_);
+ PRINT_TO_STREAM(start_immediately_);
+ PRINT_TO_STREAM(top_k_threshold_);
+ PRINT_TO_STREAM(top_k_change_threshold_);
+ PRINT_TO_STREAM(profile_type_);
+ PRINT_TO_STREAM(max_stack_depth_);
+
+ stream << "}";
+
+ return stream;
+#undef PRINT_TO_STREAM
+}
+
+template <>
+struct CmdlineType<TestProfilerOptions> : CmdlineTypeParser<TestProfilerOptions> {
+ using Result = CmdlineParseResult<TestProfilerOptions>;
+
+ private:
+ using StringResult = CmdlineParseResult<std::string>;
+ using DoubleResult = CmdlineParseResult<double>;
+
+ template <typename T>
+ static Result ParseInto(TestProfilerOptions& options,
+ T TestProfilerOptions::*pField,
+ CmdlineParseResult<T>&& result) {
+ assert(pField != nullptr);
+
+ if (result.IsSuccess()) {
+ options.*pField = result.ReleaseValue();
+ return Result::SuccessNoValue();
+ }
+
+ return Result::CastError(result);
+ }
+
+ template <typename T>
+ static Result ParseIntoRangeCheck(TestProfilerOptions& options,
+ T TestProfilerOptions::*pField,
+ CmdlineParseResult<T>&& result,
+ T min,
+ T max) {
+ if (result.IsSuccess()) {
+ const T& value = result.GetValue();
+
+ if (value < min || value > max) {
+ CmdlineParseResult<T> out_of_range = CmdlineParseResult<T>::OutOfRange(value, min, max);
+ return Result::CastError(out_of_range);
+ }
+ }
+
+ return ParseInto(options, pField, std::forward<CmdlineParseResult<T>>(result));
+ }
+
+ static StringResult ParseStringAfterChar(const std::string& s, char c) {
+ std::string parsed_value;
+
+ std::string::size_type colon = s.find(c);
+ if (colon == std::string::npos) {
+ return StringResult::Usage(std::string() + "Missing char " + c + " in option " + s);
+ }
+ // Add one to remove the char we were trimming until.
+ parsed_value = s.substr(colon + 1);
+ return StringResult::Success(parsed_value);
+ }
+
+ static std::string RemovePrefix(const std::string& source) {
+ size_t prefix_idx = source.find(":");
+
+ if (prefix_idx == std::string::npos) {
+ return "";
+ }
+
+ return source.substr(prefix_idx + 1);
+ }
+
+ public:
+ Result ParseAndAppend(const std::string& option, TestProfilerOptions& existing) {
+ // Special case which doesn't include a wildcard argument definition.
+ // We pass-it through as-is.
+ if (option == "-Xenable-profiler") {
+ existing.enabled_ = true;
+ return Result::SuccessNoValue();
+ }
+
+ // The rest of these options are always the wildcard from '-Xprofile-*'
+ std::string suffix = RemovePrefix(option);
+
+ if (StartsWith(option, "filename:")) {
+ CmdlineType<std::string> type_parser;
+
+ return ParseInto(existing,
+ &TestProfilerOptions::output_file_name_,
+ type_parser.Parse(suffix));
+ } else if (StartsWith(option, "period:")) {
+ CmdlineType<unsigned int> type_parser;
+
+ return ParseInto(existing,
+ &TestProfilerOptions::period_s_,
+ type_parser.Parse(suffix));
+ } else if (StartsWith(option, "duration:")) {
+ CmdlineType<unsigned int> type_parser;
+
+ return ParseInto(existing,
+ &TestProfilerOptions::duration_s_,
+ type_parser.Parse(suffix));
+ } else if (StartsWith(option, "interval:")) {
+ CmdlineType<unsigned int> type_parser;
+
+ return ParseInto(existing,
+ &TestProfilerOptions::interval_us_,
+ type_parser.Parse(suffix));
+ } else if (StartsWith(option, "backoff:")) {
+ CmdlineType<double> type_parser;
+
+ return ParseIntoRangeCheck(existing,
+ &TestProfilerOptions::backoff_coefficient_,
+ type_parser.Parse(suffix),
+ 1.0,
+ 10.0);
+
+ } else if (option == "start-immediately") {
+ existing.start_immediately_ = true;
+ return Result::SuccessNoValue();
+ } else if (StartsWith(option, "top-k-threshold:")) {
+ CmdlineType<double> type_parser;
+
+ return ParseIntoRangeCheck(existing,
+ &TestProfilerOptions::top_k_threshold_,
+ type_parser.Parse(suffix),
+ 0.0,
+ 100.0);
+ } else if (StartsWith(option, "top-k-change-threshold:")) {
+ CmdlineType<double> type_parser;
+
+ return ParseIntoRangeCheck(existing,
+ &TestProfilerOptions::top_k_change_threshold_,
+ type_parser.Parse(suffix),
+ 0.0,
+ 100.0);
+ } else if (option == "type:method") {
+ existing.profile_type_ = kProfilerMethod;
+ return Result::SuccessNoValue();
+ } else if (option == "type:stack") {
+ existing.profile_type_ = kProfilerBoundedStack;
+ return Result::SuccessNoValue();
+ } else if (StartsWith(option, "max-stack-depth:")) {
+ CmdlineType<unsigned int> type_parser;
+
+ return ParseInto(existing,
+ &TestProfilerOptions::max_stack_depth_,
+ type_parser.Parse(suffix));
+ } else {
+ return Result::Failure(std::string("Invalid suboption '") + option + "'");
+ }
+ }
+
+ static const char* Name() { return "TestProfilerOptions"; }
+ static constexpr bool kCanParseBlankless = true;
+};
+
+
+} // namespace art
+#endif // ART_CMDLINE_CMDLINE_TYPES_H_