blob: bd5d498de9602c2d429bd25f4b753a491a4db3aa [file] [log] [blame]
[email protected]ac4c6682012-01-04 00:57:391// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]3617ea92011-02-23 07:27:022// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]8fd11832011-07-14 20:01:135#include "chrome/browser/metrics/thread_watcher.h"
6
[email protected]f8614c32011-06-19 23:21:107#include <math.h> // ceil
8
[email protected]28e76d82011-09-30 23:14:189#include "base/bind.h"
[email protected]e3aaeaf42011-11-08 07:48:3010#include "base/compiler_specific.h"
[email protected]8fd11832011-07-14 20:01:1311#include "base/debug/alias.h"
[email protected]02c3f6832011-11-16 18:37:4012#include "base/lazy_instance.h"
[email protected]b1c20f532013-02-12 05:29:3213#include "base/stringprintf.h"
[email protected]3ea1b182013-02-08 22:38:4114#include "base/strings/string_number_conversions.h"
[email protected]1988e1c2013-02-28 20:27:4215#include "base/strings/string_split.h"
[email protected]f4ebe772013-02-02 00:21:3916#include "base/strings/string_tokenizer.h"
[email protected]3617ea92011-02-23 07:27:0217#include "base/threading/thread_restrictions.h"
[email protected]a55edc42011-02-24 20:17:2818#include "build/build_config.h"
[email protected]3617ea92011-02-23 07:27:0219#include "chrome/browser/metrics/metrics_service.h"
[email protected]f8614c32011-06-19 23:21:1020#include "chrome/common/chrome_switches.h"
[email protected]6a084f02011-07-26 21:34:3621#include "chrome/common/chrome_version_info.h"
[email protected]fe69aa12013-02-01 03:37:2722#include "chrome/common/dump_without_crashing.h"
[email protected]b69941522011-10-08 03:17:3723#include "chrome/common/logging_chrome.h"
[email protected]3617ea92011-02-23 07:27:0224
[email protected]0b565182011-03-02 18:11:1525#if defined(OS_WIN)
[email protected]85339942011-08-29 21:03:4326#include "base/win/windows_version.h"
[email protected]0b565182011-03-02 18:11:1527#endif
28
[email protected]631bb742011-11-02 11:29:3929using content::BrowserThread;
30
[email protected]e3aaeaf42011-11-08 07:48:3031namespace {
32
33// The following are unique function names for forcing the crash when a thread
34// is unresponsive. This makes it possible to tell from the callstack alone what
35// thread was unresponsive.
36//
37// We disable optimizations for this block of functions so the compiler doesn't
38// merge them all together.
[email protected]baec9522012-07-26 02:26:5139MSVC_DISABLE_OPTIMIZE()
[email protected]e3aaeaf42011-11-08 07:48:3040MSVC_PUSH_DISABLE_WARNING(4748)
[email protected]e3aaeaf42011-11-08 07:48:3041
[email protected]da14c772012-07-12 19:33:5142int* NullPointer() {
43 return reinterpret_cast<int*>(NULL);
44}
45
[email protected]143ca852013-02-28 19:20:0146void NullPointerCrash(int line_number) {
47#ifndef NDEBUG
48 *NullPointer() = line_number; // Crash.
49#else
50 logging::DumpWithoutCrashing();
51#endif
52}
53
[email protected]720f6e92013-04-17 03:05:0654NOINLINE void ShutdownCrash() {
55 NullPointerCrash(__LINE__);
56}
57
[email protected]baec9522012-07-26 02:26:5158NOINLINE void ThreadUnresponsive_UI() {
[email protected]143ca852013-02-28 19:20:0159 NullPointerCrash(__LINE__);
[email protected]e3aaeaf42011-11-08 07:48:3060}
61
[email protected]baec9522012-07-26 02:26:5162NOINLINE void ThreadUnresponsive_DB() {
[email protected]143ca852013-02-28 19:20:0163 NullPointerCrash(__LINE__);
[email protected]e3aaeaf42011-11-08 07:48:3064}
65
[email protected]baec9522012-07-26 02:26:5166NOINLINE void ThreadUnresponsive_WEBKIT() {
[email protected]143ca852013-02-28 19:20:0167 NullPointerCrash(__LINE__);
[email protected]e3aaeaf42011-11-08 07:48:3068}
69
[email protected]baec9522012-07-26 02:26:5170NOINLINE void ThreadUnresponsive_FILE() {
[email protected]143ca852013-02-28 19:20:0171 NullPointerCrash(__LINE__);
[email protected]e3aaeaf42011-11-08 07:48:3072}
73
[email protected]baec9522012-07-26 02:26:5174NOINLINE void ThreadUnresponsive_FILE_USER_BLOCKING() {
[email protected]143ca852013-02-28 19:20:0175 NullPointerCrash(__LINE__);
[email protected]31dbf9d2011-12-07 01:25:3076}
77
[email protected]baec9522012-07-26 02:26:5178NOINLINE void ThreadUnresponsive_PROCESS_LAUNCHER() {
[email protected]143ca852013-02-28 19:20:0179 NullPointerCrash(__LINE__);
[email protected]e3aaeaf42011-11-08 07:48:3080}
81
[email protected]baec9522012-07-26 02:26:5182NOINLINE void ThreadUnresponsive_CACHE() {
[email protected]143ca852013-02-28 19:20:0183 NullPointerCrash(__LINE__);
[email protected]e3aaeaf42011-11-08 07:48:3084}
85
[email protected]baec9522012-07-26 02:26:5186NOINLINE void ThreadUnresponsive_IO() {
[email protected]143ca852013-02-28 19:20:0187 NullPointerCrash(__LINE__);
[email protected]e3aaeaf42011-11-08 07:48:3088}
89
[email protected]e3aaeaf42011-11-08 07:48:3090MSVC_POP_WARNING()
[email protected]baec9522012-07-26 02:26:5191MSVC_ENABLE_OPTIMIZE();
[email protected]e3aaeaf42011-11-08 07:48:3092
93void CrashBecauseThreadWasUnresponsive(BrowserThread::ID thread_id) {
94 base::debug::Alias(&thread_id);
95
96 switch (thread_id) {
97 case BrowserThread::UI:
98 return ThreadUnresponsive_UI();
99 case BrowserThread::DB:
100 return ThreadUnresponsive_DB();
[email protected]e1dd5622011-12-20 12:28:58101 case BrowserThread::WEBKIT_DEPRECATED:
[email protected]e3aaeaf42011-11-08 07:48:30102 return ThreadUnresponsive_WEBKIT();
103 case BrowserThread::FILE:
104 return ThreadUnresponsive_FILE();
[email protected]31dbf9d2011-12-07 01:25:30105 case BrowserThread::FILE_USER_BLOCKING:
106 return ThreadUnresponsive_FILE_USER_BLOCKING();
[email protected]e3aaeaf42011-11-08 07:48:30107 case BrowserThread::PROCESS_LAUNCHER:
108 return ThreadUnresponsive_PROCESS_LAUNCHER();
109 case BrowserThread::CACHE:
110 return ThreadUnresponsive_CACHE();
111 case BrowserThread::IO:
112 return ThreadUnresponsive_IO();
[email protected]e3aaeaf42011-11-08 07:48:30113 case BrowserThread::ID_COUNT:
114 CHECK(false); // This shouldn't actually be reached!
115 break;
116
117 // Omission of the default hander is intentional -- that way the compiler
118 // should warn if our switch becomes outdated.
119 }
120
[email protected]da14c772012-07-12 19:33:51121 CHECK(false) << "Unknown thread was unresponsive."; // Shouldn't be reached.
[email protected]e3aaeaf42011-11-08 07:48:30122}
123
124} // namespace
125
[email protected]3617ea92011-02-23 07:27:02126// ThreadWatcher methods and members.
[email protected]28e76d82011-09-30 23:14:18127ThreadWatcher::ThreadWatcher(const WatchingParams& params)
128 : thread_id_(params.thread_id),
129 thread_name_(params.thread_name),
130 watched_loop_(
131 BrowserThread::GetMessageLoopProxyForThread(params.thread_id)),
132 sleep_time_(params.sleep_time),
133 unresponsive_time_(params.unresponsive_time),
[email protected]3617ea92011-02-23 07:27:02134 ping_time_(base::TimeTicks::Now()),
[email protected]9a4386342011-04-23 22:41:26135 pong_time_(ping_time_),
[email protected]3617ea92011-02-23 07:27:02136 ping_sequence_number_(0),
137 active_(false),
[email protected]28e76d82011-09-30 23:14:18138 ping_count_(params.unresponsive_threshold),
[email protected]9a4386342011-04-23 22:41:26139 response_time_histogram_(NULL),
140 unresponsive_time_histogram_(NULL),
141 unresponsive_count_(0),
[email protected]8125a9a2011-05-17 15:06:21142 hung_processing_complete_(false),
[email protected]28e76d82011-09-30 23:14:18143 unresponsive_threshold_(params.unresponsive_threshold),
144 crash_on_hang_(params.crash_on_hang),
145 live_threads_threshold_(params.live_threads_threshold),
[email protected]9c009092013-05-01 03:14:09146 weak_ptr_factory_(this) {
[email protected]f8614c32011-06-19 23:21:10147 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
[email protected]3617ea92011-02-23 07:27:02148 Initialize();
149}
150
[email protected]1d28d692011-02-23 22:05:38151ThreadWatcher::~ThreadWatcher() {}
152
[email protected]3617ea92011-02-23 07:27:02153// static
[email protected]28e76d82011-09-30 23:14:18154void ThreadWatcher::StartWatching(const WatchingParams& params) {
155 DCHECK_GE(params.sleep_time.InMilliseconds(), 0);
156 DCHECK_GE(params.unresponsive_time.InMilliseconds(),
157 params.sleep_time.InMilliseconds());
[email protected]3617ea92011-02-23 07:27:02158
[email protected]0b565182011-03-02 18:11:15159 // If we are not on WatchDogThread, then post a task to call StartWatching on
160 // WatchDogThread.
161 if (!WatchDogThread::CurrentlyOnWatchDogThread()) {
162 WatchDogThread::PostTask(
[email protected]db5bdf32011-02-28 07:57:40163 FROM_HERE,
[email protected]28e76d82011-09-30 23:14:18164 base::Bind(&ThreadWatcher::StartWatching, params));
[email protected]3617ea92011-02-23 07:27:02165 return;
166 }
167
[email protected]0b565182011-03-02 18:11:15168 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
[email protected]3617ea92011-02-23 07:27:02169
170 // Create a new thread watcher object for the given thread and activate it.
[email protected]28e76d82011-09-30 23:14:18171 ThreadWatcher* watcher = new ThreadWatcher(params);
172
[email protected]3617ea92011-02-23 07:27:02173 DCHECK(watcher);
[email protected]dedfabae2011-03-04 04:00:40174 // If we couldn't register the thread watcher object, we are shutting down,
175 // then don't activate thread watching.
[email protected]28e76d82011-09-30 23:14:18176 if (!ThreadWatcherList::IsRegistered(params.thread_id))
[email protected]dedfabae2011-03-04 04:00:40177 return;
[email protected]3617ea92011-02-23 07:27:02178 watcher->ActivateThreadWatching();
179}
180
181void ThreadWatcher::ActivateThreadWatching() {
[email protected]0b565182011-03-02 18:11:15182 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
[email protected]3617ea92011-02-23 07:27:02183 if (active_) return;
184 active_ = true;
[email protected]26ecd8a2e2011-09-02 00:51:54185 ping_count_ = unresponsive_threshold_;
[email protected]3628ecaf2011-05-27 16:10:52186 ResetHangCounters();
[email protected]3617ea92011-02-23 07:27:02187 MessageLoop::current()->PostTask(
188 FROM_HERE,
[email protected]28e76d82011-09-30 23:14:18189 base::Bind(&ThreadWatcher::PostPingMessage,
190 weak_ptr_factory_.GetWeakPtr()));
[email protected]3617ea92011-02-23 07:27:02191}
192
193void ThreadWatcher::DeActivateThreadWatching() {
[email protected]0b565182011-03-02 18:11:15194 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
[email protected]3617ea92011-02-23 07:27:02195 active_ = false;
196 ping_count_ = 0;
[email protected]28e76d82011-09-30 23:14:18197 weak_ptr_factory_.InvalidateWeakPtrs();
[email protected]3617ea92011-02-23 07:27:02198}
199
200void ThreadWatcher::WakeUp() {
[email protected]0b565182011-03-02 18:11:15201 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
[email protected]3617ea92011-02-23 07:27:02202 // There is some user activity, PostPingMessage task of thread watcher if
203 // needed.
204 if (!active_) return;
205
[email protected]01a605f2011-09-06 00:14:42206 // Throw away the previous |unresponsive_count_| and start over again. Just
207 // before going to sleep, |unresponsive_count_| could be very close to
208 // |unresponsive_threshold_| and when user becomes active,
209 // |unresponsive_count_| can go over |unresponsive_threshold_| if there was no
210 // response for ping messages. Reset |unresponsive_count_| to start measuring
211 // the unresponsiveness of the threads when system becomes active.
212 unresponsive_count_ = 0;
213
[email protected]3617ea92011-02-23 07:27:02214 if (ping_count_ <= 0) {
[email protected]26ecd8a2e2011-09-02 00:51:54215 ping_count_ = unresponsive_threshold_;
[email protected]3628ecaf2011-05-27 16:10:52216 ResetHangCounters();
[email protected]3617ea92011-02-23 07:27:02217 PostPingMessage();
218 } else {
[email protected]26ecd8a2e2011-09-02 00:51:54219 ping_count_ = unresponsive_threshold_;
[email protected]3617ea92011-02-23 07:27:02220 }
221}
222
223void ThreadWatcher::PostPingMessage() {
[email protected]0b565182011-03-02 18:11:15224 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
[email protected]3617ea92011-02-23 07:27:02225 // If we have stopped watching or if the user is idle, then stop sending
226 // ping messages.
227 if (!active_ || ping_count_ <= 0)
228 return;
229
230 // Save the current time when we have sent ping message.
231 ping_time_ = base::TimeTicks::Now();
232
[email protected]28e76d82011-09-30 23:14:18233 // Send a ping message to the watched thread. Callback will be called on
234 // the WatchDogThread.
235 base::Closure callback(
236 base::Bind(&ThreadWatcher::OnPongMessage, weak_ptr_factory_.GetWeakPtr(),
237 ping_sequence_number_));
[email protected]8fd11832011-07-14 20:01:13238 if (watched_loop_->PostTask(
[email protected]89fb232c22011-02-24 01:45:10239 FROM_HERE,
[email protected]28e76d82011-09-30 23:14:18240 base::Bind(&ThreadWatcher::OnPingMessage, thread_id_,
241 callback))) {
[email protected]89fb232c22011-02-24 01:45:10242 // Post a task to check the responsiveness of watched thread.
243 MessageLoop::current()->PostDelayedTask(
244 FROM_HERE,
[email protected]28e76d82011-09-30 23:14:18245 base::Bind(&ThreadWatcher::OnCheckResponsiveness,
246 weak_ptr_factory_.GetWeakPtr(), ping_sequence_number_),
[email protected]fc4252a72012-01-12 21:58:47247 unresponsive_time_);
[email protected]89fb232c22011-02-24 01:45:10248 } else {
249 // Watched thread might have gone away, stop watching it.
[email protected]89fb232c22011-02-24 01:45:10250 DeActivateThreadWatching();
251 }
[email protected]3617ea92011-02-23 07:27:02252}
253
254void ThreadWatcher::OnPongMessage(uint64 ping_sequence_number) {
[email protected]0b565182011-03-02 18:11:15255 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
[email protected]9a4386342011-04-23 22:41:26256
[email protected]3617ea92011-02-23 07:27:02257 // Record watched thread's response time.
[email protected]9a4386342011-04-23 22:41:26258 base::TimeTicks now = base::TimeTicks::Now();
259 base::TimeDelta response_time = now - ping_time_;
260 response_time_histogram_->AddTime(response_time);
261
262 // Save the current time when we have got pong message.
263 pong_time_ = now;
[email protected]3617ea92011-02-23 07:27:02264
265 // Check if there are any extra pings in flight.
266 DCHECK_EQ(ping_sequence_number_, ping_sequence_number);
267 if (ping_sequence_number_ != ping_sequence_number)
268 return;
269
270 // Increment sequence number for the next ping message to indicate watched
271 // thread is responsive.
272 ++ping_sequence_number_;
273
274 // If we have stopped watching or if the user is idle, then stop sending
275 // ping messages.
276 if (!active_ || --ping_count_ <= 0)
277 return;
278
279 MessageLoop::current()->PostDelayedTask(
280 FROM_HERE,
[email protected]28e76d82011-09-30 23:14:18281 base::Bind(&ThreadWatcher::PostPingMessage,
282 weak_ptr_factory_.GetWeakPtr()),
[email protected]fc4252a72012-01-12 21:58:47283 sleep_time_);
[email protected]3617ea92011-02-23 07:27:02284}
285
[email protected]28e76d82011-09-30 23:14:18286void ThreadWatcher::OnCheckResponsiveness(uint64 ping_sequence_number) {
[email protected]0b565182011-03-02 18:11:15287 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
[email protected]3617ea92011-02-23 07:27:02288 // If we have stopped watching then consider thread as responding.
[email protected]28e76d82011-09-30 23:14:18289 if (!active_) {
290 responsive_ = true;
291 return;
292 }
[email protected]3617ea92011-02-23 07:27:02293 // If the latest ping_sequence_number_ is not same as the ping_sequence_number
294 // that is passed in, then we can assume OnPongMessage was called.
295 // OnPongMessage increments ping_sequence_number_.
[email protected]9a4386342011-04-23 22:41:26296 if (ping_sequence_number_ != ping_sequence_number) {
297 // Reset unresponsive_count_ to zero because we got a response from the
298 // watched thread.
[email protected]3628ecaf2011-05-27 16:10:52299 ResetHangCounters();
[email protected]28e76d82011-09-30 23:14:18300
301 responsive_ = true;
302 return;
[email protected]9a4386342011-04-23 22:41:26303 }
304 // Record that we got no response from watched thread.
305 GotNoResponse();
306
307 // Post a task to check the responsiveness of watched thread.
308 MessageLoop::current()->PostDelayedTask(
309 FROM_HERE,
[email protected]28e76d82011-09-30 23:14:18310 base::Bind(&ThreadWatcher::OnCheckResponsiveness,
311 weak_ptr_factory_.GetWeakPtr(), ping_sequence_number_),
[email protected]fc4252a72012-01-12 21:58:47312 unresponsive_time_);
[email protected]28e76d82011-09-30 23:14:18313 responsive_ = false;
[email protected]3617ea92011-02-23 07:27:02314}
315
316void ThreadWatcher::Initialize() {
[email protected]f8614c32011-06-19 23:21:10317 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
[email protected]3617ea92011-02-23 07:27:02318 ThreadWatcherList::Register(this);
[email protected]9a4386342011-04-23 22:41:26319
320 const std::string response_time_histogram_name =
[email protected]f6179ec2011-03-17 00:25:46321 "ThreadWatcher.ResponseTime." + thread_name_;
[email protected]9a4386342011-04-23 22:41:26322 response_time_histogram_ = base::Histogram::FactoryTimeGet(
323 response_time_histogram_name,
324 base::TimeDelta::FromMilliseconds(1),
325 base::TimeDelta::FromSeconds(100), 50,
326 base::Histogram::kUmaTargetedHistogramFlag);
327
328 const std::string unresponsive_time_histogram_name =
329 "ThreadWatcher.Unresponsive." + thread_name_;
330 unresponsive_time_histogram_ = base::Histogram::FactoryTimeGet(
331 unresponsive_time_histogram_name,
[email protected]3617ea92011-02-23 07:27:02332 base::TimeDelta::FromMilliseconds(1),
333 base::TimeDelta::FromSeconds(100), 50,
334 base::Histogram::kUmaTargetedHistogramFlag);
[email protected]42499b82011-04-28 22:47:39335
336 const std::string responsive_count_histogram_name =
337 "ThreadWatcher.ResponsiveThreads." + thread_name_;
338 responsive_count_histogram_ = base::LinearHistogram::FactoryGet(
339 responsive_count_histogram_name, 1, 10, 11,
340 base::Histogram::kUmaTargetedHistogramFlag);
341
342 const std::string unresponsive_count_histogram_name =
343 "ThreadWatcher.UnresponsiveThreads." + thread_name_;
344 unresponsive_count_histogram_ = base::LinearHistogram::FactoryGet(
345 unresponsive_count_histogram_name, 1, 10, 11,
346 base::Histogram::kUmaTargetedHistogramFlag);
[email protected]3617ea92011-02-23 07:27:02347}
348
349// static
[email protected]f6179ec2011-03-17 00:25:46350void ThreadWatcher::OnPingMessage(const BrowserThread::ID& thread_id,
[email protected]28e76d82011-09-30 23:14:18351 const base::Closure& callback_task) {
[email protected]3617ea92011-02-23 07:27:02352 // This method is called on watched thread.
353 DCHECK(BrowserThread::CurrentlyOn(thread_id));
[email protected]0b565182011-03-02 18:11:15354 WatchDogThread::PostTask(FROM_HERE, callback_task);
[email protected]3617ea92011-02-23 07:27:02355}
356
[email protected]3628ecaf2011-05-27 16:10:52357void ThreadWatcher::ResetHangCounters() {
[email protected]9a4386342011-04-23 22:41:26358 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
359 unresponsive_count_ = 0;
[email protected]8125a9a2011-05-17 15:06:21360 hung_processing_complete_ = false;
[email protected]9a4386342011-04-23 22:41:26361}
362
363void ThreadWatcher::GotNoResponse() {
364 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
[email protected]519b3ec72011-05-14 02:44:25365
[email protected]f8614c32011-06-19 23:21:10366 ++unresponsive_count_;
367 if (!IsVeryUnresponsive())
[email protected]519b3ec72011-05-14 02:44:25368 return;
369
[email protected]42499b82011-04-28 22:47:39370 // Record total unresponsive_time since last pong message.
371 base::TimeDelta unresponse_time = base::TimeTicks::Now() - pong_time_;
372 unresponsive_time_histogram_->AddTime(unresponse_time);
373
[email protected]8125a9a2011-05-17 15:06:21374 // We have already collected stats for the non-responding watched thread.
375 if (hung_processing_complete_)
376 return;
377
[email protected]f8614c32011-06-19 23:21:10378 // Record how other threads are responding.
379 uint32 responding_thread_count = 0;
380 uint32 unresponding_thread_count = 0;
381 ThreadWatcherList::GetStatusOfThreads(&responding_thread_count,
382 &unresponding_thread_count);
[email protected]42499b82011-04-28 22:47:39383
384 // Record how many watched threads are responding.
[email protected]f8614c32011-06-19 23:21:10385 responsive_count_histogram_->Add(responding_thread_count);
[email protected]42499b82011-04-28 22:47:39386
387 // Record how many watched threads are not responding.
[email protected]f8614c32011-06-19 23:21:10388 unresponsive_count_histogram_->Add(unresponding_thread_count);
[email protected]8125a9a2011-05-17 15:06:21389
[email protected]f8614c32011-06-19 23:21:10390 // Crash the browser if the watched thread is to be crashed on hang and if the
[email protected]9b4e64342012-06-11 22:06:20391 // number of other threads responding is less than or equal to
392 // live_threads_threshold_ and at least one other thread is responding.
393 if (crash_on_hang_ &&
394 responding_thread_count > 0 &&
395 responding_thread_count <= live_threads_threshold_) {
[email protected]d31058ba2011-10-20 23:14:49396 static bool crashed_once = false;
397 if (!crashed_once) {
398 crashed_once = true;
[email protected]e3aaeaf42011-11-08 07:48:30399 CrashBecauseThreadWasUnresponsive(thread_id_);
[email protected]d31058ba2011-10-20 23:14:49400 }
401 }
[email protected]8125a9a2011-05-17 15:06:21402
403 hung_processing_complete_ = true;
[email protected]9a4386342011-04-23 22:41:26404}
405
[email protected]f8614c32011-06-19 23:21:10406bool ThreadWatcher::IsVeryUnresponsive() {
407 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
408 return unresponsive_count_ >= unresponsive_threshold_;
409}
410
[email protected]3617ea92011-02-23 07:27:02411// ThreadWatcherList methods and members.
[email protected]f6179ec2011-03-17 00:25:46412//
[email protected]3617ea92011-02-23 07:27:02413// static
[email protected]f8614c32011-06-19 23:21:10414ThreadWatcherList* ThreadWatcherList::g_thread_watcher_list_ = NULL;
[email protected]9a4386342011-04-23 22:41:26415// static
[email protected]3628ecaf2011-05-27 16:10:52416const int ThreadWatcherList::kSleepSeconds = 1;
[email protected]9a4386342011-04-23 22:41:26417// static
[email protected]3628ecaf2011-05-27 16:10:52418const int ThreadWatcherList::kUnresponsiveSeconds = 2;
[email protected]f8614c32011-06-19 23:21:10419// static
[email protected]26ecd8a2e2011-09-02 00:51:54420const int ThreadWatcherList::kUnresponsiveCount = 9;
[email protected]f8614c32011-06-19 23:21:10421// static
[email protected]4631bf52013-04-03 20:07:29422const int ThreadWatcherList::kLiveThreadsThreshold = 2;
[email protected]3617ea92011-02-23 07:27:02423
[email protected]b1c20f532013-02-12 05:29:32424ThreadWatcherList::CrashDataThresholds::CrashDataThresholds(
425 uint32 live_threads_threshold,
426 uint32 unresponsive_threshold)
427 : live_threads_threshold(live_threads_threshold),
428 unresponsive_threshold(unresponsive_threshold) {
429}
430
431ThreadWatcherList::CrashDataThresholds::CrashDataThresholds()
432 : live_threads_threshold(kLiveThreadsThreshold),
433 unresponsive_threshold(kUnresponsiveCount) {
434}
435
[email protected]3617ea92011-02-23 07:27:02436// static
[email protected]f8614c32011-06-19 23:21:10437void ThreadWatcherList::StartWatchingAll(const CommandLine& command_line) {
[email protected]f8614c32011-06-19 23:21:10438 uint32 unresponsive_threshold;
[email protected]8d943372012-12-14 04:08:45439 CrashOnHangThreadMap crash_on_hang_threads;
[email protected]f8614c32011-06-19 23:21:10440 ParseCommandLine(command_line,
441 &unresponsive_threshold,
[email protected]8d943372012-12-14 04:08:45442 &crash_on_hang_threads);
[email protected]dedfabae2011-03-04 04:00:40443
[email protected]26ecd8a2e2011-09-02 00:51:54444 ThreadWatcherObserver::SetupNotifications(
445 base::TimeDelta::FromSeconds(kSleepSeconds * unresponsive_threshold));
446
[email protected]f8614c32011-06-19 23:21:10447 WatchDogThread::PostDelayedTask(
448 FROM_HERE,
[email protected]28e76d82011-09-30 23:14:18449 base::Bind(&ThreadWatcherList::InitializeAndStartWatching,
450 unresponsive_threshold,
[email protected]8d943372012-12-14 04:08:45451 crash_on_hang_threads),
[email protected]fc4252a72012-01-12 21:58:47452 base::TimeDelta::FromSeconds(120));
[email protected]0b565182011-03-02 18:11:15453}
454
455// static
[email protected]3617ea92011-02-23 07:27:02456void ThreadWatcherList::StopWatchingAll() {
[email protected]f8614c32011-06-19 23:21:10457 ThreadWatcherObserver::RemoveNotifications();
458 DeleteAll();
[email protected]3617ea92011-02-23 07:27:02459}
460
461// static
[email protected]f8614c32011-06-19 23:21:10462void ThreadWatcherList::Register(ThreadWatcher* watcher) {
[email protected]9a4386342011-04-23 22:41:26463 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
[email protected]f8614c32011-06-19 23:21:10464 if (!g_thread_watcher_list_)
465 return;
466 DCHECK(!g_thread_watcher_list_->Find(watcher->thread_id()));
467 g_thread_watcher_list_->registered_[watcher->thread_id()] = watcher;
468}
469
470// static
471bool ThreadWatcherList::IsRegistered(const BrowserThread::ID thread_id) {
472 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
473 return NULL != ThreadWatcherList::Find(thread_id);
474}
475
476// static
477void ThreadWatcherList::GetStatusOfThreads(uint32* responding_thread_count,
478 uint32* unresponding_thread_count) {
479 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
480 *responding_thread_count = 0;
481 *unresponding_thread_count = 0;
482 if (!g_thread_watcher_list_)
[email protected]42499b82011-04-28 22:47:39483 return;
[email protected]9a4386342011-04-23 22:41:26484
[email protected]f8614c32011-06-19 23:21:10485 for (RegistrationList::iterator it =
486 g_thread_watcher_list_->registered_.begin();
487 g_thread_watcher_list_->registered_.end() != it;
[email protected]9a4386342011-04-23 22:41:26488 ++it) {
[email protected]f8614c32011-06-19 23:21:10489 if (it->second->IsVeryUnresponsive())
490 ++(*unresponding_thread_count);
[email protected]ae53f112011-05-19 23:29:08491 else
[email protected]f8614c32011-06-19 23:21:10492 ++(*responding_thread_count);
[email protected]9a4386342011-04-23 22:41:26493 }
[email protected]9a4386342011-04-23 22:41:26494}
495
[email protected]f8614c32011-06-19 23:21:10496// static
[email protected]3617ea92011-02-23 07:27:02497void ThreadWatcherList::WakeUpAll() {
[email protected]0b565182011-03-02 18:11:15498 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
[email protected]f8614c32011-06-19 23:21:10499 if (!g_thread_watcher_list_)
[email protected]dedfabae2011-03-04 04:00:40500 return;
[email protected]f8614c32011-06-19 23:21:10501
502 for (RegistrationList::iterator it =
503 g_thread_watcher_list_->registered_.begin();
504 g_thread_watcher_list_->registered_.end() != it;
[email protected]3617ea92011-02-23 07:27:02505 ++it)
506 it->second->WakeUp();
507}
508
[email protected]f8614c32011-06-19 23:21:10509ThreadWatcherList::ThreadWatcherList() {
510 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
511 CHECK(!g_thread_watcher_list_);
512 g_thread_watcher_list_ = this;
[email protected]3617ea92011-02-23 07:27:02513}
514
[email protected]f8614c32011-06-19 23:21:10515ThreadWatcherList::~ThreadWatcherList() {
516 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
517 DCHECK(this == g_thread_watcher_list_);
518 g_thread_watcher_list_ = NULL;
519}
520
521// static
522void ThreadWatcherList::ParseCommandLine(
523 const CommandLine& command_line,
524 uint32* unresponsive_threshold,
[email protected]8d943372012-12-14 04:08:45525 CrashOnHangThreadMap* crash_on_hang_threads) {
[email protected]b1c20f532013-02-12 05:29:32526 // Initialize |unresponsive_threshold| to a default value.
[email protected]f8614c32011-06-19 23:21:10527 *unresponsive_threshold = kUnresponsiveCount;
[email protected]85339942011-08-29 21:03:43528
[email protected]d8c65a82011-09-10 23:17:09529 // Increase the unresponsive_threshold on the Stable and Beta channels to
530 // reduce the number of crashes due to ThreadWatcher.
531 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
532 if (channel == chrome::VersionInfo::CHANNEL_STABLE) {
533 *unresponsive_threshold *= 4;
534 } else if (channel == chrome::VersionInfo::CHANNEL_BETA) {
[email protected]85339942011-08-29 21:03:43535 *unresponsive_threshold *= 2;
[email protected]85339942011-08-29 21:03:43536 }
537
[email protected]d8c65a82011-09-10 23:17:09538#if defined(OS_WIN)
539 // For Windows XP (old systems), double the unresponsive_threshold to give
[email protected]ac76fc312011-11-07 23:43:32540 // the OS a chance to schedule UI/IO threads a time slice to respond with a
541 // pong message (to get around limitations with the OS).
[email protected]d8c65a82011-09-10 23:17:09542 if (base::win::GetVersion() <= base::win::VERSION_XP)
543 *unresponsive_threshold *= 2;
544#endif
545
[email protected]b1c20f532013-02-12 05:29:32546 uint32 crash_seconds = *unresponsive_threshold * kUnresponsiveSeconds;
[email protected]8d943372012-12-14 04:08:45547 std::string crash_on_hang_thread_names;
[email protected]8d943372012-12-14 04:08:45548 bool has_command_line_overwrite = false;
[email protected]f8614c32011-06-19 23:21:10549 if (command_line.HasSwitch(switches::kCrashOnHangThreads)) {
[email protected]8d943372012-12-14 04:08:45550 crash_on_hang_thread_names =
[email protected]f8614c32011-06-19 23:21:10551 command_line.GetSwitchValueASCII(switches::kCrashOnHangThreads);
[email protected]8d943372012-12-14 04:08:45552 has_command_line_overwrite = true;
[email protected]b1c20f532013-02-12 05:29:32553 } else if (channel != chrome::VersionInfo::CHANNEL_STABLE) {
554 // Default to crashing the browser if UI or IO or FILE threads are not
555 // responsive except in stable channel.
556 crash_on_hang_thread_names = base::StringPrintf(
557 "UI:%d:%d,IO:%d:%d,FILE:%d:%d",
558 kLiveThreadsThreshold, crash_seconds,
559 kLiveThreadsThreshold, crash_seconds,
560 kLiveThreadsThreshold, crash_seconds * 5);
[email protected]f8614c32011-06-19 23:21:10561 }
[email protected]b1c20f532013-02-12 05:29:32562
563 ParseCommandLineCrashOnHangThreads(crash_on_hang_thread_names,
564 kLiveThreadsThreshold,
565 crash_seconds,
566 crash_on_hang_threads);
[email protected]f8614c32011-06-19 23:21:10567
[email protected]8d943372012-12-14 04:08:45568 if (channel != chrome::VersionInfo::CHANNEL_CANARY ||
569 has_command_line_overwrite) {
570 return;
[email protected]f8614c32011-06-19 23:21:10571 }
[email protected]8d943372012-12-14 04:08:45572
[email protected]143ca852013-02-28 19:20:01573 // Set up a field trial for 100% of the users to crash if either UI or IO
574 // thread is not responsive for 30 seconds (or 15 pings).
[email protected]8d943372012-12-14 04:08:45575 scoped_refptr<base::FieldTrial> field_trial(
576 base::FieldTrialList::FactoryGetFieldTrial(
577 "ThreadWatcher", 100, "default_hung_threads",
578 2013, 10, 30, NULL));
[email protected]143ca852013-02-28 19:20:01579 int hung_thread_group = field_trial->AppendGroup("hung_thread", 100);
580 if (field_trial->group() == hung_thread_group) {
581 for (CrashOnHangThreadMap::iterator it = crash_on_hang_threads->begin();
582 crash_on_hang_threads->end() != it;
583 ++it) {
[email protected]6c5dd0f32013-03-02 18:16:45584 if (it->first != "IO")
[email protected]143ca852013-02-28 19:20:01585 continue;
[email protected]143ca852013-02-28 19:20:01586 it->second.live_threads_threshold = INT_MAX;
587 it->second.unresponsive_threshold = 15;
588 }
[email protected]b1c20f532013-02-12 05:29:32589 }
590}
591
592// static
593void ThreadWatcherList::ParseCommandLineCrashOnHangThreads(
594 const std::string& crash_on_hang_thread_names,
595 uint32 default_live_threads_threshold,
596 uint32 default_crash_seconds,
597 CrashOnHangThreadMap* crash_on_hang_threads) {
598 base::StringTokenizer tokens(crash_on_hang_thread_names, ",");
599 std::vector<std::string> values;
600 while (tokens.GetNext()) {
601 const std::string& token = tokens.token();
602 base::SplitString(token, ':', &values);
603 std::string thread_name = values[0];
604
605 uint32 live_threads_threshold = default_live_threads_threshold;
606 uint32 crash_seconds = default_crash_seconds;
607 if (values.size() >= 2 &&
608 (!base::StringToUint(values[1], &live_threads_threshold))) {
609 continue;
610 }
611 if (values.size() >= 3 &&
612 (!base::StringToUint(values[2], &crash_seconds))) {
613 continue;
614 }
615 uint32 unresponsive_threshold = static_cast<uint32>(
616 ceil(static_cast<float>(crash_seconds) / kUnresponsiveSeconds));
617
618 CrashDataThresholds crash_data(live_threads_threshold,
619 unresponsive_threshold);
620 // Use the last specifier.
621 (*crash_on_hang_threads)[thread_name] = crash_data;
622 }
[email protected]f8614c32011-06-19 23:21:10623}
624
625// static
626void ThreadWatcherList::InitializeAndStartWatching(
627 uint32 unresponsive_threshold,
[email protected]8d943372012-12-14 04:08:45628 const CrashOnHangThreadMap& crash_on_hang_threads) {
[email protected]f8614c32011-06-19 23:21:10629 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
630
631 ThreadWatcherList* thread_watcher_list = new ThreadWatcherList();
632 CHECK(thread_watcher_list);
633
[email protected]75715c312012-01-19 01:46:51634 BrowserThread::PostTask(
635 BrowserThread::UI,
636 FROM_HERE,
637 base::Bind(&StartupTimeBomb::DisarmStartupTimeBomb));
638
[email protected]f8614c32011-06-19 23:21:10639 const base::TimeDelta kSleepTime =
640 base::TimeDelta::FromSeconds(kSleepSeconds);
641 const base::TimeDelta kUnresponsiveTime =
642 base::TimeDelta::FromSeconds(kUnresponsiveSeconds);
643
644 StartWatching(BrowserThread::UI, "UI", kSleepTime, kUnresponsiveTime,
[email protected]8d943372012-12-14 04:08:45645 unresponsive_threshold, crash_on_hang_threads);
[email protected]f8614c32011-06-19 23:21:10646 StartWatching(BrowserThread::IO, "IO", kSleepTime, kUnresponsiveTime,
[email protected]8d943372012-12-14 04:08:45647 unresponsive_threshold, crash_on_hang_threads);
[email protected]f8614c32011-06-19 23:21:10648 StartWatching(BrowserThread::DB, "DB", kSleepTime, kUnresponsiveTime,
[email protected]8d943372012-12-14 04:08:45649 unresponsive_threshold, crash_on_hang_threads);
[email protected]f8614c32011-06-19 23:21:10650 StartWatching(BrowserThread::FILE, "FILE", kSleepTime, kUnresponsiveTime,
[email protected]8d943372012-12-14 04:08:45651 unresponsive_threshold, crash_on_hang_threads);
[email protected]f8614c32011-06-19 23:21:10652 StartWatching(BrowserThread::CACHE, "CACHE", kSleepTime, kUnresponsiveTime,
[email protected]8d943372012-12-14 04:08:45653 unresponsive_threshold, crash_on_hang_threads);
[email protected]f8614c32011-06-19 23:21:10654}
655
656// static
657void ThreadWatcherList::StartWatching(
658 const BrowserThread::ID& thread_id,
659 const std::string& thread_name,
660 const base::TimeDelta& sleep_time,
661 const base::TimeDelta& unresponsive_time,
662 uint32 unresponsive_threshold,
[email protected]8d943372012-12-14 04:08:45663 const CrashOnHangThreadMap& crash_on_hang_threads) {
[email protected]f8614c32011-06-19 23:21:10664 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
665
[email protected]8d943372012-12-14 04:08:45666 CrashOnHangThreadMap::const_iterator it =
667 crash_on_hang_threads.find(thread_name);
668 bool crash_on_hang = false;
669 uint32 live_threads_threshold = 0;
670 if (it != crash_on_hang_threads.end()) {
671 crash_on_hang = true;
[email protected]b1c20f532013-02-12 05:29:32672 live_threads_threshold = it->second.live_threads_threshold;
673 unresponsive_threshold = it->second.unresponsive_threshold;
[email protected]8d943372012-12-14 04:08:45674 }
[email protected]f8614c32011-06-19 23:21:10675
[email protected]28e76d82011-09-30 23:14:18676 ThreadWatcher::StartWatching(
677 ThreadWatcher::WatchingParams(thread_id,
678 thread_name,
679 sleep_time,
680 unresponsive_time,
681 unresponsive_threshold,
682 crash_on_hang,
683 live_threads_threshold));
[email protected]f8614c32011-06-19 23:21:10684}
685
686// static
687void ThreadWatcherList::DeleteAll() {
688 if (!WatchDogThread::CurrentlyOnWatchDogThread()) {
689 WatchDogThread::PostTask(
690 FROM_HERE,
[email protected]28e76d82011-09-30 23:14:18691 base::Bind(&ThreadWatcherList::DeleteAll));
[email protected]f8614c32011-06-19 23:21:10692 return;
693 }
694
695 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
696 if (!g_thread_watcher_list_)
697 return;
698
699 // Delete all thread watcher objects.
700 while (!g_thread_watcher_list_->registered_.empty()) {
701 RegistrationList::iterator it = g_thread_watcher_list_->registered_.begin();
702 delete it->second;
703 g_thread_watcher_list_->registered_.erase(it);
704 }
705
706 delete g_thread_watcher_list_;
707}
708
709// static
710ThreadWatcher* ThreadWatcherList::Find(const BrowserThread::ID& thread_id) {
711 DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
712 if (!g_thread_watcher_list_)
713 return NULL;
714 RegistrationList::iterator it =
715 g_thread_watcher_list_->registered_.find(thread_id);
716 if (g_thread_watcher_list_->registered_.end() == it)
[email protected]3617ea92011-02-23 07:27:02717 return NULL;
718 return it->second;
719}
720
[email protected]f8614c32011-06-19 23:21:10721// ThreadWatcherObserver methods and members.
722//
723// static
724ThreadWatcherObserver* ThreadWatcherObserver::g_thread_watcher_observer_ = NULL;
725
726ThreadWatcherObserver::ThreadWatcherObserver(
727 const base::TimeDelta& wakeup_interval)
728 : last_wakeup_time_(base::TimeTicks::Now()),
729 wakeup_interval_(wakeup_interval) {
730 CHECK(!g_thread_watcher_observer_);
731 g_thread_watcher_observer_ = this;
732}
733
734ThreadWatcherObserver::~ThreadWatcherObserver() {
735 DCHECK(this == g_thread_watcher_observer_);
736 g_thread_watcher_observer_ = NULL;
737}
738
739// static
740void ThreadWatcherObserver::SetupNotifications(
741 const base::TimeDelta& wakeup_interval) {
742 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
743 ThreadWatcherObserver* observer = new ThreadWatcherObserver(wakeup_interval);
744 MetricsService::SetUpNotifications(&observer->registrar_, observer);
745}
746
747// static
748void ThreadWatcherObserver::RemoveNotifications() {
749 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
750 if (!g_thread_watcher_observer_)
751 return;
752 g_thread_watcher_observer_->registrar_.RemoveAll();
753 delete g_thread_watcher_observer_;
754}
755
[email protected]6c2381d2011-10-19 02:52:53756void ThreadWatcherObserver::Observe(
757 int type,
758 const content::NotificationSource& source,
759 const content::NotificationDetails& details) {
[email protected]f8614c32011-06-19 23:21:10760 // There is some user activity, see if thread watchers are to be awakened.
761 base::TimeTicks now = base::TimeTicks::Now();
762 if ((now - last_wakeup_time_) < wakeup_interval_)
763 return;
764 last_wakeup_time_ = now;
765 WatchDogThread::PostTask(
766 FROM_HERE,
[email protected]28e76d82011-09-30 23:14:18767 base::Bind(&ThreadWatcherList::WakeUpAll));
[email protected]f8614c32011-06-19 23:21:10768}
769
[email protected]3617ea92011-02-23 07:27:02770// WatchDogThread methods and members.
[email protected]02c3f6832011-11-16 18:37:40771
772// This lock protects g_watchdog_thread.
[email protected]67f92bc32012-01-26 01:56:19773static base::LazyInstance<base::Lock>::Leaky
[email protected]02c3f6832011-11-16 18:37:40774 g_watchdog_lock = LAZY_INSTANCE_INITIALIZER;
775
776// The singleton of this class.
777static WatchDogThread* g_watchdog_thread = NULL;
778
[email protected]9c68d422011-10-18 21:11:47779WatchDogThread::WatchDogThread() : Thread("BrowserWatchdog") {
[email protected]3617ea92011-02-23 07:27:02780}
781
782WatchDogThread::~WatchDogThread() {
[email protected]3617ea92011-02-23 07:27:02783 Stop();
784}
785
[email protected]0b565182011-03-02 18:11:15786// static
787bool WatchDogThread::CurrentlyOnWatchDogThread() {
[email protected]02c3f6832011-11-16 18:37:40788 base::AutoLock lock(g_watchdog_lock.Get());
789 return g_watchdog_thread &&
790 g_watchdog_thread->message_loop() == MessageLoop::current();
[email protected]0b565182011-03-02 18:11:15791}
792
793// static
794bool WatchDogThread::PostTask(const tracked_objects::Location& from_here,
[email protected]28e76d82011-09-30 23:14:18795 const base::Closure& task) {
[email protected]fc4252a72012-01-12 21:58:47796 return PostTaskHelper(from_here, task, base::TimeDelta());
[email protected]0b565182011-03-02 18:11:15797}
798
799// static
800bool WatchDogThread::PostDelayedTask(const tracked_objects::Location& from_here,
[email protected]28e76d82011-09-30 23:14:18801 const base::Closure& task,
[email protected]fc4252a72012-01-12 21:58:47802 base::TimeDelta delay) {
803 return PostTaskHelper(from_here, task, delay);
[email protected]0b565182011-03-02 18:11:15804}
805
806// static
807bool WatchDogThread::PostTaskHelper(
808 const tracked_objects::Location& from_here,
[email protected]28e76d82011-09-30 23:14:18809 const base::Closure& task,
[email protected]fc4252a72012-01-12 21:58:47810 base::TimeDelta delay) {
[email protected]0b565182011-03-02 18:11:15811 {
[email protected]02c3f6832011-11-16 18:37:40812 base::AutoLock lock(g_watchdog_lock.Get());
[email protected]0b565182011-03-02 18:11:15813
[email protected]02c3f6832011-11-16 18:37:40814 MessageLoop* message_loop = g_watchdog_thread ?
815 g_watchdog_thread->message_loop() : NULL;
[email protected]0b565182011-03-02 18:11:15816 if (message_loop) {
[email protected]fc4252a72012-01-12 21:58:47817 message_loop->PostDelayedTask(from_here, task, delay);
[email protected]0b565182011-03-02 18:11:15818 return true;
819 }
820 }
[email protected]0b565182011-03-02 18:11:15821
822 return false;
823}
824
[email protected]3617ea92011-02-23 07:27:02825void WatchDogThread::Init() {
826 // This thread shouldn't be allowed to perform any blocking disk I/O.
827 base::ThreadRestrictions::SetIOAllowed(false);
828
[email protected]02c3f6832011-11-16 18:37:40829 base::AutoLock lock(g_watchdog_lock.Get());
830 CHECK(!g_watchdog_thread);
831 g_watchdog_thread = this;
[email protected]5315ff72011-03-02 00:11:35832}
[email protected]ed590632011-03-02 00:17:37833
[email protected]0b565182011-03-02 18:11:15834void WatchDogThread::CleanUp() {
[email protected]02c3f6832011-11-16 18:37:40835 base::AutoLock lock(g_watchdog_lock.Get());
836 g_watchdog_thread = NULL;
[email protected]0b565182011-03-02 18:11:15837}
[email protected]6d823b42011-09-05 02:54:02838
839namespace {
840
[email protected]b69941522011-10-08 03:17:37841// StartupWatchDogThread methods and members.
842//
843// Class for detecting hangs during startup.
844class StartupWatchDogThread : public base::Watchdog {
845 public:
846 // Constructor specifies how long the StartupWatchDogThread will wait before
847 // alarming.
848 explicit StartupWatchDogThread(const base::TimeDelta& duration)
849 : base::Watchdog(duration, "Startup watchdog thread", true) {
850 }
851
852 // Alarm is called if the time expires after an Arm() without someone calling
853 // Disarm(). When Alarm goes off, in release mode we get the crash dump
854 // without crashing and in debug mode we break into the debugger.
[email protected]b94584a2013-02-07 03:02:08855 virtual void Alarm() OVERRIDE {
[email protected]b69941522011-10-08 03:17:37856#ifndef NDEBUG
857 DCHECK(false);
858#else
859 logging::DumpWithoutCrashing();
860#endif
861 }
862
863 DISALLOW_COPY_AND_ASSIGN(StartupWatchDogThread);
864};
865
[email protected]6d823b42011-09-05 02:54:02866// ShutdownWatchDogThread methods and members.
867//
[email protected]b69941522011-10-08 03:17:37868// Class for detecting hangs during shutdown.
[email protected]6d823b42011-09-05 02:54:02869class ShutdownWatchDogThread : public base::Watchdog {
870 public:
871 // Constructor specifies how long the ShutdownWatchDogThread will wait before
872 // alarming.
873 explicit ShutdownWatchDogThread(const base::TimeDelta& duration)
874 : base::Watchdog(duration, "Shutdown watchdog thread", true) {
875 }
876
877 // Alarm is called if the time expires after an Arm() without someone calling
878 // Disarm(). We crash the browser if this method is called.
[email protected]b94584a2013-02-07 03:02:08879 virtual void Alarm() OVERRIDE {
[email protected]720f6e92013-04-17 03:05:06880 ShutdownCrash();
[email protected]6d823b42011-09-05 02:54:02881 }
882
883 DISALLOW_COPY_AND_ASSIGN(ShutdownWatchDogThread);
884};
885} // namespace
886
[email protected]b69941522011-10-08 03:17:37887// StartupTimeBomb methods and members.
888//
889// static
[email protected]08f1c5e2011-12-01 01:54:34890StartupTimeBomb* StartupTimeBomb::g_startup_timebomb_ = NULL;
[email protected]b69941522011-10-08 03:17:37891
[email protected]08f1c5e2011-12-01 01:54:34892StartupTimeBomb::StartupTimeBomb()
893 : startup_watchdog_(NULL),
894 thread_id_(base::PlatformThread::CurrentId()) {
895 CHECK(!g_startup_timebomb_);
896 g_startup_timebomb_ = this;
897}
898
899StartupTimeBomb::~StartupTimeBomb() {
900 DCHECK(this == g_startup_timebomb_);
901 DCHECK_EQ(thread_id_, base::PlatformThread::CurrentId());
902 if (startup_watchdog_)
903 Disarm();
904 g_startup_timebomb_ = NULL;
905}
906
[email protected]b69941522011-10-08 03:17:37907void StartupTimeBomb::Arm(const base::TimeDelta& duration) {
[email protected]08f1c5e2011-12-01 01:54:34908 DCHECK_EQ(thread_id_, base::PlatformThread::CurrentId());
[email protected]b69941522011-10-08 03:17:37909 DCHECK(!startup_watchdog_);
[email protected]a2841be2012-02-07 23:44:38910 // TODO(rtenneti): http://crbug.com/112970. Don't arm the startup timebomb
911 // until we fix breakpad code not to crash in logging::DumpWithoutCrashing().
912 // startup_watchdog_ = new StartupWatchDogThread(duration);
913 // startup_watchdog_->Arm();
914 return;
[email protected]b69941522011-10-08 03:17:37915}
916
[email protected]b69941522011-10-08 03:17:37917void StartupTimeBomb::Disarm() {
[email protected]08f1c5e2011-12-01 01:54:34918 DCHECK_EQ(thread_id_, base::PlatformThread::CurrentId());
[email protected]b69941522011-10-08 03:17:37919 if (startup_watchdog_) {
920 startup_watchdog_->Disarm();
[email protected]75715c312012-01-19 01:46:51921 startup_watchdog_->Cleanup();
922 DeleteStartupWatchdog();
923 }
924}
925
926void StartupTimeBomb::DeleteStartupWatchdog() {
927 DCHECK_EQ(thread_id_, base::PlatformThread::CurrentId());
928 if (startup_watchdog_->IsJoinable()) {
[email protected]b69941522011-10-08 03:17:37929 // Allow the watchdog thread to shutdown on UI. Watchdog thread shutdowns
930 // very fast.
931 base::ThreadRestrictions::SetIOAllowed(true);
932 delete startup_watchdog_;
933 startup_watchdog_ = NULL;
[email protected]75715c312012-01-19 01:46:51934 return;
[email protected]b69941522011-10-08 03:17:37935 }
[email protected]75715c312012-01-19 01:46:51936 MessageLoop::current()->PostDelayedTask(
937 FROM_HERE,
938 base::Bind(&StartupTimeBomb::DeleteStartupWatchdog,
939 base::Unretained(this)),
[email protected]09ced2b2012-01-30 22:08:08940 base::TimeDelta::FromSeconds(10));
[email protected]b69941522011-10-08 03:17:37941}
942
[email protected]08f1c5e2011-12-01 01:54:34943// static
944void StartupTimeBomb::DisarmStartupTimeBomb() {
945 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
946 if (g_startup_timebomb_)
947 g_startup_timebomb_->Disarm();
948}
949
[email protected]6d823b42011-09-05 02:54:02950// ShutdownWatcherHelper methods and members.
951//
[email protected]b69941522011-10-08 03:17:37952// ShutdownWatcherHelper is a wrapper class for detecting hangs during
[email protected]6d823b42011-09-05 02:54:02953// shutdown.
[email protected]08f1c5e2011-12-01 01:54:34954ShutdownWatcherHelper::ShutdownWatcherHelper()
955 : shutdown_watchdog_(NULL),
956 thread_id_(base::PlatformThread::CurrentId()) {
[email protected]6d823b42011-09-05 02:54:02957}
958
959ShutdownWatcherHelper::~ShutdownWatcherHelper() {
[email protected]08f1c5e2011-12-01 01:54:34960 DCHECK_EQ(thread_id_, base::PlatformThread::CurrentId());
[email protected]6d823b42011-09-05 02:54:02961 if (shutdown_watchdog_) {
962 shutdown_watchdog_->Disarm();
963 delete shutdown_watchdog_;
964 shutdown_watchdog_ = NULL;
965 }
966}
967
968void ShutdownWatcherHelper::Arm(const base::TimeDelta& duration) {
[email protected]08f1c5e2011-12-01 01:54:34969 DCHECK_EQ(thread_id_, base::PlatformThread::CurrentId());
[email protected]6d823b42011-09-05 02:54:02970 DCHECK(!shutdown_watchdog_);
[email protected]6e2c54e2011-09-06 20:35:39971 base::TimeDelta actual_duration = duration;
[email protected]d8c65a82011-09-10 23:17:09972
[email protected]6e2c54e2011-09-06 20:35:39973 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
974 if (channel == chrome::VersionInfo::CHANNEL_STABLE) {
[email protected]56bdb712012-01-27 00:35:59975 actual_duration *= 20;
[email protected]6e2c54e2011-09-06 20:35:39976 } else if (channel == chrome::VersionInfo::CHANNEL_BETA ||
977 channel == chrome::VersionInfo::CHANNEL_DEV) {
[email protected]56bdb712012-01-27 00:35:59978 actual_duration *= 10;
[email protected]6e2c54e2011-09-06 20:35:39979 }
[email protected]d8c65a82011-09-10 23:17:09980
981#if defined(OS_WIN)
982 // On Windows XP, give twice the time for shutdown.
983 if (base::win::GetVersion() <= base::win::VERSION_XP)
984 actual_duration *= 2;
985#endif
986
[email protected]6e2c54e2011-09-06 20:35:39987 shutdown_watchdog_ = new ShutdownWatchDogThread(actual_duration);
[email protected]6d823b42011-09-05 02:54:02988 shutdown_watchdog_->Arm();
989}