| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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 <string> | 5 #include <string> |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/bind_helpers.h" | 8 #include "base/bind_helpers.h" |
| 9 #include "base/callback.h" | 9 #include "base/callback.h" |
| 10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 47 // There may be too many session restores to collect a profile each time. Limit | 47 // There may be too many session restores to collect a profile each time. Limit |
| 48 // the collection rate by collecting one per 10 restores. Adjust this number as | 48 // the collection rate by collecting one per 10 restores. Adjust this number as |
| 49 // needed. | 49 // needed. |
| 50 const int kRestoreSessionSamplingFactor = 10; | 50 const int kRestoreSessionSamplingFactor = 10; |
| 51 | 51 |
| 52 // This is used to space out session restore collections in the face of several | 52 // This is used to space out session restore collections in the face of several |
| 53 // notifications in a short period of time. There should be no less than this | 53 // notifications in a short period of time. There should be no less than this |
| 54 // much time between collections. The current value is 30 seconds. | 54 // much time between collections. The current value is 30 seconds. |
| 55 const int kMinIntervalBetweenSessionRestoreCollectionsMs = 30 * 1000; | 55 const int kMinIntervalBetweenSessionRestoreCollectionsMs = 30 * 1000; |
| 56 | 56 |
| 57 // If collecting after a resume, add a random delay before collecting. The delay |
| 58 // should be randomly selected between 0 and this value. Currently the value is |
| 59 // equal to 5 seconds. |
| 60 const int kMaxResumeCollectionDelayMs = 5 * 1000; |
| 61 |
| 62 // If collecting after a session restore, add a random delay before collecting. |
| 63 // The delay should be randomly selected between 0 and this value. Currently the |
| 64 // value is equal to 10 seconds. |
| 65 const int kMaxRestoreSessionCollectionDelayMs = 10 * 1000; |
| 66 |
| 57 // Enumeration representing success and various failure modes for collecting and | 67 // Enumeration representing success and various failure modes for collecting and |
| 58 // sending perf data. | 68 // sending perf data. |
| 59 enum GetPerfDataOutcome { | 69 enum GetPerfDataOutcome { |
| 60 SUCCESS, | 70 SUCCESS, |
| 61 NOT_READY_TO_UPLOAD, | 71 NOT_READY_TO_UPLOAD, |
| 62 NOT_READY_TO_COLLECT, | 72 NOT_READY_TO_COLLECT, |
| 63 INCOGNITO_ACTIVE, | 73 INCOGNITO_ACTIVE, |
| 64 INCOGNITO_LAUNCHED, | 74 INCOGNITO_LAUNCHED, |
| 65 PROTOBUF_NOT_PARSED, | 75 PROTOBUF_NOT_PARSED, |
| 66 NUM_OUTCOMES | 76 NUM_OUTCOMES |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 183 // the lid or idling when not logged in is currently to shut down instead of | 193 // the lid or idling when not logged in is currently to shut down instead of |
| 184 // suspending. But it's good to enforce the rule here in case that changes. | 194 // suspending. But it's good to enforce the rule here in case that changes. |
| 185 if (!IsNormalUserLoggedIn()) | 195 if (!IsNormalUserLoggedIn()) |
| 186 return; | 196 return; |
| 187 | 197 |
| 188 // Collect a profile only 1/|kResumeSamplingFactor| of the time, to avoid | 198 // Collect a profile only 1/|kResumeSamplingFactor| of the time, to avoid |
| 189 // collecting too much data. | 199 // collecting too much data. |
| 190 if (base::RandGenerator(kResumeSamplingFactor) != 0) | 200 if (base::RandGenerator(kResumeSamplingFactor) != 0) |
| 191 return; | 201 return; |
| 192 | 202 |
| 193 // Fill out a SampledProfile protobuf that will contain the collected data. | 203 // Override any existing profiling. |
| 194 scoped_ptr<SampledProfile> sampled_profile(new SampledProfile); | 204 if (timer_.IsRunning()) |
| 195 sampled_profile->set_trigger_event(SampledProfile::RESUME_FROM_SUSPEND); | 205 timer_.Stop(); |
| 196 sampled_profile->set_suspend_duration_ms(sleep_duration.InMilliseconds()); | 206 |
| 197 // TODO(sque): Vary the time after resume at which to collect a profile. | 207 // Randomly pick a delay before doing the collection. |
| 198 // http://crbug.com/358778. | 208 base::TimeDelta collection_delay = |
| 199 sampled_profile->set_ms_after_resume(0); | 209 base::TimeDelta::FromMilliseconds( |
| 200 CollectIfNecessary(sampled_profile.Pass()); | 210 base::RandGenerator(kMaxResumeCollectionDelayMs)); |
| 211 timer_.Start(FROM_HERE, |
| 212 collection_delay, |
| 213 base::Bind(&PerfProvider::CollectPerfDataAfterResume, |
| 214 weak_factory_.GetWeakPtr(), |
| 215 sleep_duration, |
| 216 collection_delay)); |
| 201 } | 217 } |
| 202 | 218 |
| 203 void PerfProvider::Observe(int type, | 219 void PerfProvider::Observe(int type, |
| 204 const content::NotificationSource& source, | 220 const content::NotificationSource& source, |
| 205 const content::NotificationDetails& details) { | 221 const content::NotificationDetails& details) { |
| 206 // Only handle session restore notifications. | 222 // Only handle session restore notifications. |
| 207 DCHECK_EQ(type, chrome::NOTIFICATION_SESSION_RESTORE_DONE); | 223 DCHECK_EQ(type, chrome::NOTIFICATION_SESSION_RESTORE_DONE); |
| 208 | 224 |
| 225 // Do not collect a profile unless logged in as a normal user. |
| 226 if (!IsNormalUserLoggedIn()) |
| 227 return; |
| 228 |
| 209 // Collect a profile only 1/|kRestoreSessionSamplingFactor| of the time, to | 229 // Collect a profile only 1/|kRestoreSessionSamplingFactor| of the time, to |
| 210 // avoid collecting too much data and potentially causing UI latency. | 230 // avoid collecting too much data and potentially causing UI latency. |
| 211 if (base::RandGenerator(kRestoreSessionSamplingFactor) != 0) | 231 if (base::RandGenerator(kRestoreSessionSamplingFactor) != 0) |
| 212 return; | 232 return; |
| 213 | 233 |
| 214 const base::TimeDelta min_interval = | 234 const base::TimeDelta min_interval = |
| 215 base::TimeDelta::FromMilliseconds( | 235 base::TimeDelta::FromMilliseconds( |
| 216 kMinIntervalBetweenSessionRestoreCollectionsMs); | 236 kMinIntervalBetweenSessionRestoreCollectionsMs); |
| 217 const base::TimeDelta time_since_last_collection = | 237 const base::TimeDelta time_since_last_collection = |
| 218 (base::TimeTicks::Now() - last_session_restore_collection_time_); | 238 (base::TimeTicks::Now() - last_session_restore_collection_time_); |
| 219 // Do not collect if there hasn't been enough elapsed time since the last | 239 // Do not collect if there hasn't been enough elapsed time since the last |
| 220 // collection. | 240 // collection. |
| 221 if (!last_session_restore_collection_time_.is_null() && | 241 if (!last_session_restore_collection_time_.is_null() && |
| 222 time_since_last_collection < min_interval) { | 242 time_since_last_collection < min_interval) { |
| 223 return; | 243 return; |
| 224 } | 244 } |
| 225 | 245 |
| 226 // Fill out a SampledProfile protobuf that will contain the collected data. | 246 // Stop any existing scheduled collection. |
| 227 scoped_ptr<SampledProfile> sampled_profile(new SampledProfile); | 247 if (timer_.IsRunning()) |
| 228 sampled_profile->set_trigger_event(SampledProfile::RESTORE_SESSION); | 248 timer_.Stop(); |
| 229 // TODO(sque): Vary the time after restore at which to collect a profile, | |
| 230 // and find a way to determine the number of tabs restored. | |
| 231 // http://crbug.com/358778. | |
| 232 sampled_profile->set_ms_after_restore(0); | |
| 233 | 249 |
| 234 CollectIfNecessary(sampled_profile.Pass()); | 250 // Randomly pick a delay before doing the collection. |
| 235 last_session_restore_collection_time_ = base::TimeTicks::Now(); | 251 base::TimeDelta collection_delay = |
| 252 base::TimeDelta::FromMilliseconds( |
| 253 base::RandGenerator(kMaxRestoreSessionCollectionDelayMs)); |
| 254 timer_.Start( |
| 255 FROM_HERE, |
| 256 collection_delay, |
| 257 base::Bind(&PerfProvider::CollectPerfDataAfterSessionRestore, |
| 258 weak_factory_.GetWeakPtr(), |
| 259 collection_delay)); |
| 236 } | 260 } |
| 237 | 261 |
| 238 void PerfProvider::OnUserLoggedIn() { | 262 void PerfProvider::OnUserLoggedIn() { |
| 239 login_time_ = base::TimeTicks::Now(); | 263 login_time_ = base::TimeTicks::Now(); |
| 240 ScheduleCollection(); | 264 ScheduleIntervalCollection(); |
| 241 } | 265 } |
| 242 | 266 |
| 243 void PerfProvider::Deactivate() { | 267 void PerfProvider::Deactivate() { |
| 244 // Stop the timer, but leave |cached_perf_data_| intact. | 268 // Stop the timer, but leave |cached_perf_data_| intact. |
| 245 timer_.Stop(); | 269 timer_.Stop(); |
| 246 } | 270 } |
| 247 | 271 |
| 248 void PerfProvider::ScheduleCollection() { | 272 void PerfProvider::ScheduleIntervalCollection() { |
| 249 DCHECK(CalledOnValidThread()); | 273 DCHECK(CalledOnValidThread()); |
| 250 if (timer_.IsRunning()) | 274 if (timer_.IsRunning()) |
| 251 return; | 275 return; |
| 252 | 276 |
| 253 // Pick a random time in the current interval. | 277 // Pick a random time in the current interval. |
| 254 base::TimeTicks scheduled_time = | 278 base::TimeTicks scheduled_time = |
| 255 next_profiling_interval_start_ + | 279 next_profiling_interval_start_ + |
| 256 base::TimeDelta::FromMilliseconds( | 280 base::TimeDelta::FromMilliseconds( |
| 257 base::RandGenerator(kPerfProfilingIntervalMs)); | 281 base::RandGenerator(kPerfProfilingIntervalMs)); |
| 258 | 282 |
| 259 // If the scheduled time has already passed in the time it took to make the | 283 // If the scheduled time has already passed in the time it took to make the |
| 260 // above calculations, trigger the collection event immediately. | 284 // above calculations, trigger the collection event immediately. |
| 261 base::TimeTicks now = base::TimeTicks::Now(); | 285 base::TimeTicks now = base::TimeTicks::Now(); |
| 262 if (scheduled_time < now) | 286 if (scheduled_time < now) |
| 263 scheduled_time = now; | 287 scheduled_time = now; |
| 264 | 288 |
| 265 timer_.Start(FROM_HERE, scheduled_time - now, this, | 289 timer_.Start(FROM_HERE, scheduled_time - now, this, |
| 266 &PerfProvider::DoPeriodicCollection); | 290 &PerfProvider::DoPeriodicCollection); |
| 267 | 291 |
| 268 // Update the profiling interval tracker to the start of the next interval. | 292 // Update the profiling interval tracker to the start of the next interval. |
| 269 next_profiling_interval_start_ += | 293 next_profiling_interval_start_ += |
| 270 base::TimeDelta::FromMilliseconds(kPerfProfilingIntervalMs); | 294 base::TimeDelta::FromMilliseconds(kPerfProfilingIntervalMs); |
| 271 } | 295 } |
| 272 | 296 |
| 273 void PerfProvider::CollectIfNecessary( | 297 void PerfProvider::CollectIfNecessary( |
| 274 scoped_ptr<SampledProfile> sampled_profile) { | 298 scoped_ptr<SampledProfile> sampled_profile) { |
| 275 DCHECK(CalledOnValidThread()); | 299 DCHECK(CalledOnValidThread()); |
| 276 | 300 |
| 301 // Schedule another interval collection. This call makes sense regardless of |
| 302 // whether or not the current collection was interval-triggered. If it had |
| 303 // been another type of trigger event, the interval timer would have been |
| 304 // halted, so it makes sense to reschedule a new interval collection. |
| 305 ScheduleIntervalCollection(); |
| 306 |
| 277 // Do not collect further data if we've already collected a substantial amount | 307 // Do not collect further data if we've already collected a substantial amount |
| 278 // of data, as indicated by |kCachedPerfDataProtobufSizeThreshold|. | 308 // of data, as indicated by |kCachedPerfDataProtobufSizeThreshold|. |
| 279 size_t cached_perf_data_size = 0; | 309 size_t cached_perf_data_size = 0; |
| 280 for (size_t i = 0; i < cached_perf_data_.size(); ++i) { | 310 for (size_t i = 0; i < cached_perf_data_.size(); ++i) { |
| 281 cached_perf_data_size += cached_perf_data_[i].ByteSize(); | 311 cached_perf_data_size += cached_perf_data_[i].ByteSize(); |
| 282 } | 312 } |
| 283 if (cached_perf_data_size >= kCachedPerfDataProtobufSizeThreshold) { | 313 if (cached_perf_data_size >= kCachedPerfDataProtobufSizeThreshold) { |
| 284 AddToPerfHistogram(NOT_READY_TO_COLLECT); | 314 AddToPerfHistogram(NOT_READY_TO_COLLECT); |
| 285 return; | 315 return; |
| 286 } | 316 } |
| (...skipping 19 matching lines...) Expand all Loading... |
| 306 weak_factory_.GetWeakPtr(), | 336 weak_factory_.GetWeakPtr(), |
| 307 base::Passed(&incognito_observer), | 337 base::Passed(&incognito_observer), |
| 308 base::Passed(&sampled_profile))); | 338 base::Passed(&sampled_profile))); |
| 309 } | 339 } |
| 310 | 340 |
| 311 void PerfProvider::DoPeriodicCollection() { | 341 void PerfProvider::DoPeriodicCollection() { |
| 312 scoped_ptr<SampledProfile> sampled_profile(new SampledProfile); | 342 scoped_ptr<SampledProfile> sampled_profile(new SampledProfile); |
| 313 sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION); | 343 sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION); |
| 314 | 344 |
| 315 CollectIfNecessary(sampled_profile.Pass()); | 345 CollectIfNecessary(sampled_profile.Pass()); |
| 316 ScheduleCollection(); | 346 } |
| 347 |
| 348 void PerfProvider::CollectPerfDataAfterResume( |
| 349 const base::TimeDelta& sleep_duration, |
| 350 const base::TimeDelta& time_after_resume) { |
| 351 // Fill out a SampledProfile protobuf that will contain the collected data. |
| 352 scoped_ptr<SampledProfile> sampled_profile(new SampledProfile); |
| 353 sampled_profile->set_trigger_event(SampledProfile::RESUME_FROM_SUSPEND); |
| 354 sampled_profile->set_suspend_duration_ms(sleep_duration.InMilliseconds()); |
| 355 sampled_profile->set_ms_after_resume(time_after_resume.InMilliseconds()); |
| 356 |
| 357 CollectIfNecessary(sampled_profile.Pass()); |
| 358 } |
| 359 |
| 360 void PerfProvider::CollectPerfDataAfterSessionRestore( |
| 361 const base::TimeDelta& time_after_restore) { |
| 362 // Fill out a SampledProfile protobuf that will contain the collected data. |
| 363 scoped_ptr<SampledProfile> sampled_profile(new SampledProfile); |
| 364 sampled_profile->set_trigger_event(SampledProfile::RESTORE_SESSION); |
| 365 sampled_profile->set_ms_after_restore(time_after_restore.InMilliseconds()); |
| 366 |
| 367 CollectIfNecessary(sampled_profile.Pass()); |
| 368 last_session_restore_collection_time_ = base::TimeTicks::Now(); |
| 317 } | 369 } |
| 318 | 370 |
| 319 void PerfProvider::ParseProtoIfValid( | 371 void PerfProvider::ParseProtoIfValid( |
| 320 scoped_ptr<WindowedIncognitoObserver> incognito_observer, | 372 scoped_ptr<WindowedIncognitoObserver> incognito_observer, |
| 321 scoped_ptr<SampledProfile> sampled_profile, | 373 scoped_ptr<SampledProfile> sampled_profile, |
| 322 const std::vector<uint8>& data) { | 374 const std::vector<uint8>& data) { |
| 323 DCHECK(CalledOnValidThread()); | 375 DCHECK(CalledOnValidThread()); |
| 324 | 376 |
| 325 if (incognito_observer->incognito_launched()) { | 377 if (incognito_observer->incognito_launched()) { |
| 326 AddToPerfHistogram(INCOGNITO_LAUNCHED); | 378 AddToPerfHistogram(INCOGNITO_LAUNCHED); |
| (...skipping 19 matching lines...) Expand all Loading... |
| 346 DCHECK(!login_time_.is_null()); | 398 DCHECK(!login_time_.is_null()); |
| 347 collection_data. | 399 collection_data. |
| 348 set_ms_after_login((base::TimeTicks::Now() - login_time_) | 400 set_ms_after_login((base::TimeTicks::Now() - login_time_) |
| 349 .InMilliseconds()); | 401 .InMilliseconds()); |
| 350 | 402 |
| 351 // Finally, store the perf data itself. | 403 // Finally, store the perf data itself. |
| 352 collection_data.mutable_perf_data()->Swap(&perf_data_proto); | 404 collection_data.mutable_perf_data()->Swap(&perf_data_proto); |
| 353 } | 405 } |
| 354 | 406 |
| 355 } // namespace metrics | 407 } // namespace metrics |
| OLD | NEW |