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