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