OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <limits> | 5 #include <limits> |
6 | 6 |
7 #include "chrome/browser/jankometer.h" | 7 #include "chrome/browser/jankometer.h" |
8 | 8 |
9 #include "base/basictypes.h" | 9 #include "base/basictypes.h" |
10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
74 Watchdog::Alarm(); | 74 Watchdog::Alarm(); |
75 } | 75 } |
76 | 76 |
77 private: | 77 private: |
78 std::string thread_name_watched_; | 78 std::string thread_name_watched_; |
79 int alarm_count_; | 79 int alarm_count_; |
80 | 80 |
81 DISALLOW_COPY_AND_ASSIGN(JankWatchdog); | 81 DISALLOW_COPY_AND_ASSIGN(JankWatchdog); |
82 }; | 82 }; |
83 | 83 |
| 84 class JankObserverHelper { |
| 85 public: |
| 86 JankObserverHelper(const std::string& thread_name, |
| 87 const TimeDelta& excessive_duration, |
| 88 bool watchdog_enable); |
| 89 ~JankObserverHelper(); |
| 90 |
| 91 void StartProcessingTimers(const TimeDelta& queueing_time); |
| 92 void EndProcessingTimers(); |
| 93 |
| 94 private: |
| 95 const TimeDelta max_message_delay_; |
| 96 |
| 97 // Time at which the current message processing began. |
| 98 TimeTicks begin_process_message_; |
| 99 |
| 100 // Time the current message spent in the queue -- delta between message |
| 101 // construction time and message processing time. |
| 102 TimeDelta queueing_time_; |
| 103 |
| 104 // Counters for the two types of jank we measure. |
| 105 StatsCounter slow_processing_counter_; // Messages with long processing time. |
| 106 StatsCounter queueing_delay_counter_; // Messages with long queueing delay. |
| 107 scoped_refptr<Histogram> process_times_; // Time spent processing task. |
| 108 scoped_refptr<Histogram> total_times_; // Total queueing plus processing. |
| 109 JankWatchdog total_time_watchdog_; // Watching for excessive total_time. |
| 110 |
| 111 DISALLOW_COPY_AND_ASSIGN(JankObserverHelper); |
| 112 }; |
| 113 |
| 114 JankObserverHelper::JankObserverHelper( |
| 115 const std::string& thread_name, |
| 116 const TimeDelta& excessive_duration, |
| 117 bool watchdog_enable) |
| 118 : max_message_delay_(excessive_duration), |
| 119 slow_processing_counter_(std::string("Chrome.SlowMsg") + thread_name), |
| 120 queueing_delay_counter_(std::string("Chrome.DelayMsg") + thread_name), |
| 121 total_time_watchdog_(excessive_duration, thread_name, watchdog_enable) { |
| 122 process_times_ = Histogram::FactoryGet( |
| 123 std::string("Chrome.ProcMsgL ") + thread_name, |
| 124 1, 3600000, 50, Histogram::kUmaTargetedHistogramFlag); |
| 125 total_times_ = Histogram::FactoryGet( |
| 126 std::string("Chrome.TotalMsgL ") + thread_name, |
| 127 1, 3600000, 50, Histogram::kUmaTargetedHistogramFlag); |
| 128 } |
| 129 |
| 130 JankObserverHelper::~JankObserverHelper() {} |
| 131 |
| 132 // Called when a message has just begun processing, initializes |
| 133 // per-message variables and timers. |
| 134 void JankObserverHelper::StartProcessingTimers(const TimeDelta& queueing_time) { |
| 135 begin_process_message_ = TimeTicks::Now(); |
| 136 queueing_time_ = queueing_time; |
| 137 |
| 138 // Simulate arming when the message entered the queue. |
| 139 total_time_watchdog_.ArmSomeTimeDeltaAgo(queueing_time_); |
| 140 if (queueing_time_ > max_message_delay_) { |
| 141 // Message is too delayed. |
| 142 queueing_delay_counter_.Increment(); |
| 143 #if defined(OS_WIN) |
| 144 if (kPlaySounds) |
| 145 MessageBeep(MB_ICONASTERISK); |
| 146 #endif |
| 147 } |
| 148 } |
| 149 |
| 150 // Called when a message has just finished processing, finalizes |
| 151 // per-message variables and timers. |
| 152 void JankObserverHelper::EndProcessingTimers() { |
| 153 total_time_watchdog_.Disarm(); |
| 154 TimeTicks now = TimeTicks::Now(); |
| 155 if (begin_process_message_ != TimeTicks()) { |
| 156 TimeDelta processing_time = now - begin_process_message_; |
| 157 process_times_->AddTime(processing_time); |
| 158 total_times_->AddTime(queueing_time_ + processing_time); |
| 159 } |
| 160 if (now - begin_process_message_ > |
| 161 TimeDelta::FromMilliseconds(kMaxMessageProcessingMs)) { |
| 162 // Message took too long to process. |
| 163 slow_processing_counter_.Increment(); |
| 164 #if defined(OS_WIN) |
| 165 if (kPlaySounds) |
| 166 MessageBeep(MB_ICONHAND); |
| 167 #endif |
| 168 } |
| 169 |
| 170 // Reset message specific times. |
| 171 begin_process_message_ = base::TimeTicks(); |
| 172 queueing_time_ = base::TimeDelta(); |
| 173 } |
| 174 |
84 //------------------------------------------------------------------------------ | 175 //------------------------------------------------------------------------------ |
85 class JankObserver : public base::RefCountedThreadSafe<JankObserver>, | 176 class IOJankObserver : public base::RefCountedThreadSafe<IOJankObserver>, |
86 public MessageLoopForUI::Observer { | 177 public MessageLoopForIO::IOObserver, |
| 178 public MessageLoop::TaskObserver { |
87 public: | 179 public: |
88 JankObserver(const char* thread_name, | 180 IOJankObserver(const char* thread_name, |
89 const TimeDelta& excessive_duration, | 181 TimeDelta excessive_duration, |
90 bool watchdog_enable) | 182 bool watchdog_enable) |
91 : MaxMessageDelay_(excessive_duration), | 183 : helper_(thread_name, excessive_duration, watchdog_enable) {} |
92 slow_processing_counter_(std::string("Chrome.SlowMsg") + thread_name), | 184 |
93 queueing_delay_counter_(std::string("Chrome.DelayMsg") + thread_name), | 185 ~IOJankObserver() {} |
94 total_time_watchdog_(excessive_duration, thread_name, watchdog_enable) { | |
95 process_times_ = Histogram::FactoryGet( | |
96 std::string("Chrome.ProcMsgL ") + thread_name, | |
97 1, 3600000, 50, Histogram::kUmaTargetedHistogramFlag); | |
98 total_times_ = Histogram::FactoryGet( | |
99 std::string("Chrome.TotalMsgL ") + thread_name, | |
100 1, 3600000, 50, Histogram::kUmaTargetedHistogramFlag); | |
101 } | |
102 | 186 |
103 // Attaches the observer to the current thread's message loop. You can only | 187 // Attaches the observer to the current thread's message loop. You can only |
104 // attach to the current thread, so this function can be invoked on another | 188 // attach to the current thread, so this function can be invoked on another |
105 // thread to attach it. | 189 // thread to attach it. |
106 void AttachToCurrentThread() { | 190 void AttachToCurrentThread() { |
107 // TODO(darin): support monitoring jankiness on non-UI threads! | 191 MessageLoop::current()->AddTaskObserver(this); |
108 if (MessageLoop::current()->type() == MessageLoop::TYPE_UI) | 192 MessageLoopForIO::current()->AddIOObserver(this); |
109 MessageLoopForUI::current()->AddObserver(this); | |
110 } | 193 } |
111 | 194 |
112 // Detaches the observer to the current thread's message loop. | 195 // Detaches the observer to the current thread's message loop. |
113 void DetachFromCurrentThread() { | 196 void DetachFromCurrentThread() { |
114 if (MessageLoop::current()->type() == MessageLoop::TYPE_UI) | 197 MessageLoopForIO::current()->RemoveIOObserver(this); |
115 MessageLoopForUI::current()->RemoveObserver(this); | 198 MessageLoop::current()->RemoveTaskObserver(this); |
116 } | 199 } |
117 | 200 |
118 // Called when a message has just begun processing, initializes | 201 virtual void WillProcessIOEvent() { |
119 // per-message variables and timers. | 202 helper_.StartProcessingTimers(base::TimeDelta()); |
120 void StartProcessingTimers() { | |
121 // Simulate arming when the message entered the queue. | |
122 total_time_watchdog_.ArmSomeTimeDeltaAgo(queueing_time_); | |
123 if (queueing_time_ > MaxMessageDelay_) { | |
124 // Message is too delayed. | |
125 queueing_delay_counter_.Increment(); | |
126 #if defined(OS_WIN) | |
127 if (kPlaySounds) | |
128 MessageBeep(MB_ICONASTERISK); | |
129 #endif | |
130 } | |
131 } | 203 } |
132 | 204 |
133 // Called when a message has just finished processing, finalizes | 205 virtual void DidProcessIOEvent() { |
134 // per-message variables and timers. | 206 helper_.EndProcessingTimers(); |
135 void EndProcessingTimers() { | 207 } |
136 total_time_watchdog_.Disarm(); | 208 |
137 TimeTicks now = TimeTicks::Now(); | 209 virtual void WillProcessTask(base::TimeTicks birth_time) { |
138 if (begin_process_message_ != TimeTicks()) { | 210 base::TimeTicks now = base::TimeTicks::Now(); |
139 TimeDelta processing_time = now - begin_process_message_; | 211 const base::TimeDelta queueing_time = now - birth_time; |
140 process_times_->AddTime(processing_time); | 212 helper_.StartProcessingTimers(queueing_time); |
141 total_times_->AddTime(queueing_time_ + processing_time); | 213 } |
142 } | 214 |
143 if (now - begin_process_message_ > | 215 virtual void DidProcessTask() { |
144 TimeDelta::FromMilliseconds(kMaxMessageProcessingMs)) { | 216 helper_.EndProcessingTimers(); |
145 // Message took too long to process. | 217 } |
146 slow_processing_counter_.Increment(); | 218 |
147 #if defined(OS_WIN) | 219 private: |
148 if (kPlaySounds) | 220 friend class base::RefCountedThreadSafe<IOJankObserver>; |
149 MessageBeep(MB_ICONHAND); | 221 |
150 #endif | 222 JankObserverHelper helper_; |
151 } | 223 |
| 224 DISALLOW_COPY_AND_ASSIGN(IOJankObserver); |
| 225 }; |
| 226 |
| 227 //------------------------------------------------------------------------------ |
| 228 class UIJankObserver : public base::RefCountedThreadSafe<UIJankObserver>, |
| 229 public MessageLoop::TaskObserver, |
| 230 public MessageLoopForUI::Observer { |
| 231 public: |
| 232 UIJankObserver(const char* thread_name, |
| 233 TimeDelta excessive_duration, |
| 234 bool watchdog_enable) |
| 235 : helper_(thread_name, excessive_duration, watchdog_enable) {} |
| 236 |
| 237 // Attaches the observer to the current thread's message loop. You can only |
| 238 // attach to the current thread, so this function can be invoked on another |
| 239 // thread to attach it. |
| 240 void AttachToCurrentThread() { |
| 241 DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI); |
| 242 MessageLoopForUI::current()->AddObserver(this); |
| 243 MessageLoop::current()->AddTaskObserver(this); |
| 244 } |
| 245 |
| 246 // Detaches the observer to the current thread's message loop. |
| 247 void DetachFromCurrentThread() { |
| 248 DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI); |
| 249 MessageLoop::current()->RemoveTaskObserver(this); |
| 250 MessageLoopForUI::current()->RemoveObserver(this); |
| 251 } |
| 252 |
| 253 virtual void WillProcessTask(base::TimeTicks birth_time) { |
| 254 base::TimeTicks now = base::TimeTicks::Now(); |
| 255 const base::TimeDelta queueing_time = now - birth_time; |
| 256 helper_.StartProcessingTimers(queueing_time); |
| 257 } |
| 258 |
| 259 virtual void DidProcessTask() { |
| 260 helper_.EndProcessingTimers(); |
152 } | 261 } |
153 | 262 |
154 #if defined(OS_WIN) | 263 #if defined(OS_WIN) |
155 virtual void WillProcessMessage(const MSG& msg) { | 264 virtual void WillProcessMessage(const MSG& msg) { |
156 begin_process_message_ = TimeTicks::Now(); | |
157 | |
158 // GetMessageTime returns a LONG (signed 32-bit) and GetTickCount returns | 265 // GetMessageTime returns a LONG (signed 32-bit) and GetTickCount returns |
159 // a DWORD (unsigned 32-bit). They both wrap around when the time is longer | 266 // a DWORD (unsigned 32-bit). They both wrap around when the time is longer |
160 // than they can hold. I'm not sure if GetMessageTime wraps around to 0, | 267 // than they can hold. I'm not sure if GetMessageTime wraps around to 0, |
161 // or if the original time comes from GetTickCount, it might wrap around | 268 // or if the original time comes from GetTickCount, it might wrap around |
162 // to -1. | 269 // to -1. |
163 // | 270 // |
164 // Therefore, I cast to DWORD so if it wraps to -1 we will correct it. If | 271 // Therefore, I cast to DWORD so if it wraps to -1 we will correct it. If |
165 // it doesn't, then our time delta will be negative if a message happens | 272 // it doesn't, then our time delta will be negative if a message happens |
166 // to straddle the wraparound point, it will still be OK. | 273 // to straddle the wraparound point, it will still be OK. |
167 DWORD cur_message_issue_time = static_cast<DWORD>(msg.time); | 274 DWORD cur_message_issue_time = static_cast<DWORD>(msg.time); |
168 DWORD cur_time = GetTickCount(); | 275 DWORD cur_time = GetTickCount(); |
169 queueing_time_ = | 276 base::TimeDelta queueing_time = |
170 base::TimeDelta::FromMilliseconds(cur_time - cur_message_issue_time); | 277 base::TimeDelta::FromMilliseconds(cur_time - cur_message_issue_time); |
171 | 278 |
172 StartProcessingTimers(); | 279 helper_.StartProcessingTimers(queueing_time); |
173 } | 280 } |
174 | 281 |
175 virtual void DidProcessMessage(const MSG& msg) { | 282 virtual void DidProcessMessage(const MSG& msg) { |
176 EndProcessingTimers(); | 283 helper_.EndProcessingTimers(); |
177 } | 284 } |
178 #elif defined(TOOLKIT_USES_GTK) | 285 #elif defined(TOOLKIT_USES_GTK) |
179 virtual void WillProcessEvent(GdkEvent* event) { | 286 virtual void WillProcessEvent(GdkEvent* event) { |
180 begin_process_message_ = TimeTicks::Now(); | |
181 // TODO(evanm): we want to set queueing_time_ using | 287 // TODO(evanm): we want to set queueing_time_ using |
182 // gdk_event_get_time, but how do you convert that info | 288 // gdk_event_get_time, but how do you convert that info |
183 // into a delta? | 289 // into a delta? |
184 // guint event_time = gdk_event_get_time(event); | 290 // guint event_time = gdk_event_get_time(event); |
185 queueing_time_ = base::TimeDelta::FromMilliseconds(0); | 291 base::TimeDelta queueing_time = base::TimeDelta::FromMilliseconds(0); |
186 StartProcessingTimers(); | 292 helper_.StartProcessingTimers(queueing_time); |
187 } | 293 } |
188 | 294 |
189 virtual void DidProcessEvent(GdkEvent* event) { | 295 virtual void DidProcessEvent(GdkEvent* event) { |
190 EndProcessingTimers(); | 296 helper_.EndProcessingTimers(); |
191 } | 297 } |
192 #endif | 298 #endif |
193 | 299 |
194 private: | 300 private: |
195 friend class base::RefCountedThreadSafe<JankObserver>; | 301 friend class base::RefCountedThreadSafe<UIJankObserver>; |
196 | 302 |
197 ~JankObserver() {} | 303 ~UIJankObserver() {} |
198 | 304 |
199 const TimeDelta MaxMessageDelay_; | 305 JankObserverHelper helper_; |
200 | 306 |
201 // Time at which the current message processing began. | 307 DISALLOW_COPY_AND_ASSIGN(UIJankObserver); |
202 TimeTicks begin_process_message_; | |
203 | |
204 // Time the current message spent in the queue -- delta between message | |
205 // construction time and message processing time. | |
206 TimeDelta queueing_time_; | |
207 | |
208 // Counters for the two types of jank we measure. | |
209 StatsCounter slow_processing_counter_; // Messages with long processing time. | |
210 StatsCounter queueing_delay_counter_; // Messages with long queueing delay. | |
211 scoped_refptr<Histogram> process_times_; // Time spent processing task. | |
212 scoped_refptr<Histogram> total_times_; // Total queueing plus processing. | |
213 JankWatchdog total_time_watchdog_; // Watching for excessive total_time. | |
214 | |
215 DISALLOW_EVIL_CONSTRUCTORS(JankObserver); | |
216 }; | 308 }; |
217 | 309 |
218 // These objects are created by InstallJankometer and leaked. | 310 // These objects are created by InstallJankometer and leaked. |
219 JankObserver* ui_observer = NULL; | 311 const scoped_refptr<UIJankObserver>* ui_observer = NULL; |
220 JankObserver* io_observer = NULL; | 312 const scoped_refptr<IOJankObserver>* io_observer = NULL; |
221 | 313 |
222 } // namespace | 314 } // namespace |
223 | 315 |
224 void InstallJankometer(const CommandLine& parsed_command_line) { | 316 void InstallJankometer(const CommandLine& parsed_command_line) { |
225 if (ui_observer || io_observer) { | 317 if (ui_observer || io_observer) { |
226 NOTREACHED() << "Initializing jank-o-meter twice"; | 318 NOTREACHED() << "Initializing jank-o-meter twice"; |
227 return; | 319 return; |
228 } | 320 } |
229 | 321 |
230 bool ui_watchdog_enabled = false; | 322 bool ui_watchdog_enabled = false; |
231 bool io_watchdog_enabled = false; | 323 bool io_watchdog_enabled = false; |
232 if (parsed_command_line.HasSwitch(switches::kEnableWatchdog)) { | 324 if (parsed_command_line.HasSwitch(switches::kEnableWatchdog)) { |
233 std::string list = | 325 std::string list = |
234 parsed_command_line.GetSwitchValueASCII(switches::kEnableWatchdog); | 326 parsed_command_line.GetSwitchValueASCII(switches::kEnableWatchdog); |
235 if (list.npos != list.find("ui")) | 327 if (list.npos != list.find("ui")) |
236 ui_watchdog_enabled = true; | 328 ui_watchdog_enabled = true; |
237 if (list.npos != list.find("io")) | 329 if (list.npos != list.find("io")) |
238 io_watchdog_enabled = true; | 330 io_watchdog_enabled = true; |
239 } | 331 } |
240 | 332 |
241 // Install on the UI thread. | 333 // Install on the UI thread. |
242 ui_observer = new JankObserver( | 334 ui_observer = new scoped_refptr<UIJankObserver>( |
243 "UI", | 335 new UIJankObserver( |
244 TimeDelta::FromMilliseconds(kMaxUIMessageDelayMs), | 336 "UI", |
245 ui_watchdog_enabled); | 337 TimeDelta::FromMilliseconds(kMaxUIMessageDelayMs), |
246 ui_observer->AddRef(); | 338 ui_watchdog_enabled)); |
247 ui_observer->AttachToCurrentThread(); | 339 (*ui_observer)->AttachToCurrentThread(); |
248 | 340 |
249 // Now install on the I/O thread. Hiccups on that thread will block | 341 // Now install on the I/O thread. Hiccups on that thread will block |
250 // interaction with web pages. We must proxy to that thread before we can | 342 // interaction with web pages. We must proxy to that thread before we can |
251 // add our observer. | 343 // add our observer. |
252 io_observer = new JankObserver( | 344 io_observer = new scoped_refptr<IOJankObserver>( |
253 "IO", | 345 new IOJankObserver( |
254 TimeDelta::FromMilliseconds(kMaxIOMessageDelayMs), | 346 "IO", |
255 io_watchdog_enabled); | 347 TimeDelta::FromMilliseconds(kMaxIOMessageDelayMs), |
256 io_observer->AddRef(); | 348 io_watchdog_enabled)); |
257 ChromeThread::PostTask( | 349 ChromeThread::PostTask( |
258 ChromeThread::IO, FROM_HERE, | 350 ChromeThread::IO, FROM_HERE, |
259 NewRunnableMethod(io_observer, &JankObserver::AttachToCurrentThread)); | 351 NewRunnableMethod(io_observer->get(), |
| 352 &IOJankObserver::AttachToCurrentThread)); |
260 } | 353 } |
261 | 354 |
262 void UninstallJankometer() { | 355 void UninstallJankometer() { |
263 if (ui_observer) { | 356 if (ui_observer) { |
264 ui_observer->DetachFromCurrentThread(); | 357 (*ui_observer)->DetachFromCurrentThread(); |
265 ui_observer->Release(); | 358 delete ui_observer; |
266 ui_observer = NULL; | 359 ui_observer = NULL; |
267 } | 360 } |
268 if (io_observer) { | 361 if (io_observer) { |
269 // IO thread can't be running when we remove observers. | 362 // IO thread can't be running when we remove observers. |
270 DCHECK((!g_browser_process) || !(g_browser_process->io_thread())); | 363 DCHECK((!g_browser_process) || !(g_browser_process->io_thread())); |
271 io_observer->Release(); | 364 delete io_observer; |
272 io_observer = NULL; | 365 io_observer = NULL; |
273 } | 366 } |
274 } | 367 } |
OLD | NEW |