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