[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 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 |
| 3 | // found in the LICENSE file. |
| 4 | |
[email protected] | 8fd1183 | 2011-07-14 20:01:13 | [diff] [blame] | 5 | #include "chrome/browser/metrics/thread_watcher.h" |
| 6 | |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 7 | #include <math.h> // ceil |
| 8 | |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 9 | #include "base/bind.h" |
[email protected] | 8fd1183 | 2011-07-14 20:01:13 | [diff] [blame] | 10 | #include "base/debug/alias.h" |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 11 | #include "base/string_tokenizer.h" |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 12 | #include "base/threading/thread_restrictions.h" |
[email protected] | a55edc4 | 2011-02-24 20:17:28 | [diff] [blame] | 13 | #include "build/build_config.h" |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 14 | #include "chrome/browser/metrics/metrics_service.h" |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 15 | #include "chrome/common/chrome_switches.h" |
[email protected] | 6a084f0 | 2011-07-26 21:34:36 | [diff] [blame] | 16 | #include "chrome/common/chrome_version_info.h" |
[email protected] | b6994152 | 2011-10-08 03:17:37 | [diff] [blame] | 17 | #include "chrome/common/logging_chrome.h" |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 18 | |
[email protected] | 0b56518 | 2011-03-02 18:11:15 | [diff] [blame] | 19 | #if defined(OS_WIN) |
[email protected] | 8533994 | 2011-08-29 21:03:43 | [diff] [blame] | 20 | #include "base/win/windows_version.h" |
[email protected] | 0b56518 | 2011-03-02 18:11:15 | [diff] [blame] | 21 | #endif |
| 22 | |
[email protected] | 631bb74 | 2011-11-02 11:29:39 | [diff] [blame^] | 23 | using content::BrowserThread; |
| 24 | |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 25 | // ThreadWatcher methods and members. |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 26 | ThreadWatcher::ThreadWatcher(const WatchingParams& params) |
| 27 | : thread_id_(params.thread_id), |
| 28 | thread_name_(params.thread_name), |
| 29 | watched_loop_( |
| 30 | BrowserThread::GetMessageLoopProxyForThread(params.thread_id)), |
| 31 | sleep_time_(params.sleep_time), |
| 32 | unresponsive_time_(params.unresponsive_time), |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 33 | ping_time_(base::TimeTicks::Now()), |
[email protected] | 9a438634 | 2011-04-23 22:41:26 | [diff] [blame] | 34 | pong_time_(ping_time_), |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 35 | ping_sequence_number_(0), |
| 36 | active_(false), |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 37 | ping_count_(params.unresponsive_threshold), |
[email protected] | 9a438634 | 2011-04-23 22:41:26 | [diff] [blame] | 38 | response_time_histogram_(NULL), |
| 39 | unresponsive_time_histogram_(NULL), |
| 40 | unresponsive_count_(0), |
[email protected] | 8125a9a | 2011-05-17 15:06:21 | [diff] [blame] | 41 | hung_processing_complete_(false), |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 42 | unresponsive_threshold_(params.unresponsive_threshold), |
| 43 | crash_on_hang_(params.crash_on_hang), |
| 44 | live_threads_threshold_(params.live_threads_threshold), |
| 45 | ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 46 | DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 47 | Initialize(); |
| 48 | } |
| 49 | |
[email protected] | 1d28d69 | 2011-02-23 22:05:38 | [diff] [blame] | 50 | ThreadWatcher::~ThreadWatcher() {} |
| 51 | |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 52 | // static |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 53 | void ThreadWatcher::StartWatching(const WatchingParams& params) { |
| 54 | DCHECK_GE(params.sleep_time.InMilliseconds(), 0); |
| 55 | DCHECK_GE(params.unresponsive_time.InMilliseconds(), |
| 56 | params.sleep_time.InMilliseconds()); |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 57 | |
[email protected] | 0b56518 | 2011-03-02 18:11:15 | [diff] [blame] | 58 | // If we are not on WatchDogThread, then post a task to call StartWatching on |
| 59 | // WatchDogThread. |
| 60 | if (!WatchDogThread::CurrentlyOnWatchDogThread()) { |
| 61 | WatchDogThread::PostTask( |
[email protected] | db5bdf3 | 2011-02-28 07:57:40 | [diff] [blame] | 62 | FROM_HERE, |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 63 | base::Bind(&ThreadWatcher::StartWatching, params)); |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 64 | return; |
| 65 | } |
| 66 | |
[email protected] | 0b56518 | 2011-03-02 18:11:15 | [diff] [blame] | 67 | DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 68 | |
| 69 | // Create a new thread watcher object for the given thread and activate it. |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 70 | ThreadWatcher* watcher = new ThreadWatcher(params); |
| 71 | |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 72 | DCHECK(watcher); |
[email protected] | dedfabae | 2011-03-04 04:00:40 | [diff] [blame] | 73 | // If we couldn't register the thread watcher object, we are shutting down, |
| 74 | // then don't activate thread watching. |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 75 | if (!ThreadWatcherList::IsRegistered(params.thread_id)) |
[email protected] | dedfabae | 2011-03-04 04:00:40 | [diff] [blame] | 76 | return; |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 77 | watcher->ActivateThreadWatching(); |
| 78 | } |
| 79 | |
| 80 | void ThreadWatcher::ActivateThreadWatching() { |
[email protected] | 0b56518 | 2011-03-02 18:11:15 | [diff] [blame] | 81 | DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 82 | if (active_) return; |
| 83 | active_ = true; |
[email protected] | 26ecd8a2e | 2011-09-02 00:51:54 | [diff] [blame] | 84 | ping_count_ = unresponsive_threshold_; |
[email protected] | 3628ecaf | 2011-05-27 16:10:52 | [diff] [blame] | 85 | ResetHangCounters(); |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 86 | MessageLoop::current()->PostTask( |
| 87 | FROM_HERE, |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 88 | base::Bind(&ThreadWatcher::PostPingMessage, |
| 89 | weak_ptr_factory_.GetWeakPtr())); |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 90 | } |
| 91 | |
| 92 | void ThreadWatcher::DeActivateThreadWatching() { |
[email protected] | 0b56518 | 2011-03-02 18:11:15 | [diff] [blame] | 93 | DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 94 | active_ = false; |
| 95 | ping_count_ = 0; |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 96 | weak_ptr_factory_.InvalidateWeakPtrs(); |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 97 | } |
| 98 | |
| 99 | void ThreadWatcher::WakeUp() { |
[email protected] | 0b56518 | 2011-03-02 18:11:15 | [diff] [blame] | 100 | DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 101 | // There is some user activity, PostPingMessage task of thread watcher if |
| 102 | // needed. |
| 103 | if (!active_) return; |
| 104 | |
[email protected] | 01a605f | 2011-09-06 00:14:42 | [diff] [blame] | 105 | // Throw away the previous |unresponsive_count_| and start over again. Just |
| 106 | // before going to sleep, |unresponsive_count_| could be very close to |
| 107 | // |unresponsive_threshold_| and when user becomes active, |
| 108 | // |unresponsive_count_| can go over |unresponsive_threshold_| if there was no |
| 109 | // response for ping messages. Reset |unresponsive_count_| to start measuring |
| 110 | // the unresponsiveness of the threads when system becomes active. |
| 111 | unresponsive_count_ = 0; |
| 112 | |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 113 | if (ping_count_ <= 0) { |
[email protected] | 26ecd8a2e | 2011-09-02 00:51:54 | [diff] [blame] | 114 | ping_count_ = unresponsive_threshold_; |
[email protected] | 3628ecaf | 2011-05-27 16:10:52 | [diff] [blame] | 115 | ResetHangCounters(); |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 116 | PostPingMessage(); |
| 117 | } else { |
[email protected] | 26ecd8a2e | 2011-09-02 00:51:54 | [diff] [blame] | 118 | ping_count_ = unresponsive_threshold_; |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 119 | } |
| 120 | } |
| 121 | |
| 122 | void ThreadWatcher::PostPingMessage() { |
[email protected] | 0b56518 | 2011-03-02 18:11:15 | [diff] [blame] | 123 | DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 124 | // If we have stopped watching or if the user is idle, then stop sending |
| 125 | // ping messages. |
| 126 | if (!active_ || ping_count_ <= 0) |
| 127 | return; |
| 128 | |
| 129 | // Save the current time when we have sent ping message. |
| 130 | ping_time_ = base::TimeTicks::Now(); |
| 131 | |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 132 | // Send a ping message to the watched thread. Callback will be called on |
| 133 | // the WatchDogThread. |
| 134 | base::Closure callback( |
| 135 | base::Bind(&ThreadWatcher::OnPongMessage, weak_ptr_factory_.GetWeakPtr(), |
| 136 | ping_sequence_number_)); |
[email protected] | 8fd1183 | 2011-07-14 20:01:13 | [diff] [blame] | 137 | if (watched_loop_->PostTask( |
[email protected] | 89fb232c2 | 2011-02-24 01:45:10 | [diff] [blame] | 138 | FROM_HERE, |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 139 | base::Bind(&ThreadWatcher::OnPingMessage, thread_id_, |
| 140 | callback))) { |
[email protected] | 89fb232c2 | 2011-02-24 01:45:10 | [diff] [blame] | 141 | // Post a task to check the responsiveness of watched thread. |
| 142 | MessageLoop::current()->PostDelayedTask( |
| 143 | FROM_HERE, |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 144 | base::Bind(&ThreadWatcher::OnCheckResponsiveness, |
| 145 | weak_ptr_factory_.GetWeakPtr(), ping_sequence_number_), |
[email protected] | 89fb232c2 | 2011-02-24 01:45:10 | [diff] [blame] | 146 | unresponsive_time_.InMilliseconds()); |
| 147 | } else { |
| 148 | // Watched thread might have gone away, stop watching it. |
[email protected] | 89fb232c2 | 2011-02-24 01:45:10 | [diff] [blame] | 149 | DeActivateThreadWatching(); |
| 150 | } |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 151 | } |
| 152 | |
| 153 | void ThreadWatcher::OnPongMessage(uint64 ping_sequence_number) { |
[email protected] | 0b56518 | 2011-03-02 18:11:15 | [diff] [blame] | 154 | DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
[email protected] | 9a438634 | 2011-04-23 22:41:26 | [diff] [blame] | 155 | |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 156 | // Record watched thread's response time. |
[email protected] | 9a438634 | 2011-04-23 22:41:26 | [diff] [blame] | 157 | base::TimeTicks now = base::TimeTicks::Now(); |
| 158 | base::TimeDelta response_time = now - ping_time_; |
| 159 | response_time_histogram_->AddTime(response_time); |
| 160 | |
| 161 | // Save the current time when we have got pong message. |
| 162 | pong_time_ = now; |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 163 | |
| 164 | // Check if there are any extra pings in flight. |
| 165 | DCHECK_EQ(ping_sequence_number_, ping_sequence_number); |
| 166 | if (ping_sequence_number_ != ping_sequence_number) |
| 167 | return; |
| 168 | |
| 169 | // Increment sequence number for the next ping message to indicate watched |
| 170 | // thread is responsive. |
| 171 | ++ping_sequence_number_; |
| 172 | |
| 173 | // If we have stopped watching or if the user is idle, then stop sending |
| 174 | // ping messages. |
| 175 | if (!active_ || --ping_count_ <= 0) |
| 176 | return; |
| 177 | |
| 178 | MessageLoop::current()->PostDelayedTask( |
| 179 | FROM_HERE, |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 180 | base::Bind(&ThreadWatcher::PostPingMessage, |
| 181 | weak_ptr_factory_.GetWeakPtr()), |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 182 | sleep_time_.InMilliseconds()); |
| 183 | } |
| 184 | |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 185 | void ThreadWatcher::OnCheckResponsiveness(uint64 ping_sequence_number) { |
[email protected] | 0b56518 | 2011-03-02 18:11:15 | [diff] [blame] | 186 | DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 187 | // If we have stopped watching then consider thread as responding. |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 188 | if (!active_) { |
| 189 | responsive_ = true; |
| 190 | return; |
| 191 | } |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 192 | // If the latest ping_sequence_number_ is not same as the ping_sequence_number |
| 193 | // that is passed in, then we can assume OnPongMessage was called. |
| 194 | // OnPongMessage increments ping_sequence_number_. |
[email protected] | 9a438634 | 2011-04-23 22:41:26 | [diff] [blame] | 195 | if (ping_sequence_number_ != ping_sequence_number) { |
| 196 | // Reset unresponsive_count_ to zero because we got a response from the |
| 197 | // watched thread. |
[email protected] | 3628ecaf | 2011-05-27 16:10:52 | [diff] [blame] | 198 | ResetHangCounters(); |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 199 | |
| 200 | responsive_ = true; |
| 201 | return; |
[email protected] | 9a438634 | 2011-04-23 22:41:26 | [diff] [blame] | 202 | } |
| 203 | // Record that we got no response from watched thread. |
| 204 | GotNoResponse(); |
| 205 | |
| 206 | // Post a task to check the responsiveness of watched thread. |
| 207 | MessageLoop::current()->PostDelayedTask( |
| 208 | FROM_HERE, |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 209 | base::Bind(&ThreadWatcher::OnCheckResponsiveness, |
| 210 | weak_ptr_factory_.GetWeakPtr(), ping_sequence_number_), |
[email protected] | 9a438634 | 2011-04-23 22:41:26 | [diff] [blame] | 211 | unresponsive_time_.InMilliseconds()); |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 212 | responsive_ = false; |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 213 | } |
| 214 | |
| 215 | void ThreadWatcher::Initialize() { |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 216 | DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 217 | ThreadWatcherList::Register(this); |
[email protected] | 9a438634 | 2011-04-23 22:41:26 | [diff] [blame] | 218 | |
| 219 | const std::string response_time_histogram_name = |
[email protected] | f6179ec | 2011-03-17 00:25:46 | [diff] [blame] | 220 | "ThreadWatcher.ResponseTime." + thread_name_; |
[email protected] | 9a438634 | 2011-04-23 22:41:26 | [diff] [blame] | 221 | response_time_histogram_ = base::Histogram::FactoryTimeGet( |
| 222 | response_time_histogram_name, |
| 223 | base::TimeDelta::FromMilliseconds(1), |
| 224 | base::TimeDelta::FromSeconds(100), 50, |
| 225 | base::Histogram::kUmaTargetedHistogramFlag); |
| 226 | |
| 227 | const std::string unresponsive_time_histogram_name = |
| 228 | "ThreadWatcher.Unresponsive." + thread_name_; |
| 229 | unresponsive_time_histogram_ = base::Histogram::FactoryTimeGet( |
| 230 | unresponsive_time_histogram_name, |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 231 | base::TimeDelta::FromMilliseconds(1), |
| 232 | base::TimeDelta::FromSeconds(100), 50, |
| 233 | base::Histogram::kUmaTargetedHistogramFlag); |
[email protected] | 42499b8 | 2011-04-28 22:47:39 | [diff] [blame] | 234 | |
| 235 | const std::string responsive_count_histogram_name = |
| 236 | "ThreadWatcher.ResponsiveThreads." + thread_name_; |
| 237 | responsive_count_histogram_ = base::LinearHistogram::FactoryGet( |
| 238 | responsive_count_histogram_name, 1, 10, 11, |
| 239 | base::Histogram::kUmaTargetedHistogramFlag); |
| 240 | |
| 241 | const std::string unresponsive_count_histogram_name = |
| 242 | "ThreadWatcher.UnresponsiveThreads." + thread_name_; |
| 243 | unresponsive_count_histogram_ = base::LinearHistogram::FactoryGet( |
| 244 | unresponsive_count_histogram_name, 1, 10, 11, |
| 245 | base::Histogram::kUmaTargetedHistogramFlag); |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 246 | } |
| 247 | |
| 248 | // static |
[email protected] | f6179ec | 2011-03-17 00:25:46 | [diff] [blame] | 249 | void ThreadWatcher::OnPingMessage(const BrowserThread::ID& thread_id, |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 250 | const base::Closure& callback_task) { |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 251 | // This method is called on watched thread. |
| 252 | DCHECK(BrowserThread::CurrentlyOn(thread_id)); |
[email protected] | 0b56518 | 2011-03-02 18:11:15 | [diff] [blame] | 253 | WatchDogThread::PostTask(FROM_HERE, callback_task); |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 254 | } |
| 255 | |
[email protected] | 3628ecaf | 2011-05-27 16:10:52 | [diff] [blame] | 256 | void ThreadWatcher::ResetHangCounters() { |
[email protected] | 9a438634 | 2011-04-23 22:41:26 | [diff] [blame] | 257 | DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 258 | unresponsive_count_ = 0; |
[email protected] | 8125a9a | 2011-05-17 15:06:21 | [diff] [blame] | 259 | hung_processing_complete_ = false; |
[email protected] | 9a438634 | 2011-04-23 22:41:26 | [diff] [blame] | 260 | } |
| 261 | |
| 262 | void ThreadWatcher::GotNoResponse() { |
| 263 | DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
[email protected] | 519b3ec7 | 2011-05-14 02:44:25 | [diff] [blame] | 264 | |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 265 | ++unresponsive_count_; |
| 266 | if (!IsVeryUnresponsive()) |
[email protected] | 519b3ec7 | 2011-05-14 02:44:25 | [diff] [blame] | 267 | return; |
| 268 | |
[email protected] | 42499b8 | 2011-04-28 22:47:39 | [diff] [blame] | 269 | // Record total unresponsive_time since last pong message. |
| 270 | base::TimeDelta unresponse_time = base::TimeTicks::Now() - pong_time_; |
| 271 | unresponsive_time_histogram_->AddTime(unresponse_time); |
| 272 | |
[email protected] | 8125a9a | 2011-05-17 15:06:21 | [diff] [blame] | 273 | // We have already collected stats for the non-responding watched thread. |
| 274 | if (hung_processing_complete_) |
| 275 | return; |
| 276 | |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 277 | // Record how other threads are responding. |
| 278 | uint32 responding_thread_count = 0; |
| 279 | uint32 unresponding_thread_count = 0; |
| 280 | ThreadWatcherList::GetStatusOfThreads(&responding_thread_count, |
| 281 | &unresponding_thread_count); |
[email protected] | 42499b8 | 2011-04-28 22:47:39 | [diff] [blame] | 282 | |
| 283 | // Record how many watched threads are responding. |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 284 | responsive_count_histogram_->Add(responding_thread_count); |
[email protected] | 42499b8 | 2011-04-28 22:47:39 | [diff] [blame] | 285 | |
| 286 | // Record how many watched threads are not responding. |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 287 | unresponsive_count_histogram_->Add(unresponding_thread_count); |
[email protected] | 8125a9a | 2011-05-17 15:06:21 | [diff] [blame] | 288 | |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 289 | // Crash the browser if the watched thread is to be crashed on hang and if the |
| 290 | // number of other threads responding is equal to live_threads_threshold_. |
[email protected] | 8fd1183 | 2011-07-14 20:01:13 | [diff] [blame] | 291 | int thread_id = thread_id_; |
| 292 | base::debug::Alias(&thread_id); |
[email protected] | d31058ba | 2011-10-20 23:14:49 | [diff] [blame] | 293 | if (crash_on_hang_ && responding_thread_count == live_threads_threshold_) { |
| 294 | static bool crashed_once = false; |
| 295 | if (!crashed_once) { |
| 296 | crashed_once = true; |
| 297 | CHECK(false); |
| 298 | } |
| 299 | } |
[email protected] | 8125a9a | 2011-05-17 15:06:21 | [diff] [blame] | 300 | |
| 301 | hung_processing_complete_ = true; |
[email protected] | 9a438634 | 2011-04-23 22:41:26 | [diff] [blame] | 302 | } |
| 303 | |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 304 | bool ThreadWatcher::IsVeryUnresponsive() { |
| 305 | DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 306 | return unresponsive_count_ >= unresponsive_threshold_; |
| 307 | } |
| 308 | |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 309 | // ThreadWatcherList methods and members. |
[email protected] | f6179ec | 2011-03-17 00:25:46 | [diff] [blame] | 310 | // |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 311 | // static |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 312 | ThreadWatcherList* ThreadWatcherList::g_thread_watcher_list_ = NULL; |
[email protected] | 9a438634 | 2011-04-23 22:41:26 | [diff] [blame] | 313 | // static |
[email protected] | 3628ecaf | 2011-05-27 16:10:52 | [diff] [blame] | 314 | const int ThreadWatcherList::kSleepSeconds = 1; |
[email protected] | 9a438634 | 2011-04-23 22:41:26 | [diff] [blame] | 315 | // static |
[email protected] | 3628ecaf | 2011-05-27 16:10:52 | [diff] [blame] | 316 | const int ThreadWatcherList::kUnresponsiveSeconds = 2; |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 317 | // static |
[email protected] | 26ecd8a2e | 2011-09-02 00:51:54 | [diff] [blame] | 318 | const int ThreadWatcherList::kUnresponsiveCount = 9; |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 319 | // static |
| 320 | const int ThreadWatcherList::kLiveThreadsThreshold = 1; |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 321 | |
| 322 | // static |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 323 | void ThreadWatcherList::StartWatchingAll(const CommandLine& command_line) { |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 324 | uint32 unresponsive_threshold; |
| 325 | std::set<std::string> crash_on_hang_thread_names; |
| 326 | uint32 live_threads_threshold; |
| 327 | ParseCommandLine(command_line, |
| 328 | &unresponsive_threshold, |
| 329 | &crash_on_hang_thread_names, |
| 330 | &live_threads_threshold); |
[email protected] | dedfabae | 2011-03-04 04:00:40 | [diff] [blame] | 331 | |
[email protected] | 26ecd8a2e | 2011-09-02 00:51:54 | [diff] [blame] | 332 | ThreadWatcherObserver::SetupNotifications( |
| 333 | base::TimeDelta::FromSeconds(kSleepSeconds * unresponsive_threshold)); |
| 334 | |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 335 | WatchDogThread::PostDelayedTask( |
| 336 | FROM_HERE, |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 337 | base::Bind(&ThreadWatcherList::InitializeAndStartWatching, |
| 338 | unresponsive_threshold, |
| 339 | crash_on_hang_thread_names, |
| 340 | live_threads_threshold), |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 341 | base::TimeDelta::FromSeconds(120).InMilliseconds()); |
[email protected] | 0b56518 | 2011-03-02 18:11:15 | [diff] [blame] | 342 | } |
| 343 | |
| 344 | // static |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 345 | void ThreadWatcherList::StopWatchingAll() { |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 346 | ThreadWatcherObserver::RemoveNotifications(); |
| 347 | DeleteAll(); |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 348 | } |
| 349 | |
| 350 | // static |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 351 | void ThreadWatcherList::Register(ThreadWatcher* watcher) { |
[email protected] | 9a438634 | 2011-04-23 22:41:26 | [diff] [blame] | 352 | DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 353 | if (!g_thread_watcher_list_) |
| 354 | return; |
| 355 | DCHECK(!g_thread_watcher_list_->Find(watcher->thread_id())); |
| 356 | g_thread_watcher_list_->registered_[watcher->thread_id()] = watcher; |
| 357 | } |
| 358 | |
| 359 | // static |
| 360 | bool ThreadWatcherList::IsRegistered(const BrowserThread::ID thread_id) { |
| 361 | DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 362 | return NULL != ThreadWatcherList::Find(thread_id); |
| 363 | } |
| 364 | |
| 365 | // static |
| 366 | void ThreadWatcherList::GetStatusOfThreads(uint32* responding_thread_count, |
| 367 | uint32* unresponding_thread_count) { |
| 368 | DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 369 | *responding_thread_count = 0; |
| 370 | *unresponding_thread_count = 0; |
| 371 | if (!g_thread_watcher_list_) |
[email protected] | 42499b8 | 2011-04-28 22:47:39 | [diff] [blame] | 372 | return; |
[email protected] | 9a438634 | 2011-04-23 22:41:26 | [diff] [blame] | 373 | |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 374 | for (RegistrationList::iterator it = |
| 375 | g_thread_watcher_list_->registered_.begin(); |
| 376 | g_thread_watcher_list_->registered_.end() != it; |
[email protected] | 9a438634 | 2011-04-23 22:41:26 | [diff] [blame] | 377 | ++it) { |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 378 | if (it->second->IsVeryUnresponsive()) |
| 379 | ++(*unresponding_thread_count); |
[email protected] | ae53f11 | 2011-05-19 23:29:08 | [diff] [blame] | 380 | else |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 381 | ++(*responding_thread_count); |
[email protected] | 9a438634 | 2011-04-23 22:41:26 | [diff] [blame] | 382 | } |
[email protected] | 9a438634 | 2011-04-23 22:41:26 | [diff] [blame] | 383 | } |
| 384 | |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 385 | // static |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 386 | void ThreadWatcherList::WakeUpAll() { |
[email protected] | 0b56518 | 2011-03-02 18:11:15 | [diff] [blame] | 387 | DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 388 | if (!g_thread_watcher_list_) |
[email protected] | dedfabae | 2011-03-04 04:00:40 | [diff] [blame] | 389 | return; |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 390 | |
| 391 | for (RegistrationList::iterator it = |
| 392 | g_thread_watcher_list_->registered_.begin(); |
| 393 | g_thread_watcher_list_->registered_.end() != it; |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 394 | ++it) |
| 395 | it->second->WakeUp(); |
| 396 | } |
| 397 | |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 398 | ThreadWatcherList::ThreadWatcherList() { |
| 399 | DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 400 | CHECK(!g_thread_watcher_list_); |
| 401 | g_thread_watcher_list_ = this; |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 402 | } |
| 403 | |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 404 | ThreadWatcherList::~ThreadWatcherList() { |
| 405 | DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 406 | DCHECK(this == g_thread_watcher_list_); |
| 407 | g_thread_watcher_list_ = NULL; |
| 408 | } |
| 409 | |
| 410 | // static |
| 411 | void ThreadWatcherList::ParseCommandLine( |
| 412 | const CommandLine& command_line, |
| 413 | uint32* unresponsive_threshold, |
| 414 | std::set<std::string>* crash_on_hang_thread_names, |
| 415 | uint32* live_threads_threshold) { |
| 416 | // Determine |unresponsive_threshold| based on switches::kCrashOnHangSeconds. |
| 417 | *unresponsive_threshold = kUnresponsiveCount; |
[email protected] | 8533994 | 2011-08-29 21:03:43 | [diff] [blame] | 418 | |
[email protected] | d8c65a8 | 2011-09-10 23:17:09 | [diff] [blame] | 419 | // Increase the unresponsive_threshold on the Stable and Beta channels to |
| 420 | // reduce the number of crashes due to ThreadWatcher. |
| 421 | chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); |
| 422 | if (channel == chrome::VersionInfo::CHANNEL_STABLE) { |
| 423 | *unresponsive_threshold *= 4; |
| 424 | } else if (channel == chrome::VersionInfo::CHANNEL_BETA) { |
[email protected] | 8533994 | 2011-08-29 21:03:43 | [diff] [blame] | 425 | *unresponsive_threshold *= 2; |
[email protected] | 8533994 | 2011-08-29 21:03:43 | [diff] [blame] | 426 | } |
| 427 | |
[email protected] | d8c65a8 | 2011-09-10 23:17:09 | [diff] [blame] | 428 | #if defined(OS_WIN) |
| 429 | // For Windows XP (old systems), double the unresponsive_threshold to give |
| 430 | // the OS a chance to schedule UI/IO threads a time slice to respond with a |
| 431 | // pong message (to get around limitations with the OS). |
| 432 | if (base::win::GetVersion() <= base::win::VERSION_XP) |
| 433 | *unresponsive_threshold *= 2; |
| 434 | #endif |
| 435 | |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 436 | std::string crash_on_hang_seconds = |
| 437 | command_line.GetSwitchValueASCII(switches::kCrashOnHangSeconds); |
| 438 | if (!crash_on_hang_seconds.empty()) { |
| 439 | int crash_seconds = atoi(crash_on_hang_seconds.c_str()); |
| 440 | if (crash_seconds > 0) { |
| 441 | *unresponsive_threshold = static_cast<uint32>( |
| 442 | ceil(static_cast<float>(crash_seconds) / kUnresponsiveSeconds)); |
| 443 | } |
| 444 | } |
| 445 | |
[email protected] | 6a084f0 | 2011-07-26 21:34:36 | [diff] [blame] | 446 | std::string crash_on_hang_threads; |
| 447 | |
| 448 | // Default to crashing the browser if UI or IO threads are not responsive |
| 449 | // except in stable channel. |
[email protected] | d8c65a8 | 2011-09-10 23:17:09 | [diff] [blame] | 450 | if (channel == chrome::VersionInfo::CHANNEL_STABLE) |
[email protected] | 6a084f0 | 2011-07-26 21:34:36 | [diff] [blame] | 451 | crash_on_hang_threads = ""; |
| 452 | else |
| 453 | crash_on_hang_threads = "UI,IO"; |
| 454 | |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 455 | if (command_line.HasSwitch(switches::kCrashOnHangThreads)) { |
| 456 | crash_on_hang_threads = |
| 457 | command_line.GetSwitchValueASCII(switches::kCrashOnHangThreads); |
| 458 | } |
| 459 | StringTokenizer tokens(crash_on_hang_threads, ","); |
| 460 | while (tokens.GetNext()) |
| 461 | crash_on_hang_thread_names->insert(tokens.token()); |
| 462 | |
| 463 | // Determine |live_threads_threshold| based on switches::kCrashOnLive. |
| 464 | *live_threads_threshold = kLiveThreadsThreshold; |
| 465 | if (command_line.HasSwitch(switches::kCrashOnLive)) { |
| 466 | std::string live_threads = |
| 467 | command_line.GetSwitchValueASCII(switches::kCrashOnLive); |
| 468 | *live_threads_threshold = static_cast<uint32>(atoi(live_threads.c_str())); |
| 469 | } |
| 470 | } |
| 471 | |
| 472 | // static |
| 473 | void ThreadWatcherList::InitializeAndStartWatching( |
| 474 | uint32 unresponsive_threshold, |
| 475 | const std::set<std::string>& crash_on_hang_thread_names, |
| 476 | uint32 live_threads_threshold) { |
| 477 | DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 478 | |
| 479 | ThreadWatcherList* thread_watcher_list = new ThreadWatcherList(); |
| 480 | CHECK(thread_watcher_list); |
| 481 | |
| 482 | const base::TimeDelta kSleepTime = |
| 483 | base::TimeDelta::FromSeconds(kSleepSeconds); |
| 484 | const base::TimeDelta kUnresponsiveTime = |
| 485 | base::TimeDelta::FromSeconds(kUnresponsiveSeconds); |
| 486 | |
| 487 | StartWatching(BrowserThread::UI, "UI", kSleepTime, kUnresponsiveTime, |
| 488 | unresponsive_threshold, crash_on_hang_thread_names, |
| 489 | live_threads_threshold); |
| 490 | StartWatching(BrowserThread::IO, "IO", kSleepTime, kUnresponsiveTime, |
| 491 | unresponsive_threshold, crash_on_hang_thread_names, |
| 492 | live_threads_threshold); |
| 493 | StartWatching(BrowserThread::DB, "DB", kSleepTime, kUnresponsiveTime, |
| 494 | unresponsive_threshold, crash_on_hang_thread_names, |
| 495 | live_threads_threshold); |
| 496 | StartWatching(BrowserThread::FILE, "FILE", kSleepTime, kUnresponsiveTime, |
| 497 | unresponsive_threshold, crash_on_hang_thread_names, |
| 498 | live_threads_threshold); |
| 499 | StartWatching(BrowserThread::CACHE, "CACHE", kSleepTime, kUnresponsiveTime, |
| 500 | unresponsive_threshold, crash_on_hang_thread_names, |
| 501 | live_threads_threshold); |
[email protected] | b6994152 | 2011-10-08 03:17:37 | [diff] [blame] | 502 | |
| 503 | BrowserThread::PostTask( |
| 504 | BrowserThread::UI, |
| 505 | FROM_HERE, |
| 506 | NewRunnableFunction(StartupTimeBomb::Disarm)); |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 507 | } |
| 508 | |
| 509 | // static |
| 510 | void ThreadWatcherList::StartWatching( |
| 511 | const BrowserThread::ID& thread_id, |
| 512 | const std::string& thread_name, |
| 513 | const base::TimeDelta& sleep_time, |
| 514 | const base::TimeDelta& unresponsive_time, |
| 515 | uint32 unresponsive_threshold, |
| 516 | const std::set<std::string>& crash_on_hang_thread_names, |
| 517 | uint32 live_threads_threshold) { |
| 518 | DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 519 | |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 520 | std::set<std::string>::const_iterator it = |
| 521 | crash_on_hang_thread_names.find(thread_name); |
| 522 | bool crash_on_hang = (it != crash_on_hang_thread_names.end()); |
| 523 | |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 524 | ThreadWatcher::StartWatching( |
| 525 | ThreadWatcher::WatchingParams(thread_id, |
| 526 | thread_name, |
| 527 | sleep_time, |
| 528 | unresponsive_time, |
| 529 | unresponsive_threshold, |
| 530 | crash_on_hang, |
| 531 | live_threads_threshold)); |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 532 | } |
| 533 | |
| 534 | // static |
| 535 | void ThreadWatcherList::DeleteAll() { |
| 536 | if (!WatchDogThread::CurrentlyOnWatchDogThread()) { |
| 537 | WatchDogThread::PostTask( |
| 538 | FROM_HERE, |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 539 | base::Bind(&ThreadWatcherList::DeleteAll)); |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 540 | return; |
| 541 | } |
| 542 | |
| 543 | DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 544 | if (!g_thread_watcher_list_) |
| 545 | return; |
| 546 | |
| 547 | // Delete all thread watcher objects. |
| 548 | while (!g_thread_watcher_list_->registered_.empty()) { |
| 549 | RegistrationList::iterator it = g_thread_watcher_list_->registered_.begin(); |
| 550 | delete it->second; |
| 551 | g_thread_watcher_list_->registered_.erase(it); |
| 552 | } |
| 553 | |
| 554 | delete g_thread_watcher_list_; |
| 555 | } |
| 556 | |
| 557 | // static |
| 558 | ThreadWatcher* ThreadWatcherList::Find(const BrowserThread::ID& thread_id) { |
| 559 | DCHECK(WatchDogThread::CurrentlyOnWatchDogThread()); |
| 560 | if (!g_thread_watcher_list_) |
| 561 | return NULL; |
| 562 | RegistrationList::iterator it = |
| 563 | g_thread_watcher_list_->registered_.find(thread_id); |
| 564 | if (g_thread_watcher_list_->registered_.end() == it) |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 565 | return NULL; |
| 566 | return it->second; |
| 567 | } |
| 568 | |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 569 | // ThreadWatcherObserver methods and members. |
| 570 | // |
| 571 | // static |
| 572 | ThreadWatcherObserver* ThreadWatcherObserver::g_thread_watcher_observer_ = NULL; |
| 573 | |
| 574 | ThreadWatcherObserver::ThreadWatcherObserver( |
| 575 | const base::TimeDelta& wakeup_interval) |
| 576 | : last_wakeup_time_(base::TimeTicks::Now()), |
| 577 | wakeup_interval_(wakeup_interval) { |
| 578 | CHECK(!g_thread_watcher_observer_); |
| 579 | g_thread_watcher_observer_ = this; |
| 580 | } |
| 581 | |
| 582 | ThreadWatcherObserver::~ThreadWatcherObserver() { |
| 583 | DCHECK(this == g_thread_watcher_observer_); |
| 584 | g_thread_watcher_observer_ = NULL; |
| 585 | } |
| 586 | |
| 587 | // static |
| 588 | void ThreadWatcherObserver::SetupNotifications( |
| 589 | const base::TimeDelta& wakeup_interval) { |
| 590 | DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 591 | ThreadWatcherObserver* observer = new ThreadWatcherObserver(wakeup_interval); |
| 592 | MetricsService::SetUpNotifications(&observer->registrar_, observer); |
| 593 | } |
| 594 | |
| 595 | // static |
| 596 | void ThreadWatcherObserver::RemoveNotifications() { |
| 597 | DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 598 | if (!g_thread_watcher_observer_) |
| 599 | return; |
| 600 | g_thread_watcher_observer_->registrar_.RemoveAll(); |
| 601 | delete g_thread_watcher_observer_; |
| 602 | } |
| 603 | |
[email protected] | 6c2381d | 2011-10-19 02:52:53 | [diff] [blame] | 604 | void ThreadWatcherObserver::Observe( |
| 605 | int type, |
| 606 | const content::NotificationSource& source, |
| 607 | const content::NotificationDetails& details) { |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 608 | // There is some user activity, see if thread watchers are to be awakened. |
| 609 | base::TimeTicks now = base::TimeTicks::Now(); |
| 610 | if ((now - last_wakeup_time_) < wakeup_interval_) |
| 611 | return; |
| 612 | last_wakeup_time_ = now; |
| 613 | WatchDogThread::PostTask( |
| 614 | FROM_HERE, |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 615 | base::Bind(&ThreadWatcherList::WakeUpAll)); |
[email protected] | f8614c3 | 2011-06-19 23:21:10 | [diff] [blame] | 616 | } |
| 617 | |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 618 | // WatchDogThread methods and members. |
[email protected] | f6179ec | 2011-03-17 00:25:46 | [diff] [blame] | 619 | // |
[email protected] | 0b56518 | 2011-03-02 18:11:15 | [diff] [blame] | 620 | // static |
| 621 | base::Lock WatchDogThread::lock_; |
| 622 | // static |
| 623 | WatchDogThread* WatchDogThread::watchdog_thread_ = NULL; |
| 624 | |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 625 | // The WatchDogThread object must outlive any tasks posted to the IO thread |
| 626 | // before the Quit task. |
| 627 | DISABLE_RUNNABLE_METHOD_REFCOUNT(WatchDogThread); |
| 628 | |
[email protected] | 9c68d42 | 2011-10-18 21:11:47 | [diff] [blame] | 629 | WatchDogThread::WatchDogThread() : Thread("BrowserWatchdog") { |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 630 | } |
| 631 | |
| 632 | WatchDogThread::~WatchDogThread() { |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 633 | // We cannot rely on our base class to stop the thread since we want our |
| 634 | // CleanUp function to run. |
| 635 | Stop(); |
| 636 | } |
| 637 | |
[email protected] | 0b56518 | 2011-03-02 18:11:15 | [diff] [blame] | 638 | // static |
| 639 | bool WatchDogThread::CurrentlyOnWatchDogThread() { |
| 640 | base::AutoLock lock(lock_); |
| 641 | return watchdog_thread_ && |
| 642 | watchdog_thread_->message_loop() == MessageLoop::current(); |
| 643 | } |
| 644 | |
| 645 | // static |
| 646 | bool WatchDogThread::PostTask(const tracked_objects::Location& from_here, |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 647 | const base::Closure& task) { |
[email protected] | 0b56518 | 2011-03-02 18:11:15 | [diff] [blame] | 648 | return PostTaskHelper(from_here, task, 0); |
| 649 | } |
| 650 | |
| 651 | // static |
| 652 | bool WatchDogThread::PostDelayedTask(const tracked_objects::Location& from_here, |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 653 | const base::Closure& task, |
[email protected] | 0b56518 | 2011-03-02 18:11:15 | [diff] [blame] | 654 | int64 delay_ms) { |
| 655 | return PostTaskHelper(from_here, task, delay_ms); |
| 656 | } |
| 657 | |
| 658 | // static |
| 659 | bool WatchDogThread::PostTaskHelper( |
| 660 | const tracked_objects::Location& from_here, |
[email protected] | 28e76d8 | 2011-09-30 23:14:18 | [diff] [blame] | 661 | const base::Closure& task, |
[email protected] | 0b56518 | 2011-03-02 18:11:15 | [diff] [blame] | 662 | int64 delay_ms) { |
| 663 | { |
| 664 | base::AutoLock lock(lock_); |
| 665 | |
| 666 | MessageLoop* message_loop = watchdog_thread_ ? |
| 667 | watchdog_thread_->message_loop() : NULL; |
| 668 | if (message_loop) { |
| 669 | message_loop->PostDelayedTask(from_here, task, delay_ms); |
| 670 | return true; |
| 671 | } |
| 672 | } |
[email protected] | 0b56518 | 2011-03-02 18:11:15 | [diff] [blame] | 673 | |
| 674 | return false; |
| 675 | } |
| 676 | |
[email protected] | 3617ea9 | 2011-02-23 07:27:02 | [diff] [blame] | 677 | void WatchDogThread::Init() { |
| 678 | // This thread shouldn't be allowed to perform any blocking disk I/O. |
| 679 | base::ThreadRestrictions::SetIOAllowed(false); |
| 680 | |
[email protected] | 0b56518 | 2011-03-02 18:11:15 | [diff] [blame] | 681 | base::AutoLock lock(lock_); |
| 682 | CHECK(!watchdog_thread_); |
| 683 | watchdog_thread_ = this; |
[email protected] | 5315ff7 | 2011-03-02 00:11:35 | [diff] [blame] | 684 | } |
[email protected] | ed59063 | 2011-03-02 00:17:37 | [diff] [blame] | 685 | |
[email protected] | 0b56518 | 2011-03-02 18:11:15 | [diff] [blame] | 686 | void WatchDogThread::CleanUp() { |
| 687 | base::AutoLock lock(lock_); |
| 688 | watchdog_thread_ = NULL; |
| 689 | } |
[email protected] | 6d823b4 | 2011-09-05 02:54:02 | [diff] [blame] | 690 | |
| 691 | namespace { |
| 692 | |
[email protected] | b6994152 | 2011-10-08 03:17:37 | [diff] [blame] | 693 | // StartupWatchDogThread methods and members. |
| 694 | // |
| 695 | // Class for detecting hangs during startup. |
| 696 | class StartupWatchDogThread : public base::Watchdog { |
| 697 | public: |
| 698 | // Constructor specifies how long the StartupWatchDogThread will wait before |
| 699 | // alarming. |
| 700 | explicit StartupWatchDogThread(const base::TimeDelta& duration) |
| 701 | : base::Watchdog(duration, "Startup watchdog thread", true) { |
| 702 | } |
| 703 | |
| 704 | // Alarm is called if the time expires after an Arm() without someone calling |
| 705 | // Disarm(). When Alarm goes off, in release mode we get the crash dump |
| 706 | // without crashing and in debug mode we break into the debugger. |
| 707 | virtual void Alarm() { |
| 708 | #ifndef NDEBUG |
| 709 | DCHECK(false); |
| 710 | #else |
| 711 | logging::DumpWithoutCrashing(); |
| 712 | #endif |
| 713 | } |
| 714 | |
| 715 | DISALLOW_COPY_AND_ASSIGN(StartupWatchDogThread); |
| 716 | }; |
| 717 | |
[email protected] | 6d823b4 | 2011-09-05 02:54:02 | [diff] [blame] | 718 | // ShutdownWatchDogThread methods and members. |
| 719 | // |
[email protected] | b6994152 | 2011-10-08 03:17:37 | [diff] [blame] | 720 | // Class for detecting hangs during shutdown. |
[email protected] | 6d823b4 | 2011-09-05 02:54:02 | [diff] [blame] | 721 | class ShutdownWatchDogThread : public base::Watchdog { |
| 722 | public: |
| 723 | // Constructor specifies how long the ShutdownWatchDogThread will wait before |
| 724 | // alarming. |
| 725 | explicit ShutdownWatchDogThread(const base::TimeDelta& duration) |
| 726 | : base::Watchdog(duration, "Shutdown watchdog thread", true) { |
| 727 | } |
| 728 | |
| 729 | // Alarm is called if the time expires after an Arm() without someone calling |
| 730 | // Disarm(). We crash the browser if this method is called. |
| 731 | virtual void Alarm() { |
| 732 | CHECK(false); |
| 733 | } |
| 734 | |
| 735 | DISALLOW_COPY_AND_ASSIGN(ShutdownWatchDogThread); |
| 736 | }; |
| 737 | } // namespace |
| 738 | |
[email protected] | b6994152 | 2011-10-08 03:17:37 | [diff] [blame] | 739 | // StartupTimeBomb methods and members. |
| 740 | // |
| 741 | // static |
| 742 | base::Watchdog* StartupTimeBomb::startup_watchdog_ = NULL; |
| 743 | |
| 744 | // static |
| 745 | void StartupTimeBomb::Arm(const base::TimeDelta& duration) { |
| 746 | DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 747 | DCHECK(!startup_watchdog_); |
| 748 | startup_watchdog_ = new StartupWatchDogThread(duration); |
| 749 | startup_watchdog_->Arm(); |
| 750 | } |
| 751 | |
| 752 | // static |
| 753 | void StartupTimeBomb::Disarm() { |
| 754 | DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 755 | if (startup_watchdog_) { |
| 756 | startup_watchdog_->Disarm(); |
| 757 | // Allow the watchdog thread to shutdown on UI. Watchdog thread shutdowns |
| 758 | // very fast. |
| 759 | base::ThreadRestrictions::SetIOAllowed(true); |
| 760 | delete startup_watchdog_; |
| 761 | startup_watchdog_ = NULL; |
| 762 | } |
| 763 | } |
| 764 | |
[email protected] | 6d823b4 | 2011-09-05 02:54:02 | [diff] [blame] | 765 | // ShutdownWatcherHelper methods and members. |
| 766 | // |
[email protected] | b6994152 | 2011-10-08 03:17:37 | [diff] [blame] | 767 | // ShutdownWatcherHelper is a wrapper class for detecting hangs during |
[email protected] | 6d823b4 | 2011-09-05 02:54:02 | [diff] [blame] | 768 | // shutdown. |
| 769 | ShutdownWatcherHelper::ShutdownWatcherHelper() : shutdown_watchdog_(NULL) { |
| 770 | } |
| 771 | |
| 772 | ShutdownWatcherHelper::~ShutdownWatcherHelper() { |
| 773 | if (shutdown_watchdog_) { |
| 774 | shutdown_watchdog_->Disarm(); |
| 775 | delete shutdown_watchdog_; |
| 776 | shutdown_watchdog_ = NULL; |
| 777 | } |
| 778 | } |
| 779 | |
| 780 | void ShutdownWatcherHelper::Arm(const base::TimeDelta& duration) { |
| 781 | DCHECK(!shutdown_watchdog_); |
[email protected] | 6e2c54e | 2011-09-06 20:35:39 | [diff] [blame] | 782 | base::TimeDelta actual_duration = duration; |
[email protected] | d8c65a8 | 2011-09-10 23:17:09 | [diff] [blame] | 783 | |
[email protected] | 6e2c54e | 2011-09-06 20:35:39 | [diff] [blame] | 784 | chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); |
| 785 | if (channel == chrome::VersionInfo::CHANNEL_STABLE) { |
| 786 | actual_duration *= 50; |
| 787 | } else if (channel == chrome::VersionInfo::CHANNEL_BETA || |
| 788 | channel == chrome::VersionInfo::CHANNEL_DEV) { |
| 789 | actual_duration *= 25; |
| 790 | } |
[email protected] | d8c65a8 | 2011-09-10 23:17:09 | [diff] [blame] | 791 | |
| 792 | #if defined(OS_WIN) |
| 793 | // On Windows XP, give twice the time for shutdown. |
| 794 | if (base::win::GetVersion() <= base::win::VERSION_XP) |
| 795 | actual_duration *= 2; |
| 796 | #endif |
| 797 | |
[email protected] | 6e2c54e | 2011-09-06 20:35:39 | [diff] [blame] | 798 | shutdown_watchdog_ = new ShutdownWatchDogThread(actual_duration); |
[email protected] | 6d823b4 | 2011-09-05 02:54:02 | [diff] [blame] | 799 | shutdown_watchdog_->Arm(); |
| 800 | } |