Make castv2 performance test work.
[chromium-blink-merge.git] / chrome / browser / metrics / perf_provider_chromeos.cc
blob6bb48a3bef87c1f86d7d55ec51eae18ae9894c05
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 <string>
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/callback.h"
10 #include "base/command_line.h"
11 #include "base/compiler_specific.h"
12 #include "base/metrics/histogram.h"
13 #include "base/rand_util.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/threading/sequenced_worker_pool.h"
16 #include "chrome/browser/chrome_notification_types.h"
17 #include "chrome/browser/metrics/perf_provider_chromeos.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/browser/ui/browser_list.h"
21 #include "chrome/browser/ui/browser_list_observer.h"
22 #include "chrome/common/chrome_switches.h"
23 #include "chromeos/dbus/dbus_thread_manager.h"
24 #include "chromeos/dbus/debug_daemon_client.h"
25 #include "content/public/browser/notification_service.h"
27 namespace {
29 // Partition time since login into successive intervals of this size. In each
30 // interval, pick a random time to collect a profile.
31 const size_t kPerfProfilingIntervalMs = 3 * 60 * 60 * 1000;
33 // Default time in seconds perf is run for.
34 const size_t kPerfCommandDurationDefaultSeconds = 2;
36 // Limit the total size of protobufs that can be cached, so they don't take up
37 // too much memory. If the size of cached protobufs exceeds this value, stop
38 // collecting further perf data. The current value is 4 MB.
39 const size_t kCachedPerfDataProtobufSizeThreshold = 4 * 1024 * 1024;
41 // There may be too many suspends to collect a profile each time there is a
42 // resume. To limit the number of profiles, collect one for 1 in 10 resumes.
43 // Adjust this number as needed.
44 const int kResumeSamplingFactor = 10;
46 // There may be too many session restores to collect a profile each time. Limit
47 // the collection rate by collecting one per 10 restores. Adjust this number as
48 // needed.
49 const int kRestoreSessionSamplingFactor = 10;
51 // This is used to space out session restore collections in the face of several
52 // notifications in a short period of time. There should be no less than this
53 // much time between collections. The current value is 30 seconds.
54 const int kMinIntervalBetweenSessionRestoreCollectionsMs = 30 * 1000;
56 // If collecting after a resume, add a random delay before collecting. The delay
57 // should be randomly selected between 0 and this value. Currently the value is
58 // equal to 5 seconds.
59 const int kMaxResumeCollectionDelayMs = 5 * 1000;
61 // If collecting after a session restore, add a random delay before collecting.
62 // The delay should be randomly selected between 0 and this value. Currently the
63 // value is equal to 10 seconds.
64 const int kMaxRestoreSessionCollectionDelayMs = 10 * 1000;
66 // Enumeration representing success and various failure modes for collecting and
67 // sending perf data.
68 enum GetPerfDataOutcome {
69 SUCCESS,
70 NOT_READY_TO_UPLOAD,
71 NOT_READY_TO_COLLECT,
72 INCOGNITO_ACTIVE,
73 INCOGNITO_LAUNCHED,
74 PROTOBUF_NOT_PARSED,
75 NUM_OUTCOMES
78 // Name of the histogram that represents the success and various failure modes
79 // for collecting and sending perf data.
80 const char kGetPerfDataOutcomeHistogram[] = "UMA.Perf.GetData";
82 void AddToPerfHistogram(GetPerfDataOutcome outcome) {
83 UMA_HISTOGRAM_ENUMERATION(kGetPerfDataOutcomeHistogram,
84 outcome,
85 NUM_OUTCOMES);
88 // Returns true if a normal user is logged in. Returns false otherwise (e.g. if
89 // logged in as a guest or as a kiosk app).
90 bool IsNormalUserLoggedIn() {
91 return chromeos::LoginState::Get()->IsUserAuthenticated();
94 } // namespace
97 namespace metrics {
99 // This class must be created and used on the UI thread. It watches for any
100 // incognito window being opened from the time it is instantiated to the time it
101 // is destroyed.
102 class WindowedIncognitoObserver : public chrome::BrowserListObserver {
103 public:
104 WindowedIncognitoObserver() : incognito_launched_(false) {
105 BrowserList::AddObserver(this);
108 ~WindowedIncognitoObserver() override { BrowserList::RemoveObserver(this); }
110 // This method can be checked to see whether any incognito window has been
111 // opened since the time this object was created.
112 bool incognito_launched() {
113 return incognito_launched_;
116 private:
117 // chrome::BrowserListObserver implementation.
118 void OnBrowserAdded(Browser* browser) override {
119 if (browser->profile()->IsOffTheRecord())
120 incognito_launched_ = true;
123 bool incognito_launched_;
126 PerfProvider::PerfProvider()
127 : login_observer_(this),
128 next_profiling_interval_start_(base::TimeTicks::Now()),
129 weak_factory_(this) {
130 // Register the login observer with LoginState.
131 chromeos::LoginState::Get()->AddObserver(&login_observer_);
133 // Register as an observer of power manager events.
134 chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
135 AddObserver(this);
137 // Register as an observer of session restore.
138 on_session_restored_callback_subscription_ =
139 SessionRestore::RegisterOnSessionRestoredCallback(
140 base::Bind(&PerfProvider::OnSessionRestoreDone,
141 weak_factory_.GetWeakPtr()));
143 // Check the login state. At the time of writing, this class is instantiated
144 // before login. A subsequent login would activate the profiling. However,
145 // that behavior may change in the future so that the user is already logged
146 // when this class is instantiated. By calling LoggedInStateChanged() here,
147 // PerfProvider will recognize that the system is already logged in.
148 login_observer_.LoggedInStateChanged();
151 PerfProvider::~PerfProvider() {
152 chromeos::LoginState::Get()->RemoveObserver(&login_observer_);
155 bool PerfProvider::GetSampledProfiles(
156 std::vector<SampledProfile>* sampled_profiles) {
157 DCHECK(CalledOnValidThread());
158 if (cached_perf_data_.empty()) {
159 AddToPerfHistogram(NOT_READY_TO_UPLOAD);
160 return false;
163 sampled_profiles->swap(cached_perf_data_);
164 cached_perf_data_.clear();
166 AddToPerfHistogram(SUCCESS);
167 return true;
170 PerfProvider::LoginObserver::LoginObserver(PerfProvider* perf_provider)
171 : perf_provider_(perf_provider) {}
173 void PerfProvider::LoginObserver::LoggedInStateChanged() {
174 if (IsNormalUserLoggedIn())
175 perf_provider_->OnUserLoggedIn();
176 else
177 perf_provider_->Deactivate();
180 void PerfProvider::SuspendDone(const base::TimeDelta& sleep_duration) {
181 // A zero value for the suspend duration indicates that the suspend was
182 // canceled. Do not collect anything if that's the case.
183 if (sleep_duration == base::TimeDelta())
184 return;
186 // Do not collect a profile unless logged in. The system behavior when closing
187 // the lid or idling when not logged in is currently to shut down instead of
188 // suspending. But it's good to enforce the rule here in case that changes.
189 if (!IsNormalUserLoggedIn())
190 return;
192 // Collect a profile only 1/|kResumeSamplingFactor| of the time, to avoid
193 // collecting too much data.
194 if (base::RandGenerator(kResumeSamplingFactor) != 0)
195 return;
197 // Override any existing profiling.
198 if (timer_.IsRunning())
199 timer_.Stop();
201 // Randomly pick a delay before doing the collection.
202 base::TimeDelta collection_delay =
203 base::TimeDelta::FromMilliseconds(
204 base::RandGenerator(kMaxResumeCollectionDelayMs));
205 timer_.Start(FROM_HERE,
206 collection_delay,
207 base::Bind(&PerfProvider::CollectPerfDataAfterResume,
208 weak_factory_.GetWeakPtr(),
209 sleep_duration,
210 collection_delay));
213 void PerfProvider::OnSessionRestoreDone(int num_tabs_restored) {
214 // Do not collect a profile unless logged in as a normal user.
215 if (!IsNormalUserLoggedIn())
216 return;
218 // Collect a profile only 1/|kRestoreSessionSamplingFactor| of the time, to
219 // avoid collecting too much data and potentially causing UI latency.
220 if (base::RandGenerator(kRestoreSessionSamplingFactor) != 0)
221 return;
223 const base::TimeDelta min_interval =
224 base::TimeDelta::FromMilliseconds(
225 kMinIntervalBetweenSessionRestoreCollectionsMs);
226 const base::TimeDelta time_since_last_collection =
227 (base::TimeTicks::Now() - last_session_restore_collection_time_);
228 // Do not collect if there hasn't been enough elapsed time since the last
229 // collection.
230 if (!last_session_restore_collection_time_.is_null() &&
231 time_since_last_collection < min_interval) {
232 return;
235 // Stop any existing scheduled collection.
236 if (timer_.IsRunning())
237 timer_.Stop();
239 // Randomly pick a delay before doing the collection.
240 base::TimeDelta collection_delay =
241 base::TimeDelta::FromMilliseconds(
242 base::RandGenerator(kMaxRestoreSessionCollectionDelayMs));
243 timer_.Start(
244 FROM_HERE,
245 collection_delay,
246 base::Bind(&PerfProvider::CollectPerfDataAfterSessionRestore,
247 weak_factory_.GetWeakPtr(),
248 collection_delay,
249 num_tabs_restored));
252 void PerfProvider::OnUserLoggedIn() {
253 login_time_ = base::TimeTicks::Now();
254 ScheduleIntervalCollection();
257 void PerfProvider::Deactivate() {
258 // Stop the timer, but leave |cached_perf_data_| intact.
259 timer_.Stop();
262 void PerfProvider::ScheduleIntervalCollection() {
263 DCHECK(CalledOnValidThread());
264 if (timer_.IsRunning())
265 return;
267 // Pick a random time in the current interval.
268 base::TimeTicks scheduled_time =
269 next_profiling_interval_start_ +
270 base::TimeDelta::FromMilliseconds(
271 base::RandGenerator(kPerfProfilingIntervalMs));
273 // If the scheduled time has already passed in the time it took to make the
274 // above calculations, trigger the collection event immediately.
275 base::TimeTicks now = base::TimeTicks::Now();
276 if (scheduled_time < now)
277 scheduled_time = now;
279 timer_.Start(FROM_HERE, scheduled_time - now, this,
280 &PerfProvider::DoPeriodicCollection);
282 // Update the profiling interval tracker to the start of the next interval.
283 next_profiling_interval_start_ +=
284 base::TimeDelta::FromMilliseconds(kPerfProfilingIntervalMs);
287 void PerfProvider::CollectIfNecessary(
288 scoped_ptr<SampledProfile> sampled_profile) {
289 DCHECK(CalledOnValidThread());
291 // Schedule another interval collection. This call makes sense regardless of
292 // whether or not the current collection was interval-triggered. If it had
293 // been another type of trigger event, the interval timer would have been
294 // halted, so it makes sense to reschedule a new interval collection.
295 ScheduleIntervalCollection();
297 // Do not collect further data if we've already collected a substantial amount
298 // of data, as indicated by |kCachedPerfDataProtobufSizeThreshold|.
299 size_t cached_perf_data_size = 0;
300 for (size_t i = 0; i < cached_perf_data_.size(); ++i) {
301 cached_perf_data_size += cached_perf_data_[i].ByteSize();
303 if (cached_perf_data_size >= kCachedPerfDataProtobufSizeThreshold) {
304 AddToPerfHistogram(NOT_READY_TO_COLLECT);
305 return;
308 // For privacy reasons, Chrome should only collect perf data if there is no
309 // incognito session active (or gets spawned during the collection).
310 if (BrowserList::IsOffTheRecordSessionActive()) {
311 AddToPerfHistogram(INCOGNITO_ACTIVE);
312 return;
315 scoped_ptr<WindowedIncognitoObserver> incognito_observer(
316 new WindowedIncognitoObserver);
318 chromeos::DebugDaemonClient* client =
319 chromeos::DBusThreadManager::Get()->GetDebugDaemonClient();
321 base::TimeDelta collection_duration = base::TimeDelta::FromSeconds(
322 kPerfCommandDurationDefaultSeconds);
324 client->GetPerfData(collection_duration.InSeconds(),
325 base::Bind(&PerfProvider::ParseProtoIfValid,
326 weak_factory_.GetWeakPtr(),
327 base::Passed(&incognito_observer),
328 base::Passed(&sampled_profile)));
331 void PerfProvider::DoPeriodicCollection() {
332 scoped_ptr<SampledProfile> sampled_profile(new SampledProfile);
333 sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
335 CollectIfNecessary(sampled_profile.Pass());
338 void PerfProvider::CollectPerfDataAfterResume(
339 const base::TimeDelta& sleep_duration,
340 const base::TimeDelta& time_after_resume) {
341 // Fill out a SampledProfile protobuf that will contain the collected data.
342 scoped_ptr<SampledProfile> sampled_profile(new SampledProfile);
343 sampled_profile->set_trigger_event(SampledProfile::RESUME_FROM_SUSPEND);
344 sampled_profile->set_suspend_duration_ms(sleep_duration.InMilliseconds());
345 sampled_profile->set_ms_after_resume(time_after_resume.InMilliseconds());
347 CollectIfNecessary(sampled_profile.Pass());
350 void PerfProvider::CollectPerfDataAfterSessionRestore(
351 const base::TimeDelta& time_after_restore,
352 int num_tabs_restored) {
353 // Fill out a SampledProfile protobuf that will contain the collected data.
354 scoped_ptr<SampledProfile> sampled_profile(new SampledProfile);
355 sampled_profile->set_trigger_event(SampledProfile::RESTORE_SESSION);
356 sampled_profile->set_ms_after_restore(time_after_restore.InMilliseconds());
357 sampled_profile->set_num_tabs_restored(num_tabs_restored);
359 CollectIfNecessary(sampled_profile.Pass());
360 last_session_restore_collection_time_ = base::TimeTicks::Now();
363 void PerfProvider::ParseProtoIfValid(
364 scoped_ptr<WindowedIncognitoObserver> incognito_observer,
365 scoped_ptr<SampledProfile> sampled_profile,
366 const std::vector<uint8>& data) {
367 DCHECK(CalledOnValidThread());
369 if (incognito_observer->incognito_launched()) {
370 AddToPerfHistogram(INCOGNITO_LAUNCHED);
371 return;
374 PerfDataProto perf_data_proto;
375 if (!perf_data_proto.ParseFromArray(data.data(), data.size())) {
376 AddToPerfHistogram(PROTOBUF_NOT_PARSED);
377 return;
380 // Populate a profile collection protobuf with the collected perf data and
381 // extra metadata.
382 cached_perf_data_.resize(cached_perf_data_.size() + 1);
383 SampledProfile& collection_data = cached_perf_data_.back();
384 collection_data.Swap(sampled_profile.get());
386 // Fill out remaining fields of the SampledProfile protobuf.
387 collection_data.set_ms_after_boot(
388 perf_data_proto.timestamp_sec() * base::Time::kMillisecondsPerSecond);
390 DCHECK(!login_time_.is_null());
391 collection_data.
392 set_ms_after_login((base::TimeTicks::Now() - login_time_)
393 .InMilliseconds());
395 // Finally, store the perf data itself.
396 collection_data.mutable_perf_data()->Swap(&perf_data_proto);
399 } // namespace metrics