blob: a5d841d9172b129ceee6ed4708f3cf7b3801020e [file] [log] [blame]
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/jankometer.h"
6
7#include <limits>
8
9#include "base/basictypes.h"
10#include "base/bind.h"
11#include "base/command_line.h"
12#include "base/memory/ref_counted.h"
Ben Murdochca12bfa2013-07-23 11:17:05 +010013#include "base/message_loop/message_loop.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000014#include "base/metrics/histogram.h"
15#include "base/metrics/stats_counters.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000016#include "base/pending_task.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010017#include "base/strings/string_util.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000018#include "base/threading/thread.h"
19#include "base/threading/watchdog.h"
Ben Murdocheb525c52013-07-10 11:40:50 +010020#include "base/time/time.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000021#include "build/build_config.h"
22#include "chrome/browser/browser_process.h"
23#include "chrome/common/chrome_switches.h"
24#include "content/public/browser/browser_thread.h"
25
26using base::TimeDelta;
27using base::TimeTicks;
28using content::BrowserThread;
29
30namespace {
31
32// The maximum threshold of delay of the message before considering it a delay.
33// For a debug build, you may want to set IO delay around 500ms.
34// For a release build, setting it around 350ms is sensible.
35// Visit about:histograms to see what the distribution is on your system, with
36// your build. Be sure to do some work to get interesting stats.
37// The numbers below came from a warm start (you'll get about 5-10 alarms with
38// a cold start), and running the page-cycler for 5 rounds.
39#ifdef NDEBUG
40const int kMaxUIMessageDelayMs = 350;
41const int kMaxIOMessageDelayMs = 200;
42#else
43const int kMaxUIMessageDelayMs = 500;
44const int kMaxIOMessageDelayMs = 400;
45#endif
46
47// Maximum processing time (excluding queueing delay) for a message before
48// considering it delayed.
49const int kMaxMessageProcessingMs = 100;
50
Torne (Richard Coles)4e180b62013-10-18 15:46:22 +010051#if defined(OS_WIN)
Torne (Richard Coles)58218062012-11-14 11:43:16 +000052// TODO(brettw) Consider making this a pref.
53const bool kPlaySounds = false;
Torne (Richard Coles)4e180b62013-10-18 15:46:22 +010054#endif
Torne (Richard Coles)58218062012-11-14 11:43:16 +000055
56//------------------------------------------------------------------------------
57// Provide a special watchdog to make it easy to set the breakpoint on this
58// class only.
59class JankWatchdog : public base::Watchdog {
60 public:
61 JankWatchdog(const TimeDelta& duration,
62 const std::string& thread_watched_name,
63 bool enabled)
64 : Watchdog(duration, thread_watched_name, enabled),
65 thread_name_watched_(thread_watched_name),
66 alarm_count_(0) {
67 }
68
69 virtual ~JankWatchdog() {}
70
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000071 virtual void Alarm() OVERRIDE {
Torne (Richard Coles)58218062012-11-14 11:43:16 +000072 // Put break point here if you want to stop threads and look at what caused
73 // the jankiness.
74 alarm_count_++;
75 Watchdog::Alarm();
76 }
77
78 private:
79 std::string thread_name_watched_;
80 int alarm_count_;
81
82 DISALLOW_COPY_AND_ASSIGN(JankWatchdog);
83};
84
85class JankObserverHelper {
86 public:
87 JankObserverHelper(const std::string& thread_name,
88 const TimeDelta& excessive_duration,
89 bool watchdog_enable);
90 ~JankObserverHelper();
91
92 void StartProcessingTimers(const TimeDelta& queueing_time);
93 void EndProcessingTimers();
94
95 // Indicate if we will bother to measuer this message.
96 bool MessageWillBeMeasured();
97
98 static void SetDefaultMessagesToSkip(int count) { discard_count_ = count; }
99
100 private:
101 const TimeDelta max_message_delay_;
102
103 // Indicate if we'll bother measuring this message.
104 bool measure_current_message_;
105
106 // Down counter which will periodically hit 0, and only then bother to measure
107 // the corresponding message.
108 int events_till_measurement_;
109
110 // The value to re-initialize events_till_measurement_ after it reaches 0.
111 static int discard_count_;
112
113 // Time at which the current message processing began.
114 TimeTicks begin_process_message_;
115
116 // Time the current message spent in the queue -- delta between message
117 // construction time and message processing time.
118 TimeDelta queueing_time_;
119
120 // Counters for the two types of jank we measure.
121 base::StatsCounter slow_processing_counter_; // Msgs w/ long proc time.
122 base::StatsCounter queueing_delay_counter_; // Msgs w/ long queueing delay.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000123 base::HistogramBase* const process_times_; // Time spent proc. task.
124 base::HistogramBase* const total_times_; // Total queueing plus proc.
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000125 JankWatchdog total_time_watchdog_; // Watching for excessive total_time.
126
127 DISALLOW_COPY_AND_ASSIGN(JankObserverHelper);
128};
129
130JankObserverHelper::JankObserverHelper(
131 const std::string& thread_name,
132 const TimeDelta& excessive_duration,
133 bool watchdog_enable)
134 : max_message_delay_(excessive_duration),
135 measure_current_message_(true),
136 events_till_measurement_(0),
137 slow_processing_counter_(std::string("Chrome.SlowMsg") + thread_name),
138 queueing_delay_counter_(std::string("Chrome.DelayMsg") + thread_name),
139 process_times_(base::Histogram::FactoryGet(
140 std::string("Chrome.ProcMsgL ") + thread_name,
141 1, 3600000, 50, base::Histogram::kUmaTargetedHistogramFlag)),
142 total_times_(base::Histogram::FactoryGet(
143 std::string("Chrome.TotalMsgL ") + thread_name,
144 1, 3600000, 50, base::Histogram::kUmaTargetedHistogramFlag)),
145 total_time_watchdog_(excessive_duration, thread_name, watchdog_enable) {
146 if (discard_count_ > 0) {
147 // Select a vaguely random sample-start-point.
148 events_till_measurement_ = static_cast<int>(
149 (TimeTicks::Now() - TimeTicks()).InSeconds() % (discard_count_ + 1));
150 }
151}
152
153JankObserverHelper::~JankObserverHelper() {}
154
155// Called when a message has just begun processing, initializes
156// per-message variables and timers.
157void JankObserverHelper::StartProcessingTimers(const TimeDelta& queueing_time) {
158 DCHECK(measure_current_message_);
159 begin_process_message_ = TimeTicks::Now();
160 queueing_time_ = queueing_time;
161
162 // Simulate arming when the message entered the queue.
163 total_time_watchdog_.ArmSomeTimeDeltaAgo(queueing_time_);
164 if (queueing_time_ > max_message_delay_) {
165 // Message is too delayed.
166 queueing_delay_counter_.Increment();
167#if defined(OS_WIN)
168 if (kPlaySounds)
169 MessageBeep(MB_ICONASTERISK);
170#endif
171 }
172}
173
174// Called when a message has just finished processing, finalizes
175// per-message variables and timers.
176void JankObserverHelper::EndProcessingTimers() {
177 if (!measure_current_message_)
178 return;
179 total_time_watchdog_.Disarm();
180 TimeTicks now = TimeTicks::Now();
181 if (begin_process_message_ != TimeTicks()) {
182 TimeDelta processing_time = now - begin_process_message_;
183 process_times_->AddTime(processing_time);
184 total_times_->AddTime(queueing_time_ + processing_time);
185 }
186 if (now - begin_process_message_ >
187 TimeDelta::FromMilliseconds(kMaxMessageProcessingMs)) {
188 // Message took too long to process.
189 slow_processing_counter_.Increment();
190#if defined(OS_WIN)
191 if (kPlaySounds)
192 MessageBeep(MB_ICONHAND);
193#endif
194 }
195
196 // Reset message specific times.
197 begin_process_message_ = base::TimeTicks();
198 queueing_time_ = base::TimeDelta();
199}
200
201bool JankObserverHelper::MessageWillBeMeasured() {
202 measure_current_message_ = events_till_measurement_ <= 0;
203 if (!measure_current_message_)
204 --events_till_measurement_;
205 else
206 events_till_measurement_ = discard_count_;
207 return measure_current_message_;
208}
209
210// static
211int JankObserverHelper::discard_count_ = 99; // Measure only 1 in 100.
212
213//------------------------------------------------------------------------------
214class IOJankObserver : public base::RefCountedThreadSafe<IOJankObserver>,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100215 public base::MessageLoopForIO::IOObserver,
216 public base::MessageLoop::TaskObserver {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000217 public:
218 IOJankObserver(const char* thread_name,
219 TimeDelta excessive_duration,
220 bool watchdog_enable)
221 : helper_(thread_name, excessive_duration, watchdog_enable) {}
222
223 // Attaches the observer to the current thread's message loop. You can only
224 // attach to the current thread, so this function can be invoked on another
225 // thread to attach it.
226 void AttachToCurrentThread() {
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100227 base::MessageLoop::current()->AddTaskObserver(this);
228 base::MessageLoopForIO::current()->AddIOObserver(this);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000229 }
230
231 // Detaches the observer to the current thread's message loop.
232 void DetachFromCurrentThread() {
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100233 base::MessageLoopForIO::current()->RemoveIOObserver(this);
234 base::MessageLoop::current()->RemoveTaskObserver(this);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000235 }
236
237 virtual void WillProcessIOEvent() OVERRIDE {
238 if (!helper_.MessageWillBeMeasured())
239 return;
240 helper_.StartProcessingTimers(base::TimeDelta());
241 }
242
243 virtual void DidProcessIOEvent() OVERRIDE {
244 helper_.EndProcessingTimers();
245 }
246
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000247 virtual void WillProcessTask(const base::PendingTask& pending_task) OVERRIDE {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000248 if (!helper_.MessageWillBeMeasured())
249 return;
250 base::TimeTicks now = base::TimeTicks::Now();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000251 const base::TimeDelta queueing_time = now - pending_task.time_posted;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000252 helper_.StartProcessingTimers(queueing_time);
253 }
254
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000255 virtual void DidProcessTask(const base::PendingTask& pending_task) OVERRIDE {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000256 helper_.EndProcessingTimers();
257 }
258
259 private:
260 friend class base::RefCountedThreadSafe<IOJankObserver>;
261
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000262 virtual ~IOJankObserver() {}
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000263
264 JankObserverHelper helper_;
265
266 DISALLOW_COPY_AND_ASSIGN(IOJankObserver);
267};
268
269//------------------------------------------------------------------------------
270class UIJankObserver : public base::RefCountedThreadSafe<UIJankObserver>,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100271 public base::MessageLoop::TaskObserver,
272 public base::MessageLoopForUI::Observer {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000273 public:
274 UIJankObserver(const char* thread_name,
275 TimeDelta excessive_duration,
276 bool watchdog_enable)
277 : helper_(thread_name, excessive_duration, watchdog_enable) {}
278
279 // Attaches the observer to the current thread's message loop. You can only
280 // attach to the current thread, so this function can be invoked on another
281 // thread to attach it.
282 void AttachToCurrentThread() {
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100283 DCHECK_EQ(base::MessageLoop::current()->type(), base::MessageLoop::TYPE_UI);
284 base::MessageLoopForUI::current()->AddObserver(this);
285 base::MessageLoop::current()->AddTaskObserver(this);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000286 }
287
288 // Detaches the observer to the current thread's message loop.
289 void DetachFromCurrentThread() {
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100290 DCHECK_EQ(base::MessageLoop::current()->type(), base::MessageLoop::TYPE_UI);
291 base::MessageLoop::current()->RemoveTaskObserver(this);
292 base::MessageLoopForUI::current()->RemoveObserver(this);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000293 }
294
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000295 virtual void WillProcessTask(const base::PendingTask& pending_task) OVERRIDE {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000296 if (!helper_.MessageWillBeMeasured())
297 return;
298 base::TimeTicks now = base::TimeTicks::Now();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000299 const base::TimeDelta queueing_time = now - pending_task.time_posted;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000300 helper_.StartProcessingTimers(queueing_time);
301 }
302
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000303 virtual void DidProcessTask(const base::PendingTask& pending_task) OVERRIDE {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000304 helper_.EndProcessingTimers();
305 }
306
307#if defined(OS_WIN)
308 virtual base::EventStatus WillProcessEvent(
309 const base::NativeEvent& event) OVERRIDE {
310 if (!helper_.MessageWillBeMeasured())
311 return base::EVENT_CONTINUE;
312 // GetMessageTime returns a LONG (signed 32-bit) and GetTickCount returns
313 // a DWORD (unsigned 32-bit). They both wrap around when the time is longer
314 // than they can hold. I'm not sure if GetMessageTime wraps around to 0,
315 // or if the original time comes from GetTickCount, it might wrap around
316 // to -1.
317 //
318 // Therefore, I cast to DWORD so if it wraps to -1 we will correct it. If
319 // it doesn't, then our time delta will be negative if a message happens
320 // to straddle the wraparound point, it will still be OK.
321 DWORD cur_message_issue_time = static_cast<DWORD>(event.time);
322 DWORD cur_time = GetTickCount();
323 base::TimeDelta queueing_time =
324 base::TimeDelta::FromMilliseconds(cur_time - cur_message_issue_time);
325
326 helper_.StartProcessingTimers(queueing_time);
327 return base::EVENT_CONTINUE;
328 }
329
330 virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE {
331 helper_.EndProcessingTimers();
332 }
333#elif defined(USE_AURA)
334 virtual base::EventStatus WillProcessEvent(
335 const base::NativeEvent& event) OVERRIDE {
336 return base::EVENT_CONTINUE;
337 }
338
339 virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE {
340 }
341#elif defined(TOOLKIT_GTK)
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000342 virtual void WillProcessEvent(GdkEvent* event) OVERRIDE {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000343 if (!helper_.MessageWillBeMeasured())
344 return;
345 // TODO(evanm): we want to set queueing_time_ using
346 // gdk_event_get_time, but how do you convert that info
347 // into a delta?
348 // guint event_time = gdk_event_get_time(event);
349 base::TimeDelta queueing_time = base::TimeDelta::FromMilliseconds(0);
350 helper_.StartProcessingTimers(queueing_time);
351 }
352
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000353 virtual void DidProcessEvent(GdkEvent* event) OVERRIDE {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000354 helper_.EndProcessingTimers();
355 }
356#endif
357
358 private:
359 friend class base::RefCountedThreadSafe<UIJankObserver>;
360
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000361 virtual ~UIJankObserver() {}
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000362
363 JankObserverHelper helper_;
364
365 DISALLOW_COPY_AND_ASSIGN(UIJankObserver);
366};
367
368// These objects are created by InstallJankometer and leaked.
369const scoped_refptr<UIJankObserver>* ui_observer = NULL;
370const scoped_refptr<IOJankObserver>* io_observer = NULL;
371
372} // namespace
373
374void InstallJankometer(const CommandLine& parsed_command_line) {
375 if (ui_observer || io_observer) {
376 NOTREACHED() << "Initializing jank-o-meter twice";
377 return;
378 }
379
380 bool ui_watchdog_enabled = false;
381 bool io_watchdog_enabled = false;
382 if (parsed_command_line.HasSwitch(switches::kEnableWatchdog)) {
383 std::string list =
384 parsed_command_line.GetSwitchValueASCII(switches::kEnableWatchdog);
385 if (list.npos != list.find("ui"))
386 ui_watchdog_enabled = true;
387 if (list.npos != list.find("io"))
388 io_watchdog_enabled = true;
389 }
390
391 if (ui_watchdog_enabled || io_watchdog_enabled)
392 JankObserverHelper::SetDefaultMessagesToSkip(0); // Watch everything.
393
394 // Install on the UI thread.
395 ui_observer = new scoped_refptr<UIJankObserver>(
396 new UIJankObserver(
397 "UI",
398 TimeDelta::FromMilliseconds(kMaxUIMessageDelayMs),
399 ui_watchdog_enabled));
400 (*ui_observer)->AttachToCurrentThread();
401
402 // Now install on the I/O thread. Hiccups on that thread will block
403 // interaction with web pages. We must proxy to that thread before we can
404 // add our observer.
405 io_observer = new scoped_refptr<IOJankObserver>(
406 new IOJankObserver(
407 "IO",
408 TimeDelta::FromMilliseconds(kMaxIOMessageDelayMs),
409 io_watchdog_enabled));
410 BrowserThread::PostTask(
411 BrowserThread::IO, FROM_HERE,
412 base::Bind(&IOJankObserver::AttachToCurrentThread, io_observer->get()));
413}
414
415void UninstallJankometer() {
416 if (ui_observer) {
417 (*ui_observer)->DetachFromCurrentThread();
418 delete ui_observer;
419 ui_observer = NULL;
420 }
421 if (io_observer) {
422 // IO thread can't be running when we remove observers.
423 DCHECK((!g_browser_process) || !(g_browser_process->io_thread()));
424 delete io_observer;
425 io_observer = NULL;
426 }
427}