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