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