blob: e3620980a48fb76d69daf999335a017e2f09bf41 [file] [log] [blame]
[email protected]4e0f45f52012-05-18 18:00:221// Copyright (c) 2012 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit09911bf2008-07-26 23:55:294
initial.commit09911bf2008-07-26 23:55:295#include "chrome/browser/jankometer.h"
6
[email protected]efa31342012-06-11 23:36:547#include <limits>
8
initial.commit09911bf2008-07-26 23:55:299#include "base/basictypes.h"
[email protected]317c58f02011-11-09 02:15:0310#include "base/bind.h"
initial.commit09911bf2008-07-26 23:55:2911#include "base/command_line.h"
[email protected]3b63f8f42011-03-28 01:54:1512#include "base/memory/ref_counted.h"
initial.commit09911bf2008-07-26 23:55:2913#include "base/message_loop.h"
[email protected]835d7c82010-10-14 04:38:3814#include "base/metrics/histogram.h"
15#include "base/metrics/stats_counters.h"
[email protected]91bb8a42008-11-22 00:51:3216#include "base/string_util.h"
[email protected]34b99632011-01-01 01:01:0617#include "base/threading/thread.h"
[email protected]93270d002011-01-19 22:32:5918#include "base/threading/watchdog.h"
initial.commit09911bf2008-07-26 23:55:2919#include "base/time.h"
[email protected]16e0efd2009-07-08 03:58:2920#include "build/build_config.h"
initial.commit09911bf2008-07-26 23:55:2921#include "chrome/browser/browser_process.h"
22#include "chrome/common/chrome_switches.h"
[email protected]c38831a12011-10-28 12:44:4923#include "content/public/browser/browser_thread.h"
initial.commit09911bf2008-07-26 23:55:2924
[email protected]e1acf6f2008-10-27 20:43:3325using base::TimeDelta;
26using base::TimeTicks;
[email protected]631bb742011-11-02 11:29:3927using content::BrowserThread;
[email protected]e1acf6f2008-10-27 20:43:3328
initial.commit09911bf2008-07-26 23:55:2929namespace {
30
31// The maximum threshold of delay of the message before considering it a delay.
32// For a debug build, you may want to set IO delay around 500ms.
33// For a release build, setting it around 350ms is sensible.
34// Visit about:histograms to see what the distribution is on your system, with
35// your build. Be sure to do some work to get interesting stats.
36// The numbers below came from a warm start (you'll get about 5-10 alarms with
37// a cold start), and running the page-cycler for 5 rounds.
38#ifdef NDEBUG
39const int kMaxUIMessageDelayMs = 350;
40const int kMaxIOMessageDelayMs = 200;
41#else
42const int kMaxUIMessageDelayMs = 500;
43const int kMaxIOMessageDelayMs = 400;
44#endif
45
46// Maximum processing time (excluding queueing delay) for a message before
47// considering it delayed.
48const int kMaxMessageProcessingMs = 100;
49
50// TODO(brettw) Consider making this a pref.
51const bool kPlaySounds = false;
52
53//------------------------------------------------------------------------------
54// Provide a special watchdog to make it easy to set the breakpoint on this
55// class only.
[email protected]ac9ba8fe2010-12-30 18:08:3656class JankWatchdog : public base::Watchdog {
initial.commit09911bf2008-07-26 23:55:2957 public:
58 JankWatchdog(const TimeDelta& duration,
[email protected]7a1c5ab2008-11-22 00:37:2159 const std::string& thread_watched_name,
initial.commit09911bf2008-07-26 23:55:2960 bool enabled)
[email protected]95ab1552008-11-25 22:46:5961 : Watchdog(duration, thread_watched_name, enabled),
initial.commit09911bf2008-07-26 23:55:2962 thread_name_watched_(thread_watched_name),
63 alarm_count_(0) {
64 }
65
66 virtual ~JankWatchdog() {}
67
68 virtual void Alarm() {
69 // Put break point here if you want to stop threads and look at what caused
70 // the jankiness.
71 alarm_count_++;
72 Watchdog::Alarm();
73 }
74
75 private:
[email protected]7a1c5ab2008-11-22 00:37:2176 std::string thread_name_watched_;
initial.commit09911bf2008-07-26 23:55:2977 int alarm_count_;
78
[email protected]95ab1552008-11-25 22:46:5979 DISALLOW_COPY_AND_ASSIGN(JankWatchdog);
initial.commit09911bf2008-07-26 23:55:2980};
81
[email protected]9cfb89a2010-06-09 21:20:4182class JankObserverHelper {
initial.commit09911bf2008-07-26 23:55:2983 public:
[email protected]9cfb89a2010-06-09 21:20:4184 JankObserverHelper(const std::string& thread_name,
85 const TimeDelta& excessive_duration,
86 bool watchdog_enable);
87 ~JankObserverHelper();
initial.commit09911bf2008-07-26 23:55:2988
[email protected]9cfb89a2010-06-09 21:20:4189 void StartProcessingTimers(const TimeDelta& queueing_time);
90 void EndProcessingTimers();
initial.commit09911bf2008-07-26 23:55:2991
[email protected]ea812b82010-11-03 23:36:3492 // Indicate if we will bother to measuer this message.
93 bool MessageWillBeMeasured();
94
95 static void SetDefaultMessagesToSkip(int count) { discard_count_ = count; }
96
initial.commit09911bf2008-07-26 23:55:2997 private:
[email protected]9cfb89a2010-06-09 21:20:4198 const TimeDelta max_message_delay_;
[email protected]16e0efd2009-07-08 03:58:2999
[email protected]ea812b82010-11-03 23:36:34100 // Indicate if we'll bother measuring this message.
101 bool measure_current_message_;
102
103 // Down counter which will periodically hit 0, and only then bother to measure
104 // the corresponding message.
105 int events_till_measurement_;
106
107 // The value to re-initialize events_till_measurement_ after it reaches 0.
108 static int discard_count_;
109
[email protected]16e0efd2009-07-08 03:58:29110 // Time at which the current message processing began.
initial.commit09911bf2008-07-26 23:55:29111 TimeTicks begin_process_message_;
[email protected]16e0efd2009-07-08 03:58:29112
113 // Time the current message spent in the queue -- delta between message
114 // construction time and message processing time.
initial.commit09911bf2008-07-26 23:55:29115 TimeDelta queueing_time_;
116
117 // Counters for the two types of jank we measure.
[email protected]835d7c82010-10-14 04:38:38118 base::StatsCounter slow_processing_counter_; // Msgs w/ long proc time.
119 base::StatsCounter queueing_delay_counter_; // Msgs w/ long queueing delay.
[email protected]81ce9f3b2011-04-05 04:48:53120 base::Histogram* const process_times_; // Time spent proc. task.
121 base::Histogram* const total_times_; // Total queueing plus proc.
initial.commit09911bf2008-07-26 23:55:29122 JankWatchdog total_time_watchdog_; // Watching for excessive total_time.
123
[email protected]9cfb89a2010-06-09 21:20:41124 DISALLOW_COPY_AND_ASSIGN(JankObserverHelper);
125};
126
127JankObserverHelper::JankObserverHelper(
128 const std::string& thread_name,
129 const TimeDelta& excessive_duration,
130 bool watchdog_enable)
131 : max_message_delay_(excessive_duration),
[email protected]ea812b82010-11-03 23:36:34132 measure_current_message_(true),
133 events_till_measurement_(0),
[email protected]9cfb89a2010-06-09 21:20:41134 slow_processing_counter_(std::string("Chrome.SlowMsg") + thread_name),
135 queueing_delay_counter_(std::string("Chrome.DelayMsg") + thread_name),
[email protected]81ce9f3b2011-04-05 04:48:53136 process_times_(base::Histogram::FactoryGet(
137 std::string("Chrome.ProcMsgL ") + thread_name,
138 1, 3600000, 50, base::Histogram::kUmaTargetedHistogramFlag)),
139 total_times_(base::Histogram::FactoryGet(
140 std::string("Chrome.TotalMsgL ") + thread_name,
141 1, 3600000, 50, base::Histogram::kUmaTargetedHistogramFlag)),
[email protected]9cfb89a2010-06-09 21:20:41142 total_time_watchdog_(excessive_duration, thread_name, watchdog_enable) {
[email protected]ea812b82010-11-03 23:36:34143 if (discard_count_ > 0) {
144 // Select a vaguely random sample-start-point.
[email protected]0c341372010-11-05 01:18:28145 events_till_measurement_ = static_cast<int>(
146 (TimeTicks::Now() - TimeTicks()).InSeconds() % (discard_count_ + 1));
[email protected]ea812b82010-11-03 23:36:34147 }
[email protected]9cfb89a2010-06-09 21:20:41148}
149
150JankObserverHelper::~JankObserverHelper() {}
151
152// Called when a message has just begun processing, initializes
153// per-message variables and timers.
154void JankObserverHelper::StartProcessingTimers(const TimeDelta& queueing_time) {
[email protected]ea812b82010-11-03 23:36:34155 DCHECK(measure_current_message_);
[email protected]9cfb89a2010-06-09 21:20:41156 begin_process_message_ = TimeTicks::Now();
157 queueing_time_ = queueing_time;
158
159 // Simulate arming when the message entered the queue.
160 total_time_watchdog_.ArmSomeTimeDeltaAgo(queueing_time_);
161 if (queueing_time_ > max_message_delay_) {
162 // Message is too delayed.
163 queueing_delay_counter_.Increment();
164#if defined(OS_WIN)
165 if (kPlaySounds)
166 MessageBeep(MB_ICONASTERISK);
167#endif
168 }
169}
170
171// Called when a message has just finished processing, finalizes
172// per-message variables and timers.
173void JankObserverHelper::EndProcessingTimers() {
[email protected]ea812b82010-11-03 23:36:34174 if (!measure_current_message_)
175 return;
[email protected]9cfb89a2010-06-09 21:20:41176 total_time_watchdog_.Disarm();
177 TimeTicks now = TimeTicks::Now();
178 if (begin_process_message_ != TimeTicks()) {
179 TimeDelta processing_time = now - begin_process_message_;
180 process_times_->AddTime(processing_time);
181 total_times_->AddTime(queueing_time_ + processing_time);
182 }
183 if (now - begin_process_message_ >
184 TimeDelta::FromMilliseconds(kMaxMessageProcessingMs)) {
185 // Message took too long to process.
186 slow_processing_counter_.Increment();
187#if defined(OS_WIN)
188 if (kPlaySounds)
189 MessageBeep(MB_ICONHAND);
190#endif
191 }
192
193 // Reset message specific times.
194 begin_process_message_ = base::TimeTicks();
195 queueing_time_ = base::TimeDelta();
196}
197
[email protected]ea812b82010-11-03 23:36:34198bool JankObserverHelper::MessageWillBeMeasured() {
199 measure_current_message_ = events_till_measurement_ <= 0;
200 if (!measure_current_message_)
201 --events_till_measurement_;
202 else
203 events_till_measurement_ = discard_count_;
204 return measure_current_message_;
205}
206
207// static
208int JankObserverHelper::discard_count_ = 99; // Measure only 1 in 100.
209
[email protected]9cfb89a2010-06-09 21:20:41210//------------------------------------------------------------------------------
211class IOJankObserver : public base::RefCountedThreadSafe<IOJankObserver>,
212 public MessageLoopForIO::IOObserver,
213 public MessageLoop::TaskObserver {
214 public:
215 IOJankObserver(const char* thread_name,
216 TimeDelta excessive_duration,
217 bool watchdog_enable)
218 : helper_(thread_name, excessive_duration, watchdog_enable) {}
219
[email protected]9cfb89a2010-06-09 21:20:41220 // Attaches the observer to the current thread's message loop. You can only
221 // attach to the current thread, so this function can be invoked on another
222 // thread to attach it.
223 void AttachToCurrentThread() {
224 MessageLoop::current()->AddTaskObserver(this);
225 MessageLoopForIO::current()->AddIOObserver(this);
226 }
227
228 // Detaches the observer to the current thread's message loop.
229 void DetachFromCurrentThread() {
230 MessageLoopForIO::current()->RemoveIOObserver(this);
231 MessageLoop::current()->RemoveTaskObserver(this);
232 }
233
[email protected]b224f792011-04-20 16:02:23234 virtual void WillProcessIOEvent() OVERRIDE {
[email protected]ea812b82010-11-03 23:36:34235 if (!helper_.MessageWillBeMeasured())
236 return;
[email protected]9cfb89a2010-06-09 21:20:41237 helper_.StartProcessingTimers(base::TimeDelta());
238 }
239
[email protected]b224f792011-04-20 16:02:23240 virtual void DidProcessIOEvent() OVERRIDE {
[email protected]9cfb89a2010-06-09 21:20:41241 helper_.EndProcessingTimers();
242 }
243
[email protected]b224f792011-04-20 16:02:23244 virtual void WillProcessTask(base::TimeTicks time_posted) OVERRIDE {
[email protected]ea812b82010-11-03 23:36:34245 if (!helper_.MessageWillBeMeasured())
246 return;
[email protected]9cfb89a2010-06-09 21:20:41247 base::TimeTicks now = base::TimeTicks::Now();
[email protected]b224f792011-04-20 16:02:23248 const base::TimeDelta queueing_time = now - time_posted;
[email protected]9cfb89a2010-06-09 21:20:41249 helper_.StartProcessingTimers(queueing_time);
250 }
251
[email protected]b224f792011-04-20 16:02:23252 virtual void DidProcessTask(base::TimeTicks time_posted) OVERRIDE {
[email protected]9cfb89a2010-06-09 21:20:41253 helper_.EndProcessingTimers();
254 }
255
256 private:
257 friend class base::RefCountedThreadSafe<IOJankObserver>;
258
[email protected]4e0f45f52012-05-18 18:00:22259 ~IOJankObserver() {}
260
[email protected]9cfb89a2010-06-09 21:20:41261 JankObserverHelper helper_;
262
263 DISALLOW_COPY_AND_ASSIGN(IOJankObserver);
264};
265
266//------------------------------------------------------------------------------
267class UIJankObserver : public base::RefCountedThreadSafe<UIJankObserver>,
268 public MessageLoop::TaskObserver,
269 public MessageLoopForUI::Observer {
270 public:
271 UIJankObserver(const char* thread_name,
272 TimeDelta excessive_duration,
273 bool watchdog_enable)
274 : helper_(thread_name, excessive_duration, watchdog_enable) {}
275
276 // Attaches the observer to the current thread's message loop. You can only
277 // attach to the current thread, so this function can be invoked on another
278 // thread to attach it.
279 void AttachToCurrentThread() {
280 DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI);
281 MessageLoopForUI::current()->AddObserver(this);
282 MessageLoop::current()->AddTaskObserver(this);
283 }
284
285 // Detaches the observer to the current thread's message loop.
286 void DetachFromCurrentThread() {
287 DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI);
288 MessageLoop::current()->RemoveTaskObserver(this);
289 MessageLoopForUI::current()->RemoveObserver(this);
290 }
291
[email protected]b224f792011-04-20 16:02:23292 virtual void WillProcessTask(base::TimeTicks time_posted) OVERRIDE {
[email protected]ea812b82010-11-03 23:36:34293 if (!helper_.MessageWillBeMeasured())
294 return;
[email protected]9cfb89a2010-06-09 21:20:41295 base::TimeTicks now = base::TimeTicks::Now();
[email protected]b224f792011-04-20 16:02:23296 const base::TimeDelta queueing_time = now - time_posted;
[email protected]9cfb89a2010-06-09 21:20:41297 helper_.StartProcessingTimers(queueing_time);
298 }
299
[email protected]b224f792011-04-20 16:02:23300 virtual void DidProcessTask(base::TimeTicks time_posted) OVERRIDE {
[email protected]9cfb89a2010-06-09 21:20:41301 helper_.EndProcessingTimers();
302 }
303
304#if defined(OS_WIN)
[email protected]2e5597c2011-10-04 00:10:47305 virtual base::EventStatus WillProcessEvent(
306 const base::NativeEvent& event) OVERRIDE {
[email protected]ea812b82010-11-03 23:36:34307 if (!helper_.MessageWillBeMeasured())
[email protected]2e5597c2011-10-04 00:10:47308 return base::EVENT_CONTINUE;
[email protected]9cfb89a2010-06-09 21:20:41309 // GetMessageTime returns a LONG (signed 32-bit) and GetTickCount returns
310 // a DWORD (unsigned 32-bit). They both wrap around when the time is longer
311 // than they can hold. I'm not sure if GetMessageTime wraps around to 0,
312 // or if the original time comes from GetTickCount, it might wrap around
313 // to -1.
314 //
315 // Therefore, I cast to DWORD so if it wraps to -1 we will correct it. If
316 // it doesn't, then our time delta will be negative if a message happens
317 // to straddle the wraparound point, it will still be OK.
[email protected]2e5597c2011-10-04 00:10:47318 DWORD cur_message_issue_time = static_cast<DWORD>(event.time);
[email protected]9cfb89a2010-06-09 21:20:41319 DWORD cur_time = GetTickCount();
320 base::TimeDelta queueing_time =
321 base::TimeDelta::FromMilliseconds(cur_time - cur_message_issue_time);
322
323 helper_.StartProcessingTimers(queueing_time);
[email protected]2e5597c2011-10-04 00:10:47324 return base::EVENT_CONTINUE;
[email protected]9cfb89a2010-06-09 21:20:41325 }
326
[email protected]2e5597c2011-10-04 00:10:47327 virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE {
[email protected]9cfb89a2010-06-09 21:20:41328 helper_.EndProcessingTimers();
329 }
[email protected]5d84d1ed2011-12-14 23:17:48330#elif defined(USE_AURA)
[email protected]2e5597c2011-10-04 00:10:47331 virtual base::EventStatus WillProcessEvent(
332 const base::NativeEvent& event) OVERRIDE {
333 return base::EVENT_CONTINUE;
334 }
335
336 virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE {
337 }
[email protected]a13283cc2012-04-05 00:21:22338#elif defined(TOOLKIT_GTK)
[email protected]9cfb89a2010-06-09 21:20:41339 virtual void WillProcessEvent(GdkEvent* event) {
[email protected]ea812b82010-11-03 23:36:34340 if (!helper_.MessageWillBeMeasured())
341 return;
[email protected]9cfb89a2010-06-09 21:20:41342 // TODO(evanm): we want to set queueing_time_ using
343 // gdk_event_get_time, but how do you convert that info
344 // into a delta?
345 // guint event_time = gdk_event_get_time(event);
346 base::TimeDelta queueing_time = base::TimeDelta::FromMilliseconds(0);
347 helper_.StartProcessingTimers(queueing_time);
348 }
349
350 virtual void DidProcessEvent(GdkEvent* event) {
351 helper_.EndProcessingTimers();
352 }
353#endif
354
355 private:
356 friend class base::RefCountedThreadSafe<UIJankObserver>;
357
358 ~UIJankObserver() {}
359
360 JankObserverHelper helper_;
361
362 DISALLOW_COPY_AND_ASSIGN(UIJankObserver);
initial.commit09911bf2008-07-26 23:55:29363};
364
365// These objects are created by InstallJankometer and leaked.
[email protected]9cfb89a2010-06-09 21:20:41366const scoped_refptr<UIJankObserver>* ui_observer = NULL;
367const scoped_refptr<IOJankObserver>* io_observer = NULL;
initial.commit09911bf2008-07-26 23:55:29368
369} // namespace
370
[email protected]16e0efd2009-07-08 03:58:29371void InstallJankometer(const CommandLine& parsed_command_line) {
initial.commit09911bf2008-07-26 23:55:29372 if (ui_observer || io_observer) {
373 NOTREACHED() << "Initializing jank-o-meter twice";
374 return;
375 }
376
377 bool ui_watchdog_enabled = false;
378 bool io_watchdog_enabled = false;
379 if (parsed_command_line.HasSwitch(switches::kEnableWatchdog)) {
[email protected]5d39e3142009-12-17 05:54:10380 std::string list =
381 parsed_command_line.GetSwitchValueASCII(switches::kEnableWatchdog);
382 if (list.npos != list.find("ui"))
initial.commit09911bf2008-07-26 23:55:29383 ui_watchdog_enabled = true;
[email protected]5d39e3142009-12-17 05:54:10384 if (list.npos != list.find("io"))
initial.commit09911bf2008-07-26 23:55:29385 io_watchdog_enabled = true;
386 }
387
[email protected]ea812b82010-11-03 23:36:34388 if (ui_watchdog_enabled || io_watchdog_enabled)
389 JankObserverHelper::SetDefaultMessagesToSkip(0); // Watch everything.
390
initial.commit09911bf2008-07-26 23:55:29391 // Install on the UI thread.
[email protected]9cfb89a2010-06-09 21:20:41392 ui_observer = new scoped_refptr<UIJankObserver>(
393 new UIJankObserver(
394 "UI",
395 TimeDelta::FromMilliseconds(kMaxUIMessageDelayMs),
396 ui_watchdog_enabled));
397 (*ui_observer)->AttachToCurrentThread();
initial.commit09911bf2008-07-26 23:55:29398
399 // Now install on the I/O thread. Hiccups on that thread will block
400 // interaction with web pages. We must proxy to that thread before we can
401 // add our observer.
[email protected]9cfb89a2010-06-09 21:20:41402 io_observer = new scoped_refptr<IOJankObserver>(
403 new IOJankObserver(
404 "IO",
405 TimeDelta::FromMilliseconds(kMaxIOMessageDelayMs),
406 io_watchdog_enabled));
[email protected]f8b3ef82010-10-11 02:45:52407 BrowserThread::PostTask(
408 BrowserThread::IO, FROM_HERE,
[email protected]317c58f02011-11-09 02:15:03409 base::Bind(&IOJankObserver::AttachToCurrentThread, io_observer->get()));
initial.commit09911bf2008-07-26 23:55:29410}
411
412void UninstallJankometer() {
413 if (ui_observer) {
[email protected]9cfb89a2010-06-09 21:20:41414 (*ui_observer)->DetachFromCurrentThread();
415 delete ui_observer;
initial.commit09911bf2008-07-26 23:55:29416 ui_observer = NULL;
417 }
418 if (io_observer) {
419 // IO thread can't be running when we remove observers.
420 DCHECK((!g_browser_process) || !(g_browser_process->io_thread()));
[email protected]9cfb89a2010-06-09 21:20:41421 delete io_observer;
initial.commit09911bf2008-07-26 23:55:29422 io_observer = NULL;
423 }
424}