cmdline_types.h revision fc6898769ae1ef91ec3e41c0a273401213cb82cd
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
36namespace 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.
42template <typename T>
43struct CmdlineType : CmdlineTypeParser<T> {
44};
45
46// Specializations for CmdlineType<T> follow:
47
48// Parse argument definitions for Unit-typed arguments.
49template <>
50struct 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
59template <>
60struct 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
179template <size_t Divisor>
180struct 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
265template <>
266struct 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
286template <>
287struct 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.
315struct 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
349template <>
350struct 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
365template <>
366struct 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
383template <>
384struct 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
399template <char Separator>
400struct 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
433template <char Separator>
434struct 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
444static 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
462struct 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
479template <>
480struct 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
534struct 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
554template<>
555struct 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
577template <>
578struct 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.
637struct 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
679static 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
703template <>
704struct 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
850template<>
851struct 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