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