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