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