perf_provider_chromeos.cc revision bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3
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.
4
5#include <string>
6
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/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"
24#include "content/public/browser/browser_thread.h"
25
26namespace {
27
28// Default time in seconds between invocations of perf.
29// This period is roughly 6.5 hours.
30// This is chosen to be relatively prime with the number of seconds in:
31// - one minute (60)
32// - one hour (3600)
33// - one day (86400)
34const size_t kPerfCommandIntervalDefaultSeconds = 23093;
35
36// The first collection interval is different from the interval above. This is
37// because we want to collect the first profile quickly after Chrome is started.
38// If this period is too long, the user will log off and Chrome will be killed
39// before it is triggered. The following 2 variables determine the upper and
40// lower bound on the interval.
41// The reason we do not always want to collect the initial profile after a fixed
42// period is to not over-represent task X in the profile where task X always
43// runs at a fixed period after start-up. By selecting a period randomly between
44// a lower and upper bound, we will hopefully collect a more fair profile.
45const size_t kPerfCommandStartIntervalLowerBoundMinutes = 10;
46
47const size_t kPerfCommandStartIntervalUpperBoundMinutes = 20;
48
49const size_t kNumberOfSecondsInAMinute = 60;
50
51// Default time in seconds perf is run for.
52const size_t kPerfCommandDurationDefaultSeconds = 2;
53
54// Enumeration representing success and various failure modes for collecting and
55// sending perf data.
56enum GetPerfDataOutcome {
57  SUCCESS,
58  NOT_READY_TO_UPLOAD,
59  NOT_READY_TO_COLLECT,
60  INCOGNITO_ACTIVE,
61  INCOGNITO_LAUNCHED,
62  PROTOBUF_NOT_PARSED,
63  NUM_OUTCOMES
64};
65
66// Name of the histogram that represents the success and various failure modes
67// for collecting and sending perf data.
68const char kGetPerfDataOutcomeHistogram[] = "UMA.Perf.GetData";
69
70void AddToPerfHistogram(GetPerfDataOutcome outcome) {
71  UMA_HISTOGRAM_ENUMERATION(kGetPerfDataOutcomeHistogram,
72                            outcome,
73                            NUM_OUTCOMES);
74}
75
76} // namespace
77
78
79namespace metrics {
80
81// This class must be created and used on the UI thread. It watches for any
82// incognito window being opened from the time it is instantiated to the time it
83// is destroyed.
84class WindowedIncognitoObserver : public chrome::BrowserListObserver {
85 public:
86  WindowedIncognitoObserver() : incognito_launched_(false) {
87    BrowserList::AddObserver(this);
88  }
89
90  virtual ~WindowedIncognitoObserver() {
91    BrowserList::RemoveObserver(this);
92  }
93
94  // This method can be checked to see whether any incognito window has been
95  // opened since the time this object was created.
96  bool incognito_launched() {
97    return incognito_launched_;
98  }
99
100 private:
101  // chrome::BrowserListObserver implementation.
102  virtual void OnBrowserAdded(Browser* browser) OVERRIDE {
103    if (browser->profile()->IsOffTheRecord())
104      incognito_launched_ = true;
105  }
106
107  bool incognito_launched_;
108};
109
110PerfProvider::PerfProvider()
111      : state_(READY_TO_COLLECT),
112      weak_factory_(this) {
113  size_t collection_interval_minutes = base::RandInt(
114      kPerfCommandStartIntervalLowerBoundMinutes,
115      kPerfCommandStartIntervalUpperBoundMinutes);
116  ScheduleCollection(base::TimeDelta::FromMinutes(collection_interval_minutes));
117}
118
119PerfProvider::~PerfProvider() {}
120
121bool PerfProvider::GetPerfData(PerfDataProto* perf_data_proto) {
122  DCHECK(CalledOnValidThread());
123  if (state_ != READY_TO_UPLOAD) {
124    AddToPerfHistogram(NOT_READY_TO_UPLOAD);
125    return false;
126  }
127
128  *perf_data_proto = perf_data_proto_;
129  state_ = READY_TO_COLLECT;
130
131  AddToPerfHistogram(SUCCESS);
132  return true;
133}
134
135void PerfProvider::ScheduleCollection(const base::TimeDelta& interval) {
136  DCHECK(CalledOnValidThread());
137  if (timer_.IsRunning())
138    return;
139
140  timer_.Start(FROM_HERE, interval, this,
141               &PerfProvider::CollectIfNecessaryAndReschedule);
142}
143
144void PerfProvider::CollectIfNecessary() {
145  DCHECK(CalledOnValidThread());
146  if (state_ != READY_TO_COLLECT) {
147    AddToPerfHistogram(NOT_READY_TO_COLLECT);
148    return;
149  }
150
151  // For privacy reasons, Chrome should only collect perf data if there is no
152  // incognito session active (or gets spawned during the collection).
153  if (BrowserList::IsOffTheRecordSessionActive()) {
154    AddToPerfHistogram(INCOGNITO_ACTIVE);
155    return;
156  }
157
158  scoped_ptr<WindowedIncognitoObserver> incognito_observer(
159      new WindowedIncognitoObserver);
160
161  chromeos::DebugDaemonClient* client =
162      chromeos::DBusThreadManager::Get()->GetDebugDaemonClient();
163
164  base::TimeDelta collection_duration = base::TimeDelta::FromSeconds(
165      kPerfCommandDurationDefaultSeconds);
166
167  client->GetPerfData(collection_duration.InSeconds(),
168                      base::Bind(&PerfProvider::ParseProtoIfValid,
169                                 weak_factory_.GetWeakPtr(),
170                                 base::Passed(&incognito_observer)));
171}
172
173void PerfProvider::CollectIfNecessaryAndReschedule() {
174  CollectIfNecessary();
175  ScheduleCollection(
176      base::TimeDelta::FromSeconds(kPerfCommandIntervalDefaultSeconds));
177}
178
179void PerfProvider::ParseProtoIfValid(
180    scoped_ptr<WindowedIncognitoObserver> incognito_observer,
181    const std::vector<uint8>& data) {
182  DCHECK(CalledOnValidThread());
183
184  if (incognito_observer->incognito_launched()) {
185    AddToPerfHistogram(INCOGNITO_LAUNCHED);
186    return;
187  }
188
189  if (!perf_data_proto_.ParseFromArray(data.data(), data.size())) {
190    AddToPerfHistogram(PROTOBUF_NOT_PARSED);
191    perf_data_proto_.Clear();
192    return;
193  }
194
195  state_ = READY_TO_UPLOAD;
196}
197} // namespace metrics
198