diff options
Diffstat (limited to 'cmdline/cmdline_types.h')
-rw-r--r-- | cmdline/cmdline_types.h | 820 |
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_ |