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