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