• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <>
396 struct CmdlineType<std::vector<int>> : CmdlineTypeParser<std::vector<int>> {
397   Result Parse(const std::string& args) {
398     assert(false && "Use AppendValues() for a int vector type");
399     return Result::Failure("Unconditional failure: string vector must be appended: " + args);
400   }
401 
402   Result ParseAndAppend(const std::string& args,
403                         std::vector<int>& existing_value) {
404     auto result = ParseNumeric<int>(args);
405     if (result.IsSuccess()) {
406       existing_value.push_back(result.GetValue());
407     } else {
408       return Result::CastError(result);
409     }
410     return Result::SuccessNoValue();
411   }
412 
413   static const char* Name() { return "std::vector<int>"; }
414   static const char* DescribeType() { return "int values"; }
415 };
416 
417 template <typename ArgType, char Separator>
418 struct ParseList {
419   explicit ParseList(std::vector<ArgType>&& list) : list_(list) {}
420 
421   operator std::vector<ArgType>() const {
422     return list_;
423   }
424 
425   operator std::vector<ArgType>&&() && {
426     return std::move(list_);
427   }
428 
429   size_t Size() const {
430     return list_.size();
431   }
432 
433   std::string Join() const {
434     return android::base::Join(list_, Separator);
435   }
436 
437   ParseList() = default;
438   ParseList(const ParseList&) = default;
439   ParseList(ParseList&&) noexcept = default;
440 
441  private:
442   std::vector<ArgType> list_;
443 };
444 
445 template <char Separator>
446 using ParseIntList = ParseList<int, Separator>;
447 
448 template <char Separator>
449 struct ParseStringList : public ParseList<std::string, Separator> {
450   explicit ParseStringList(std::vector<std::string>&& list) : ParseList<std::string, Separator>(std::move(list)) {}
451 
452   static ParseStringList<Separator> Split(const std::string& str) {
453     std::vector<std::string> list;
454     art::Split(str, Separator, &list);
455     return ParseStringList<Separator>(std::move(list));
456   }
457 
458   ParseStringList() = default;
459   ParseStringList(const ParseStringList&) = default;
460   ParseStringList(ParseStringList&&) noexcept = default;
461 };
462 
463 template <char Separator>
464 struct CmdlineType<ParseStringList<Separator>> : CmdlineTypeParser<ParseStringList<Separator>> {
465   using Result = CmdlineParseResult<ParseStringList<Separator>>;
466 
467   Result Parse(const std::string& args) {
468     return Result::Success(ParseStringList<Separator>::Split(args));
469   }
470 
471   static const char* Name() { return "ParseStringList<Separator>"; }
472   static const char* DescribeType() {
473     static std::string str;
474     if (str.empty()) {
475       str = android::base::StringPrintf("list separated by '%c'", Separator);
476     }
477     return str.c_str();
478   }
479 };
480 
481 template <char Separator>
482 struct CmdlineType<ParseIntList<Separator>> : CmdlineTypeParser<ParseIntList<Separator>> {
483   using Result = CmdlineParseResult<ParseIntList<Separator>>;
484 
485   Result Parse(const std::string& args) {
486     std::vector<int> list;
487     const char* pos = args.c_str();
488     errno = 0;
489 
490     while (true) {
491       char* end = nullptr;
492       int64_t value = strtol(pos, &end, 10);
493       if (pos == end ||  errno == EINVAL) {
494         return Result::Failure("Failed to parse integer from " + args);
495       } else if ((errno == ERANGE) ||  // NOLINT [runtime/int] [4]
496                  value < std::numeric_limits<int>::min() ||
497                  value > std::numeric_limits<int>::max()) {
498         return Result::OutOfRange("Failed to parse integer from " + args + "; out of range");
499       }
500       list.push_back(static_cast<int>(value));
501       if (*end == '\0') {
502         break;
503       } else if (*end != Separator) {
504         return Result::Failure(std::string("Unexpected character: ") + *end);
505       }
506       pos = end + 1;
507     }
508     return Result::Success(ParseIntList<Separator>(std::move(list)));
509   }
510 
511   static const char* Name() { return "ParseIntList<Separator>"; }
512   static const char* DescribeType() {
513     static std::string str;
514     if (str.empty()) {
515       str = android::base::StringPrintf("integer list separated by '%c'", Separator);
516     }
517     return str.c_str();
518   }
519 };
520 
521 static gc::CollectorType ParseCollectorType(const std::string& option) {
522   if (option == "MS" || option == "nonconcurrent") {
523     return gc::kCollectorTypeMS;
524   } else if (option == "CMS" || option == "concurrent") {
525     return gc::kCollectorTypeCMS;
526   } else if (option == "SS") {
527     return gc::kCollectorTypeSS;
528   } else if (option == "CC") {
529     return gc::kCollectorTypeCC;
530   } else if (option == "CMC") {
531     return gc::kCollectorTypeCMC;
532   } else {
533     return gc::kCollectorTypeNone;
534   }
535 }
536 
537 struct XGcOption {
538   // These defaults are used when the command line arguments for -Xgc:
539   // are either omitted completely or partially.
540   gc::CollectorType collector_type_ = gc::kCollectorTypeDefault;
541   bool verify_pre_gc_heap_ = false;
542   bool verify_pre_sweeping_heap_ = kIsDebugBuild;
543   bool generational_cc = kEnableGenerationalCCByDefault;
544   bool verify_post_gc_heap_ = kIsDebugBuild;
545   bool verify_pre_gc_rosalloc_ = kIsDebugBuild;
546   bool verify_pre_sweeping_rosalloc_ = false;
547   bool verify_post_gc_rosalloc_ = false;
548   // Do no measurements for kUseTableLookupReadBarrier to avoid test timeouts. b/31679493
549   bool measure_ = kIsDebugBuild && !kUseTableLookupReadBarrier;
550   bool gcstress_ = false;
551 };
552 
553 template <>
554 struct CmdlineType<XGcOption> : CmdlineTypeParser<XGcOption> {
555   Result Parse(const std::string& option) {  // -Xgc: already stripped
556     XGcOption xgc{};
557 
558     std::vector<std::string> gc_options;
559     Split(option, ',', &gc_options);
560     for (const std::string& gc_option : gc_options) {
561       gc::CollectorType collector_type = ParseCollectorType(gc_option);
562       if (collector_type != gc::kCollectorTypeNone) {
563         xgc.collector_type_ = collector_type;
564       } else if (gc_option == "preverify") {
565         xgc.verify_pre_gc_heap_ = true;
566       } else if (gc_option == "nopreverify") {
567         xgc.verify_pre_gc_heap_ = false;
568       }  else if (gc_option == "presweepingverify") {
569         xgc.verify_pre_sweeping_heap_ = true;
570       } else if (gc_option == "nopresweepingverify") {
571         xgc.verify_pre_sweeping_heap_ = false;
572       } else if (gc_option == "generational_cc") {
573         // Note: Option "-Xgc:generational_cc" can be passed directly by
574         // app_process/zygote (see `android::AndroidRuntime::startVm`). If this
575         // option is ever deprecated, it should still be accepted (but ignored)
576         // for compatibility reasons (this should not prevent the runtime from
577         // starting up).
578         xgc.generational_cc = true;
579       } else if (gc_option == "nogenerational_cc") {
580         // Note: Option "-Xgc:nogenerational_cc" can be passed directly by
581         // app_process/zygote (see `android::AndroidRuntime::startVm`). If this
582         // option is ever deprecated, it should still be accepted (but ignored)
583         // for compatibility reasons (this should not prevent the runtime from
584         // starting up).
585         xgc.generational_cc = false;
586       } else if (gc_option == "postverify") {
587         xgc.verify_post_gc_heap_ = true;
588       } else if (gc_option == "nopostverify") {
589         xgc.verify_post_gc_heap_ = false;
590       } else if (gc_option == "preverify_rosalloc") {
591         xgc.verify_pre_gc_rosalloc_ = true;
592       } else if (gc_option == "nopreverify_rosalloc") {
593         xgc.verify_pre_gc_rosalloc_ = false;
594       } else if (gc_option == "presweepingverify_rosalloc") {
595         xgc.verify_pre_sweeping_rosalloc_ = true;
596       } else if (gc_option == "nopresweepingverify_rosalloc") {
597         xgc.verify_pre_sweeping_rosalloc_ = false;
598       } else if (gc_option == "postverify_rosalloc") {
599         xgc.verify_post_gc_rosalloc_ = true;
600       } else if (gc_option == "nopostverify_rosalloc") {
601         xgc.verify_post_gc_rosalloc_ = false;
602       } else if (gc_option == "gcstress") {
603         xgc.gcstress_ = true;
604       } else if (gc_option == "nogcstress") {
605         xgc.gcstress_ = false;
606       } else if (gc_option == "measure") {
607         xgc.measure_ = true;
608       } else if ((gc_option == "precise") ||
609                  (gc_option == "noprecise") ||
610                  (gc_option == "verifycardtable") ||
611                  (gc_option == "noverifycardtable")) {
612         // Ignored for backwards compatibility.
613       } else {
614         return Result::Usage(std::string("Unknown -Xgc option ") + gc_option);
615       }
616     }
617 
618     return Result::Success(std::move(xgc));
619   }
620 
621   static const char* Name() { return "XgcOption"; }
622   static const char* DescribeType() {
623     return "MS|nonconccurent|concurrent|CMS|SS|CC|[no]preverify[_rosalloc]|"
624            "[no]presweepingverify[_rosalloc]|[no]generation_cc|[no]postverify[_rosalloc]|"
625            "[no]gcstress|measure|[no]precisce|[no]verifycardtable";
626   }
627 };
628 
629 struct BackgroundGcOption {
630   // If background_collector_type_ is kCollectorTypeNone, it defaults to the
631   // XGcOption::collector_type_ after parsing options. If you set this to
632   // kCollectorTypeHSpaceCompact then we will do an hspace compaction when
633   // we transition to background instead of a normal collector transition.
634   gc::CollectorType background_collector_type_;
635 
636   BackgroundGcOption(gc::CollectorType background_collector_type)  // NOLINT [runtime/explicit] [5]
637     : background_collector_type_(background_collector_type) {}
638   BackgroundGcOption()
639     : background_collector_type_(gc::kCollectorTypeNone) {
640   }
641 
642   operator gc::CollectorType() const { return background_collector_type_; }
643 };
644 
645 template<>
646 struct CmdlineType<BackgroundGcOption>
647   : CmdlineTypeParser<BackgroundGcOption>, private BackgroundGcOption {
648   Result Parse(const std::string& substring) {
649     // Special handling for HSpaceCompact since this is only valid as a background GC type.
650     if (substring == "HSpaceCompact") {
651       background_collector_type_ = gc::kCollectorTypeHomogeneousSpaceCompact;
652     } else {
653       gc::CollectorType collector_type = ParseCollectorType(substring);
654       if (collector_type != gc::kCollectorTypeNone) {
655         background_collector_type_ = collector_type;
656       } else {
657         return Result::Failure();
658       }
659     }
660 
661     BackgroundGcOption res = *this;
662     return Result::Success(res);
663   }
664 
665   static const char* Name() { return "BackgroundGcOption"; }
666   static const char* DescribeType() {
667     return "HSpaceCompact|MS|nonconccurent|CMS|concurrent|SS|CC";
668   }
669 };
670 
671 template <>
672 struct CmdlineType<LogVerbosity> : CmdlineTypeParser<LogVerbosity> {
673   Result Parse(const std::string& options) {
674     LogVerbosity log_verbosity = LogVerbosity();
675 
676     std::vector<std::string> verbose_options;
677     Split(options, ',', &verbose_options);
678     for (size_t j = 0; j < verbose_options.size(); ++j) {
679       if (verbose_options[j] == "class") {
680         log_verbosity.class_linker = true;
681       } else if (verbose_options[j] == "collector") {
682         log_verbosity.collector = true;
683       } else if (verbose_options[j] == "compiler") {
684         log_verbosity.compiler = true;
685       } else if (verbose_options[j] == "deopt") {
686         log_verbosity.deopt = true;
687       } else if (verbose_options[j] == "gc") {
688         log_verbosity.gc = true;
689       } else if (verbose_options[j] == "heap") {
690         log_verbosity.heap = true;
691       } else if (verbose_options[j] == "interpreter") {
692         log_verbosity.interpreter = true;
693       } else if (verbose_options[j] == "jdwp") {
694         log_verbosity.jdwp = true;
695       } else if (verbose_options[j] == "jit") {
696         log_verbosity.jit = true;
697       } else if (verbose_options[j] == "jni") {
698         log_verbosity.jni = true;
699       } else if (verbose_options[j] == "monitor") {
700         log_verbosity.monitor = true;
701       } else if (verbose_options[j] == "oat") {
702         log_verbosity.oat = true;
703       } else if (verbose_options[j] == "profiler") {
704         log_verbosity.profiler = true;
705       } else if (verbose_options[j] == "signals") {
706         log_verbosity.signals = true;
707       } else if (verbose_options[j] == "simulator") {
708         log_verbosity.simulator = true;
709       } else if (verbose_options[j] == "startup") {
710         log_verbosity.startup = true;
711       } else if (verbose_options[j] == "third-party-jni") {
712         log_verbosity.third_party_jni = true;
713       } else if (verbose_options[j] == "threads") {
714         log_verbosity.threads = true;
715       } else if (verbose_options[j] == "verifier") {
716         log_verbosity.verifier = true;
717       } else if (verbose_options[j] == "verifier-debug") {
718         log_verbosity.verifier_debug = true;
719       } else if (verbose_options[j] == "image") {
720         log_verbosity.image = true;
721       } else if (verbose_options[j] == "systrace-locks") {
722         log_verbosity.systrace_lock_logging = true;
723       } else if (verbose_options[j] == "plugin") {
724         log_verbosity.plugin = true;
725       } else if (verbose_options[j] == "agents") {
726         log_verbosity.agents = true;
727       } else if (verbose_options[j] == "dex") {
728         log_verbosity.dex = true;
729       } else {
730         return Result::Usage(std::string("Unknown -verbose option ") + verbose_options[j]);
731       }
732     }
733 
734     return Result::Success(log_verbosity);
735   }
736 
737   static const char* Name() { return "LogVerbosity"; }
738   static const char* DescribeType() {
739     return "class|collector|compiler|deopt|gc|heap|interpreter|jdwp|jit|jni|monitor|oat|profiler|"
740            "signals|simulator|startup|third-party-jni|threads|verifier|verifier-debug|image|"
741            "systrace-locks|plugin|agents|dex";
742   }
743 };
744 
745 template <>
746 struct CmdlineType<ProfileSaverOptions> : CmdlineTypeParser<ProfileSaverOptions> {
747   using Result = CmdlineParseResult<ProfileSaverOptions>;
748 
749  private:
750   using StringResult = CmdlineParseResult<std::string>;
751   using DoubleResult = CmdlineParseResult<double>;
752 
753   template <typename T>
754   static Result ParseInto(ProfileSaverOptions& options,
755                           T ProfileSaverOptions::*pField,
756                           CmdlineParseResult<T>&& result) {
757     assert(pField != nullptr);
758 
759     if (result.IsSuccess()) {
760       options.*pField = result.ReleaseValue();
761       return Result::SuccessNoValue();
762     }
763 
764     return Result::CastError(result);
765   }
766 
767   static std::string RemovePrefix(const std::string& source) {
768     size_t prefix_idx = source.find(':');
769 
770     if (prefix_idx == std::string::npos) {
771       return "";
772     }
773 
774     return source.substr(prefix_idx + 1);
775   }
776 
777  public:
778   Result ParseAndAppend(const std::string& option, ProfileSaverOptions& existing) {
779     // Special case which doesn't include a wildcard argument definition.
780     // We pass-it through as-is.
781     if (option == "-Xjitsaveprofilinginfo") {
782       existing.enabled_ = true;
783       return Result::SuccessNoValue();
784     }
785 
786     if (option == "profile-boot-class-path") {
787       existing.profile_boot_class_path_ = true;
788       return Result::SuccessNoValue();
789     }
790 
791     if (option == "profile-aot-code") {
792       existing.profile_aot_code_ = true;
793       return Result::SuccessNoValue();
794     }
795 
796     if (option == "save-without-jit-notifications") {
797       existing.wait_for_jit_notifications_to_save_ = false;
798       return Result::SuccessNoValue();
799     }
800 
801     // The rest of these options are always the wildcard from '-Xps-*'
802     std::string suffix = RemovePrefix(option);
803 
804     if (android::base::StartsWith(option, "min-save-period-ms:")) {
805       CmdlineType<unsigned int> type_parser;
806       return ParseInto(existing,
807              &ProfileSaverOptions::min_save_period_ms_,
808              type_parser.Parse(suffix));
809     }
810     if (android::base::StartsWith(option, "min-first-save-ms:")) {
811       CmdlineType<unsigned int> type_parser;
812       return ParseInto(existing,
813              &ProfileSaverOptions::min_first_save_ms_,
814              type_parser.Parse(suffix));
815     }
816     if (android::base::StartsWith(option, "save-resolved-classes-delay-ms:")) {
817       CmdlineType<unsigned int> type_parser;
818       return ParseInto(existing,
819              &ProfileSaverOptions::save_resolved_classes_delay_ms_,
820              type_parser.Parse(suffix));
821     }
822     if (android::base::StartsWith(option, "hot-startup-method-samples:")) {
823       CmdlineType<unsigned int> type_parser;
824       return ParseInto(existing,
825              &ProfileSaverOptions::hot_startup_method_samples_,
826              type_parser.Parse(suffix));
827     }
828     if (android::base::StartsWith(option, "min-methods-to-save:")) {
829       CmdlineType<unsigned int> type_parser;
830       return ParseInto(existing,
831              &ProfileSaverOptions::min_methods_to_save_,
832              type_parser.Parse(suffix));
833     }
834     if (android::base::StartsWith(option, "min-classes-to-save:")) {
835       CmdlineType<unsigned int> type_parser;
836       return ParseInto(existing,
837              &ProfileSaverOptions::min_classes_to_save_,
838              type_parser.Parse(suffix));
839     }
840     if (android::base::StartsWith(option, "min-notification-before-wake:")) {
841       CmdlineType<unsigned int> type_parser;
842       return ParseInto(existing,
843              &ProfileSaverOptions::min_notification_before_wake_,
844              type_parser.Parse(suffix));
845     }
846     if (android::base::StartsWith(option, "max-notification-before-wake:")) {
847       CmdlineType<unsigned int> type_parser;
848       return ParseInto(existing,
849              &ProfileSaverOptions::max_notification_before_wake_,
850              type_parser.Parse(suffix));
851     }
852     if (android::base::StartsWith(option, "profile-path:")) {
853       existing.profile_path_ = suffix;
854       return Result::SuccessNoValue();
855     }
856 
857     return Result::Failure(std::string("Invalid suboption '") + option + "'");
858   }
859 
860   static const char* Name() { return "ProfileSaverOptions"; }
861   static const char* DescribeType() { return "string|unsigned integer"; }
862   static constexpr bool kCanParseBlankless = true;
863 };
864 
865 template<>
866 struct CmdlineType<ExperimentalFlags> : CmdlineTypeParser<ExperimentalFlags> {
867   Result ParseAndAppend(const std::string& option, ExperimentalFlags& existing) {
868     if (option == "none") {
869       existing = ExperimentalFlags::kNone;
870     } else {
871       return Result::Failure(std::string("Unknown option '") + option + "'");
872     }
873     return Result::SuccessNoValue();
874   }
875 
876   static const char* Name() { return "ExperimentalFlags"; }
877   static const char* DescribeType() { return "none"; }
878 };
879 }  // namespace art
880 #endif  // ART_CMDLINE_CMDLINE_TYPES_H_
881