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