| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 <math.h> // ceil |
| 6 |
| 7 #include "base/string_tokenizer.h" |
| 5 #include "base/threading/thread_restrictions.h" | 8 #include "base/threading/thread_restrictions.h" |
| 6 #include "build/build_config.h" | 9 #include "build/build_config.h" |
| 7 #include "chrome/browser/metrics/metrics_service.h" | 10 #include "chrome/browser/metrics/metrics_service.h" |
| 8 #include "chrome/browser/metrics/thread_watcher.h" | 11 #include "chrome/browser/metrics/thread_watcher.h" |
| 12 #include "chrome/common/chrome_switches.h" |
| 9 #include "content/common/notification_service.h" | 13 #include "content/common/notification_service.h" |
| 10 | 14 |
| 11 #if defined(OS_WIN) | 15 #if defined(OS_WIN) |
| 12 #include <Objbase.h> | 16 #include <Objbase.h> |
| 13 #endif | 17 #endif |
| 14 | 18 |
| 15 // static | 19 // static |
| 16 const int ThreadWatcher::kPingCount = 6; | 20 const int ThreadWatcher::kPingCount = 6; |
| 17 | 21 |
| 18 // static | |
| 19 const int ThreadWatcher::kUnresponsiveCount = 6; | |
| 20 | |
| 21 // ThreadWatcher methods and members. | 22 // ThreadWatcher methods and members. |
| 22 ThreadWatcher::ThreadWatcher(const BrowserThread::ID& thread_id, | 23 ThreadWatcher::ThreadWatcher(const BrowserThread::ID& thread_id, |
| 23 const std::string& thread_name, | 24 const std::string& thread_name, |
| 24 const base::TimeDelta& sleep_time, | 25 const base::TimeDelta& sleep_time, |
| 25 const base::TimeDelta& unresponsive_time) | 26 const base::TimeDelta& unresponsive_time, |
| 27 uint32 unresponsive_threshold, |
| 28 bool crash_on_hang, |
| 29 uint32 live_threads_threshold) |
| 26 : thread_id_(thread_id), | 30 : thread_id_(thread_id), |
| 27 thread_name_(thread_name), | 31 thread_name_(thread_name), |
| 28 sleep_time_(sleep_time), | 32 sleep_time_(sleep_time), |
| 29 unresponsive_time_(unresponsive_time), | 33 unresponsive_time_(unresponsive_time), |
| 30 ping_time_(base::TimeTicks::Now()), | 34 ping_time_(base::TimeTicks::Now()), |
| 31 pong_time_(ping_time_), | 35 pong_time_(ping_time_), |
| 32 ping_sequence_number_(0), | 36 ping_sequence_number_(0), |
| 33 active_(false), | 37 active_(false), |
| 34 ping_count_(kPingCount), | 38 ping_count_(kPingCount), |
| 35 response_time_histogram_(NULL), | 39 response_time_histogram_(NULL), |
| 36 unresponsive_time_histogram_(NULL), | 40 unresponsive_time_histogram_(NULL), |
| 37 unresponsive_count_(0), | 41 unresponsive_count_(0), |
| 38 hung_processing_complete_(false), | 42 hung_processing_complete_(false), |
| 43 unresponsive_threshold_(unresponsive_threshold), |
| 44 crash_on_hang_(crash_on_hang), |
| 45 live_threads_threshold_(live_threads_threshold), |
| 39 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { | 46 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { |
| 47 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 40 Initialize(); | 48 Initialize(); |
| 41 } | 49 } |
| 42 | 50 |
| 43 ThreadWatcher::~ThreadWatcher() {} | 51 ThreadWatcher::~ThreadWatcher() {} |
| 44 | 52 |
| 45 // static | 53 // static |
| 46 void ThreadWatcher::StartWatching(const BrowserThread::ID& thread_id, | 54 void ThreadWatcher::StartWatching(const BrowserThread::ID& thread_id, |
| 47 const std::string& thread_name, | 55 const std::string& thread_name, |
| 48 const base::TimeDelta& sleep_time, | 56 const base::TimeDelta& sleep_time, |
| 49 const base::TimeDelta& unresponsive_time) { | 57 const base::TimeDelta& unresponsive_time, |
| 58 uint32 unresponsive_threshold, |
| 59 bool crash_on_hang, |
| 60 uint32 live_threads_threshold) { |
| 50 DCHECK_GE(sleep_time.InMilliseconds(), 0); | 61 DCHECK_GE(sleep_time.InMilliseconds(), 0); |
| 51 DCHECK_GE(unresponsive_time.InMilliseconds(), sleep_time.InMilliseconds()); | 62 DCHECK_GE(unresponsive_time.InMilliseconds(), sleep_time.InMilliseconds()); |
| 52 | 63 |
| 53 // If we are not on WatchDogThread, then post a task to call StartWatching on | 64 // If we are not on WatchDogThread, then post a task to call StartWatching on |
| 54 // WatchDogThread. | 65 // WatchDogThread. |
| 55 if (!WatchDogThread::CurrentlyOnWatchDogThread()) { | 66 if (!WatchDogThread::CurrentlyOnWatchDogThread()) { |
| 56 WatchDogThread::PostTask( | 67 WatchDogThread::PostTask( |
| 57 FROM_HERE, | 68 FROM_HERE, |
| 58 NewRunnableFunction( | 69 NewRunnableFunction(&ThreadWatcher::StartWatching, |
| 59 &ThreadWatcher::StartWatching, | 70 thread_id, |
| 60 thread_id, thread_name, sleep_time, unresponsive_time)); | 71 thread_name, |
| 72 sleep_time, |
| 73 unresponsive_time, |
| 74 unresponsive_threshold, |
| 75 crash_on_hang, |
| 76 live_threads_threshold)); |
| 61 return; | 77 return; |
| 62 } | 78 } |
| 63 | 79 |
| 64 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); | 80 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 65 | 81 |
| 66 // Create a new thread watcher object for the given thread and activate it. | 82 // Create a new thread watcher object for the given thread and activate it. |
| 67 ThreadWatcher* watcher = | 83 ThreadWatcher* watcher = new ThreadWatcher(thread_id, |
| 68 new ThreadWatcher(thread_id, thread_name, sleep_time, unresponsive_time); | 84 thread_name, |
| 85 sleep_time, |
| 86 unresponsive_time, |
| 87 unresponsive_threshold, |
| 88 crash_on_hang, |
| 89 live_threads_threshold); |
| 69 DCHECK(watcher); | 90 DCHECK(watcher); |
| 70 // If we couldn't register the thread watcher object, we are shutting down, | 91 // If we couldn't register the thread watcher object, we are shutting down, |
| 71 // then don't activate thread watching. | 92 // then don't activate thread watching. |
| 72 if (!ThreadWatcherList::IsRegistered(thread_id)) | 93 if (!ThreadWatcherList::IsRegistered(thread_id)) |
| 73 return; | 94 return; |
| 74 watcher->ActivateThreadWatching(); | 95 watcher->ActivateThreadWatching(); |
| 75 } | 96 } |
| 76 | 97 |
| 77 void ThreadWatcher::ActivateThreadWatching() { | 98 void ThreadWatcher::ActivateThreadWatching() { |
| 78 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); | 99 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 189 // Post a task to check the responsiveness of watched thread. | 210 // Post a task to check the responsiveness of watched thread. |
| 190 MessageLoop::current()->PostDelayedTask( | 211 MessageLoop::current()->PostDelayedTask( |
| 191 FROM_HERE, | 212 FROM_HERE, |
| 192 method_factory_.NewRunnableMethod( | 213 method_factory_.NewRunnableMethod( |
| 193 &ThreadWatcher::OnCheckResponsiveness, ping_sequence_number_), | 214 &ThreadWatcher::OnCheckResponsiveness, ping_sequence_number_), |
| 194 unresponsive_time_.InMilliseconds()); | 215 unresponsive_time_.InMilliseconds()); |
| 195 return false; | 216 return false; |
| 196 } | 217 } |
| 197 | 218 |
| 198 void ThreadWatcher::Initialize() { | 219 void ThreadWatcher::Initialize() { |
| 220 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 199 ThreadWatcherList::Register(this); | 221 ThreadWatcherList::Register(this); |
| 200 | 222 |
| 201 const std::string response_time_histogram_name = | 223 const std::string response_time_histogram_name = |
| 202 "ThreadWatcher.ResponseTime." + thread_name_; | 224 "ThreadWatcher.ResponseTime." + thread_name_; |
| 203 response_time_histogram_ = base::Histogram::FactoryTimeGet( | 225 response_time_histogram_ = base::Histogram::FactoryTimeGet( |
| 204 response_time_histogram_name, | 226 response_time_histogram_name, |
| 205 base::TimeDelta::FromMilliseconds(1), | 227 base::TimeDelta::FromMilliseconds(1), |
| 206 base::TimeDelta::FromSeconds(100), 50, | 228 base::TimeDelta::FromSeconds(100), 50, |
| 207 base::Histogram::kUmaTargetedHistogramFlag); | 229 base::Histogram::kUmaTargetedHistogramFlag); |
| 208 | 230 |
| (...skipping 28 matching lines...) Expand all Loading... |
| 237 | 259 |
| 238 void ThreadWatcher::ResetHangCounters() { | 260 void ThreadWatcher::ResetHangCounters() { |
| 239 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); | 261 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 240 unresponsive_count_ = 0; | 262 unresponsive_count_ = 0; |
| 241 hung_processing_complete_ = false; | 263 hung_processing_complete_ = false; |
| 242 } | 264 } |
| 243 | 265 |
| 244 void ThreadWatcher::GotNoResponse() { | 266 void ThreadWatcher::GotNoResponse() { |
| 245 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); | 267 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 246 | 268 |
| 247 // Record how other threads are responding when we don't get a response for | 269 ++unresponsive_count_; |
| 248 // ping message atleast kUnresponsiveCount times. | 270 if (!IsVeryUnresponsive()) |
| 249 if (++unresponsive_count_ < kUnresponsiveCount) | |
| 250 return; | 271 return; |
| 251 | 272 |
| 252 // Record total unresponsive_time since last pong message. | 273 // Record total unresponsive_time since last pong message. |
| 253 base::TimeDelta unresponse_time = base::TimeTicks::Now() - pong_time_; | 274 base::TimeDelta unresponse_time = base::TimeTicks::Now() - pong_time_; |
| 254 unresponsive_time_histogram_->AddTime(unresponse_time); | 275 unresponsive_time_histogram_->AddTime(unresponse_time); |
| 255 | 276 |
| 256 // We have already collected stats for the non-responding watched thread. | 277 // We have already collected stats for the non-responding watched thread. |
| 257 if (hung_processing_complete_) | 278 if (hung_processing_complete_) |
| 258 return; | 279 return; |
| 259 | 280 |
| 260 int no_of_responding_threads = 0; | 281 // Record how other threads are responding. |
| 261 int no_of_unresponding_threads = 0; | 282 uint32 responding_thread_count = 0; |
| 262 ThreadWatcherList::GetStatusOfThreads(&no_of_responding_threads, | 283 uint32 unresponding_thread_count = 0; |
| 263 &no_of_unresponding_threads); | 284 ThreadWatcherList::GetStatusOfThreads(&responding_thread_count, |
| 285 &unresponding_thread_count); |
| 264 | 286 |
| 265 // Record how many watched threads are responding. | 287 // Record how many watched threads are responding. |
| 266 responsive_count_histogram_->Add(no_of_responding_threads); | 288 responsive_count_histogram_->Add(responding_thread_count); |
| 267 | 289 |
| 268 // Record how many watched threads are not responding. | 290 // Record how many watched threads are not responding. |
| 269 unresponsive_count_histogram_->Add(no_of_unresponding_threads); | 291 unresponsive_count_histogram_->Add(unresponding_thread_count); |
| 270 | 292 |
| 271 // Crash the browser if IO thread hasn't responded atleast kUnresponsiveCount | 293 // Crash the browser if the watched thread is to be crashed on hang and if the |
| 272 // times and if the number of other threads is equal to 1. We picked 1 to | 294 // number of other threads responding is equal to live_threads_threshold_. |
| 273 // reduce the number of crashes and to get some sample data. | 295 if (crash_on_hang_ && responding_thread_count == live_threads_threshold_) { |
| 274 if (thread_id_ == BrowserThread::IO && no_of_responding_threads == 1) { | |
| 275 int* crash = NULL; | 296 int* crash = NULL; |
| 276 CHECK(crash++); | 297 CHECK(crash + thread_id_); |
| 277 } | 298 } |
| 278 | 299 |
| 279 hung_processing_complete_ = true; | 300 hung_processing_complete_ = true; |
| 280 } | 301 } |
| 281 | 302 |
| 303 bool ThreadWatcher::IsVeryUnresponsive() { |
| 304 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 305 return unresponsive_count_ >= unresponsive_threshold_; |
| 306 } |
| 307 |
| 282 // ThreadWatcherList methods and members. | 308 // ThreadWatcherList methods and members. |
| 283 // | 309 // |
| 284 // static | 310 // static |
| 285 ThreadWatcherList* ThreadWatcherList::global_ = NULL; | 311 ThreadWatcherList* ThreadWatcherList::g_thread_watcher_list_ = NULL; |
| 286 // static | 312 // static |
| 287 const int ThreadWatcherList::kSleepSeconds = 1; | 313 const int ThreadWatcherList::kSleepSeconds = 1; |
| 288 // static | 314 // static |
| 289 const int ThreadWatcherList::kUnresponsiveSeconds = 2; | 315 const int ThreadWatcherList::kUnresponsiveSeconds = 2; |
| 290 | 316 // static |
| 291 ThreadWatcherList::ThreadWatcherList() | 317 const int ThreadWatcherList::kUnresponsiveCount = 6; |
| 292 : last_wakeup_time_(base::TimeTicks::Now()) { | 318 // static |
| 293 // Assert we are not running on WATCHDOG thread. Would be ideal to assert we | 319 const int ThreadWatcherList::kLiveThreadsThreshold = 1; |
| 294 // are on UI thread, but Unit tests are not running on UI thread. | 320 |
| 295 DCHECK(!WatchDogThread::CurrentlyOnWatchDogThread()); | 321 // static |
| 296 CHECK(!global_); | 322 void ThreadWatcherList::StartWatchingAll(const CommandLine& command_line) { |
| 297 global_ = this; | 323 ThreadWatcherObserver::SetupNotifications( |
| 298 // Register Notifications observer. | 324 base::TimeDelta::FromSeconds(kSleepSeconds * ThreadWatcher::kPingCount)); |
| 299 MetricsService::SetUpNotifications(®istrar_, this); | 325 |
| 326 uint32 unresponsive_threshold; |
| 327 std::set<std::string> crash_on_hang_thread_names; |
| 328 uint32 live_threads_threshold; |
| 329 ParseCommandLine(command_line, |
| 330 &unresponsive_threshold, |
| 331 &crash_on_hang_thread_names, |
| 332 &live_threads_threshold); |
| 333 |
| 334 WatchDogThread::PostDelayedTask( |
| 335 FROM_HERE, |
| 336 NewRunnableFunction(&ThreadWatcherList::InitializeAndStartWatching, |
| 337 unresponsive_threshold, |
| 338 crash_on_hang_thread_names, |
| 339 live_threads_threshold), |
| 340 base::TimeDelta::FromSeconds(120).InMilliseconds()); |
| 341 } |
| 342 |
| 343 // static |
| 344 void ThreadWatcherList::StopWatchingAll() { |
| 345 ThreadWatcherObserver::RemoveNotifications(); |
| 346 DeleteAll(); |
| 347 } |
| 348 |
| 349 // static |
| 350 void ThreadWatcherList::Register(ThreadWatcher* watcher) { |
| 351 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 352 if (!g_thread_watcher_list_) |
| 353 return; |
| 354 DCHECK(!g_thread_watcher_list_->Find(watcher->thread_id())); |
| 355 g_thread_watcher_list_->registered_[watcher->thread_id()] = watcher; |
| 356 } |
| 357 |
| 358 // static |
| 359 bool ThreadWatcherList::IsRegistered(const BrowserThread::ID thread_id) { |
| 360 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 361 return NULL != ThreadWatcherList::Find(thread_id); |
| 362 } |
| 363 |
| 364 // static |
| 365 void ThreadWatcherList::GetStatusOfThreads(uint32* responding_thread_count, |
| 366 uint32* unresponding_thread_count) { |
| 367 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 368 *responding_thread_count = 0; |
| 369 *unresponding_thread_count = 0; |
| 370 if (!g_thread_watcher_list_) |
| 371 return; |
| 372 |
| 373 for (RegistrationList::iterator it = |
| 374 g_thread_watcher_list_->registered_.begin(); |
| 375 g_thread_watcher_list_->registered_.end() != it; |
| 376 ++it) { |
| 377 if (it->second->IsVeryUnresponsive()) |
| 378 ++(*unresponding_thread_count); |
| 379 else |
| 380 ++(*responding_thread_count); |
| 381 } |
| 382 } |
| 383 |
| 384 // static |
| 385 void ThreadWatcherList::WakeUpAll() { |
| 386 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 387 if (!g_thread_watcher_list_) |
| 388 return; |
| 389 |
| 390 for (RegistrationList::iterator it = |
| 391 g_thread_watcher_list_->registered_.begin(); |
| 392 g_thread_watcher_list_->registered_.end() != it; |
| 393 ++it) |
| 394 it->second->WakeUp(); |
| 395 } |
| 396 |
| 397 ThreadWatcherList::ThreadWatcherList() { |
| 398 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 399 CHECK(!g_thread_watcher_list_); |
| 400 g_thread_watcher_list_ = this; |
| 300 } | 401 } |
| 301 | 402 |
| 302 ThreadWatcherList::~ThreadWatcherList() { | 403 ThreadWatcherList::~ThreadWatcherList() { |
| 303 base::AutoLock auto_lock(lock_); | 404 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 304 DCHECK(this == global_); | 405 DCHECK(this == g_thread_watcher_list_); |
| 305 global_ = NULL; | 406 g_thread_watcher_list_ = NULL; |
| 306 } | 407 } |
| 307 | 408 |
| 308 // static | 409 // static |
| 309 void ThreadWatcherList::Register(ThreadWatcher* watcher) { | 410 void ThreadWatcherList::ParseCommandLine( |
| 310 if (!global_) | 411 const CommandLine& command_line, |
| 311 return; | 412 uint32* unresponsive_threshold, |
| 312 base::AutoLock auto_lock(global_->lock_); | 413 std::set<std::string>* crash_on_hang_thread_names, |
| 313 DCHECK(!global_->PreLockedFind(watcher->thread_id())); | 414 uint32* live_threads_threshold) { |
| 314 global_->registered_[watcher->thread_id()] = watcher; | 415 // Determine |unresponsive_threshold| based on switches::kCrashOnHangSeconds. |
| 315 } | 416 *unresponsive_threshold = kUnresponsiveCount; |
| 316 | 417 std::string crash_on_hang_seconds = |
| 317 // static | 418 command_line.GetSwitchValueASCII(switches::kCrashOnHangSeconds); |
| 318 bool ThreadWatcherList::IsRegistered(const BrowserThread::ID thread_id) { | 419 if (!crash_on_hang_seconds.empty()) { |
| 319 return NULL != ThreadWatcherList::Find(thread_id); | 420 int crash_seconds = atoi(crash_on_hang_seconds.c_str()); |
| 320 } | 421 if (crash_seconds > 0) { |
| 321 | 422 *unresponsive_threshold = static_cast<uint32>( |
| 322 // static | 423 ceil(static_cast<float>(crash_seconds) / kUnresponsiveSeconds)); |
| 323 void ThreadWatcherList::StartWatchingAll() { | 424 } |
| 324 if (!WatchDogThread::CurrentlyOnWatchDogThread()) { | 425 } |
| 325 WatchDogThread::PostDelayedTask( | 426 |
| 326 FROM_HERE, | 427 // Default to crashing the browser if UI or IO threads are not responsive. |
| 327 NewRunnableFunction(&ThreadWatcherList::StartWatchingAll), | 428 std::string crash_on_hang_threads = "UI,IO"; |
| 328 base::TimeDelta::FromSeconds(120).InMilliseconds()); | 429 if (command_line.HasSwitch(switches::kCrashOnHangThreads)) { |
| 329 return; | 430 crash_on_hang_threads = |
| 330 } | 431 command_line.GetSwitchValueASCII(switches::kCrashOnHangThreads); |
| 331 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); | 432 } |
| 433 StringTokenizer tokens(crash_on_hang_threads, ","); |
| 434 while (tokens.GetNext()) |
| 435 crash_on_hang_thread_names->insert(tokens.token()); |
| 436 |
| 437 // Determine |live_threads_threshold| based on switches::kCrashOnLive. |
| 438 *live_threads_threshold = kLiveThreadsThreshold; |
| 439 if (command_line.HasSwitch(switches::kCrashOnLive)) { |
| 440 std::string live_threads = |
| 441 command_line.GetSwitchValueASCII(switches::kCrashOnLive); |
| 442 *live_threads_threshold = static_cast<uint32>(atoi(live_threads.c_str())); |
| 443 } |
| 444 } |
| 445 |
| 446 // static |
| 447 void ThreadWatcherList::InitializeAndStartWatching( |
| 448 uint32 unresponsive_threshold, |
| 449 const std::set<std::string>& crash_on_hang_thread_names, |
| 450 uint32 live_threads_threshold) { |
| 451 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 452 |
| 453 ThreadWatcherList* thread_watcher_list = new ThreadWatcherList(); |
| 454 CHECK(thread_watcher_list); |
| 455 |
| 332 const base::TimeDelta kSleepTime = | 456 const base::TimeDelta kSleepTime = |
| 333 base::TimeDelta::FromSeconds(kSleepSeconds); | 457 base::TimeDelta::FromSeconds(kSleepSeconds); |
| 334 const base::TimeDelta kUnresponsiveTime = | 458 const base::TimeDelta kUnresponsiveTime = |
| 335 base::TimeDelta::FromSeconds(kUnresponsiveSeconds); | 459 base::TimeDelta::FromSeconds(kUnresponsiveSeconds); |
| 336 if (BrowserThread::IsMessageLoopValid(BrowserThread::UI)) { | 460 |
| 337 ThreadWatcher::StartWatching(BrowserThread::UI, "UI", kSleepTime, | 461 StartWatching(BrowserThread::UI, "UI", kSleepTime, kUnresponsiveTime, |
| 338 kUnresponsiveTime); | 462 unresponsive_threshold, crash_on_hang_thread_names, |
| 339 } | 463 live_threads_threshold); |
| 340 if (BrowserThread::IsMessageLoopValid(BrowserThread::IO)) { | 464 StartWatching(BrowserThread::IO, "IO", kSleepTime, kUnresponsiveTime, |
| 341 ThreadWatcher::StartWatching(BrowserThread::IO, "IO", kSleepTime, | 465 unresponsive_threshold, crash_on_hang_thread_names, |
| 342 kUnresponsiveTime); | 466 live_threads_threshold); |
| 343 } | 467 StartWatching(BrowserThread::DB, "DB", kSleepTime, kUnresponsiveTime, |
| 344 if (BrowserThread::IsMessageLoopValid(BrowserThread::DB)) { | 468 unresponsive_threshold, crash_on_hang_thread_names, |
| 345 ThreadWatcher::StartWatching(BrowserThread::DB, "DB", kSleepTime, | 469 live_threads_threshold); |
| 346 kUnresponsiveTime); | 470 StartWatching(BrowserThread::FILE, "FILE", kSleepTime, kUnresponsiveTime, |
| 347 } | 471 unresponsive_threshold, crash_on_hang_thread_names, |
| 348 if (BrowserThread::IsMessageLoopValid(BrowserThread::FILE)) { | 472 live_threads_threshold); |
| 349 ThreadWatcher::StartWatching(BrowserThread::FILE, "FILE", kSleepTime, | 473 StartWatching(BrowserThread::CACHE, "CACHE", kSleepTime, kUnresponsiveTime, |
| 350 kUnresponsiveTime); | 474 unresponsive_threshold, crash_on_hang_thread_names, |
| 351 } | 475 live_threads_threshold); |
| 352 if (BrowserThread::IsMessageLoopValid(BrowserThread::CACHE)) { | 476 } |
| 353 ThreadWatcher::StartWatching(BrowserThread::CACHE, "CACHE", kSleepTime, | 477 |
| 354 kUnresponsiveTime); | 478 // static |
| 355 } | 479 void ThreadWatcherList::StartWatching( |
| 356 } | 480 const BrowserThread::ID& thread_id, |
| 357 | 481 const std::string& thread_name, |
| 358 // static | 482 const base::TimeDelta& sleep_time, |
| 359 void ThreadWatcherList::StopWatchingAll() { | 483 const base::TimeDelta& unresponsive_time, |
| 360 // Assert we are not running on WATCHDOG thread. Would be ideal to assert we | 484 uint32 unresponsive_threshold, |
| 361 // are on UI thread, but Unit tests are not running on UI thread. | 485 const std::set<std::string>& crash_on_hang_thread_names, |
| 362 DCHECK(!WatchDogThread::CurrentlyOnWatchDogThread()); | 486 uint32 live_threads_threshold) { |
| 363 if (!global_) | 487 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 364 return; | 488 |
| 365 | 489 if (!BrowserThread::IsMessageLoopValid(thread_id)) |
| 366 // Remove all notifications for all watched threads. | 490 return; |
| 367 RemoveNotifications(); | 491 |
| 368 | 492 std::set<std::string>::const_iterator it = |
| 369 // Delete all thread watcher objects on WatchDogThread. | 493 crash_on_hang_thread_names.find(thread_name); |
| 494 bool crash_on_hang = (it != crash_on_hang_thread_names.end()); |
| 495 |
| 496 ThreadWatcher::StartWatching(thread_id, |
| 497 thread_name, |
| 498 sleep_time, |
| 499 unresponsive_time, |
| 500 unresponsive_threshold, |
| 501 crash_on_hang, |
| 502 live_threads_threshold); |
| 503 } |
| 504 |
| 505 // static |
| 506 void ThreadWatcherList::DeleteAll() { |
| 507 if (!WatchDogThread::CurrentlyOnWatchDogThread()) { |
| 508 WatchDogThread::PostTask( |
| 509 FROM_HERE, |
| 510 NewRunnableFunction(&ThreadWatcherList::DeleteAll)); |
| 511 return; |
| 512 } |
| 513 |
| 514 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 515 if (!g_thread_watcher_list_) |
| 516 return; |
| 517 |
| 518 // Delete all thread watcher objects. |
| 519 while (!g_thread_watcher_list_->registered_.empty()) { |
| 520 RegistrationList::iterator it = g_thread_watcher_list_->registered_.begin(); |
| 521 delete it->second; |
| 522 g_thread_watcher_list_->registered_.erase(it); |
| 523 } |
| 524 |
| 525 delete g_thread_watcher_list_; |
| 526 } |
| 527 |
| 528 // static |
| 529 ThreadWatcher* ThreadWatcherList::Find(const BrowserThread::ID& thread_id) { |
| 530 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 531 if (!g_thread_watcher_list_) |
| 532 return NULL; |
| 533 RegistrationList::iterator it = |
| 534 g_thread_watcher_list_->registered_.find(thread_id); |
| 535 if (g_thread_watcher_list_->registered_.end() == it) |
| 536 return NULL; |
| 537 return it->second; |
| 538 } |
| 539 |
| 540 // ThreadWatcherObserver methods and members. |
| 541 // |
| 542 // static |
| 543 ThreadWatcherObserver* ThreadWatcherObserver::g_thread_watcher_observer_ = NULL; |
| 544 |
| 545 ThreadWatcherObserver::ThreadWatcherObserver( |
| 546 const base::TimeDelta& wakeup_interval) |
| 547 : last_wakeup_time_(base::TimeTicks::Now()), |
| 548 wakeup_interval_(wakeup_interval) { |
| 549 CHECK(!g_thread_watcher_observer_); |
| 550 g_thread_watcher_observer_ = this; |
| 551 } |
| 552 |
| 553 ThreadWatcherObserver::~ThreadWatcherObserver() { |
| 554 DCHECK(this == g_thread_watcher_observer_); |
| 555 g_thread_watcher_observer_ = NULL; |
| 556 } |
| 557 |
| 558 // static |
| 559 void ThreadWatcherObserver::SetupNotifications( |
| 560 const base::TimeDelta& wakeup_interval) { |
| 561 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 562 ThreadWatcherObserver* observer = new ThreadWatcherObserver(wakeup_interval); |
| 563 MetricsService::SetUpNotifications(&observer->registrar_, observer); |
| 564 } |
| 565 |
| 566 // static |
| 567 void ThreadWatcherObserver::RemoveNotifications() { |
| 568 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 569 if (!g_thread_watcher_observer_) |
| 570 return; |
| 571 g_thread_watcher_observer_->registrar_.RemoveAll(); |
| 572 delete g_thread_watcher_observer_; |
| 573 } |
| 574 |
| 575 void ThreadWatcherObserver::Observe(NotificationType type, |
| 576 const NotificationSource& source, |
| 577 const NotificationDetails& details) { |
| 578 // There is some user activity, see if thread watchers are to be awakened. |
| 579 base::TimeTicks now = base::TimeTicks::Now(); |
| 580 if ((now - last_wakeup_time_) < wakeup_interval_) |
| 581 return; |
| 582 last_wakeup_time_ = now; |
| 370 WatchDogThread::PostTask( | 583 WatchDogThread::PostTask( |
| 371 FROM_HERE, | 584 FROM_HERE, |
| 372 NewRunnableMethod(global_, &ThreadWatcherList::DeleteAll)); | 585 NewRunnableFunction(&ThreadWatcherList::WakeUpAll)); |
| 373 } | |
| 374 | |
| 375 // static | |
| 376 void ThreadWatcherList::RemoveNotifications() { | |
| 377 // Assert we are not running on WATCHDOG thread. Would be ideal to assert we | |
| 378 // are on UI thread, but Unit tests are not running on UI thread. | |
| 379 DCHECK(!WatchDogThread::CurrentlyOnWatchDogThread()); | |
| 380 if (!global_) | |
| 381 return; | |
| 382 base::AutoLock auto_lock(global_->lock_); | |
| 383 global_->registrar_.RemoveAll(); | |
| 384 } | |
| 385 | |
| 386 // static | |
| 387 void ThreadWatcherList::GetStatusOfThreads(int* no_of_responding_threads, | |
| 388 int* no_of_unresponding_threads) { | |
| 389 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); | |
| 390 *no_of_responding_threads = 0; | |
| 391 *no_of_unresponding_threads = 0; | |
| 392 if (!global_) | |
| 393 return; | |
| 394 | |
| 395 base::AutoLock auto_lock(global_->lock_); | |
| 396 for (RegistrationList::iterator it = global_->registered_.begin(); | |
| 397 global_->registered_.end() != it; | |
| 398 ++it) { | |
| 399 if (it->second->unresponsive_count_ < ThreadWatcher::kUnresponsiveCount) | |
| 400 ++(*no_of_responding_threads); | |
| 401 else | |
| 402 ++(*no_of_unresponding_threads); | |
| 403 } | |
| 404 } | |
| 405 | |
| 406 void ThreadWatcherList::DeleteAll() { | |
| 407 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); | |
| 408 base::AutoLock auto_lock(lock_); | |
| 409 while (!registered_.empty()) { | |
| 410 RegistrationList::iterator it = registered_.begin(); | |
| 411 delete it->second; | |
| 412 registered_.erase(it->first); | |
| 413 } | |
| 414 } | |
| 415 | |
| 416 void ThreadWatcherList::Observe(NotificationType type, | |
| 417 const NotificationSource& source, | |
| 418 const NotificationDetails& details) { | |
| 419 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 420 // There is some user activity, see if thread watchers are to be awakened. | |
| 421 bool need_to_awaken = false; | |
| 422 base::TimeTicks now = base::TimeTicks::Now(); | |
| 423 { | |
| 424 base::AutoLock lock(lock_); | |
| 425 if (now - last_wakeup_time_ > base::TimeDelta::FromSeconds(kSleepSeconds)) { | |
| 426 need_to_awaken = true; | |
| 427 last_wakeup_time_ = now; | |
| 428 } | |
| 429 } | |
| 430 if (need_to_awaken) { | |
| 431 WatchDogThread::PostTask( | |
| 432 FROM_HERE, | |
| 433 NewRunnableMethod(this, &ThreadWatcherList::WakeUpAll)); | |
| 434 } | |
| 435 } | |
| 436 | |
| 437 void ThreadWatcherList::WakeUpAll() { | |
| 438 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); | |
| 439 if (!global_) | |
| 440 return; | |
| 441 base::AutoLock auto_lock(lock_); | |
| 442 for (RegistrationList::iterator it = global_->registered_.begin(); | |
| 443 global_->registered_.end() != it; | |
| 444 ++it) | |
| 445 it->second->WakeUp(); | |
| 446 } | |
| 447 | |
| 448 // static | |
| 449 ThreadWatcher* ThreadWatcherList::Find(const BrowserThread::ID& thread_id) { | |
| 450 if (!global_) | |
| 451 return NULL; | |
| 452 base::AutoLock auto_lock(global_->lock_); | |
| 453 return global_->PreLockedFind(thread_id); | |
| 454 } | |
| 455 | |
| 456 ThreadWatcher* ThreadWatcherList::PreLockedFind( | |
| 457 const BrowserThread::ID& thread_id) { | |
| 458 RegistrationList::iterator it = registered_.find(thread_id); | |
| 459 if (registered_.end() == it) | |
| 460 return NULL; | |
| 461 return it->second; | |
| 462 } | 586 } |
| 463 | 587 |
| 464 // WatchDogThread methods and members. | 588 // WatchDogThread methods and members. |
| 465 // | 589 // |
| 466 // static | 590 // static |
| 467 base::Lock WatchDogThread::lock_; | 591 base::Lock WatchDogThread::lock_; |
| 468 // static | 592 // static |
| 469 WatchDogThread* WatchDogThread::watchdog_thread_ = NULL; | 593 WatchDogThread* WatchDogThread::watchdog_thread_ = NULL; |
| 470 | 594 |
| 471 // The WatchDogThread object must outlive any tasks posted to the IO thread | 595 // The WatchDogThread object must outlive any tasks posted to the IO thread |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 536 watchdog_thread_ = this; | 660 watchdog_thread_ = this; |
| 537 } | 661 } |
| 538 | 662 |
| 539 void WatchDogThread::CleanUp() { | 663 void WatchDogThread::CleanUp() { |
| 540 base::AutoLock lock(lock_); | 664 base::AutoLock lock(lock_); |
| 541 watchdog_thread_ = NULL; | 665 watchdog_thread_ = NULL; |
| 542 } | 666 } |
| 543 | 667 |
| 544 void WatchDogThread::CleanUpAfterMessageLoopDestruction() { | 668 void WatchDogThread::CleanUpAfterMessageLoopDestruction() { |
| 545 #if defined(OS_WIN) | 669 #if defined(OS_WIN) |
| 546 // Closes the COM library on the current thread. CoInitialize must | 670 // Closes the COM library on the current thread. CoInitialize must be balanced |
| 547 // be balanced by a corresponding call to CoUninitialize. | 671 // by a corresponding call to CoUninitialize. |
| 548 CoUninitialize(); | 672 CoUninitialize(); |
| 549 #endif | 673 #endif |
| 550 } | 674 } |
| OLD | NEW |