Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(333)

Side by Side Diff: chrome/browser/jankometer.cc

Issue 2098020: Jankometer: Generalize the code more. Add better support for monitoring IO thread. (Closed) Base URL: http://src.chromium.org/git/chromium.git
Patch Set: Address darin's comments. Created 10 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/tracked.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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 }
OLDNEW
« no previous file with comments | « base/tracked.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698