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