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