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