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