• 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 
23 #include "cmdline_type_parser.h"
24 #include "detail/cmdline_debug_detail.h"
25 #include "memory_representation.h"
26 
27 #include "android-base/logging.h"
28 #include "android-base/strings.h"
29 
30 // Includes for the types that are being specialized
31 #include <string>
32 #include "base/time_utils.h"
33 #include "experimental_flags.h"
34 #include "gc/collector_type.h"
35 #include "gc/space/large_object_space.h"
36 #include "jdwp/jdwp.h"
37 #include "jdwp_provider.h"
38 #include "jit/profile_saver_options.h"
39 #include "plugin.h"
40 #include "read_barrier_config.h"
41 #include "ti/agent.h"
42 #include "unit.h"
43 
44 namespace art {
45 
46 // The default specialization will always fail parsing the type from a string.
47 // Provide your own specialization that inherits from CmdlineTypeParser<T>
48 // and implements either Parse or ParseAndAppend
49 // (only if the argument was defined with ::AppendValues()) but not both.
50 template <typename T>
51 struct CmdlineType : CmdlineTypeParser<T> {
52 };
53 
54 // Specializations for CmdlineType<T> follow:
55 
56 // Parse argument definitions for Unit-typed arguments.
57 template <>
58 struct CmdlineType<Unit> : CmdlineTypeParser<Unit> {
59   Result Parse(const std::string& args) {
60     if (args == "") {
61       return Result::Success(Unit{});
62     }
63     return Result::Failure("Unexpected extra characters " + args);
64   }
65 };
66 
67 template <>
68 struct CmdlineType<JdwpProvider> : CmdlineTypeParser<JdwpProvider> {
69   /*
70    * Handle a single JDWP provider name. Must be either 'internal', 'default', or the file name of
71    * an agent. A plugin will make use of this and the jdwpOptions to set up jdwp when appropriate.
72    */
73   Result Parse(const std::string& option) {
74     if (option == "help") {
75       return Result::Usage(
76           "Example: -XjdwpProvider:none to disable JDWP\n"
77           "Example: -XjdwpProvider:internal for internal jdwp implementation\n"
78           "Example: -XjdwpProvider:adbconnection for adb connection mediated jdwp implementation\n"
79           "Example: -XjdwpProvider:default for the default jdwp implementation\n");
80     } else if (option == "default") {
81       return Result::Success(JdwpProvider::kDefaultJdwpProvider);
82     } else if (option == "internal") {
83       return Result::Success(JdwpProvider::kInternal);
84     } else if (option == "adbconnection") {
85       return Result::Success(JdwpProvider::kAdbConnection);
86     } else if (option == "none") {
87       return Result::Success(JdwpProvider::kNone);
88     } else {
89       return Result::Failure(std::string("not a valid jdwp provider: ") + option);
90     }
91   }
92   static const char* Name() { return "JdwpProvider"; }
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 };
180 
181 template <>
182 struct CmdlineType<double> : CmdlineTypeParser<double> {
183   Result Parse(const std::string& str) {
184     char* end = nullptr;
185     errno = 0;
186     double value = strtod(str.c_str(), &end);
187 
188     if (*end != '\0') {
189       return Result::Failure("Failed to parse double from " + str);
190     }
191     if (errno == ERANGE) {
192       return Result::OutOfRange(
193           "Failed to parse double from " + str + "; overflow/underflow occurred");
194     }
195 
196     return Result::Success(value);
197   }
198 
199   static const char* Name() { return "double"; }
200 };
201 
202 template <typename T>
203 static inline CmdlineParseResult<T> ParseNumeric(const std::string& str) {
204   static_assert(sizeof(T) < sizeof(long long int),  // NOLINT [runtime/int] [4]
205                 "Current support is restricted.");
206 
207   const char* begin = str.c_str();
208   char* end;
209 
210   // Parse into a larger type (long long) because we can't use strtoul
211   // since it silently converts negative values into unsigned long and doesn't set errno.
212   errno = 0;
213   long long int result = strtoll(begin, &end, 10);  // NOLINT [runtime/int] [4]
214   if (begin == end || *end != '\0' || errno == EINVAL) {
215     return CmdlineParseResult<T>::Failure("Failed to parse integer from " + str);
216   } else if ((errno == ERANGE) ||  // NOLINT [runtime/int] [4]
217       result < std::numeric_limits<T>::min() || result > std::numeric_limits<T>::max()) {
218     return CmdlineParseResult<T>::OutOfRange(
219         "Failed to parse integer from " + str + "; out of range");
220   }
221 
222   return CmdlineParseResult<T>::Success(static_cast<T>(result));
223 }
224 
225 template <>
226 struct CmdlineType<unsigned int> : CmdlineTypeParser<unsigned int> {
227   Result Parse(const std::string& str) {
228     return ParseNumeric<unsigned int>(str);
229   }
230 
231   static const char* Name() { return "unsigned integer"; }
232 };
233 
234 template <>
235 struct CmdlineType<int> : CmdlineTypeParser<int> {
236   Result Parse(const std::string& str) {
237     return ParseNumeric<int>(str);
238   }
239 
240   static const char* Name() { return "integer"; }
241 };
242 
243 // Lightweight nanosecond value type. Allows parser to convert user-input from milliseconds
244 // to nanoseconds automatically after parsing.
245 //
246 // All implicit conversion from uint64_t uses nanoseconds.
247 struct MillisecondsToNanoseconds {
248   // Create from nanoseconds.
249   MillisecondsToNanoseconds(uint64_t nanoseconds) : nanoseconds_(nanoseconds) {  // NOLINT [runtime/explicit] [5]
250   }
251 
252   // Create from milliseconds.
253   static MillisecondsToNanoseconds FromMilliseconds(unsigned int milliseconds) {
254     return MillisecondsToNanoseconds(MsToNs(milliseconds));
255   }
256 
257   // Get the underlying nanoseconds value.
258   uint64_t GetNanoseconds() const {
259     return nanoseconds_;
260   }
261 
262   // Get the milliseconds value [via a conversion]. Loss of precision will occur.
263   uint64_t GetMilliseconds() const {
264     return NsToMs(nanoseconds_);
265   }
266 
267   // Get the underlying nanoseconds value.
268   operator uint64_t() const {
269     return GetNanoseconds();
270   }
271 
272   // Default constructors/copy-constructors.
273   MillisecondsToNanoseconds() : nanoseconds_(0ul) {}
274   MillisecondsToNanoseconds(const MillisecondsToNanoseconds&) = default;
275   MillisecondsToNanoseconds(MillisecondsToNanoseconds&&) = default;
276 
277  private:
278   uint64_t nanoseconds_;
279 };
280 
281 template <>
282 struct CmdlineType<MillisecondsToNanoseconds> : CmdlineTypeParser<MillisecondsToNanoseconds> {
283   Result Parse(const std::string& str) {
284     CmdlineType<unsigned int> uint_parser;
285     CmdlineParseResult<unsigned int> res = uint_parser.Parse(str);
286 
287     if (res.IsSuccess()) {
288       return Result::Success(MillisecondsToNanoseconds::FromMilliseconds(res.GetValue()));
289     } else {
290       return Result::CastError(res);
291     }
292   }
293 
294   static const char* Name() { return "MillisecondsToNanoseconds"; }
295 };
296 
297 template <>
298 struct CmdlineType<std::string> : CmdlineTypeParser<std::string> {
299   Result Parse(const std::string& args) {
300     return Result::Success(args);
301   }
302 
303   Result ParseAndAppend(const std::string& args,
304                         std::string& existing_value) {
305     if (existing_value.empty()) {
306       existing_value = args;
307     } else {
308       existing_value += ' ';
309       existing_value += args;
310     }
311     return Result::SuccessNoValue();
312   }
313 };
314 
315 template <>
316 struct CmdlineType<std::vector<Plugin>> : CmdlineTypeParser<std::vector<Plugin>> {
317   Result Parse(const std::string& args) {
318     assert(false && "Use AppendValues() for a Plugin vector type");
319     return Result::Failure("Unconditional failure: Plugin vector must be appended: " + args);
320   }
321 
322   Result ParseAndAppend(const std::string& args,
323                         std::vector<Plugin>& existing_value) {
324     existing_value.push_back(Plugin::Create(args));
325     return Result::SuccessNoValue();
326   }
327 
328   static const char* Name() { return "std::vector<Plugin>"; }
329 };
330 
331 template <>
332 struct CmdlineType<std::list<ti::AgentSpec>> : CmdlineTypeParser<std::list<ti::AgentSpec>> {
333   Result Parse(const std::string& args) {
334     assert(false && "Use AppendValues() for an Agent list type");
335     return Result::Failure("Unconditional failure: Agent list must be appended: " + args);
336   }
337 
338   Result ParseAndAppend(const std::string& args,
339                         std::list<ti::AgentSpec>& existing_value) {
340     existing_value.emplace_back(args);
341     return Result::SuccessNoValue();
342   }
343 
344   static const char* Name() { return "std::list<ti::AgentSpec>"; }
345 };
346 
347 template <>
348 struct CmdlineType<std::vector<std::string>> : CmdlineTypeParser<std::vector<std::string>> {
349   Result Parse(const std::string& args) {
350     assert(false && "Use AppendValues() for a string vector type");
351     return Result::Failure("Unconditional failure: string vector must be appended: " + args);
352   }
353 
354   Result ParseAndAppend(const std::string& args,
355                         std::vector<std::string>& existing_value) {
356     existing_value.push_back(args);
357     return Result::SuccessNoValue();
358   }
359 
360   static const char* Name() { return "std::vector<std::string>"; }
361 };
362 
363 template <char Separator>
364 struct ParseStringList {
365   explicit ParseStringList(std::vector<std::string>&& list) : list_(list) {}
366 
367   operator std::vector<std::string>() const {
368     return list_;
369   }
370 
371   operator std::vector<std::string>&&() && {
372     return std::move(list_);
373   }
374 
375   size_t Size() const {
376     return list_.size();
377   }
378 
379   std::string Join() const {
380     return android::base::Join(list_, Separator);
381   }
382 
383   static ParseStringList<Separator> Split(const std::string& str) {
384     std::vector<std::string> list;
385     art::Split(str, Separator, &list);
386     return ParseStringList<Separator>(std::move(list));
387   }
388 
389   ParseStringList() = default;
390   ParseStringList(const ParseStringList&) = default;
391   ParseStringList(ParseStringList&&) = default;
392 
393  private:
394   std::vector<std::string> list_;
395 };
396 
397 template <char Separator>
398 struct CmdlineType<ParseStringList<Separator>> : CmdlineTypeParser<ParseStringList<Separator>> {
399   using Result = CmdlineParseResult<ParseStringList<Separator>>;
400 
401   Result Parse(const std::string& args) {
402     return Result::Success(ParseStringList<Separator>::Split(args));
403   }
404 
405   static const char* Name() { return "ParseStringList<Separator>"; }
406 };
407 
408 static gc::CollectorType ParseCollectorType(const std::string& option) {
409   if (option == "MS" || option == "nonconcurrent") {
410     return gc::kCollectorTypeMS;
411   } else if (option == "CMS" || option == "concurrent") {
412     return gc::kCollectorTypeCMS;
413   } else if (option == "SS") {
414     return gc::kCollectorTypeSS;
415   } else if (option == "GSS") {
416     return gc::kCollectorTypeGSS;
417   } else if (option == "CC") {
418     return gc::kCollectorTypeCC;
419   } else {
420     return gc::kCollectorTypeNone;
421   }
422 }
423 
424 struct XGcOption {
425   // These defaults are used when the command line arguments for -Xgc:
426   // are either omitted completely or partially.
427   gc::CollectorType collector_type_ = gc::kCollectorTypeDefault;
428   bool verify_pre_gc_heap_ = false;
429   bool verify_pre_sweeping_heap_ = kIsDebugBuild;
430   bool generational_cc = kEnableGenerationalCCByDefault;
431   bool verify_post_gc_heap_ = false;
432   bool verify_pre_gc_rosalloc_ = kIsDebugBuild;
433   bool verify_pre_sweeping_rosalloc_ = false;
434   bool verify_post_gc_rosalloc_ = false;
435   // Do no measurements for kUseTableLookupReadBarrier to avoid test timeouts. b/31679493
436   bool measure_ = kIsDebugBuild && !kUseTableLookupReadBarrier;
437   bool gcstress_ = false;
438 };
439 
440 template <>
441 struct CmdlineType<XGcOption> : CmdlineTypeParser<XGcOption> {
442   Result Parse(const std::string& option) {  // -Xgc: already stripped
443     XGcOption xgc{};
444 
445     std::vector<std::string> gc_options;
446     Split(option, ',', &gc_options);
447     for (const std::string& gc_option : gc_options) {
448       gc::CollectorType collector_type = ParseCollectorType(gc_option);
449       if (collector_type != gc::kCollectorTypeNone) {
450         xgc.collector_type_ = collector_type;
451       } else if (gc_option == "preverify") {
452         xgc.verify_pre_gc_heap_ = true;
453       } else if (gc_option == "nopreverify") {
454         xgc.verify_pre_gc_heap_ = false;
455       }  else if (gc_option == "presweepingverify") {
456         xgc.verify_pre_sweeping_heap_ = true;
457       } else if (gc_option == "nopresweepingverify") {
458         xgc.verify_pre_sweeping_heap_ = false;
459       } else if (gc_option == "generational_cc") {
460         // Note: Option "-Xgc:generational_cc" can be passed directly by
461         // app_process/zygote (see `android::AndroidRuntime::startVm`). If this
462         // option is ever deprecated, it should still be accepted (but ignored)
463         // for compatibility reasons (this should not prevent the runtime from
464         // starting up).
465         xgc.generational_cc = true;
466       } else if (gc_option == "nogenerational_cc") {
467         // Note: Option "-Xgc:nogenerational_cc" can be passed directly by
468         // app_process/zygote (see `android::AndroidRuntime::startVm`). If this
469         // option is ever deprecated, it should still be accepted (but ignored)
470         // for compatibility reasons (this should not prevent the runtime from
471         // starting up).
472         xgc.generational_cc = false;
473       } else if (gc_option == "postverify") {
474         xgc.verify_post_gc_heap_ = true;
475       } else if (gc_option == "nopostverify") {
476         xgc.verify_post_gc_heap_ = false;
477       } else if (gc_option == "preverify_rosalloc") {
478         xgc.verify_pre_gc_rosalloc_ = true;
479       } else if (gc_option == "nopreverify_rosalloc") {
480         xgc.verify_pre_gc_rosalloc_ = false;
481       } else if (gc_option == "presweepingverify_rosalloc") {
482         xgc.verify_pre_sweeping_rosalloc_ = true;
483       } else if (gc_option == "nopresweepingverify_rosalloc") {
484         xgc.verify_pre_sweeping_rosalloc_ = false;
485       } else if (gc_option == "postverify_rosalloc") {
486         xgc.verify_post_gc_rosalloc_ = true;
487       } else if (gc_option == "nopostverify_rosalloc") {
488         xgc.verify_post_gc_rosalloc_ = false;
489       } else if (gc_option == "gcstress") {
490         xgc.gcstress_ = true;
491       } else if (gc_option == "nogcstress") {
492         xgc.gcstress_ = false;
493       } else if (gc_option == "measure") {
494         xgc.measure_ = true;
495       } else if ((gc_option == "precise") ||
496                  (gc_option == "noprecise") ||
497                  (gc_option == "verifycardtable") ||
498                  (gc_option == "noverifycardtable")) {
499         // Ignored for backwards compatibility.
500       } else {
501         return Result::Usage(std::string("Unknown -Xgc option ") + gc_option);
502       }
503     }
504 
505     return Result::Success(std::move(xgc));
506   }
507 
508   static const char* Name() { return "XgcOption"; }
509 };
510 
511 struct BackgroundGcOption {
512   // If background_collector_type_ is kCollectorTypeNone, it defaults to the
513   // XGcOption::collector_type_ after parsing options. If you set this to
514   // kCollectorTypeHSpaceCompact then we will do an hspace compaction when
515   // we transition to background instead of a normal collector transition.
516   gc::CollectorType background_collector_type_;
517 
518   BackgroundGcOption(gc::CollectorType background_collector_type)  // NOLINT [runtime/explicit] [5]
519     : background_collector_type_(background_collector_type) {}
520   BackgroundGcOption()
521     : background_collector_type_(gc::kCollectorTypeNone) {
522   }
523 
524   operator gc::CollectorType() const { return background_collector_type_; }
525 };
526 
527 template<>
528 struct CmdlineType<BackgroundGcOption>
529   : CmdlineTypeParser<BackgroundGcOption>, private BackgroundGcOption {
530   Result Parse(const std::string& substring) {
531     // Special handling for HSpaceCompact since this is only valid as a background GC type.
532     if (substring == "HSpaceCompact") {
533       background_collector_type_ = gc::kCollectorTypeHomogeneousSpaceCompact;
534     } else {
535       gc::CollectorType collector_type = ParseCollectorType(substring);
536       if (collector_type != gc::kCollectorTypeNone) {
537         background_collector_type_ = collector_type;
538       } else {
539         return Result::Failure();
540       }
541     }
542 
543     BackgroundGcOption res = *this;
544     return Result::Success(res);
545   }
546 
547   static const char* Name() { return "BackgroundGcOption"; }
548 };
549 
550 template <>
551 struct CmdlineType<LogVerbosity> : CmdlineTypeParser<LogVerbosity> {
552   Result Parse(const std::string& options) {
553     LogVerbosity log_verbosity = LogVerbosity();
554 
555     std::vector<std::string> verbose_options;
556     Split(options, ',', &verbose_options);
557     for (size_t j = 0; j < verbose_options.size(); ++j) {
558       if (verbose_options[j] == "class") {
559         log_verbosity.class_linker = true;
560       } else if (verbose_options[j] == "collector") {
561         log_verbosity.collector = true;
562       } else if (verbose_options[j] == "compiler") {
563         log_verbosity.compiler = true;
564       } else if (verbose_options[j] == "deopt") {
565         log_verbosity.deopt = true;
566       } else if (verbose_options[j] == "gc") {
567         log_verbosity.gc = true;
568       } else if (verbose_options[j] == "heap") {
569         log_verbosity.heap = true;
570       } else if (verbose_options[j] == "jdwp") {
571         log_verbosity.jdwp = true;
572       } else if (verbose_options[j] == "jit") {
573         log_verbosity.jit = true;
574       } else if (verbose_options[j] == "jni") {
575         log_verbosity.jni = true;
576       } else if (verbose_options[j] == "monitor") {
577         log_verbosity.monitor = true;
578       } else if (verbose_options[j] == "oat") {
579         log_verbosity.oat = true;
580       } else if (verbose_options[j] == "profiler") {
581         log_verbosity.profiler = true;
582       } else if (verbose_options[j] == "signals") {
583         log_verbosity.signals = true;
584       } else if (verbose_options[j] == "simulator") {
585         log_verbosity.simulator = true;
586       } else if (verbose_options[j] == "startup") {
587         log_verbosity.startup = true;
588       } else if (verbose_options[j] == "third-party-jni") {
589         log_verbosity.third_party_jni = true;
590       } else if (verbose_options[j] == "threads") {
591         log_verbosity.threads = true;
592       } else if (verbose_options[j] == "verifier") {
593         log_verbosity.verifier = true;
594       } else if (verbose_options[j] == "verifier-debug") {
595         log_verbosity.verifier_debug = true;
596       } else if (verbose_options[j] == "image") {
597         log_verbosity.image = true;
598       } else if (verbose_options[j] == "systrace-locks") {
599         log_verbosity.systrace_lock_logging = true;
600       } else if (verbose_options[j] == "agents") {
601         log_verbosity.agents = true;
602       } else if (verbose_options[j] == "dex") {
603         log_verbosity.dex = true;
604       } else {
605         return Result::Usage(std::string("Unknown -verbose option ") + verbose_options[j]);
606       }
607     }
608 
609     return Result::Success(log_verbosity);
610   }
611 
612   static const char* Name() { return "LogVerbosity"; }
613 };
614 
615 template <>
616 struct CmdlineType<ProfileSaverOptions> : CmdlineTypeParser<ProfileSaverOptions> {
617   using Result = CmdlineParseResult<ProfileSaverOptions>;
618 
619  private:
620   using StringResult = CmdlineParseResult<std::string>;
621   using DoubleResult = CmdlineParseResult<double>;
622 
623   template <typename T>
624   static Result ParseInto(ProfileSaverOptions& options,
625                           T ProfileSaverOptions::*pField,
626                           CmdlineParseResult<T>&& result) {
627     assert(pField != nullptr);
628 
629     if (result.IsSuccess()) {
630       options.*pField = result.ReleaseValue();
631       return Result::SuccessNoValue();
632     }
633 
634     return Result::CastError(result);
635   }
636 
637   static std::string RemovePrefix(const std::string& source) {
638     size_t prefix_idx = source.find(':');
639 
640     if (prefix_idx == std::string::npos) {
641       return "";
642     }
643 
644     return source.substr(prefix_idx + 1);
645   }
646 
647  public:
648   Result ParseAndAppend(const std::string& option, ProfileSaverOptions& existing) {
649     // Special case which doesn't include a wildcard argument definition.
650     // We pass-it through as-is.
651     if (option == "-Xjitsaveprofilinginfo") {
652       existing.enabled_ = true;
653       return Result::SuccessNoValue();
654     }
655 
656     if (option == "profile-boot-class-path") {
657       existing.profile_boot_class_path_ = true;
658       return Result::SuccessNoValue();
659     }
660 
661     if (option == "profile-aot-code") {
662       existing.profile_aot_code_ = true;
663       return Result::SuccessNoValue();
664     }
665 
666     if (option == "save-without-jit-notifications") {
667       existing.wait_for_jit_notifications_to_save_ = false;
668       return Result::SuccessNoValue();
669     }
670 
671     // The rest of these options are always the wildcard from '-Xps-*'
672     std::string suffix = RemovePrefix(option);
673 
674     if (android::base::StartsWith(option, "min-save-period-ms:")) {
675       CmdlineType<unsigned int> type_parser;
676       return ParseInto(existing,
677              &ProfileSaverOptions::min_save_period_ms_,
678              type_parser.Parse(suffix));
679     }
680     if (android::base::StartsWith(option, "save-resolved-classes-delay-ms:")) {
681       CmdlineType<unsigned int> type_parser;
682       return ParseInto(existing,
683              &ProfileSaverOptions::save_resolved_classes_delay_ms_,
684              type_parser.Parse(suffix));
685     }
686     if (android::base::StartsWith(option, "hot-startup-method-samples:")) {
687       CmdlineType<unsigned int> type_parser;
688       return ParseInto(existing,
689              &ProfileSaverOptions::hot_startup_method_samples_,
690              type_parser.Parse(suffix));
691     }
692     if (android::base::StartsWith(option, "min-methods-to-save:")) {
693       CmdlineType<unsigned int> type_parser;
694       return ParseInto(existing,
695              &ProfileSaverOptions::min_methods_to_save_,
696              type_parser.Parse(suffix));
697     }
698     if (android::base::StartsWith(option, "min-classes-to-save:")) {
699       CmdlineType<unsigned int> type_parser;
700       return ParseInto(existing,
701              &ProfileSaverOptions::min_classes_to_save_,
702              type_parser.Parse(suffix));
703     }
704     if (android::base::StartsWith(option, "min-notification-before-wake:")) {
705       CmdlineType<unsigned int> type_parser;
706       return ParseInto(existing,
707              &ProfileSaverOptions::min_notification_before_wake_,
708              type_parser.Parse(suffix));
709     }
710     if (android::base::StartsWith(option, "max-notification-before-wake:")) {
711       CmdlineType<unsigned int> type_parser;
712       return ParseInto(existing,
713              &ProfileSaverOptions::max_notification_before_wake_,
714              type_parser.Parse(suffix));
715     }
716     if (android::base::StartsWith(option, "profile-path:")) {
717       existing.profile_path_ = suffix;
718       return Result::SuccessNoValue();
719     }
720 
721     return Result::Failure(std::string("Invalid suboption '") + option + "'");
722   }
723 
724   static const char* Name() { return "ProfileSaverOptions"; }
725   static constexpr bool kCanParseBlankless = true;
726 };
727 
728 template<>
729 struct CmdlineType<ExperimentalFlags> : CmdlineTypeParser<ExperimentalFlags> {
730   Result ParseAndAppend(const std::string& option, ExperimentalFlags& existing) {
731     if (option == "none") {
732       existing = ExperimentalFlags::kNone;
733     } else {
734       return Result::Failure(std::string("Unknown option '") + option + "'");
735     }
736     return Result::SuccessNoValue();
737   }
738 
739   static const char* Name() { return "ExperimentalFlags"; }
740 };
741 }  // namespace art
742 #endif  // ART_CMDLINE_CMDLINE_TYPES_H_
743