perf_provider_chromeos.cc revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
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// Limit the total size of protobufs that can be cached, so they don't take up 52// too much memory. If the size of cached protobufs exceeds this value, stop 53// collecting further perf data. The current value is 4 MB. 54const size_t kCachedPerfDataProtobufSizeThreshold = 4 * 1024 * 1024; 55 56// Enumeration representing success and various failure modes for collecting and 57// sending perf data. 58enum GetPerfDataOutcome { 59 SUCCESS, 60 NOT_READY_TO_UPLOAD, 61 NOT_READY_TO_COLLECT, 62 INCOGNITO_ACTIVE, 63 INCOGNITO_LAUNCHED, 64 PROTOBUF_NOT_PARSED, 65 NUM_OUTCOMES 66}; 67 68// Name of the histogram that represents the success and various failure modes 69// for collecting and sending perf data. 70const char kGetPerfDataOutcomeHistogram[] = "UMA.Perf.GetData"; 71 72void AddToPerfHistogram(GetPerfDataOutcome outcome) { 73 UMA_HISTOGRAM_ENUMERATION(kGetPerfDataOutcomeHistogram, 74 outcome, 75 NUM_OUTCOMES); 76} 77 78} // namespace 79 80 81namespace metrics { 82 83// This class must be created and used on the UI thread. It watches for any 84// incognito window being opened from the time it is instantiated to the time it 85// is destroyed. 86class WindowedIncognitoObserver : public chrome::BrowserListObserver { 87 public: 88 WindowedIncognitoObserver() : incognito_launched_(false) { 89 BrowserList::AddObserver(this); 90 } 91 92 virtual ~WindowedIncognitoObserver() { 93 BrowserList::RemoveObserver(this); 94 } 95 96 // This method can be checked to see whether any incognito window has been 97 // opened since the time this object was created. 98 bool incognito_launched() { 99 return incognito_launched_; 100 } 101 102 private: 103 // chrome::BrowserListObserver implementation. 104 virtual void OnBrowserAdded(Browser* browser) OVERRIDE { 105 if (browser->profile()->IsOffTheRecord()) 106 incognito_launched_ = true; 107 } 108 109 bool incognito_launched_; 110}; 111 112PerfProvider::PerfProvider() 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(std::vector<PerfDataProto>* perf_data) { 123 DCHECK(CalledOnValidThread()); 124 if (cached_perf_data_.empty()) { 125 AddToPerfHistogram(NOT_READY_TO_UPLOAD); 126 return false; 127 } 128 129 perf_data->swap(cached_perf_data_); 130 cached_perf_data_.clear(); 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 148 // Do not collect further data if we've already collected a substantial amount 149 // of data, as indicated by |kCachedPerfDataProtobufSizeThreshold|. 150 size_t cached_perf_data_size = 0; 151 for (size_t i = 0; i < cached_perf_data_.size(); ++i) { 152 cached_perf_data_size += cached_perf_data_[i].ByteSize(); 153 } 154 if (cached_perf_data_size >= kCachedPerfDataProtobufSizeThreshold) { 155 AddToPerfHistogram(NOT_READY_TO_COLLECT); 156 return; 157 } 158 159 // For privacy reasons, Chrome should only collect perf data if there is no 160 // incognito session active (or gets spawned during the collection). 161 if (BrowserList::IsOffTheRecordSessionActive()) { 162 AddToPerfHistogram(INCOGNITO_ACTIVE); 163 return; 164 } 165 166 scoped_ptr<WindowedIncognitoObserver> incognito_observer( 167 new WindowedIncognitoObserver); 168 169 chromeos::DebugDaemonClient* client = 170 chromeos::DBusThreadManager::Get()->GetDebugDaemonClient(); 171 172 base::TimeDelta collection_duration = base::TimeDelta::FromSeconds( 173 kPerfCommandDurationDefaultSeconds); 174 175 client->GetPerfData(collection_duration.InSeconds(), 176 base::Bind(&PerfProvider::ParseProtoIfValid, 177 weak_factory_.GetWeakPtr(), 178 base::Passed(&incognito_observer))); 179} 180 181void PerfProvider::CollectIfNecessaryAndReschedule() { 182 CollectIfNecessary(); 183 ScheduleCollection( 184 base::TimeDelta::FromSeconds(kPerfCommandIntervalDefaultSeconds)); 185} 186 187void PerfProvider::ParseProtoIfValid( 188 scoped_ptr<WindowedIncognitoObserver> incognito_observer, 189 const std::vector<uint8>& data) { 190 DCHECK(CalledOnValidThread()); 191 192 if (incognito_observer->incognito_launched()) { 193 AddToPerfHistogram(INCOGNITO_LAUNCHED); 194 return; 195 } 196 197 PerfDataProto perf_data_proto; 198 if (!perf_data_proto.ParseFromArray(data.data(), data.size())) { 199 AddToPerfHistogram(PROTOBUF_NOT_PARSED); 200 return; 201 } 202 203 // Append a new PerfDataProto to the |cached_perf_data_| vector and swap in 204 // the contents. 205 cached_perf_data_.resize(cached_perf_data_.size() + 1); 206 cached_perf_data_.back().Swap(&perf_data_proto); 207} 208 209} // namespace metrics 210