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 "memory_representation.h"
22 #include "detail/cmdline_debug_detail.h"
23 #include "cmdline_type_parser.h"
24 
25 // Includes for the types that are being specialized
26 #include <string>
27 #include "unit.h"
28 #include "jdwp/jdwp.h"
29 #include "base/logging.h"
30 #include "base/time_utils.h"
31 #include "experimental_flags.h"
32 #include "gc/collector_type.h"
33 #include "gc/space/large_object_space.h"
34 #include "profiler_options.h"
35 
36 namespace art {
37 
38 // The default specialization will always fail parsing the type from a string.
39 // Provide your own specialization that inherits from CmdlineTypeParser<T>
40 // and implements either Parse or ParseAndAppend
41 // (only if the argument was defined with ::AppendValues()) but not both.
42 template <typename T>
43 struct CmdlineType : CmdlineTypeParser<T> {
44 };
45 
46 // Specializations for CmdlineType<T> follow:
47 
48 // Parse argument definitions for Unit-typed arguments.
49 template <>
50 struct CmdlineType<Unit> : CmdlineTypeParser<Unit> {
51   Result Parse(const std::string& args) {
52     if (args == "") {
53       return Result::Success(Unit{});  // NOLINT [whitespace/braces] [5]
54     }
55     return Result::Failure("Unexpected extra characters " + args);
56   }
57 };
58 
59 template <>
60 struct CmdlineType<JDWP::JdwpOptions> : CmdlineTypeParser<JDWP::JdwpOptions> {
61   /*
62    * Handle one of the JDWP name/value pairs.
63    *
64    * JDWP options are:
65    *  help: if specified, show help message and bail
66    *  transport: may be dt_socket or dt_shmem
67    *  address: for dt_socket, "host:port", or just "port" when listening
68    *  server: if "y", wait for debugger to attach; if "n", attach to debugger
69    *  timeout: how long to wait for debugger to connect / listen
70    *
71    * Useful with server=n (these aren't supported yet):
72    *  onthrow=<exception-name>: connect to debugger when exception thrown
73    *  onuncaught=y|n: connect to debugger when uncaught exception thrown
74    *  launch=<command-line>: launch the debugger itself
75    *
76    * The "transport" option is required, as is "address" if server=n.
77    */
78   Result Parse(const std::string& options) {
79     VLOG(jdwp) << "ParseJdwpOptions: " << options;
80 
81     if (options == "help") {
82       return Result::Usage(
83           "Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n"
84           "Example: -Xrunjdwp:transport=dt_socket,address=localhost:6500,server=n\n");
85     }
86 
87     const std::string s;
88 
89     std::vector<std::string> pairs;
90     Split(options, ',', &pairs);
91 
92     JDWP::JdwpOptions jdwp_options;
93 
94     for (const std::string& jdwp_option : pairs) {
95       std::string::size_type equals_pos = jdwp_option.find('=');
96       if (equals_pos == std::string::npos) {
97         return Result::Failure(s +
98             "Can't parse JDWP option '" + jdwp_option + "' in '" + options + "'");
99       }
100 
101       Result parse_attempt = ParseJdwpOption(jdwp_option.substr(0, equals_pos),
102                                              jdwp_option.substr(equals_pos + 1),
103                                              &jdwp_options);
104       if (parse_attempt.IsError()) {
105         // We fail to parse this JDWP option.
106         return parse_attempt;
107       }
108     }
109 
110     if (jdwp_options.transport == JDWP::kJdwpTransportUnknown) {
111       return Result::Failure(s + "Must specify JDWP transport: " + options);
112     }
113     if (!jdwp_options.server && (jdwp_options.host.empty() || jdwp_options.port == 0)) {
114       return Result::Failure(s + "Must specify JDWP host and port when server=n: " + options);
115     }
116 
117     return Result::Success(std::move(jdwp_options));
118   }
119 
120   Result ParseJdwpOption(const std::string& name, const std::string& value,
121                          JDWP::JdwpOptions* jdwp_options) {
122     if (name == "transport") {
123       if (value == "dt_socket") {
124         jdwp_options->transport = JDWP::kJdwpTransportSocket;
125       } else if (value == "dt_android_adb") {
126         jdwp_options->transport = JDWP::kJdwpTransportAndroidAdb;
127       } else {
128         return Result::Failure("JDWP transport not supported: " + value);
129       }
130     } else if (name == "server") {
131       if (value == "n") {
132         jdwp_options->server = false;
133       } else if (value == "y") {
134         jdwp_options->server = true;
135       } else {
136         return Result::Failure("JDWP option 'server' must be 'y' or 'n'");
137       }
138     } else if (name == "suspend") {
139       if (value == "n") {
140         jdwp_options->suspend = false;
141       } else if (value == "y") {
142         jdwp_options->suspend = true;
143       } else {
144         return Result::Failure("JDWP option 'suspend' must be 'y' or 'n'");
145       }
146     } else if (name == "address") {
147       /* this is either <port> or <host>:<port> */
148       std::string port_string;
149       jdwp_options->host.clear();
150       std::string::size_type colon = value.find(':');
151       if (colon != std::string::npos) {
152         jdwp_options->host = value.substr(0, colon);
153         port_string = value.substr(colon + 1);
154       } else {
155         port_string = value;
156       }
157       if (port_string.empty()) {
158         return Result::Failure("JDWP address missing port: " + value);
159       }
160       char* end;
161       uint64_t port = strtoul(port_string.c_str(), &end, 10);
162       if (*end != '\0' || port > 0xffff) {
163         return Result::Failure("JDWP address has junk in port field: " + value);
164       }
165       jdwp_options->port = port;
166     } else if (name == "launch" || name == "onthrow" || name == "oncaught" || name == "timeout") {
167       /* valid but unsupported */
168       LOG(INFO) << "Ignoring JDWP option '" << name << "'='" << value << "'";
169     } else {
170       LOG(INFO) << "Ignoring unrecognized JDWP option '" << name << "'='" << value << "'";
171     }
172 
173     return Result::SuccessNoValue();
174   }
175 
176   static const char* Name() { return "JdwpOptions"; }
177 };
178 
179 template <size_t Divisor>
180 struct CmdlineType<Memory<Divisor>> : CmdlineTypeParser<Memory<Divisor>> {
181   using typename CmdlineTypeParser<Memory<Divisor>>::Result;
182 
183   Result Parse(const std::string arg) {
184     CMDLINE_DEBUG_LOG << "Parsing memory: " << arg << std::endl;
185     size_t val = ParseMemoryOption(arg.c_str(), Divisor);
186     CMDLINE_DEBUG_LOG << "Memory parsed to size_t value: " << val << std::endl;
187 
188     if (val == 0) {
189       return Result::Failure(std::string("not a valid memory value, or not divisible by ")
190                              + std::to_string(Divisor));
191     }
192 
193     return Result::Success(Memory<Divisor>(val));
194   }
195 
196   // Parse a string of the form /[0-9]+[kKmMgG]?/, which is used to specify
197   // memory sizes.  [kK] indicates kilobytes, [mM] megabytes, and
198   // [gG] gigabytes.
199   //
200   // "s" should point just past the "-Xm?" part of the string.
201   // "div" specifies a divisor, e.g. 1024 if the value must be a multiple
202   // of 1024.
203   //
204   // The spec says the -Xmx and -Xms options must be multiples of 1024.  It
205   // doesn't say anything about -Xss.
206   //
207   // Returns 0 (a useless size) if "s" is malformed or specifies a low or
208   // non-evenly-divisible value.
209   //
210   static size_t ParseMemoryOption(const char* s, size_t div) {
211     // strtoul accepts a leading [+-], which we don't want,
212     // so make sure our string starts with a decimal digit.
213     if (isdigit(*s)) {
214       char* s2;
215       size_t val = strtoul(s, &s2, 10);
216       if (s2 != s) {
217         // s2 should be pointing just after the number.
218         // If this is the end of the string, the user
219         // has specified a number of bytes.  Otherwise,
220         // there should be exactly one more character
221         // that specifies a multiplier.
222         if (*s2 != '\0') {
223           // The remainder of the string is either a single multiplier
224           // character, or nothing to indicate that the value is in
225           // bytes.
226           char c = *s2++;
227           if (*s2 == '\0') {
228             size_t mul;
229             if (c == '\0') {
230               mul = 1;
231             } else if (c == 'k' || c == 'K') {
232               mul = KB;
233             } else if (c == 'm' || c == 'M') {
234               mul = MB;
235             } else if (c == 'g' || c == 'G') {
236               mul = GB;
237             } else {
238               // Unknown multiplier character.
239               return 0;
240             }
241 
242             if (val <= std::numeric_limits<size_t>::max() / mul) {
243               val *= mul;
244             } else {
245               // Clamp to a multiple of 1024.
246               val = std::numeric_limits<size_t>::max() & ~(1024-1);
247             }
248           } else {
249             // There's more than one character after the numeric part.
250             return 0;
251           }
252         }
253         // The man page says that a -Xm value must be a multiple of 1024.
254         if (val % div == 0) {
255           return val;
256         }
257       }
258     }
259     return 0;
260   }
261 
262   static const char* Name() { return Memory<Divisor>::Name(); }
263 };
264 
265 template <>
266 struct CmdlineType<double> : CmdlineTypeParser<double> {
267   Result Parse(const std::string& str) {
268     char* end = nullptr;
269     errno = 0;
270     double value = strtod(str.c_str(), &end);
271 
272     if (*end != '\0') {
273       return Result::Failure("Failed to parse double from " + str);
274     }
275     if (errno == ERANGE) {
276       return Result::OutOfRange(
277           "Failed to parse double from " + str + "; overflow/underflow occurred");
278     }
279 
280     return Result::Success(value);
281   }
282 
283   static const char* Name() { return "double"; }
284 };
285 
286 template <>
287 struct CmdlineType<unsigned int> : CmdlineTypeParser<unsigned int> {
288   Result Parse(const std::string& str) {
289     const char* begin = str.c_str();
290     char* end;
291 
292     // Parse into a larger type (long long) because we can't use strtoul
293     // since it silently converts negative values into unsigned long and doesn't set errno.
294     errno = 0;
295     long long int result = strtoll(begin, &end, 10);  // NOLINT [runtime/int] [4]
296     if (begin == end || *end != '\0' || errno == EINVAL) {
297       return Result::Failure("Failed to parse integer from " + str);
298     } else if ((errno == ERANGE) ||  // NOLINT [runtime/int] [4]
299         result < std::numeric_limits<int>::min()
300         || result > std::numeric_limits<unsigned int>::max() || result < 0) {
301       return Result::OutOfRange(
302           "Failed to parse integer from " + str + "; out of unsigned int range");
303     }
304 
305     return Result::Success(static_cast<unsigned int>(result));
306   }
307 
308   static const char* Name() { return "unsigned integer"; }
309 };
310 
311 // Lightweight nanosecond value type. Allows parser to convert user-input from milliseconds
312 // to nanoseconds automatically after parsing.
313 //
314 // All implicit conversion from uint64_t uses nanoseconds.
315 struct MillisecondsToNanoseconds {
316   // Create from nanoseconds.
317   MillisecondsToNanoseconds(uint64_t nanoseconds) : nanoseconds_(nanoseconds) {  // NOLINT [runtime/explicit] [5]
318   }
319 
320   // Create from milliseconds.
321   static MillisecondsToNanoseconds FromMilliseconds(unsigned int milliseconds) {
322     return MillisecondsToNanoseconds(MsToNs(milliseconds));
323   }
324 
325   // Get the underlying nanoseconds value.
326   uint64_t GetNanoseconds() const {
327     return nanoseconds_;
328   }
329 
330   // Get the milliseconds value [via a conversion]. Loss of precision will occur.
331   uint64_t GetMilliseconds() const {
332     return NsToMs(nanoseconds_);
333   }
334 
335   // Get the underlying nanoseconds value.
336   operator uint64_t() const {
337     return GetNanoseconds();
338   }
339 
340   // Default constructors/copy-constructors.
341   MillisecondsToNanoseconds() : nanoseconds_(0ul) {}
342   MillisecondsToNanoseconds(const MillisecondsToNanoseconds&) = default;
343   MillisecondsToNanoseconds(MillisecondsToNanoseconds&&) = default;
344 
345  private:
346   uint64_t nanoseconds_;
347 };
348 
349 template <>
350 struct CmdlineType<MillisecondsToNanoseconds> : CmdlineTypeParser<MillisecondsToNanoseconds> {
351   Result Parse(const std::string& str) {
352     CmdlineType<unsigned int> uint_parser;
353     CmdlineParseResult<unsigned int> res = uint_parser.Parse(str);
354 
355     if (res.IsSuccess()) {
356       return Result::Success(MillisecondsToNanoseconds::FromMilliseconds(res.GetValue()));
357     } else {
358       return Result::CastError(res);
359     }
360   }
361 
362   static const char* Name() { return "MillisecondsToNanoseconds"; }
363 };
364 
365 template <>
366 struct CmdlineType<std::string> : CmdlineTypeParser<std::string> {
367   Result Parse(const std::string& args) {
368     return Result::Success(args);
369   }
370 
371   Result ParseAndAppend(const std::string& args,
372                         std::string& existing_value) {
373     if (existing_value.empty()) {
374       existing_value = args;
375     } else {
376       existing_value += ' ';
377       existing_value += args;
378     }
379     return Result::SuccessNoValue();
380   }
381 };
382 
383 template <>
384 struct CmdlineType<std::vector<std::string>> : CmdlineTypeParser<std::vector<std::string>> {
385   Result Parse(const std::string& args) {
386     assert(false && "Use AppendValues() for a string vector type");
387     return Result::Failure("Unconditional failure: string vector must be appended: " + args);
388   }
389 
390   Result ParseAndAppend(const std::string& args,
391                         std::vector<std::string>& existing_value) {
392     existing_value.push_back(args);
393     return Result::SuccessNoValue();
394   }
395 
396   static const char* Name() { return "std::vector<std::string>"; }
397 };
398 
399 template <char Separator>
400 struct ParseStringList {
401   explicit ParseStringList(std::vector<std::string>&& list) : list_(list) {}
402 
403   operator std::vector<std::string>() const {
404     return list_;
405   }
406 
407   operator std::vector<std::string>&&() && {
408     return std::move(list_);
409   }
410 
411   size_t Size() const {
412     return list_.size();
413   }
414 
415   std::string Join() const {
416     return art::Join(list_, Separator);
417   }
418 
419   static ParseStringList<Separator> Split(const std::string& str) {
420     std::vector<std::string> list;
421     art::Split(str, Separator, &list);
422     return ParseStringList<Separator>(std::move(list));
423   }
424 
425   ParseStringList() = default;
426   ParseStringList(const ParseStringList&) = default;
427   ParseStringList(ParseStringList&&) = default;
428 
429  private:
430   std::vector<std::string> list_;
431 };
432 
433 template <char Separator>
434 struct CmdlineType<ParseStringList<Separator>> : CmdlineTypeParser<ParseStringList<Separator>> {
435   using Result = CmdlineParseResult<ParseStringList<Separator>>;
436 
437   Result Parse(const std::string& args) {
438     return Result::Success(ParseStringList<Separator>::Split(args));
439   }
440 
441   static const char* Name() { return "ParseStringList<Separator>"; }
442 };
443 
444 static gc::CollectorType ParseCollectorType(const std::string& option) {
445   if (option == "MS" || option == "nonconcurrent") {
446     return gc::kCollectorTypeMS;
447   } else if (option == "CMS" || option == "concurrent") {
448     return gc::kCollectorTypeCMS;
449   } else if (option == "SS") {
450     return gc::kCollectorTypeSS;
451   } else if (option == "GSS") {
452     return gc::kCollectorTypeGSS;
453   } else if (option == "CC") {
454     return gc::kCollectorTypeCC;
455   } else if (option == "MC") {
456     return gc::kCollectorTypeMC;
457   } else {
458     return gc::kCollectorTypeNone;
459   }
460 }
461 
462 struct XGcOption {
463   // These defaults are used when the command line arguments for -Xgc:
464   // are either omitted completely or partially.
465   gc::CollectorType collector_type_ =  kUseReadBarrier ?
466                                            // If RB is enabled (currently a build-time decision),
467                                            // use CC as the default GC.
468                                            gc::kCollectorTypeCC :
469                                            gc::kCollectorTypeDefault;
470   bool verify_pre_gc_heap_ = false;
471   bool verify_pre_sweeping_heap_ = kIsDebugBuild;
472   bool verify_post_gc_heap_ = false;
473   bool verify_pre_gc_rosalloc_ = kIsDebugBuild;
474   bool verify_pre_sweeping_rosalloc_ = false;
475   bool verify_post_gc_rosalloc_ = false;
476   bool gcstress_ = false;
477 };
478 
479 template <>
480 struct CmdlineType<XGcOption> : CmdlineTypeParser<XGcOption> {
481   Result Parse(const std::string& option) {  // -Xgc: already stripped
482     XGcOption xgc{};  // NOLINT [readability/braces] [4]
483 
484     std::vector<std::string> gc_options;
485     Split(option, ',', &gc_options);
486     for (const std::string& gc_option : gc_options) {
487       gc::CollectorType collector_type = ParseCollectorType(gc_option);
488       if (collector_type != gc::kCollectorTypeNone) {
489         xgc.collector_type_ = collector_type;
490       } else if (gc_option == "preverify") {
491         xgc.verify_pre_gc_heap_ = true;
492       } else if (gc_option == "nopreverify") {
493         xgc.verify_pre_gc_heap_ = false;
494       }  else if (gc_option == "presweepingverify") {
495         xgc.verify_pre_sweeping_heap_ = true;
496       } else if (gc_option == "nopresweepingverify") {
497         xgc.verify_pre_sweeping_heap_ = false;
498       } else if (gc_option == "postverify") {
499         xgc.verify_post_gc_heap_ = true;
500       } else if (gc_option == "nopostverify") {
501         xgc.verify_post_gc_heap_ = false;
502       } else if (gc_option == "preverify_rosalloc") {
503         xgc.verify_pre_gc_rosalloc_ = true;
504       } else if (gc_option == "nopreverify_rosalloc") {
505         xgc.verify_pre_gc_rosalloc_ = false;
506       } else if (gc_option == "presweepingverify_rosalloc") {
507         xgc.verify_pre_sweeping_rosalloc_ = true;
508       } else if (gc_option == "nopresweepingverify_rosalloc") {
509         xgc.verify_pre_sweeping_rosalloc_ = false;
510       } else if (gc_option == "postverify_rosalloc") {
511         xgc.verify_post_gc_rosalloc_ = true;
512       } else if (gc_option == "nopostverify_rosalloc") {
513         xgc.verify_post_gc_rosalloc_ = false;
514       } else if (gc_option == "gcstress") {
515         xgc.gcstress_ = true;
516       } else if (gc_option == "nogcstress") {
517         xgc.gcstress_ = false;
518       } else if ((gc_option == "precise") ||
519                  (gc_option == "noprecise") ||
520                  (gc_option == "verifycardtable") ||
521                  (gc_option == "noverifycardtable")) {
522         // Ignored for backwards compatibility.
523       } else {
524         return Result::Usage(std::string("Unknown -Xgc option ") + gc_option);
525       }
526     }
527 
528     return Result::Success(std::move(xgc));
529   }
530 
531   static const char* Name() { return "XgcOption"; }
532 };
533 
534 struct BackgroundGcOption {
535   // If background_collector_type_ is kCollectorTypeNone, it defaults to the
536   // XGcOption::collector_type_ after parsing options. If you set this to
537   // kCollectorTypeHSpaceCompact then we will do an hspace compaction when
538   // we transition to background instead of a normal collector transition.
539   gc::CollectorType background_collector_type_;
540 
541   BackgroundGcOption(gc::CollectorType background_collector_type)  // NOLINT [runtime/explicit] [5]
542     : background_collector_type_(background_collector_type) {}
543   BackgroundGcOption()
544     : background_collector_type_(gc::kCollectorTypeNone) {
545 
546     if (kUseReadBarrier) {
547       background_collector_type_ = gc::kCollectorTypeCC;  // Disable background compaction for CC.
548     }
549   }
550 
551   operator gc::CollectorType() const { return background_collector_type_; }
552 };
553 
554 template<>
555 struct CmdlineType<BackgroundGcOption>
556   : CmdlineTypeParser<BackgroundGcOption>, private BackgroundGcOption {
557   Result Parse(const std::string& substring) {
558     // Special handling for HSpaceCompact since this is only valid as a background GC type.
559     if (substring == "HSpaceCompact") {
560       background_collector_type_ = gc::kCollectorTypeHomogeneousSpaceCompact;
561     } else {
562       gc::CollectorType collector_type = ParseCollectorType(substring);
563       if (collector_type != gc::kCollectorTypeNone) {
564         background_collector_type_ = collector_type;
565       } else {
566         return Result::Failure();
567       }
568     }
569 
570     BackgroundGcOption res = *this;
571     return Result::Success(res);
572   }
573 
574   static const char* Name() { return "BackgroundGcOption"; }
575 };
576 
577 template <>
578 struct CmdlineType<LogVerbosity> : CmdlineTypeParser<LogVerbosity> {
579   Result Parse(const std::string& options) {
580     LogVerbosity log_verbosity = LogVerbosity();
581 
582     std::vector<std::string> verbose_options;
583     Split(options, ',', &verbose_options);
584     for (size_t j = 0; j < verbose_options.size(); ++j) {
585       if (verbose_options[j] == "class") {
586         log_verbosity.class_linker = true;
587       } else if (verbose_options[j] == "collector") {
588         log_verbosity.collector = true;
589       } else if (verbose_options[j] == "compiler") {
590         log_verbosity.compiler = true;
591       } else if (verbose_options[j] == "deopt") {
592         log_verbosity.deopt = true;
593       } else if (verbose_options[j] == "gc") {
594         log_verbosity.gc = true;
595       } else if (verbose_options[j] == "heap") {
596         log_verbosity.heap = true;
597       } else if (verbose_options[j] == "jdwp") {
598         log_verbosity.jdwp = true;
599       } else if (verbose_options[j] == "jit") {
600         log_verbosity.jit = true;
601       } else if (verbose_options[j] == "jni") {
602         log_verbosity.jni = true;
603       } else if (verbose_options[j] == "monitor") {
604         log_verbosity.monitor = true;
605       } else if (verbose_options[j] == "oat") {
606         log_verbosity.oat = true;
607       } else if (verbose_options[j] == "profiler") {
608         log_verbosity.profiler = true;
609       } else if (verbose_options[j] == "signals") {
610         log_verbosity.signals = true;
611       } else if (verbose_options[j] == "simulator") {
612         log_verbosity.simulator = true;
613       } else if (verbose_options[j] == "startup") {
614         log_verbosity.startup = true;
615       } else if (verbose_options[j] == "third-party-jni") {
616         log_verbosity.third_party_jni = true;
617       } else if (verbose_options[j] == "threads") {
618         log_verbosity.threads = true;
619       } else if (verbose_options[j] == "verifier") {
620         log_verbosity.verifier = true;
621       } else if (verbose_options[j] == "image") {
622         log_verbosity.image = true;
623       } else if (verbose_options[j] == "systrace-locks") {
624         log_verbosity.systrace_lock_logging = true;
625       } else {
626         return Result::Usage(std::string("Unknown -verbose option ") + verbose_options[j]);
627       }
628     }
629 
630     return Result::Success(log_verbosity);
631   }
632 
633   static const char* Name() { return "LogVerbosity"; }
634 };
635 
636 // TODO: Replace with art::ProfilerOptions for the real thing.
637 struct TestProfilerOptions {
638   // Whether or not the applications should be profiled.
639   bool enabled_;
640   // Destination file name where the profiling data will be saved into.
641   std::string output_file_name_;
642   // Generate profile every n seconds.
643   uint32_t period_s_;
644   // Run profile for n seconds.
645   uint32_t duration_s_;
646   // Microseconds between samples.
647   uint32_t interval_us_;
648   // Coefficient to exponential backoff.
649   double backoff_coefficient_;
650   // Whether the profile should start upon app startup or be delayed by some random offset.
651   bool start_immediately_;
652   // Top K% of samples that are considered relevant when deciding if the app should be recompiled.
653   double top_k_threshold_;
654   // How much the top K% samples needs to change in order for the app to be recompiled.
655   double top_k_change_threshold_;
656   // The type of profile data dumped to the disk.
657   ProfileDataType profile_type_;
658   // The max depth of the stack collected by the profiler
659   uint32_t max_stack_depth_;
660 
661   TestProfilerOptions() :
662     enabled_(false),
663     output_file_name_(),
664     period_s_(0),
665     duration_s_(0),
666     interval_us_(0),
667     backoff_coefficient_(0),
668     start_immediately_(0),
669     top_k_threshold_(0),
670     top_k_change_threshold_(0),
671     profile_type_(ProfileDataType::kProfilerMethod),
672     max_stack_depth_(0) {
673   }
674 
675   TestProfilerOptions(const TestProfilerOptions&) = default;
676   TestProfilerOptions(TestProfilerOptions&&) = default;
677 };
678 
679 static inline std::ostream& operator<<(std::ostream& stream, const TestProfilerOptions& options) {
680   stream << "TestProfilerOptions {" << std::endl;
681 
682 #define PRINT_TO_STREAM(field) \
683   stream << #field << ": '" << options.field << "'" << std::endl;
684 
685   PRINT_TO_STREAM(enabled_);
686   PRINT_TO_STREAM(output_file_name_);
687   PRINT_TO_STREAM(period_s_);
688   PRINT_TO_STREAM(duration_s_);
689   PRINT_TO_STREAM(interval_us_);
690   PRINT_TO_STREAM(backoff_coefficient_);
691   PRINT_TO_STREAM(start_immediately_);
692   PRINT_TO_STREAM(top_k_threshold_);
693   PRINT_TO_STREAM(top_k_change_threshold_);
694   PRINT_TO_STREAM(profile_type_);
695   PRINT_TO_STREAM(max_stack_depth_);
696 
697   stream << "}";
698 
699   return stream;
700 #undef PRINT_TO_STREAM
701 }
702 
703 template <>
704 struct CmdlineType<TestProfilerOptions> : CmdlineTypeParser<TestProfilerOptions> {
705   using Result = CmdlineParseResult<TestProfilerOptions>;
706 
707  private:
708   using StringResult = CmdlineParseResult<std::string>;
709   using DoubleResult = CmdlineParseResult<double>;
710 
711   template <typename T>
712   static Result ParseInto(TestProfilerOptions& options,
713                           T TestProfilerOptions::*pField,
714                           CmdlineParseResult<T>&& result) {
715     assert(pField != nullptr);
716 
717     if (result.IsSuccess()) {
718       options.*pField = result.ReleaseValue();
719       return Result::SuccessNoValue();
720     }
721 
722     return Result::CastError(result);
723   }
724 
725   template <typename T>
726   static Result ParseIntoRangeCheck(TestProfilerOptions& options,
727                                     T TestProfilerOptions::*pField,
728                                     CmdlineParseResult<T>&& result,
729                                     T min,
730                                     T max) {
731     if (result.IsSuccess()) {
732       const T& value = result.GetValue();
733 
734       if (value < min || value > max) {
735         CmdlineParseResult<T> out_of_range = CmdlineParseResult<T>::OutOfRange(value, min, max);
736         return Result::CastError(out_of_range);
737       }
738     }
739 
740     return ParseInto(options, pField, std::forward<CmdlineParseResult<T>>(result));
741   }
742 
743   static StringResult ParseStringAfterChar(const std::string& s, char c) {
744     std::string parsed_value;
745 
746     std::string::size_type colon = s.find(c);
747     if (colon == std::string::npos) {
748       return StringResult::Usage(std::string() + "Missing char " + c + " in option " + s);
749     }
750     // Add one to remove the char we were trimming until.
751     parsed_value = s.substr(colon + 1);
752     return StringResult::Success(parsed_value);
753   }
754 
755   static std::string RemovePrefix(const std::string& source) {
756     size_t prefix_idx = source.find(":");
757 
758     if (prefix_idx == std::string::npos) {
759       return "";
760     }
761 
762     return source.substr(prefix_idx + 1);
763   }
764 
765  public:
766   Result ParseAndAppend(const std::string& option, TestProfilerOptions& existing) {
767     // Special case which doesn't include a wildcard argument definition.
768     // We pass-it through as-is.
769     if (option == "-Xenable-profiler") {
770       existing.enabled_ = true;
771       return Result::SuccessNoValue();
772     }
773 
774     // The rest of these options are always the wildcard from '-Xprofile-*'
775     std::string suffix = RemovePrefix(option);
776 
777     if (StartsWith(option, "filename:")) {
778       CmdlineType<std::string> type_parser;
779 
780       return ParseInto(existing,
781                        &TestProfilerOptions::output_file_name_,
782                        type_parser.Parse(suffix));
783     } else if (StartsWith(option, "period:")) {
784       CmdlineType<unsigned int> type_parser;
785 
786       return ParseInto(existing,
787                        &TestProfilerOptions::period_s_,
788                        type_parser.Parse(suffix));
789     } else if (StartsWith(option, "duration:")) {
790       CmdlineType<unsigned int> type_parser;
791 
792       return ParseInto(existing,
793                        &TestProfilerOptions::duration_s_,
794                        type_parser.Parse(suffix));
795     } else if (StartsWith(option, "interval:")) {
796       CmdlineType<unsigned int> type_parser;
797 
798       return ParseInto(existing,
799                        &TestProfilerOptions::interval_us_,
800                        type_parser.Parse(suffix));
801     } else if (StartsWith(option, "backoff:")) {
802       CmdlineType<double> type_parser;
803 
804       return ParseIntoRangeCheck(existing,
805                                  &TestProfilerOptions::backoff_coefficient_,
806                                  type_parser.Parse(suffix),
807                                  1.0,
808                                  10.0);
809 
810     } else if (option == "start-immediately") {
811       existing.start_immediately_ = true;
812       return Result::SuccessNoValue();
813     } else if (StartsWith(option, "top-k-threshold:")) {
814       CmdlineType<double> type_parser;
815 
816       return ParseIntoRangeCheck(existing,
817                                  &TestProfilerOptions::top_k_threshold_,
818                                  type_parser.Parse(suffix),
819                                  0.0,
820                                  100.0);
821     } else if (StartsWith(option, "top-k-change-threshold:")) {
822       CmdlineType<double> type_parser;
823 
824       return ParseIntoRangeCheck(existing,
825                                  &TestProfilerOptions::top_k_change_threshold_,
826                                  type_parser.Parse(suffix),
827                                  0.0,
828                                  100.0);
829     } else if (option == "type:method") {
830       existing.profile_type_ = kProfilerMethod;
831       return Result::SuccessNoValue();
832     } else if (option == "type:stack") {
833       existing.profile_type_ = kProfilerBoundedStack;
834       return Result::SuccessNoValue();
835     } else if (StartsWith(option, "max-stack-depth:")) {
836       CmdlineType<unsigned int> type_parser;
837 
838       return ParseInto(existing,
839                        &TestProfilerOptions::max_stack_depth_,
840                        type_parser.Parse(suffix));
841     } else {
842       return Result::Failure(std::string("Invalid suboption '") + option + "'");
843     }
844   }
845 
846   static const char* Name() { return "TestProfilerOptions"; }
847   static constexpr bool kCanParseBlankless = true;
848 };
849 
850 template<>
851 struct CmdlineType<ExperimentalFlags> : CmdlineTypeParser<ExperimentalFlags> {
852   Result ParseAndAppend(const std::string& option, ExperimentalFlags& existing) {
853     if (option == "none") {
854       existing = ExperimentalFlags::kNone;
855     } else if (option == "lambdas") {
856       existing = existing | ExperimentalFlags::kLambdas;
857     } else {
858       return Result::Failure(std::string("Unknown option '") + option + "'");
859     }
860     return Result::SuccessNoValue();
861   }
862 
863   static const char* Name() { return "ExperimentalFlags"; }
864 };
865 
866 }  // namespace art
867 #endif  // ART_CMDLINE_CMDLINE_TYPES_H_
868