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