1 // 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.
5 #include "chrome/browser/jankometer.h"
9 #include "base/basictypes.h"
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/metrics/histogram.h"
15 #include "base/metrics/stats_counters.h"
16 #include "base/pending_task.h"
17 #include "base/strings/string_util.h"
18 #include "base/threading/thread.h"
19 #include "base/threading/watchdog.h"
20 #include "base/time/time.h"
21 #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"
26 using base::TimeDelta
;
27 using base::TimeTicks
;
28 using content::BrowserThread
;
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.
40 const int kMaxUIMessageDelayMs
= 350;
41 const int kMaxIOMessageDelayMs
= 200;
43 const int kMaxUIMessageDelayMs
= 500;
44 const int kMaxIOMessageDelayMs
= 400;
47 // Maximum processing time (excluding queueing delay) for a message before
48 // considering it delayed.
49 const int kMaxMessageProcessingMs
= 100;
52 // TODO(brettw) Consider making this a pref.
53 const bool kPlaySounds
= false;
56 //------------------------------------------------------------------------------
57 // Provide a special watchdog to make it easy to set the breakpoint on this
59 class JankWatchdog
: public base::Watchdog
{
61 JankWatchdog(const TimeDelta
& duration
,
62 const std::string
& thread_watched_name
,
64 : Watchdog(duration
, thread_watched_name
, enabled
),
65 thread_name_watched_(thread_watched_name
),
69 virtual ~JankWatchdog() {}
71 virtual void Alarm() OVERRIDE
{
72 // Put break point here if you want to stop threads and look at what caused
79 std::string thread_name_watched_
;
82 DISALLOW_COPY_AND_ASSIGN(JankWatchdog
);
85 class JankObserverHelper
{
87 JankObserverHelper(const std::string
& thread_name
,
88 const TimeDelta
& excessive_duration
,
89 bool watchdog_enable
);
90 ~JankObserverHelper();
92 void StartProcessingTimers(const TimeDelta
& queueing_time
);
93 void EndProcessingTimers();
95 // Indicate if we will bother to measuer this message.
96 bool MessageWillBeMeasured();
98 static void SetDefaultMessagesToSkip(int count
) { discard_count_
= count
; }
101 const TimeDelta max_message_delay_
;
103 // Indicate if we'll bother measuring this message.
104 bool measure_current_message_
;
106 // Down counter which will periodically hit 0, and only then bother to measure
107 // the corresponding message.
108 int events_till_measurement_
;
110 // The value to re-initialize events_till_measurement_ after it reaches 0.
111 static int discard_count_
;
113 // Time at which the current message processing began.
114 TimeTicks begin_process_message_
;
116 // Time the current message spent in the queue -- delta between message
117 // construction time and message processing time.
118 TimeDelta queueing_time_
;
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.
123 base::HistogramBase
* const process_times_
; // Time spent proc. task.
124 base::HistogramBase
* const total_times_
; // Total queueing plus proc.
125 JankWatchdog total_time_watchdog_
; // Watching for excessive total_time.
127 DISALLOW_COPY_AND_ASSIGN(JankObserverHelper
);
130 JankObserverHelper::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));
153 JankObserverHelper::~JankObserverHelper() {}
155 // Called when a message has just begun processing, initializes
156 // per-message variables and timers.
157 void JankObserverHelper::StartProcessingTimers(const TimeDelta
& queueing_time
) {
158 DCHECK(measure_current_message_
);
159 begin_process_message_
= TimeTicks::Now();
160 queueing_time_
= queueing_time
;
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();
169 MessageBeep(MB_ICONASTERISK
);
174 // Called when a message has just finished processing, finalizes
175 // per-message variables and timers.
176 void JankObserverHelper::EndProcessingTimers() {
177 if (!measure_current_message_
)
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
);
186 if (now
- begin_process_message_
>
187 TimeDelta::FromMilliseconds(kMaxMessageProcessingMs
)) {
188 // Message took too long to process.
189 slow_processing_counter_
.Increment();
192 MessageBeep(MB_ICONHAND
);
196 // Reset message specific times.
197 begin_process_message_
= base::TimeTicks();
198 queueing_time_
= base::TimeDelta();
201 bool JankObserverHelper::MessageWillBeMeasured() {
202 measure_current_message_
= events_till_measurement_
<= 0;
203 if (!measure_current_message_
)
204 --events_till_measurement_
;
206 events_till_measurement_
= discard_count_
;
207 return measure_current_message_
;
211 int JankObserverHelper::discard_count_
= 99; // Measure only 1 in 100.
213 //------------------------------------------------------------------------------
214 class IOJankObserver
: public base::RefCountedThreadSafe
<IOJankObserver
>,
215 public base::MessageLoopForIO::IOObserver
,
216 public base::MessageLoop::TaskObserver
{
218 IOJankObserver(const char* thread_name
,
219 TimeDelta excessive_duration
,
220 bool watchdog_enable
)
221 : helper_(thread_name
, excessive_duration
, watchdog_enable
) {}
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() {
227 base::MessageLoop::current()->AddTaskObserver(this);
228 base::MessageLoopForIO::current()->AddIOObserver(this);
231 // Detaches the observer to the current thread's message loop.
232 void DetachFromCurrentThread() {
233 base::MessageLoopForIO::current()->RemoveIOObserver(this);
234 base::MessageLoop::current()->RemoveTaskObserver(this);
237 virtual void WillProcessIOEvent() OVERRIDE
{
238 if (!helper_
.MessageWillBeMeasured())
240 helper_
.StartProcessingTimers(base::TimeDelta());
243 virtual void DidProcessIOEvent() OVERRIDE
{
244 helper_
.EndProcessingTimers();
247 virtual void WillProcessTask(const base::PendingTask
& pending_task
) OVERRIDE
{
248 if (!helper_
.MessageWillBeMeasured())
250 base::TimeTicks now
= base::TimeTicks::Now();
251 const base::TimeDelta queueing_time
= now
- pending_task
.time_posted
;
252 helper_
.StartProcessingTimers(queueing_time
);
255 virtual void DidProcessTask(const base::PendingTask
& pending_task
) OVERRIDE
{
256 helper_
.EndProcessingTimers();
260 friend class base::RefCountedThreadSafe
<IOJankObserver
>;
262 virtual ~IOJankObserver() {}
264 JankObserverHelper helper_
;
266 DISALLOW_COPY_AND_ASSIGN(IOJankObserver
);
269 //------------------------------------------------------------------------------
270 class UIJankObserver
: public base::RefCountedThreadSafe
<UIJankObserver
>,
271 public base::MessageLoop::TaskObserver
,
272 public base::MessageLoopForUI::Observer
{
274 UIJankObserver(const char* thread_name
,
275 TimeDelta excessive_duration
,
276 bool watchdog_enable
)
277 : helper_(thread_name
, excessive_duration
, watchdog_enable
) {}
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() {
283 DCHECK_EQ(base::MessageLoop::current()->type(), base::MessageLoop::TYPE_UI
);
284 base::MessageLoopForUI::current()->AddObserver(this);
285 base::MessageLoop::current()->AddTaskObserver(this);
288 // Detaches the observer to the current thread's message loop.
289 void DetachFromCurrentThread() {
290 DCHECK_EQ(base::MessageLoop::current()->type(), base::MessageLoop::TYPE_UI
);
291 base::MessageLoop::current()->RemoveTaskObserver(this);
292 base::MessageLoopForUI::current()->RemoveObserver(this);
295 virtual void WillProcessTask(const base::PendingTask
& pending_task
) OVERRIDE
{
296 if (!helper_
.MessageWillBeMeasured())
298 base::TimeTicks now
= base::TimeTicks::Now();
299 const base::TimeDelta queueing_time
= now
- pending_task
.time_posted
;
300 helper_
.StartProcessingTimers(queueing_time
);
303 virtual void DidProcessTask(const base::PendingTask
& pending_task
) OVERRIDE
{
304 helper_
.EndProcessingTimers();
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
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
);
326 helper_
.StartProcessingTimers(queueing_time
);
327 return base::EVENT_CONTINUE
;
330 virtual void DidProcessEvent(const base::NativeEvent
& event
) OVERRIDE
{
331 helper_
.EndProcessingTimers();
333 #elif defined(USE_AURA)
334 virtual base::EventStatus
WillProcessEvent(
335 const base::NativeEvent
& event
) OVERRIDE
{
336 return base::EVENT_CONTINUE
;
339 virtual void DidProcessEvent(const base::NativeEvent
& event
) OVERRIDE
{
341 #elif defined(TOOLKIT_GTK)
342 virtual void WillProcessEvent(GdkEvent
* event
) OVERRIDE
{
343 if (!helper_
.MessageWillBeMeasured())
345 // TODO(evanm): we want to set queueing_time_ using
346 // gdk_event_get_time, but how do you convert that info
348 // guint event_time = gdk_event_get_time(event);
349 base::TimeDelta queueing_time
= base::TimeDelta::FromMilliseconds(0);
350 helper_
.StartProcessingTimers(queueing_time
);
353 virtual void DidProcessEvent(GdkEvent
* event
) OVERRIDE
{
354 helper_
.EndProcessingTimers();
359 friend class base::RefCountedThreadSafe
<UIJankObserver
>;
361 virtual ~UIJankObserver() {}
363 JankObserverHelper helper_
;
365 DISALLOW_COPY_AND_ASSIGN(UIJankObserver
);
368 // These objects are created by InstallJankometer and leaked.
369 const scoped_refptr
<UIJankObserver
>* ui_observer
= NULL
;
370 const scoped_refptr
<IOJankObserver
>* io_observer
= NULL
;
374 void InstallJankometer(const CommandLine
& parsed_command_line
) {
375 if (ui_observer
|| io_observer
) {
376 NOTREACHED() << "Initializing jank-o-meter twice";
380 bool ui_watchdog_enabled
= false;
381 bool io_watchdog_enabled
= false;
382 if (parsed_command_line
.HasSwitch(switches::kEnableWatchdog
)) {
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;
391 if (ui_watchdog_enabled
|| io_watchdog_enabled
)
392 JankObserverHelper::SetDefaultMessagesToSkip(0); // Watch everything.
394 // Install on the UI thread.
395 ui_observer
= new scoped_refptr
<UIJankObserver
>(
398 TimeDelta::FromMilliseconds(kMaxUIMessageDelayMs
),
399 ui_watchdog_enabled
));
400 (*ui_observer
)->AttachToCurrentThread();
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
405 io_observer
= new scoped_refptr
<IOJankObserver
>(
408 TimeDelta::FromMilliseconds(kMaxIOMessageDelayMs
),
409 io_watchdog_enabled
));
410 BrowserThread::PostTask(
411 BrowserThread::IO
, FROM_HERE
,
412 base::Bind(&IOJankObserver::AttachToCurrentThread
, io_observer
->get()));
415 void UninstallJankometer() {
417 (*ui_observer
)->DetachFromCurrentThread();
422 // IO thread can't be running when we remove observers.
423 DCHECK((!g_browser_process
) || !(g_browser_process
->io_thread()));