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