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