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.
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/metrics/perf_provider_chromeos.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/ui/browser.h"
19 #include "chrome/browser/ui/browser_list.h"
20 #include "chrome/browser/ui/browser_list_observer.h"
21 #include "chrome/common/chrome_switches.h"
22 #include "chromeos/dbus/dbus_thread_manager.h"
23 #include "chromeos/dbus/debug_daemon_client.h"
27 // Default time in seconds between invocations of perf.
28 // This period is roughly 6.5 hours.
29 // This is chosen to be relatively prime with the number of seconds in:
33 const size_t kPerfCommandIntervalDefaultSeconds = 23093;
35 // The first collection interval is different from the interval above. This is
36 // because we want to collect the first profile quickly after Chrome is started.
37 // If this period is too long, the user will log off and Chrome will be killed
38 // before it is triggered. The following 2 variables determine the upper and
39 // lower bound on the interval.
40 // The reason we do not always want to collect the initial profile after a fixed
41 // period is to not over-represent task X in the profile where task X always
42 // runs at a fixed period after start-up. By selecting a period randomly between
43 // a lower and upper bound, we will hopefully collect a more fair profile.
44 const size_t kPerfCommandStartIntervalLowerBoundMinutes = 10;
46 const size_t kPerfCommandStartIntervalUpperBoundMinutes = 20;
48 // Default time in seconds perf is run for.
49 const size_t kPerfCommandDurationDefaultSeconds = 2;
51 // Limit the total size of protobufs that can be cached, so they don't take up
52 // too much memory. If the size of cached protobufs exceeds this value, stop
53 // collecting further perf data. The current value is 4 MB.
54 const size_t kCachedPerfDataProtobufSizeThreshold = 4 * 1024 * 1024;
56 // Enumeration representing success and various failure modes for collecting and
58 enum GetPerfDataOutcome {
68 // Name of the histogram that represents the success and various failure modes
69 // for collecting and sending perf data.
70 const char kGetPerfDataOutcomeHistogram[] = "UMA.Perf.GetData";
72 void AddToPerfHistogram(GetPerfDataOutcome outcome) {
73 UMA_HISTOGRAM_ENUMERATION(kGetPerfDataOutcomeHistogram,
83 // This class must be created and used on the UI thread. It watches for any
84 // incognito window being opened from the time it is instantiated to the time it
86 class WindowedIncognitoObserver : public chrome::BrowserListObserver {
88 WindowedIncognitoObserver() : incognito_launched_(false) {
89 BrowserList::AddObserver(this);
92 virtual ~WindowedIncognitoObserver() {
93 BrowserList::RemoveObserver(this);
96 // This method can be checked to see whether any incognito window has been
97 // opened since the time this object was created.
98 bool incognito_launched() {
99 return incognito_launched_;
103 // chrome::BrowserListObserver implementation.
104 virtual void OnBrowserAdded(Browser* browser) OVERRIDE {
105 if (browser->profile()->IsOffTheRecord())
106 incognito_launched_ = true;
109 bool incognito_launched_;
112 PerfProvider::PerfProvider()
113 : weak_factory_(this) {
114 size_t collection_interval_minutes = base::RandInt(
115 kPerfCommandStartIntervalLowerBoundMinutes,
116 kPerfCommandStartIntervalUpperBoundMinutes);
117 ScheduleCollection(base::TimeDelta::FromMinutes(collection_interval_minutes));
120 PerfProvider::~PerfProvider() {}
122 bool PerfProvider::GetPerfData(std::vector<PerfDataProto>* perf_data) {
123 DCHECK(CalledOnValidThread());
124 if (cached_perf_data_.empty()) {
125 AddToPerfHistogram(NOT_READY_TO_UPLOAD);
129 perf_data->swap(cached_perf_data_);
130 cached_perf_data_.clear();
132 AddToPerfHistogram(SUCCESS);
136 void PerfProvider::ScheduleCollection(const base::TimeDelta& interval) {
137 DCHECK(CalledOnValidThread());
138 if (timer_.IsRunning())
141 timer_.Start(FROM_HERE, interval, this,
142 &PerfProvider::CollectIfNecessaryAndReschedule);
145 void PerfProvider::CollectIfNecessary() {
146 DCHECK(CalledOnValidThread());
148 // Do not collect further data if we've already collected a substantial amount
149 // of data, as indicated by |kCachedPerfDataProtobufSizeThreshold|.
150 size_t cached_perf_data_size = 0;
151 for (size_t i = 0; i < cached_perf_data_.size(); ++i) {
152 cached_perf_data_size += cached_perf_data_[i].ByteSize();
154 if (cached_perf_data_size >= kCachedPerfDataProtobufSizeThreshold) {
155 AddToPerfHistogram(NOT_READY_TO_COLLECT);
159 // For privacy reasons, Chrome should only collect perf data if there is no
160 // incognito session active (or gets spawned during the collection).
161 if (BrowserList::IsOffTheRecordSessionActive()) {
162 AddToPerfHistogram(INCOGNITO_ACTIVE);
166 scoped_ptr<WindowedIncognitoObserver> incognito_observer(
167 new WindowedIncognitoObserver);
169 chromeos::DebugDaemonClient* client =
170 chromeos::DBusThreadManager::Get()->GetDebugDaemonClient();
172 base::TimeDelta collection_duration = base::TimeDelta::FromSeconds(
173 kPerfCommandDurationDefaultSeconds);
175 client->GetPerfData(collection_duration.InSeconds(),
176 base::Bind(&PerfProvider::ParseProtoIfValid,
177 weak_factory_.GetWeakPtr(),
178 base::Passed(&incognito_observer)));
181 void PerfProvider::CollectIfNecessaryAndReschedule() {
182 CollectIfNecessary();
184 base::TimeDelta::FromSeconds(kPerfCommandIntervalDefaultSeconds));
187 void PerfProvider::ParseProtoIfValid(
188 scoped_ptr<WindowedIncognitoObserver> incognito_observer,
189 const std::vector<uint8>& data) {
190 DCHECK(CalledOnValidThread());
192 if (incognito_observer->incognito_launched()) {
193 AddToPerfHistogram(INCOGNITO_LAUNCHED);
197 PerfDataProto perf_data_proto;
198 if (!perf_data_proto.ParseFromArray(data.data(), data.size())) {
199 AddToPerfHistogram(PROTOBUF_NOT_PARSED);
203 // Append a new PerfDataProto to the |cached_perf_data_| vector and swap in
205 cached_perf_data_.resize(cached_perf_data_.size() + 1);
206 cached_perf_data_.back().Swap(&perf_data_proto);
209 } // namespace metrics