Home | History | Annotate | Download | only in cmdline
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 #ifndef ART_CMDLINE_CMDLINE_TYPES_H_
     17 #define ART_CMDLINE_CMDLINE_TYPES_H_
     18 
     19 #define CMDLINE_NDEBUG 1  // Do not output any debugging information for parsing.
     20 
     21 #include <list>
     22 
     23 #include "cmdline_type_parser.h"
     24 #include "detail/cmdline_debug_detail.h"
     25 #include "memory_representation.h"
     26 
     27 #include "android-base/strings.h"
     28 
     29 // Includes for the types that are being specialized
     30 #include <string>
     31 #include "base/logging.h"
     32 #include "base/time_utils.h"
     33 #include "experimental_flags.h"
     34 #include "gc/collector_type.h"
     35 #include "gc/space/large_object_space.h"
     36 #include "jdwp/jdwp.h"
     37 #include "jdwp_provider.h"
     38 #include "jit/profile_saver_options.h"
     39 #include "plugin.h"
     40 #include "read_barrier_config.h"
     41 #include "ti/agent.h"
     42 #include "unit.h"
     43 
     44 namespace art {
     45 
     46 // The default specialization will always fail parsing the type from a string.
     47 // Provide your own specialization that inherits from CmdlineTypeParser<T>
     48 // and implements either Parse or ParseAndAppend
     49 // (only if the argument was defined with ::AppendValues()) but not both.
     50 template <typename T>
     51 struct CmdlineType : CmdlineTypeParser<T> {
     52 };
     53 
     54 // Specializations for CmdlineType<T> follow:
     55 
     56 // Parse argument definitions for Unit-typed arguments.
     57 template <>
     58 struct CmdlineType<Unit> : CmdlineTypeParser<Unit> {
     59   Result Parse(const std::string& args) {
     60     if (args == "") {
     61       return Result::Success(Unit{});
     62     }
     63     return Result::Failure("Unexpected extra characters " + args);
     64   }
     65 };
     66 
     67 template <>
     68 struct CmdlineType<JdwpProvider> : CmdlineTypeParser<JdwpProvider> {
     69   /*
     70    * Handle a single JDWP provider name. Must be either 'internal', 'default', or the file name of
     71    * an agent. A plugin will make use of this and the jdwpOptions to set up jdwp when appropriate.
     72    */
     73   Result Parse(const std::string& option) {
     74     if (option == "help") {
     75       return Result::Usage(
     76           "Example: -XjdwpProvider:none to disable JDWP\n"
     77           "Example: -XjdwpProvider:internal for internal jdwp implementation\n"
     78           "Example: -XjdwpProvider:adbconnection for adb connection mediated jdwp implementation\n"
     79           "Example: -XjdwpProvider:default for the default jdwp implementation\n");
     80     } else if (option == "default") {
     81       return Result::Success(JdwpProvider::kDefaultJdwpProvider);
     82     } else if (option == "internal") {
     83       return Result::Success(JdwpProvider::kInternal);
     84     } else if (option == "adbconnection") {
     85       return Result::Success(JdwpProvider::kAdbConnection);
     86     } else if (option == "none") {
     87       return Result::Success(JdwpProvider::kNone);
     88     } else {
     89       return Result::Failure(std::string("not a valid jdwp provider: ") + option);
     90     }
     91   }
     92   static const char* Name() { return "JdwpProvider"; }
     93 };
     94 
     95 template <size_t Divisor>
     96 struct CmdlineType<Memory<Divisor>> : CmdlineTypeParser<Memory<Divisor>> {
     97   using typename CmdlineTypeParser<Memory<Divisor>>::Result;
     98 
     99   Result Parse(const std::string& arg) {
    100     CMDLINE_DEBUG_LOG << "Parsing memory: " << arg << std::endl;
    101     size_t val = ParseMemoryOption(arg.c_str(), Divisor);
    102     CMDLINE_DEBUG_LOG << "Memory parsed to size_t value: " << val << std::endl;
    103 
    104     if (val == 0) {
    105       return Result::Failure(std::string("not a valid memory value, or not divisible by ")
    106                              + std::to_string(Divisor));
    107     }
    108 
    109     return Result::Success(Memory<Divisor>(val));
    110   }
    111 
    112   // Parse a string of the form /[0-9]+[kKmMgG]?/, which is used to specify
    113   // memory sizes.  [kK] indicates kilobytes, [mM] megabytes, and
    114   // [gG] gigabytes.
    115   //
    116   // "s" should point just past the "-Xm?" part of the string.
    117   // "div" specifies a divisor, e.g. 1024 if the value must be a multiple
    118   // of 1024.
    119   //
    120   // The spec says the -Xmx and -Xms options must be multiples of 1024.  It
    121   // doesn't say anything about -Xss.
    122   //
    123   // Returns 0 (a useless size) if "s" is malformed or specifies a low or
    124   // non-evenly-divisible value.
    125   //
    126   static size_t ParseMemoryOption(const char* s, size_t div) {
    127     // strtoul accepts a leading [+-], which we don't want,
    128     // so make sure our string starts with a decimal digit.
    129     if (isdigit(*s)) {
    130       char* s2;
    131       size_t val = strtoul(s, &s2, 10);
    132       if (s2 != s) {
    133         // s2 should be pointing just after the number.
    134         // If this is the end of the string, the user
    135         // has specified a number of bytes.  Otherwise,
    136         // there should be exactly one more character
    137         // that specifies a multiplier.
    138         if (*s2 != '\0') {
    139           // The remainder of the string is either a single multiplier
    140           // character, or nothing to indicate that the value is in
    141           // bytes.
    142           char c = *s2++;
    143           if (*s2 == '\0') {
    144             size_t mul;
    145             if (c == '\0') {
    146               mul = 1;
    147             } else if (c == 'k' || c == 'K') {
    148               mul = KB;
    149             } else if (c == 'm' || c == 'M') {
    150               mul = MB;
    151             } else if (c == 'g' || c == 'G') {
    152               mul = GB;
    153             } else {
    154               // Unknown multiplier character.
    155               return 0;
    156             }
    157 
    158             if (val <= std::numeric_limits<size_t>::max() / mul) {
    159               val *= mul;
    160             } else {
    161               // Clamp to a multiple of 1024.
    162               val = std::numeric_limits<size_t>::max() & ~(1024-1);
    163             }
    164           } else {
    165             // There's more than one character after the numeric part.
    166             return 0;
    167           }
    168         }
    169         // The man page says that a -Xm value must be a multiple of 1024.
    170         if (val % div == 0) {
    171           return val;
    172         }
    173       }
    174     }
    175     return 0;
    176   }
    177 
    178   static const char* Name() { return Memory<Divisor>::Name(); }
    179 };
    180 
    181 template <>
    182 struct CmdlineType<double> : CmdlineTypeParser<double> {
    183   Result Parse(const std::string& str) {
    184     char* end = nullptr;
    185     errno = 0;
    186     double value = strtod(str.c_str(), &end);
    187 
    188     if (*end != '\0') {
    189       return Result::Failure("Failed to parse double from " + str);
    190     }
    191     if (errno == ERANGE) {
    192       return Result::OutOfRange(
    193           "Failed to parse double from " + str + "; overflow/underflow occurred");
    194     }
    195 
    196     return Result::Success(value);
    197   }
    198 
    199   static const char* Name() { return "double"; }
    200 };
    201 
    202 template <typename T>
    203 static inline CmdlineParseResult<T> ParseNumeric(const std::string& str) {
    204   static_assert(sizeof(T) < sizeof(long long int),  // NOLINT [runtime/int] [4]
    205                 "Current support is restricted.");
    206 
    207   const char* begin = str.c_str();
    208   char* end;
    209 
    210   // Parse into a larger type (long long) because we can't use strtoul
    211   // since it silently converts negative values into unsigned long and doesn't set errno.
    212   errno = 0;
    213   long long int result = strtoll(begin, &end, 10);  // NOLINT [runtime/int] [4]
    214   if (begin == end || *end != '\0' || errno == EINVAL) {
    215     return CmdlineParseResult<T>::Failure("Failed to parse integer from " + str);
    216   } else if ((errno == ERANGE) ||  // NOLINT [runtime/int] [4]
    217       result < std::numeric_limits<T>::min() || result > std::numeric_limits<T>::max()) {
    218     return CmdlineParseResult<T>::OutOfRange(
    219         "Failed to parse integer from " + str + "; out of range");
    220   }
    221 
    222   return CmdlineParseResult<T>::Success(static_cast<T>(result));
    223 }
    224 
    225 template <>
    226 struct CmdlineType<unsigned int> : CmdlineTypeParser<unsigned int> {
    227   Result Parse(const std::string& str) {
    228     return ParseNumeric<unsigned int>(str);
    229   }
    230 
    231   static const char* Name() { return "unsigned integer"; }
    232 };
    233 
    234 template <>
    235 struct CmdlineType<int> : CmdlineTypeParser<int> {
    236   Result Parse(const std::string& str) {
    237     return ParseNumeric<int>(str);
    238   }
    239 
    240   static const char* Name() { return "unsigned integer"; }
    241 };
    242 
    243 // Lightweight nanosecond value type. Allows parser to convert user-input from milliseconds
    244 // to nanoseconds automatically after parsing.
    245 //
    246 // All implicit conversion from uint64_t uses nanoseconds.
    247 struct MillisecondsToNanoseconds {
    248   // Create from nanoseconds.
    249   MillisecondsToNanoseconds(uint64_t nanoseconds) : nanoseconds_(nanoseconds) {  // NOLINT [runtime/explicit] [5]
    250   }
    251 
    252   // Create from milliseconds.
    253   static MillisecondsToNanoseconds FromMilliseconds(unsigned int milliseconds) {
    254     return MillisecondsToNanoseconds(MsToNs(milliseconds));
    255   }
    256 
    257   // Get the underlying nanoseconds value.
    258   uint64_t GetNanoseconds() const {
    259     return nanoseconds_;
    260   }
    261 
    262   // Get the milliseconds value [via a conversion]. Loss of precision will occur.
    263   uint64_t GetMilliseconds() const {
    264     return NsToMs(nanoseconds_);
    265   }
    266 
    267   // Get the underlying nanoseconds value.
    268   operator uint64_t() const {
    269     return GetNanoseconds();
    270   }
    271 
    272   // Default constructors/copy-constructors.
    273   MillisecondsToNanoseconds() : nanoseconds_(0ul) {}
    274   MillisecondsToNanoseconds(const MillisecondsToNanoseconds&) = default;
    275   MillisecondsToNanoseconds(MillisecondsToNanoseconds&&) = default;
    276 
    277  private:
    278   uint64_t nanoseconds_;
    279 };
    280 
    281 template <>
    282 struct CmdlineType<MillisecondsToNanoseconds> : CmdlineTypeParser<MillisecondsToNanoseconds> {
    283   Result Parse(const std::string& str) {
    284     CmdlineType<unsigned int> uint_parser;
    285     CmdlineParseResult<unsigned int> res = uint_parser.Parse(str);
    286 
    287     if (res.IsSuccess()) {
    288       return Result::Success(MillisecondsToNanoseconds::FromMilliseconds(res.GetValue()));
    289     } else {
    290       return Result::CastError(res);
    291     }
    292   }
    293 
    294   static const char* Name() { return "MillisecondsToNanoseconds"; }
    295 };
    296 
    297 template <>
    298 struct CmdlineType<std::string> : CmdlineTypeParser<std::string> {
    299   Result Parse(const std::string& args) {
    300     return Result::Success(args);
    301   }
    302 
    303   Result ParseAndAppend(const std::string& args,
    304                         std::string& existing_value) {
    305     if (existing_value.empty()) {
    306       existing_value = args;
    307     } else {
    308       existing_value += ' ';
    309       existing_value += args;
    310     }
    311     return Result::SuccessNoValue();
    312   }
    313 };
    314 
    315 template <>
    316 struct CmdlineType<std::vector<Plugin>> : CmdlineTypeParser<std::vector<Plugin>> {
    317   Result Parse(const std::string& args) {
    318     assert(false && "Use AppendValues() for a Plugin vector type");
    319     return Result::Failure("Unconditional failure: Plugin vector must be appended: " + args);
    320   }
    321 
    322   Result ParseAndAppend(const std::string& args,
    323                         std::vector<Plugin>& existing_value) {
    324     existing_value.push_back(Plugin::Create(args));
    325     return Result::SuccessNoValue();
    326   }
    327 
    328   static const char* Name() { return "std::vector<Plugin>"; }
    329 };
    330 
    331 template <>
    332 struct CmdlineType<std::list<ti::AgentSpec>> : CmdlineTypeParser<std::list<ti::AgentSpec>> {
    333   Result Parse(const std::string& args) {
    334     assert(false && "Use AppendValues() for an Agent list type");
    335     return Result::Failure("Unconditional failure: Agent list must be appended: " + args);
    336   }
    337 
    338   Result ParseAndAppend(const std::string& args,
    339                         std::list<ti::AgentSpec>& existing_value) {
    340     existing_value.emplace_back(args);
    341     return Result::SuccessNoValue();
    342   }
    343 
    344   static const char* Name() { return "std::list<ti::AgentSpec>"; }
    345 };
    346 
    347 template <>
    348 struct CmdlineType<std::vector<std::string>> : CmdlineTypeParser<std::vector<std::string>> {
    349   Result Parse(const std::string& args) {
    350     assert(false && "Use AppendValues() for a string vector type");
    351     return Result::Failure("Unconditional failure: string vector must be appended: " + args);
    352   }
    353 
    354   Result ParseAndAppend(const std::string& args,
    355                         std::vector<std::string>& existing_value) {
    356     existing_value.push_back(args);
    357     return Result::SuccessNoValue();
    358   }
    359 
    360   static const char* Name() { return "std::vector<std::string>"; }
    361 };
    362 
    363 template <char Separator>
    364 struct ParseStringList {
    365   explicit ParseStringList(std::vector<std::string>&& list) : list_(list) {}
    366 
    367   operator std::vector<std::string>() const {
    368     return list_;
    369   }
    370 
    371   operator std::vector<std::string>&&() && {
    372     return std::move(list_);
    373   }
    374 
    375   size_t Size() const {
    376     return list_.size();
    377   }
    378 
    379   std::string Join() const {
    380     return android::base::Join(list_, Separator);
    381   }
    382 
    383   static ParseStringList<Separator> Split(const std::string& str) {
    384     std::vector<std::string> list;
    385     art::Split(str, Separator, &list);
    386     return ParseStringList<Separator>(std::move(list));
    387   }
    388 
    389   ParseStringList() = default;
    390   ParseStringList(const ParseStringList&) = default;
    391   ParseStringList(ParseStringList&&) = default;
    392 
    393  private:
    394   std::vector<std::string> list_;
    395 };
    396 
    397 template <char Separator>
    398 struct CmdlineType<ParseStringList<Separator>> : CmdlineTypeParser<ParseStringList<Separator>> {
    399   using Result = CmdlineParseResult<ParseStringList<Separator>>;
    400 
    401   Result Parse(const std::string& args) {
    402     return Result::Success(ParseStringList<Separator>::Split(args));
    403   }
    404 
    405   static const char* Name() { return "ParseStringList<Separator>"; }
    406 };
    407 
    408 static gc::CollectorType ParseCollectorType(const std::string& option) {
    409   if (option == "MS" || option == "nonconcurrent") {
    410     return gc::kCollectorTypeMS;
    411   } else if (option == "CMS" || option == "concurrent") {
    412     return gc::kCollectorTypeCMS;
    413   } else if (option == "SS") {
    414     return gc::kCollectorTypeSS;
    415   } else if (option == "GSS") {
    416     return gc::kCollectorTypeGSS;
    417   } else if (option == "CC") {
    418     return gc::kCollectorTypeCC;
    419   } else if (option == "MC") {
    420     return gc::kCollectorTypeMC;
    421   } else {
    422     return gc::kCollectorTypeNone;
    423   }
    424 }
    425 
    426 struct XGcOption {
    427   // These defaults are used when the command line arguments for -Xgc:
    428   // are either omitted completely or partially.
    429   gc::CollectorType collector_type_ = gc::kCollectorTypeDefault;
    430   bool verify_pre_gc_heap_ = false;
    431   bool verify_pre_sweeping_heap_ = kIsDebugBuild;
    432   bool verify_post_gc_heap_ = false;
    433   bool verify_pre_gc_rosalloc_ = kIsDebugBuild;
    434   bool verify_pre_sweeping_rosalloc_ = false;
    435   bool verify_post_gc_rosalloc_ = false;
    436   // Do no measurements for kUseTableLookupReadBarrier to avoid test timeouts. b/31679493
    437   bool measure_ = kIsDebugBuild && !kUseTableLookupReadBarrier;
    438   bool gcstress_ = false;
    439 };
    440 
    441 template <>
    442 struct CmdlineType<XGcOption> : CmdlineTypeParser<XGcOption> {
    443   Result Parse(const std::string& option) {  // -Xgc: already stripped
    444     XGcOption xgc{};
    445 
    446     std::vector<std::string> gc_options;
    447     Split(option, ',', &gc_options);
    448     for (const std::string& gc_option : gc_options) {
    449       gc::CollectorType collector_type = ParseCollectorType(gc_option);
    450       if (collector_type != gc::kCollectorTypeNone) {
    451         xgc.collector_type_ = collector_type;
    452       } else if (gc_option == "preverify") {
    453         xgc.verify_pre_gc_heap_ = true;
    454       } else if (gc_option == "nopreverify") {
    455         xgc.verify_pre_gc_heap_ = false;
    456       }  else if (gc_option == "presweepingverify") {
    457         xgc.verify_pre_sweeping_heap_ = true;
    458       } else if (gc_option == "nopresweepingverify") {
    459         xgc.verify_pre_sweeping_heap_ = false;
    460       } else if (gc_option == "postverify") {
    461         xgc.verify_post_gc_heap_ = true;
    462       } else if (gc_option == "nopostverify") {
    463         xgc.verify_post_gc_heap_ = false;
    464       } else if (gc_option == "preverify_rosalloc") {
    465         xgc.verify_pre_gc_rosalloc_ = true;
    466       } else if (gc_option == "nopreverify_rosalloc") {
    467         xgc.verify_pre_gc_rosalloc_ = false;
    468       } else if (gc_option == "presweepingverify_rosalloc") {
    469         xgc.verify_pre_sweeping_rosalloc_ = true;
    470       } else if (gc_option == "nopresweepingverify_rosalloc") {
    471         xgc.verify_pre_sweeping_rosalloc_ = false;
    472       } else if (gc_option == "postverify_rosalloc") {
    473         xgc.verify_post_gc_rosalloc_ = true;
    474       } else if (gc_option == "nopostverify_rosalloc") {
    475         xgc.verify_post_gc_rosalloc_ = false;
    476       } else if (gc_option == "gcstress") {
    477         xgc.gcstress_ = true;
    478       } else if (gc_option == "nogcstress") {
    479         xgc.gcstress_ = false;
    480       } else if (gc_option == "measure") {
    481         xgc.measure_ = true;
    482       } else if ((gc_option == "precise") ||
    483                  (gc_option == "noprecise") ||
    484                  (gc_option == "verifycardtable") ||
    485                  (gc_option == "noverifycardtable")) {
    486         // Ignored for backwards compatibility.
    487       } else {
    488         return Result::Usage(std::string("Unknown -Xgc option ") + gc_option);
    489       }
    490     }
    491 
    492     return Result::Success(std::move(xgc));
    493   }
    494 
    495   static const char* Name() { return "XgcOption"; }
    496 };
    497 
    498 struct BackgroundGcOption {
    499   // If background_collector_type_ is kCollectorTypeNone, it defaults to the
    500   // XGcOption::collector_type_ after parsing options. If you set this to
    501   // kCollectorTypeHSpaceCompact then we will do an hspace compaction when
    502   // we transition to background instead of a normal collector transition.
    503   gc::CollectorType background_collector_type_;
    504 
    505   BackgroundGcOption(gc::CollectorType background_collector_type)  // NOLINT [runtime/explicit] [5]
    506     : background_collector_type_(background_collector_type) {}
    507   BackgroundGcOption()
    508     : background_collector_type_(gc::kCollectorTypeNone) {
    509   }
    510 
    511   operator gc::CollectorType() const { return background_collector_type_; }
    512 };
    513 
    514 template<>
    515 struct CmdlineType<BackgroundGcOption>
    516   : CmdlineTypeParser<BackgroundGcOption>, private BackgroundGcOption {
    517   Result Parse(const std::string& substring) {
    518     // Special handling for HSpaceCompact since this is only valid as a background GC type.
    519     if (substring == "HSpaceCompact") {
    520       background_collector_type_ = gc::kCollectorTypeHomogeneousSpaceCompact;
    521     } else {
    522       gc::CollectorType collector_type = ParseCollectorType(substring);
    523       if (collector_type != gc::kCollectorTypeNone) {
    524         background_collector_type_ = collector_type;
    525       } else {
    526         return Result::Failure();
    527       }
    528     }
    529 
    530     BackgroundGcOption res = *this;
    531     return Result::Success(res);
    532   }
    533 
    534   static const char* Name() { return "BackgroundGcOption"; }
    535 };
    536 
    537 template <>
    538 struct CmdlineType<LogVerbosity> : CmdlineTypeParser<LogVerbosity> {
    539   Result Parse(const std::string& options) {
    540     LogVerbosity log_verbosity = LogVerbosity();
    541 
    542     std::vector<std::string> verbose_options;
    543     Split(options, ',', &verbose_options);
    544     for (size_t j = 0; j < verbose_options.size(); ++j) {
    545       if (verbose_options[j] == "class") {
    546         log_verbosity.class_linker = true;
    547       } else if (verbose_options[j] == "collector") {
    548         log_verbosity.collector = true;
    549       } else if (verbose_options[j] == "compiler") {
    550         log_verbosity.compiler = true;
    551       } else if (verbose_options[j] == "deopt") {
    552         log_verbosity.deopt = true;
    553       } else if (verbose_options[j] == "gc") {
    554         log_verbosity.gc = true;
    555       } else if (verbose_options[j] == "heap") {
    556         log_verbosity.heap = true;
    557       } else if (verbose_options[j] == "jdwp") {
    558         log_verbosity.jdwp = true;
    559       } else if (verbose_options[j] == "jit") {
    560         log_verbosity.jit = true;
    561       } else if (verbose_options[j] == "jni") {
    562         log_verbosity.jni = true;
    563       } else if (verbose_options[j] == "monitor") {
    564         log_verbosity.monitor = true;
    565       } else if (verbose_options[j] == "oat") {
    566         log_verbosity.oat = true;
    567       } else if (verbose_options[j] == "profiler") {
    568         log_verbosity.profiler = true;
    569       } else if (verbose_options[j] == "signals") {
    570         log_verbosity.signals = true;
    571       } else if (verbose_options[j] == "simulator") {
    572         log_verbosity.simulator = true;
    573       } else if (verbose_options[j] == "startup") {
    574         log_verbosity.startup = true;
    575       } else if (verbose_options[j] == "third-party-jni") {
    576         log_verbosity.third_party_jni = true;
    577       } else if (verbose_options[j] == "threads") {
    578         log_verbosity.threads = true;
    579       } else if (verbose_options[j] == "verifier") {
    580         log_verbosity.verifier = true;
    581       } else if (verbose_options[j] == "verifier-debug") {
    582         log_verbosity.verifier_debug = true;
    583       } else if (verbose_options[j] == "image") {
    584         log_verbosity.image = true;
    585       } else if (verbose_options[j] == "systrace-locks") {
    586         log_verbosity.systrace_lock_logging = true;
    587       } else if (verbose_options[j] == "agents") {
    588         log_verbosity.agents = true;
    589       } else if (verbose_options[j] == "dex") {
    590         log_verbosity.dex = true;
    591       } else {
    592         return Result::Usage(std::string("Unknown -verbose option ") + verbose_options[j]);
    593       }
    594     }
    595 
    596     return Result::Success(log_verbosity);
    597   }
    598 
    599   static const char* Name() { return "LogVerbosity"; }
    600 };
    601 
    602 template <>
    603 struct CmdlineType<ProfileSaverOptions> : CmdlineTypeParser<ProfileSaverOptions> {
    604   using Result = CmdlineParseResult<ProfileSaverOptions>;
    605 
    606  private:
    607   using StringResult = CmdlineParseResult<std::string>;
    608   using DoubleResult = CmdlineParseResult<double>;
    609 
    610   template <typename T>
    611   static Result ParseInto(ProfileSaverOptions& options,
    612                           T ProfileSaverOptions::*pField,
    613                           CmdlineParseResult<T>&& result) {
    614     assert(pField != nullptr);
    615 
    616     if (result.IsSuccess()) {
    617       options.*pField = result.ReleaseValue();
    618       return Result::SuccessNoValue();
    619     }
    620 
    621     return Result::CastError(result);
    622   }
    623 
    624   static std::string RemovePrefix(const std::string& source) {
    625     size_t prefix_idx = source.find(':');
    626 
    627     if (prefix_idx == std::string::npos) {
    628       return "";
    629     }
    630 
    631     return source.substr(prefix_idx + 1);
    632   }
    633 
    634  public:
    635   Result ParseAndAppend(const std::string& option, ProfileSaverOptions& existing) {
    636     // Special case which doesn't include a wildcard argument definition.
    637     // We pass-it through as-is.
    638     if (option == "-Xjitsaveprofilinginfo") {
    639       existing.enabled_ = true;
    640       return Result::SuccessNoValue();
    641     }
    642 
    643     if (option == "profile-boot-class-path") {
    644       existing.profile_boot_class_path_ = true;
    645       return Result::SuccessNoValue();
    646     }
    647 
    648     if (option == "profile-aot-code") {
    649       existing.profile_aot_code_ = true;
    650       return Result::SuccessNoValue();
    651     }
    652 
    653     if (option == "save-without-jit-notifications") {
    654       existing.wait_for_jit_notifications_to_save_ = false;
    655       return Result::SuccessNoValue();
    656     }
    657 
    658     // The rest of these options are always the wildcard from '-Xps-*'
    659     std::string suffix = RemovePrefix(option);
    660 
    661     if (android::base::StartsWith(option, "min-save-period-ms:")) {
    662       CmdlineType<unsigned int> type_parser;
    663       return ParseInto(existing,
    664              &ProfileSaverOptions::min_save_period_ms_,
    665              type_parser.Parse(suffix));
    666     }
    667     if (android::base::StartsWith(option, "save-resolved-classes-delay-ms:")) {
    668       CmdlineType<unsigned int> type_parser;
    669       return ParseInto(existing,
    670              &ProfileSaverOptions::save_resolved_classes_delay_ms_,
    671              type_parser.Parse(suffix));
    672     }
    673     if (android::base::StartsWith(option, "hot-startup-method-samples:")) {
    674       CmdlineType<unsigned int> type_parser;
    675       return ParseInto(existing,
    676              &ProfileSaverOptions::hot_startup_method_samples_,
    677              type_parser.Parse(suffix));
    678     }
    679     if (android::base::StartsWith(option, "min-methods-to-save:")) {
    680       CmdlineType<unsigned int> type_parser;
    681       return ParseInto(existing,
    682              &ProfileSaverOptions::min_methods_to_save_,
    683              type_parser.Parse(suffix));
    684     }
    685     if (android::base::StartsWith(option, "min-classes-to-save:")) {
    686       CmdlineType<unsigned int> type_parser;
    687       return ParseInto(existing,
    688              &ProfileSaverOptions::min_classes_to_save_,
    689              type_parser.Parse(suffix));
    690     }
    691     if (android::base::StartsWith(option, "min-notification-before-wake:")) {
    692       CmdlineType<unsigned int> type_parser;
    693       return ParseInto(existing,
    694              &ProfileSaverOptions::min_notification_before_wake_,
    695              type_parser.Parse(suffix));
    696     }
    697     if (android::base::StartsWith(option, "max-notification-before-wake:")) {
    698       CmdlineType<unsigned int> type_parser;
    699       return ParseInto(existing,
    700              &ProfileSaverOptions::max_notification_before_wake_,
    701              type_parser.Parse(suffix));
    702     }
    703     if (android::base::StartsWith(option, "profile-path:")) {
    704       existing.profile_path_ = suffix;
    705       return Result::SuccessNoValue();
    706     }
    707 
    708     return Result::Failure(std::string("Invalid suboption '") + option + "'");
    709   }
    710 
    711   static const char* Name() { return "ProfileSaverOptions"; }
    712   static constexpr bool kCanParseBlankless = true;
    713 };
    714 
    715 template<>
    716 struct CmdlineType<ExperimentalFlags> : CmdlineTypeParser<ExperimentalFlags> {
    717   Result ParseAndAppend(const std::string& option, ExperimentalFlags& existing) {
    718     if (option == "none") {
    719       existing = ExperimentalFlags::kNone;
    720     } else {
    721       return Result::Failure(std::string("Unknown option '") + option + "'");
    722     }
    723     return Result::SuccessNoValue();
    724   }
    725 
    726   static const char* Name() { return "ExperimentalFlags"; }
    727 };
    728 }  // namespace art
    729 #endif  // ART_CMDLINE_CMDLINE_TYPES_H_
    730