| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/metrics/thread_watcher.h" | 5 #include "chrome/browser/metrics/thread_watcher.h" |
| 6 | 6 |
| 7 #include <math.h> // ceil | 7 #include <math.h> // ceil |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/compiler_specific.h" | 10 #include "base/compiler_specific.h" |
| 11 #include "base/debug/alias.h" | 11 #include "base/debug/alias.h" |
| 12 #include "base/lazy_instance.h" | 12 #include "base/lazy_instance.h" |
| 13 #include "base/string_split.h" | 13 #include "base/string_split.h" |
| 14 #include "base/stringprintf.h" |
| 14 #include "base/strings/string_number_conversions.h" | 15 #include "base/strings/string_number_conversions.h" |
| 15 #include "base/strings/string_tokenizer.h" | 16 #include "base/strings/string_tokenizer.h" |
| 16 #include "base/threading/thread_restrictions.h" | 17 #include "base/threading/thread_restrictions.h" |
| 17 #include "build/build_config.h" | 18 #include "build/build_config.h" |
| 18 #include "chrome/browser/metrics/metrics_service.h" | 19 #include "chrome/browser/metrics/metrics_service.h" |
| 19 #include "chrome/common/chrome_switches.h" | 20 #include "chrome/common/chrome_switches.h" |
| 20 #include "chrome/common/chrome_version_info.h" | 21 #include "chrome/common/chrome_version_info.h" |
| 21 #include "chrome/common/dump_without_crashing.h" | 22 #include "chrome/common/dump_without_crashing.h" |
| 22 #include "chrome/common/logging_chrome.h" | 23 #include "chrome/common/logging_chrome.h" |
| 23 | 24 |
| (...skipping 377 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 401 ThreadWatcherList* ThreadWatcherList::g_thread_watcher_list_ = NULL; | 402 ThreadWatcherList* ThreadWatcherList::g_thread_watcher_list_ = NULL; |
| 402 // static | 403 // static |
| 403 const int ThreadWatcherList::kSleepSeconds = 1; | 404 const int ThreadWatcherList::kSleepSeconds = 1; |
| 404 // static | 405 // static |
| 405 const int ThreadWatcherList::kUnresponsiveSeconds = 2; | 406 const int ThreadWatcherList::kUnresponsiveSeconds = 2; |
| 406 // static | 407 // static |
| 407 const int ThreadWatcherList::kUnresponsiveCount = 9; | 408 const int ThreadWatcherList::kUnresponsiveCount = 9; |
| 408 // static | 409 // static |
| 409 const int ThreadWatcherList::kLiveThreadsThreshold = 3; | 410 const int ThreadWatcherList::kLiveThreadsThreshold = 3; |
| 410 | 411 |
| 412 ThreadWatcherList::CrashDataThresholds::CrashDataThresholds( |
| 413 uint32 live_threads_threshold, |
| 414 uint32 unresponsive_threshold) |
| 415 : live_threads_threshold(live_threads_threshold), |
| 416 unresponsive_threshold(unresponsive_threshold) { |
| 417 } |
| 418 |
| 419 ThreadWatcherList::CrashDataThresholds::CrashDataThresholds() |
| 420 : live_threads_threshold(kLiveThreadsThreshold), |
| 421 unresponsive_threshold(kUnresponsiveCount) { |
| 422 } |
| 423 |
| 411 // static | 424 // static |
| 412 void ThreadWatcherList::StartWatchingAll(const CommandLine& command_line) { | 425 void ThreadWatcherList::StartWatchingAll(const CommandLine& command_line) { |
| 413 uint32 unresponsive_threshold; | 426 uint32 unresponsive_threshold; |
| 414 CrashOnHangThreadMap crash_on_hang_threads; | 427 CrashOnHangThreadMap crash_on_hang_threads; |
| 415 ParseCommandLine(command_line, | 428 ParseCommandLine(command_line, |
| 416 &unresponsive_threshold, | 429 &unresponsive_threshold, |
| 417 &crash_on_hang_threads); | 430 &crash_on_hang_threads); |
| 418 | 431 |
| 419 ThreadWatcherObserver::SetupNotifications( | 432 ThreadWatcherObserver::SetupNotifications( |
| 420 base::TimeDelta::FromSeconds(kSleepSeconds * unresponsive_threshold)); | 433 base::TimeDelta::FromSeconds(kSleepSeconds * unresponsive_threshold)); |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 491 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); | 504 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 492 DCHECK(this == g_thread_watcher_list_); | 505 DCHECK(this == g_thread_watcher_list_); |
| 493 g_thread_watcher_list_ = NULL; | 506 g_thread_watcher_list_ = NULL; |
| 494 } | 507 } |
| 495 | 508 |
| 496 // static | 509 // static |
| 497 void ThreadWatcherList::ParseCommandLine( | 510 void ThreadWatcherList::ParseCommandLine( |
| 498 const CommandLine& command_line, | 511 const CommandLine& command_line, |
| 499 uint32* unresponsive_threshold, | 512 uint32* unresponsive_threshold, |
| 500 CrashOnHangThreadMap* crash_on_hang_threads) { | 513 CrashOnHangThreadMap* crash_on_hang_threads) { |
| 501 // Determine |unresponsive_threshold| based on switches::kCrashOnHangSeconds. | 514 // Initialize |unresponsive_threshold| to a default value. |
| 502 *unresponsive_threshold = kUnresponsiveCount; | 515 *unresponsive_threshold = kUnresponsiveCount; |
| 503 | 516 |
| 504 // Increase the unresponsive_threshold on the Stable and Beta channels to | 517 // Increase the unresponsive_threshold on the Stable and Beta channels to |
| 505 // reduce the number of crashes due to ThreadWatcher. | 518 // reduce the number of crashes due to ThreadWatcher. |
| 506 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); | 519 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); |
| 507 if (channel == chrome::VersionInfo::CHANNEL_STABLE) { | 520 if (channel == chrome::VersionInfo::CHANNEL_STABLE) { |
| 508 *unresponsive_threshold *= 4; | 521 *unresponsive_threshold *= 4; |
| 509 } else if (channel == chrome::VersionInfo::CHANNEL_BETA) { | 522 } else if (channel == chrome::VersionInfo::CHANNEL_BETA) { |
| 510 *unresponsive_threshold *= 2; | 523 *unresponsive_threshold *= 2; |
| 511 } | 524 } |
| 512 | 525 |
| 513 #if defined(OS_WIN) | 526 #if defined(OS_WIN) |
| 514 // For Windows XP (old systems), double the unresponsive_threshold to give | 527 // For Windows XP (old systems), double the unresponsive_threshold to give |
| 515 // the OS a chance to schedule UI/IO threads a time slice to respond with a | 528 // the OS a chance to schedule UI/IO threads a time slice to respond with a |
| 516 // pong message (to get around limitations with the OS). | 529 // pong message (to get around limitations with the OS). |
| 517 if (base::win::GetVersion() <= base::win::VERSION_XP) | 530 if (base::win::GetVersion() <= base::win::VERSION_XP) |
| 518 *unresponsive_threshold *= 2; | 531 *unresponsive_threshold *= 2; |
| 519 #endif | 532 #endif |
| 520 | 533 |
| 521 std::string crash_on_hang_seconds = | 534 uint32 crash_seconds = *unresponsive_threshold * kUnresponsiveSeconds; |
| 522 command_line.GetSwitchValueASCII(switches::kCrashOnHangSeconds); | |
| 523 if (!crash_on_hang_seconds.empty()) { | |
| 524 int crash_seconds = atoi(crash_on_hang_seconds.c_str()); | |
| 525 if (crash_seconds > 0) { | |
| 526 *unresponsive_threshold = static_cast<uint32>( | |
| 527 ceil(static_cast<float>(crash_seconds) / kUnresponsiveSeconds)); | |
| 528 } | |
| 529 } | |
| 530 | |
| 531 std::string crash_on_hang_thread_names; | 535 std::string crash_on_hang_thread_names; |
| 532 | |
| 533 // Default to crashing the browser if UI or IO threads are not responsive | |
| 534 // except in stable channel. | |
| 535 if (channel == chrome::VersionInfo::CHANNEL_STABLE) | |
| 536 crash_on_hang_thread_names = ""; | |
| 537 else | |
| 538 crash_on_hang_thread_names = "UI:3,IO:9"; | |
| 539 | |
| 540 bool has_command_line_overwrite = false; | 536 bool has_command_line_overwrite = false; |
| 541 if (command_line.HasSwitch(switches::kCrashOnHangThreads)) { | 537 if (command_line.HasSwitch(switches::kCrashOnHangThreads)) { |
| 542 crash_on_hang_thread_names = | 538 crash_on_hang_thread_names = |
| 543 command_line.GetSwitchValueASCII(switches::kCrashOnHangThreads); | 539 command_line.GetSwitchValueASCII(switches::kCrashOnHangThreads); |
| 544 has_command_line_overwrite = true; | 540 has_command_line_overwrite = true; |
| 541 } else if (channel != chrome::VersionInfo::CHANNEL_STABLE) { |
| 542 // Default to crashing the browser if UI or IO or FILE threads are not |
| 543 // responsive except in stable channel. |
| 544 crash_on_hang_thread_names = base::StringPrintf( |
| 545 "UI:%d:%d,IO:%d:%d,FILE:%d:%d", |
| 546 kLiveThreadsThreshold, crash_seconds, |
| 547 kLiveThreadsThreshold, crash_seconds, |
| 548 kLiveThreadsThreshold, crash_seconds * 5); |
| 545 } | 549 } |
| 546 base::StringTokenizer tokens(crash_on_hang_thread_names, ","); | 550 |
| 547 std::vector<std::string> values; | 551 ParseCommandLineCrashOnHangThreads(crash_on_hang_thread_names, |
| 548 while (tokens.GetNext()) { | 552 kLiveThreadsThreshold, |
| 549 const std::string& token = tokens.token(); | 553 crash_seconds, |
| 550 base::SplitString(token, ':', &values); | 554 crash_on_hang_threads); |
| 551 if (values.size() != 2) | |
| 552 continue; | |
| 553 std::string thread_name = values[0]; | |
| 554 uint32 live_threads_threshold; | |
| 555 if (!base::StringToUint(values[1], &live_threads_threshold)) | |
| 556 continue; | |
| 557 CrashOnHangThreadMap::iterator it = | |
| 558 crash_on_hang_threads->find(thread_name); | |
| 559 if (crash_on_hang_threads->end() == it) | |
| 560 (*crash_on_hang_threads)[thread_name] = live_threads_threshold; | |
| 561 } | |
| 562 | 555 |
| 563 if (channel != chrome::VersionInfo::CHANNEL_CANARY || | 556 if (channel != chrome::VersionInfo::CHANNEL_CANARY || |
| 564 has_command_line_overwrite) { | 557 has_command_line_overwrite) { |
| 565 return; | 558 return; |
| 566 } | 559 } |
| 567 | 560 |
| 568 // Set up a field trial for 10% of the users to crash if IO thread is not | 561 // Set up a field trial for 10% of the users to crash if IO thread is not |
| 569 // responsive. | 562 // responsive. |
| 570 CrashOnHangThreadMap::iterator it = crash_on_hang_threads->find("IO"); | 563 CrashOnHangThreadMap::iterator it = crash_on_hang_threads->find("IO"); |
| 571 if (crash_on_hang_threads->end() == it) | 564 if (crash_on_hang_threads->end() == it) |
| 572 return; | 565 return; |
| 573 | 566 |
| 574 scoped_refptr<base::FieldTrial> field_trial( | 567 scoped_refptr<base::FieldTrial> field_trial( |
| 575 base::FieldTrialList::FactoryGetFieldTrial( | 568 base::FieldTrialList::FactoryGetFieldTrial( |
| 576 "ThreadWatcher", 100, "default_hung_threads", | 569 "ThreadWatcher", 100, "default_hung_threads", |
| 577 2013, 10, 30, NULL)); | 570 2013, 10, 30, NULL)); |
| 578 int io_hung_thread_group = field_trial->AppendGroup("io_hung_thread", 10); | 571 int io_hung_thread_group = field_trial->AppendGroup("io_hung_thread", 10); |
| 579 if (field_trial->group() == io_hung_thread_group) | 572 if (field_trial->group() == io_hung_thread_group) { |
| 580 it->second = INT_MAX; // Crash anytime IO thread hangs. | 573 // Crash anytime IO thread hangs. |
| 574 it->second.live_threads_threshold = INT_MAX; |
| 575 } |
| 576 } |
| 577 |
| 578 // static |
| 579 void ThreadWatcherList::ParseCommandLineCrashOnHangThreads( |
| 580 const std::string& crash_on_hang_thread_names, |
| 581 uint32 default_live_threads_threshold, |
| 582 uint32 default_crash_seconds, |
| 583 CrashOnHangThreadMap* crash_on_hang_threads) { |
| 584 base::StringTokenizer tokens(crash_on_hang_thread_names, ","); |
| 585 std::vector<std::string> values; |
| 586 while (tokens.GetNext()) { |
| 587 const std::string& token = tokens.token(); |
| 588 base::SplitString(token, ':', &values); |
| 589 std::string thread_name = values[0]; |
| 590 |
| 591 uint32 live_threads_threshold = default_live_threads_threshold; |
| 592 uint32 crash_seconds = default_crash_seconds; |
| 593 if (values.size() >= 2 && |
| 594 (!base::StringToUint(values[1], &live_threads_threshold))) { |
| 595 continue; |
| 596 } |
| 597 if (values.size() >= 3 && |
| 598 (!base::StringToUint(values[2], &crash_seconds))) { |
| 599 continue; |
| 600 } |
| 601 uint32 unresponsive_threshold = static_cast<uint32>( |
| 602 ceil(static_cast<float>(crash_seconds) / kUnresponsiveSeconds)); |
| 603 |
| 604 CrashDataThresholds crash_data(live_threads_threshold, |
| 605 unresponsive_threshold); |
| 606 // Use the last specifier. |
| 607 (*crash_on_hang_threads)[thread_name] = crash_data; |
| 608 } |
| 581 } | 609 } |
| 582 | 610 |
| 583 // static | 611 // static |
| 584 void ThreadWatcherList::InitializeAndStartWatching( | 612 void ThreadWatcherList::InitializeAndStartWatching( |
| 585 uint32 unresponsive_threshold, | 613 uint32 unresponsive_threshold, |
| 586 const CrashOnHangThreadMap& crash_on_hang_threads) { | 614 const CrashOnHangThreadMap& crash_on_hang_threads) { |
| 587 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); | 615 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 588 | 616 |
| 589 ThreadWatcherList* thread_watcher_list = new ThreadWatcherList(); | 617 ThreadWatcherList* thread_watcher_list = new ThreadWatcherList(); |
| 590 CHECK(thread_watcher_list); | 618 CHECK(thread_watcher_list); |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 623 uint32 unresponsive_threshold, | 651 uint32 unresponsive_threshold, |
| 624 const CrashOnHangThreadMap& crash_on_hang_threads) { | 652 const CrashOnHangThreadMap& crash_on_hang_threads) { |
| 625 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); | 653 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 626 | 654 |
| 627 CrashOnHangThreadMap::const_iterator it = | 655 CrashOnHangThreadMap::const_iterator it = |
| 628 crash_on_hang_threads.find(thread_name); | 656 crash_on_hang_threads.find(thread_name); |
| 629 bool crash_on_hang = false; | 657 bool crash_on_hang = false; |
| 630 uint32 live_threads_threshold = 0; | 658 uint32 live_threads_threshold = 0; |
| 631 if (it != crash_on_hang_threads.end()) { | 659 if (it != crash_on_hang_threads.end()) { |
| 632 crash_on_hang = true; | 660 crash_on_hang = true; |
| 633 live_threads_threshold = it->second; | 661 live_threads_threshold = it->second.live_threads_threshold; |
| 662 unresponsive_threshold = it->second.unresponsive_threshold; |
| 634 } | 663 } |
| 635 | 664 |
| 636 ThreadWatcher::StartWatching( | 665 ThreadWatcher::StartWatching( |
| 637 ThreadWatcher::WatchingParams(thread_id, | 666 ThreadWatcher::WatchingParams(thread_id, |
| 638 thread_name, | 667 thread_name, |
| 639 sleep_time, | 668 sleep_time, |
| 640 unresponsive_time, | 669 unresponsive_time, |
| 641 unresponsive_threshold, | 670 unresponsive_threshold, |
| 642 crash_on_hang, | 671 crash_on_hang, |
| 643 live_threads_threshold)); | 672 live_threads_threshold)); |
| (...skipping 296 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 940 | 969 |
| 941 #if defined(OS_WIN) | 970 #if defined(OS_WIN) |
| 942 // On Windows XP, give twice the time for shutdown. | 971 // On Windows XP, give twice the time for shutdown. |
| 943 if (base::win::GetVersion() <= base::win::VERSION_XP) | 972 if (base::win::GetVersion() <= base::win::VERSION_XP) |
| 944 actual_duration *= 2; | 973 actual_duration *= 2; |
| 945 #endif | 974 #endif |
| 946 | 975 |
| 947 shutdown_watchdog_ = new ShutdownWatchDogThread(actual_duration); | 976 shutdown_watchdog_ = new ShutdownWatchDogThread(actual_duration); |
| 948 shutdown_watchdog_->Arm(); | 977 shutdown_watchdog_->Arm(); |
| 949 } | 978 } |
| OLD | NEW |