Add new certificateProvider extension API.
[chromium-blink-merge.git] / chrome / browser / metrics / perf_provider_chromeos.cc
blob0a6891190074fbf1e65a0515f43f9a9892c53453
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/metrics/perf_provider_chromeos.h"
7 #include <string>
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/compiler_specific.h"
12 #include "base/metrics/histogram.h"
13 #include "base/rand_util.h"
14 #include "base/threading/sequenced_worker_pool.h"
15 #include "chrome/browser/metrics/windowed_incognito_observer.h"
16 #include "chrome/browser/ui/browser_list.h"
17 #include "chromeos/dbus/dbus_thread_manager.h"
18 #include "chromeos/dbus/debug_daemon_client.h"
20 namespace {
22 // Partition time since login into successive intervals of this size. In each
23 // interval, pick a random time to collect a profile.
24 const size_t kPerfProfilingIntervalMs = 3 * 60 * 60 * 1000;
26 // Default time in seconds perf is run for.
27 const size_t kPerfCommandDurationDefaultSeconds = 2;
29 // Limit the total size of protobufs that can be cached, so they don't take up
30 // too much memory. If the size of cached protobufs exceeds this value, stop
31 // collecting further perf data. The current value is 4 MB.
32 const size_t kCachedPerfDataProtobufSizeThreshold = 4 * 1024 * 1024;
34 // There may be too many suspends to collect a profile each time there is a
35 // resume. To limit the number of profiles, collect one for 1 in 10 resumes.
36 // Adjust this number as needed.
37 const int kResumeSamplingFactor = 10;
39 // There may be too many session restores to collect a profile each time. Limit
40 // the collection rate by collecting one per 10 restores. Adjust this number as
41 // needed.
42 const int kRestoreSessionSamplingFactor = 10;
44 // This is used to space out session restore collections in the face of several
45 // notifications in a short period of time. There should be no less than this
46 // much time between collections. The current value is 30 seconds.
47 const int kMinIntervalBetweenSessionRestoreCollectionsMs = 30 * 1000;
49 // If collecting after a resume, add a random delay before collecting. The delay
50 // should be randomly selected between 0 and this value. Currently the value is
51 // equal to 5 seconds.
52 const int kMaxResumeCollectionDelayMs = 5 * 1000;
54 // If collecting after a session restore, add a random delay before collecting.
55 // The delay should be randomly selected between 0 and this value. Currently the
56 // value is equal to 10 seconds.
57 const int kMaxRestoreSessionCollectionDelayMs = 10 * 1000;
59 // Enumeration representing success and various failure modes for collecting and
60 // sending perf data.
61 enum GetPerfDataOutcome {
62 SUCCESS,
63 NOT_READY_TO_UPLOAD,
64 NOT_READY_TO_COLLECT,
65 INCOGNITO_ACTIVE,
66 INCOGNITO_LAUNCHED,
67 PROTOBUF_NOT_PARSED,
68 ILLEGAL_DATA_RETURNED,
69 NUM_OUTCOMES
72 // Name of the histogram that represents the success and various failure modes
73 // for collecting and sending perf data.
74 const char kGetPerfDataOutcomeHistogram[] = "UMA.Perf.GetData";
76 void AddToPerfHistogram(GetPerfDataOutcome outcome) {
77 UMA_HISTOGRAM_ENUMERATION(kGetPerfDataOutcomeHistogram,
78 outcome,
79 NUM_OUTCOMES);
82 // Returns true if a normal user is logged in. Returns false otherwise (e.g. if
83 // logged in as a guest or as a kiosk app).
84 bool IsNormalUserLoggedIn() {
85 return chromeos::LoginState::Get()->IsUserAuthenticated();
88 } // namespace
90 namespace metrics {
92 PerfProvider::PerfProvider()
93 : login_observer_(this),
94 next_profiling_interval_start_(base::TimeTicks::Now()),
95 weak_factory_(this) {
96 // Register the login observer with LoginState.
97 chromeos::LoginState::Get()->AddObserver(&login_observer_);
99 // Register as an observer of power manager events.
100 chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
101 AddObserver(this);
103 // Register as an observer of session restore.
104 on_session_restored_callback_subscription_ =
105 SessionRestore::RegisterOnSessionRestoredCallback(
106 base::Bind(&PerfProvider::OnSessionRestoreDone,
107 weak_factory_.GetWeakPtr()));
109 // Check the login state. At the time of writing, this class is instantiated
110 // before login. A subsequent login would activate the profiling. However,
111 // that behavior may change in the future so that the user is already logged
112 // when this class is instantiated. By calling LoggedInStateChanged() here,
113 // PerfProvider will recognize that the system is already logged in.
114 login_observer_.LoggedInStateChanged();
117 PerfProvider::~PerfProvider() {
118 chromeos::LoginState::Get()->RemoveObserver(&login_observer_);
121 bool PerfProvider::GetSampledProfiles(
122 std::vector<SampledProfile>* sampled_profiles) {
123 DCHECK(CalledOnValidThread());
124 if (cached_perf_data_.empty()) {
125 AddToPerfHistogram(NOT_READY_TO_UPLOAD);
126 return false;
129 sampled_profiles->swap(cached_perf_data_);
130 cached_perf_data_.clear();
132 AddToPerfHistogram(SUCCESS);
133 return true;
136 void PerfProvider::ParseOutputProtoIfValid(
137 scoped_ptr<WindowedIncognitoObserver> incognito_observer,
138 scoped_ptr<SampledProfile> sampled_profile,
139 int result,
140 const std::vector<uint8>& perf_data,
141 const std::vector<uint8>& perf_stat) {
142 DCHECK(CalledOnValidThread());
144 if (incognito_observer->incognito_launched()) {
145 AddToPerfHistogram(INCOGNITO_LAUNCHED);
146 return;
149 if (result != 0 || (perf_data.empty() && perf_stat.empty())) {
150 AddToPerfHistogram(PROTOBUF_NOT_PARSED);
151 return;
154 if (!perf_data.empty() && !perf_stat.empty()) {
155 AddToPerfHistogram(ILLEGAL_DATA_RETURNED);
156 return;
159 if (!perf_data.empty()) {
160 PerfDataProto perf_data_proto;
161 if (!perf_data_proto.ParseFromArray(perf_data.data(), perf_data.size())) {
162 AddToPerfHistogram(PROTOBUF_NOT_PARSED);
163 return;
165 sampled_profile->set_ms_after_boot(
166 perf_data_proto.timestamp_sec() * base::Time::kMillisecondsPerSecond);
167 sampled_profile->mutable_perf_data()->Swap(&perf_data_proto);
168 } else {
169 DCHECK(!perf_stat.empty());
170 PerfStatProto perf_stat_proto;
171 if (!perf_stat_proto.ParseFromArray(perf_stat.data(), perf_stat.size())) {
172 AddToPerfHistogram(PROTOBUF_NOT_PARSED);
173 return;
175 sampled_profile->mutable_perf_stat()->Swap(&perf_stat_proto);
178 DCHECK(!login_time_.is_null());
179 sampled_profile->set_ms_after_login(
180 (base::TimeTicks::Now() - login_time_).InMilliseconds());
182 // Add the collected data to the container of collected SampledProfiles.
183 cached_perf_data_.resize(cached_perf_data_.size() + 1);
184 cached_perf_data_.back().Swap(sampled_profile.get());
187 PerfProvider::LoginObserver::LoginObserver(PerfProvider* perf_provider)
188 : perf_provider_(perf_provider) {}
190 void PerfProvider::LoginObserver::LoggedInStateChanged() {
191 if (IsNormalUserLoggedIn())
192 perf_provider_->OnUserLoggedIn();
193 else
194 perf_provider_->Deactivate();
197 void PerfProvider::SuspendDone(const base::TimeDelta& sleep_duration) {
198 // A zero value for the suspend duration indicates that the suspend was
199 // canceled. Do not collect anything if that's the case.
200 if (sleep_duration == base::TimeDelta())
201 return;
203 // Do not collect a profile unless logged in. The system behavior when closing
204 // the lid or idling when not logged in is currently to shut down instead of
205 // suspending. But it's good to enforce the rule here in case that changes.
206 if (!IsNormalUserLoggedIn())
207 return;
209 // Collect a profile only 1/|kResumeSamplingFactor| of the time, to avoid
210 // collecting too much data.
211 if (base::RandGenerator(kResumeSamplingFactor) != 0)
212 return;
214 // Override any existing profiling.
215 if (timer_.IsRunning())
216 timer_.Stop();
218 // Randomly pick a delay before doing the collection.
219 base::TimeDelta collection_delay =
220 base::TimeDelta::FromMilliseconds(
221 base::RandGenerator(kMaxResumeCollectionDelayMs));
222 timer_.Start(FROM_HERE,
223 collection_delay,
224 base::Bind(&PerfProvider::CollectPerfDataAfterResume,
225 weak_factory_.GetWeakPtr(),
226 sleep_duration,
227 collection_delay));
230 void PerfProvider::OnSessionRestoreDone(int num_tabs_restored) {
231 // Do not collect a profile unless logged in as a normal user.
232 if (!IsNormalUserLoggedIn())
233 return;
235 // Collect a profile only 1/|kRestoreSessionSamplingFactor| of the time, to
236 // avoid collecting too much data and potentially causing UI latency.
237 if (base::RandGenerator(kRestoreSessionSamplingFactor) != 0)
238 return;
240 const base::TimeDelta min_interval =
241 base::TimeDelta::FromMilliseconds(
242 kMinIntervalBetweenSessionRestoreCollectionsMs);
243 const base::TimeDelta time_since_last_collection =
244 (base::TimeTicks::Now() - last_session_restore_collection_time_);
245 // Do not collect if there hasn't been enough elapsed time since the last
246 // collection.
247 if (!last_session_restore_collection_time_.is_null() &&
248 time_since_last_collection < min_interval) {
249 return;
252 // Stop any existing scheduled collection.
253 if (timer_.IsRunning())
254 timer_.Stop();
256 // Randomly pick a delay before doing the collection.
257 base::TimeDelta collection_delay =
258 base::TimeDelta::FromMilliseconds(
259 base::RandGenerator(kMaxRestoreSessionCollectionDelayMs));
260 timer_.Start(
261 FROM_HERE,
262 collection_delay,
263 base::Bind(&PerfProvider::CollectPerfDataAfterSessionRestore,
264 weak_factory_.GetWeakPtr(),
265 collection_delay,
266 num_tabs_restored));
269 void PerfProvider::OnUserLoggedIn() {
270 login_time_ = base::TimeTicks::Now();
271 ScheduleIntervalCollection();
274 void PerfProvider::Deactivate() {
275 // Stop the timer, but leave |cached_perf_data_| intact.
276 timer_.Stop();
279 void PerfProvider::ScheduleIntervalCollection() {
280 DCHECK(CalledOnValidThread());
281 if (timer_.IsRunning())
282 return;
284 // Pick a random time in the current interval.
285 base::TimeTicks scheduled_time =
286 next_profiling_interval_start_ +
287 base::TimeDelta::FromMilliseconds(
288 base::RandGenerator(kPerfProfilingIntervalMs));
290 // If the scheduled time has already passed in the time it took to make the
291 // above calculations, trigger the collection event immediately.
292 base::TimeTicks now = base::TimeTicks::Now();
293 if (scheduled_time < now)
294 scheduled_time = now;
296 timer_.Start(FROM_HERE, scheduled_time - now, this,
297 &PerfProvider::DoPeriodicCollection);
299 // Update the profiling interval tracker to the start of the next interval.
300 next_profiling_interval_start_ +=
301 base::TimeDelta::FromMilliseconds(kPerfProfilingIntervalMs);
304 void PerfProvider::CollectIfNecessary(
305 scoped_ptr<SampledProfile> sampled_profile) {
306 DCHECK(CalledOnValidThread());
308 // Schedule another interval collection. This call makes sense regardless of
309 // whether or not the current collection was interval-triggered. If it had
310 // been another type of trigger event, the interval timer would have been
311 // halted, so it makes sense to reschedule a new interval collection.
312 ScheduleIntervalCollection();
314 // Do not collect further data if we've already collected a substantial amount
315 // of data, as indicated by |kCachedPerfDataProtobufSizeThreshold|.
316 size_t cached_perf_data_size = 0;
317 for (size_t i = 0; i < cached_perf_data_.size(); ++i) {
318 cached_perf_data_size += cached_perf_data_[i].ByteSize();
320 if (cached_perf_data_size >= kCachedPerfDataProtobufSizeThreshold) {
321 AddToPerfHistogram(NOT_READY_TO_COLLECT);
322 return;
325 // For privacy reasons, Chrome should only collect perf data if there is no
326 // incognito session active (or gets spawned during the collection).
327 if (BrowserList::IsOffTheRecordSessionActive()) {
328 AddToPerfHistogram(INCOGNITO_ACTIVE);
329 return;
332 scoped_ptr<WindowedIncognitoObserver> incognito_observer(
333 new WindowedIncognitoObserver);
335 chromeos::DebugDaemonClient* client =
336 chromeos::DBusThreadManager::Get()->GetDebugDaemonClient();
338 base::TimeDelta collection_duration = base::TimeDelta::FromSeconds(
339 kPerfCommandDurationDefaultSeconds);
341 client->GetPerfOutput(
342 collection_duration.InSeconds(),
343 base::Bind(&PerfProvider::ParseOutputProtoIfValid,
344 weak_factory_.GetWeakPtr(), base::Passed(&incognito_observer),
345 base::Passed(&sampled_profile)));
348 void PerfProvider::DoPeriodicCollection() {
349 scoped_ptr<SampledProfile> sampled_profile(new SampledProfile);
350 sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
352 CollectIfNecessary(sampled_profile.Pass());
355 void PerfProvider::CollectPerfDataAfterResume(
356 const base::TimeDelta& sleep_duration,
357 const base::TimeDelta& time_after_resume) {
358 // Fill out a SampledProfile protobuf that will contain the collected data.
359 scoped_ptr<SampledProfile> sampled_profile(new SampledProfile);
360 sampled_profile->set_trigger_event(SampledProfile::RESUME_FROM_SUSPEND);
361 sampled_profile->set_suspend_duration_ms(sleep_duration.InMilliseconds());
362 sampled_profile->set_ms_after_resume(time_after_resume.InMilliseconds());
364 CollectIfNecessary(sampled_profile.Pass());
367 void PerfProvider::CollectPerfDataAfterSessionRestore(
368 const base::TimeDelta& time_after_restore,
369 int num_tabs_restored) {
370 // Fill out a SampledProfile protobuf that will contain the collected data.
371 scoped_ptr<SampledProfile> sampled_profile(new SampledProfile);
372 sampled_profile->set_trigger_event(SampledProfile::RESTORE_SESSION);
373 sampled_profile->set_ms_after_restore(time_after_restore.InMilliseconds());
374 sampled_profile->set_num_tabs_restored(num_tabs_restored);
376 CollectIfNecessary(sampled_profile.Pass());
377 last_session_restore_collection_time_ = base::TimeTicks::Now();
380 } // namespace metrics