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 // Default time in seconds perf is run for. 50 const size_t kPerfCommandDurationDefaultSeconds = 2; 51 52 // Enumeration representing success and various failure modes for collecting and 53 // sending perf data. 54 enum 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. 66 const char kGetPerfDataOutcomeHistogram[] = "UMA.Perf.GetData"; 67 68 void AddToPerfHistogram(GetPerfDataOutcome outcome) { 69 UMA_HISTOGRAM_ENUMERATION(kGetPerfDataOutcomeHistogram, 70 outcome, 71 NUM_OUTCOMES); 72 } 73 74 } // namespace 75 76 77 namespace 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. 82 class 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 108 PerfProvider::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 117 PerfProvider::~PerfProvider() {} 118 119 bool 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 133 void 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 142 void 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 171 void PerfProvider::CollectIfNecessaryAndReschedule() { 172 CollectIfNecessary(); 173 ScheduleCollection( 174 base::TimeDelta::FromSeconds(kPerfCommandIntervalDefaultSeconds)); 175 } 176 177 void 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