• 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/strings.h"
28 
29 // Includes for the types that are being specialized
30 #include <string>
31 #include "base/logging.h"
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 "unsigned 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 if (option == "MC") {
420     return gc::kCollectorTypeMC;
421   } else {
422     return gc::kCollectorTypeNone;
423   }
424 }
425 
426 struct XGcOption {
427   // These defaults are used when the command line arguments for -Xgc:
428   // are either omitted completely or partially.
429   gc::CollectorType collector_type_ = gc::kCollectorTypeDefault;
430   bool verify_pre_gc_heap_ = false;
431   bool verify_pre_sweeping_heap_ = kIsDebugBuild;
432   bool verify_post_gc_heap_ = false;
433   bool verify_pre_gc_rosalloc_ = kIsDebugBuild;
434   bool verify_pre_sweeping_rosalloc_ = false;
435   bool verify_post_gc_rosalloc_ = false;
436   // Do no measurements for kUseTableLookupReadBarrier to avoid test timeouts. b/31679493
437   bool measure_ = kIsDebugBuild && !kUseTableLookupReadBarrier;
438   bool gcstress_ = false;
439 };
440 
441 template <>
442 struct CmdlineType<XGcOption> : CmdlineTypeParser<XGcOption> {
443   Result Parse(const std::string& option) {  // -Xgc: already stripped
444     XGcOption xgc{};
445 
446     std::vector<std::string> gc_options;
447     Split(option, ',', &gc_options);
448     for (const std::string& gc_option : gc_options) {
449       gc::CollectorType collector_type = ParseCollectorType(gc_option);
450       if (collector_type != gc::kCollectorTypeNone) {
451         xgc.collector_type_ = collector_type;
452       } else if (gc_option == "preverify") {
453         xgc.verify_pre_gc_heap_ = true;
454       } else if (gc_option == "nopreverify") {
455         xgc.verify_pre_gc_heap_ = false;
456       }  else if (gc_option == "presweepingverify") {
457         xgc.verify_pre_sweeping_heap_ = true;
458       } else if (gc_option == "nopresweepingverify") {
459         xgc.verify_pre_sweeping_heap_ = false;
460       } else if (gc_option == "postverify") {
461         xgc.verify_post_gc_heap_ = true;
462       } else if (gc_option == "nopostverify") {
463         xgc.verify_post_gc_heap_ = false;
464       } else if (gc_option == "preverify_rosalloc") {
465         xgc.verify_pre_gc_rosalloc_ = true;
466       } else if (gc_option == "nopreverify_rosalloc") {
467         xgc.verify_pre_gc_rosalloc_ = false;
468       } else if (gc_option == "presweepingverify_rosalloc") {
469         xgc.verify_pre_sweeping_rosalloc_ = true;
470       } else if (gc_option == "nopresweepingverify_rosalloc") {
471         xgc.verify_pre_sweeping_rosalloc_ = false;
472       } else if (gc_option == "postverify_rosalloc") {
473         xgc.verify_post_gc_rosalloc_ = true;
474       } else if (gc_option == "nopostverify_rosalloc") {
475         xgc.verify_post_gc_rosalloc_ = false;
476       } else if (gc_option == "gcstress") {
477         xgc.gcstress_ = true;
478       } else if (gc_option == "nogcstress") {
479         xgc.gcstress_ = false;
480       } else if (gc_option == "measure") {
481         xgc.measure_ = true;
482       } else if ((gc_option == "precise") ||
483                  (gc_option == "noprecise") ||
484                  (gc_option == "verifycardtable") ||
485                  (gc_option == "noverifycardtable")) {
486         // Ignored for backwards compatibility.
487       } else {
488         return Result::Usage(std::string("Unknown -Xgc option ") + gc_option);
489       }
490     }
491 
492     return Result::Success(std::move(xgc));
493   }
494 
495   static const char* Name() { return "XgcOption"; }
496 };
497 
498 struct BackgroundGcOption {
499   // If background_collector_type_ is kCollectorTypeNone, it defaults to the
500   // XGcOption::collector_type_ after parsing options. If you set this to
501   // kCollectorTypeHSpaceCompact then we will do an hspace compaction when
502   // we transition to background instead of a normal collector transition.
503   gc::CollectorType background_collector_type_;
504 
505   BackgroundGcOption(gc::CollectorType background_collector_type)  // NOLINT [runtime/explicit] [5]
506     : background_collector_type_(background_collector_type) {}
507   BackgroundGcOption()
508     : background_collector_type_(gc::kCollectorTypeNone) {
509   }
510 
511   operator gc::CollectorType() const { return background_collector_type_; }
512 };
513 
514 template<>
515 struct CmdlineType<BackgroundGcOption>
516   : CmdlineTypeParser<BackgroundGcOption>, private BackgroundGcOption {
517   Result Parse(const std::string& substring) {
518     // Special handling for HSpaceCompact since this is only valid as a background GC type.
519     if (substring == "HSpaceCompact") {
520       background_collector_type_ = gc::kCollectorTypeHomogeneousSpaceCompact;
521     } else {
522       gc::CollectorType collector_type = ParseCollectorType(substring);
523       if (collector_type != gc::kCollectorTypeNone) {
524         background_collector_type_ = collector_type;
525       } else {
526         return Result::Failure();
527       }
528     }
529 
530     BackgroundGcOption res = *this;
531     return Result::Success(res);
532   }
533 
534   static const char* Name() { return "BackgroundGcOption"; }
535 };
536 
537 template <>
538 struct CmdlineType<LogVerbosity> : CmdlineTypeParser<LogVerbosity> {
539   Result Parse(const std::string& options) {
540     LogVerbosity log_verbosity = LogVerbosity();
541 
542     std::vector<std::string> verbose_options;
543     Split(options, ',', &verbose_options);
544     for (size_t j = 0; j < verbose_options.size(); ++j) {
545       if (verbose_options[j] == "class") {
546         log_verbosity.class_linker = true;
547       } else if (verbose_options[j] == "collector") {
548         log_verbosity.collector = true;
549       } else if (verbose_options[j] == "compiler") {
550         log_verbosity.compiler = true;
551       } else if (verbose_options[j] == "deopt") {
552         log_verbosity.deopt = true;
553       } else if (verbose_options[j] == "gc") {
554         log_verbosity.gc = true;
555       } else if (verbose_options[j] == "heap") {
556         log_verbosity.heap = true;
557       } else if (verbose_options[j] == "jdwp") {
558         log_verbosity.jdwp = true;
559       } else if (verbose_options[j] == "jit") {
560         log_verbosity.jit = true;
561       } else if (verbose_options[j] == "jni") {
562         log_verbosity.jni = true;
563       } else if (verbose_options[j] == "monitor") {
564         log_verbosity.monitor = true;
565       } else if (verbose_options[j] == "oat") {
566         log_verbosity.oat = true;
567       } else if (verbose_options[j] == "profiler") {
568         log_verbosity.profiler = true;
569       } else if (verbose_options[j] == "signals") {
570         log_verbosity.signals = true;
571       } else if (verbose_options[j] == "simulator") {
572         log_verbosity.simulator = true;
573       } else if (verbose_options[j] == "startup") {
574         log_verbosity.startup = true;
575       } else if (verbose_options[j] == "third-party-jni") {
576         log_verbosity.third_party_jni = true;
577       } else if (verbose_options[j] == "threads") {
578         log_verbosity.threads = true;
579       } else if (verbose_options[j] == "verifier") {
580         log_verbosity.verifier = true;
581       } else if (verbose_options[j] == "verifier-debug") {
582         log_verbosity.verifier_debug = true;
583       } else if (verbose_options[j] == "image") {
584         log_verbosity.image = true;
585       } else if (verbose_options[j] == "systrace-locks") {
586         log_verbosity.systrace_lock_logging = true;
587       } else if (verbose_options[j] == "agents") {
588         log_verbosity.agents = true;
589       } else if (verbose_options[j] == "dex") {
590         log_verbosity.dex = true;
591       } else {
592         return Result::Usage(std::string("Unknown -verbose option ") + verbose_options[j]);
593       }
594     }
595 
596     return Result::Success(log_verbosity);
597   }
598 
599   static const char* Name() { return "LogVerbosity"; }
600 };
601 
602 template <>
603 struct CmdlineType<ProfileSaverOptions> : CmdlineTypeParser<ProfileSaverOptions> {
604   using Result = CmdlineParseResult<ProfileSaverOptions>;
605 
606  private:
607   using StringResult = CmdlineParseResult<std::string>;
608   using DoubleResult = CmdlineParseResult<double>;
609 
610   template <typename T>
611   static Result ParseInto(ProfileSaverOptions& options,
612                           T ProfileSaverOptions::*pField,
613                           CmdlineParseResult<T>&& result) {
614     assert(pField != nullptr);
615 
616     if (result.IsSuccess()) {
617       options.*pField = result.ReleaseValue();
618       return Result::SuccessNoValue();
619     }
620 
621     return Result::CastError(result);
622   }
623 
624   static std::string RemovePrefix(const std::string& source) {
625     size_t prefix_idx = source.find(':');
626 
627     if (prefix_idx == std::string::npos) {
628       return "";
629     }
630 
631     return source.substr(prefix_idx + 1);
632   }
633 
634  public:
635   Result ParseAndAppend(const std::string& option, ProfileSaverOptions& existing) {
636     // Special case which doesn't include a wildcard argument definition.
637     // We pass-it through as-is.
638     if (option == "-Xjitsaveprofilinginfo") {
639       existing.enabled_ = true;
640       return Result::SuccessNoValue();
641     }
642 
643     if (option == "profile-boot-class-path") {
644       existing.profile_boot_class_path_ = true;
645       return Result::SuccessNoValue();
646     }
647 
648     if (option == "profile-aot-code") {
649       existing.profile_aot_code_ = true;
650       return Result::SuccessNoValue();
651     }
652 
653     if (option == "save-without-jit-notifications") {
654       existing.wait_for_jit_notifications_to_save_ = false;
655       return Result::SuccessNoValue();
656     }
657 
658     // The rest of these options are always the wildcard from '-Xps-*'
659     std::string suffix = RemovePrefix(option);
660 
661     if (android::base::StartsWith(option, "min-save-period-ms:")) {
662       CmdlineType<unsigned int> type_parser;
663       return ParseInto(existing,
664              &ProfileSaverOptions::min_save_period_ms_,
665              type_parser.Parse(suffix));
666     }
667     if (android::base::StartsWith(option, "save-resolved-classes-delay-ms:")) {
668       CmdlineType<unsigned int> type_parser;
669       return ParseInto(existing,
670              &ProfileSaverOptions::save_resolved_classes_delay_ms_,
671              type_parser.Parse(suffix));
672     }
673     if (android::base::StartsWith(option, "hot-startup-method-samples:")) {
674       CmdlineType<unsigned int> type_parser;
675       return ParseInto(existing,
676              &ProfileSaverOptions::hot_startup_method_samples_,
677              type_parser.Parse(suffix));
678     }
679     if (android::base::StartsWith(option, "min-methods-to-save:")) {
680       CmdlineType<unsigned int> type_parser;
681       return ParseInto(existing,
682              &ProfileSaverOptions::min_methods_to_save_,
683              type_parser.Parse(suffix));
684     }
685     if (android::base::StartsWith(option, "min-classes-to-save:")) {
686       CmdlineType<unsigned int> type_parser;
687       return ParseInto(existing,
688              &ProfileSaverOptions::min_classes_to_save_,
689              type_parser.Parse(suffix));
690     }
691     if (android::base::StartsWith(option, "min-notification-before-wake:")) {
692       CmdlineType<unsigned int> type_parser;
693       return ParseInto(existing,
694              &ProfileSaverOptions::min_notification_before_wake_,
695              type_parser.Parse(suffix));
696     }
697     if (android::base::StartsWith(option, "max-notification-before-wake:")) {
698       CmdlineType<unsigned int> type_parser;
699       return ParseInto(existing,
700              &ProfileSaverOptions::max_notification_before_wake_,
701              type_parser.Parse(suffix));
702     }
703     if (android::base::StartsWith(option, "profile-path:")) {
704       existing.profile_path_ = suffix;
705       return Result::SuccessNoValue();
706     }
707 
708     return Result::Failure(std::string("Invalid suboption '") + option + "'");
709   }
710 
711   static const char* Name() { return "ProfileSaverOptions"; }
712   static constexpr bool kCanParseBlankless = true;
713 };
714 
715 template<>
716 struct CmdlineType<ExperimentalFlags> : CmdlineTypeParser<ExperimentalFlags> {
717   Result ParseAndAppend(const std::string& option, ExperimentalFlags& existing) {
718     if (option == "none") {
719       existing = ExperimentalFlags::kNone;
720     } else {
721       return Result::Failure(std::string("Unknown option '") + option + "'");
722     }
723     return Result::SuccessNoValue();
724   }
725 
726   static const char* Name() { return "ExperimentalFlags"; }
727 };
728 }  // namespace art
729 #endif  // ART_CMDLINE_CMDLINE_TYPES_H_
730