chrome_metrics_service_client.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
1// Copyright 2014 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 "chrome/browser/metrics/chrome_metrics_service_client.h" 6 7#include "base/bind.h" 8#include "base/callback.h" 9#include "base/command_line.h" 10#include "base/logging.h" 11#include "base/metrics/histogram.h" 12#include "base/strings/string16.h" 13#include "base/strings/string_util.h" 14#include "base/strings/utf_string_conversions.h" 15#include "base/threading/platform_thread.h" 16#include "base/time/time.h" 17#include "chrome/browser/browser_process.h" 18#include "chrome/browser/chrome_notification_types.h" 19#include "chrome/browser/google/google_util.h" 20#include "chrome/browser/memory_details.h" 21#include "chrome/browser/metrics/extensions_metrics_provider.h" 22#include "chrome/browser/metrics/metrics_service.h" 23#include "chrome/browser/ui/browser_otr_state.h" 24#include "chrome/common/chrome_constants.h" 25#include "chrome/common/chrome_switches.h" 26#include "chrome/common/chrome_version_info.h" 27#include "chrome/common/crash_keys.h" 28#include "chrome/common/render_messages.h" 29#include "content/public/browser/browser_thread.h" 30#include "content/public/browser/histogram_fetcher.h" 31#include "content/public/browser/notification_service.h" 32#include "content/public/browser/render_process_host.h" 33 34#if !defined(OS_ANDROID) 35#include "chrome/browser/service_process/service_process_control.h" 36#endif 37 38#if defined(OS_CHROMEOS) 39#include "chrome/browser/metrics/chromeos_metrics_provider.h" 40#endif 41 42#if defined(OS_WIN) 43#include <windows.h> 44#include "base/win/registry.h" 45#include "chrome/browser/metrics/google_update_metrics_provider_win.h" 46#endif 47 48namespace { 49 50// This specifies the amount of time to wait for all renderers to send their 51// data. 52const int kMaxHistogramGatheringWaitDuration = 60000; // 60 seconds. 53 54metrics::SystemProfileProto::Channel AsProtobufChannel( 55 chrome::VersionInfo::Channel channel) { 56 switch (channel) { 57 case chrome::VersionInfo::CHANNEL_UNKNOWN: 58 return metrics::SystemProfileProto::CHANNEL_UNKNOWN; 59 case chrome::VersionInfo::CHANNEL_CANARY: 60 return metrics::SystemProfileProto::CHANNEL_CANARY; 61 case chrome::VersionInfo::CHANNEL_DEV: 62 return metrics::SystemProfileProto::CHANNEL_DEV; 63 case chrome::VersionInfo::CHANNEL_BETA: 64 return metrics::SystemProfileProto::CHANNEL_BETA; 65 case chrome::VersionInfo::CHANNEL_STABLE: 66 return metrics::SystemProfileProto::CHANNEL_STABLE; 67 } 68 NOTREACHED(); 69 return metrics::SystemProfileProto::CHANNEL_UNKNOWN; 70} 71 72// Handles asynchronous fetching of memory details. 73// Will run the provided task after finished. 74class MetricsMemoryDetails : public MemoryDetails { 75 public: 76 explicit MetricsMemoryDetails(const base::Closure& callback) 77 : callback_(callback) {} 78 79 virtual void OnDetailsAvailable() OVERRIDE { 80 base::MessageLoop::current()->PostTask(FROM_HERE, callback_); 81 } 82 83 private: 84 virtual ~MetricsMemoryDetails() {} 85 86 base::Closure callback_; 87 88 DISALLOW_COPY_AND_ASSIGN(MetricsMemoryDetails); 89}; 90 91} // namespace 92 93ChromeMetricsServiceClient::ChromeMetricsServiceClient( 94 metrics::MetricsStateManager* state_manager) 95 : metrics_state_manager_(state_manager), 96 chromeos_metrics_provider_(NULL), 97 waiting_for_collect_final_metrics_step_(false), 98 num_async_histogram_fetches_in_progress_(0), 99 weak_ptr_factory_(this) { 100 DCHECK(thread_checker_.CalledOnValidThread()); 101 RecordCommandLineMetrics(); 102 RegisterForNotifications(); 103 104#if defined(OS_WIN) 105 CountBrowserCrashDumpAttempts(); 106#endif // defined(OS_WIN) 107} 108 109ChromeMetricsServiceClient::~ChromeMetricsServiceClient() { 110 DCHECK(thread_checker_.CalledOnValidThread()); 111} 112 113// static 114scoped_ptr<ChromeMetricsServiceClient> ChromeMetricsServiceClient::Create( 115 metrics::MetricsStateManager* state_manager, 116 PrefService* local_state) { 117 // Perform two-phase initialization so that |client->metrics_service_| only 118 // receives pointers to fully constructed objects. 119 scoped_ptr<ChromeMetricsServiceClient> client( 120 new ChromeMetricsServiceClient(state_manager)); 121 client->Initialize(); 122 123 return client.Pass(); 124} 125 126void ChromeMetricsServiceClient::Initialize() { 127 metrics_service_.reset(new MetricsService( 128 metrics_state_manager_, this, g_browser_process->local_state())); 129 130 // Register metrics providers. 131 metrics_service_->RegisterMetricsProvider( 132 scoped_ptr<metrics::MetricsProvider>( 133 new ExtensionsMetricsProvider(metrics_state_manager_))); 134 135#if defined(OS_CHROMEOS) 136 ChromeOSMetricsProvider* chromeos_metrics_provider = 137 new ChromeOSMetricsProvider; 138 chromeos_metrics_provider_ = chromeos_metrics_provider; 139 metrics_service_->RegisterMetricsProvider( 140 scoped_ptr<metrics::MetricsProvider>(chromeos_metrics_provider)); 141#endif 142} 143 144void ChromeMetricsServiceClient::SetClientID(const std::string& client_id) { 145 crash_keys::SetClientID(client_id); 146} 147 148bool ChromeMetricsServiceClient::IsOffTheRecordSessionActive() { 149 return !chrome::IsOffTheRecordSessionActive(); 150} 151 152std::string ChromeMetricsServiceClient::GetApplicationLocale() { 153 return g_browser_process->GetApplicationLocale(); 154} 155 156bool ChromeMetricsServiceClient::GetBrand(std::string* brand_code) { 157 return google_util::GetBrand(brand_code); 158} 159 160metrics::SystemProfileProto::Channel ChromeMetricsServiceClient::GetChannel() { 161 return AsProtobufChannel(chrome::VersionInfo::GetChannel()); 162} 163 164std::string ChromeMetricsServiceClient::GetVersionString() { 165 chrome::VersionInfo version_info; 166 if (!version_info.is_valid()) { 167 NOTREACHED(); 168 return std::string(); 169 } 170 171 std::string version = version_info.Version(); 172#if defined(ARCH_CPU_64_BITS) 173 version += "-64"; 174#endif // defined(ARCH_CPU_64_BITS) 175 if (!version_info.IsOfficialBuild()) 176 version.append("-devel"); 177 return version; 178} 179 180void ChromeMetricsServiceClient::OnLogUploadComplete() { 181 // Collect network stats after each UMA upload. 182 network_stats_uploader_.CollectAndReportNetworkStats(); 183} 184 185void ChromeMetricsServiceClient::CollectFinalMetrics( 186 const base::Closure& done_callback) { 187 DCHECK(thread_checker_.CalledOnValidThread()); 188 189 collect_final_metrics_done_callback_ = done_callback; 190 191 // Begin the multi-step process of collecting memory usage histograms: 192 // First spawn a task to collect the memory details; when that task is 193 // finished, it will call OnMemoryDetailCollectionDone. That will in turn 194 // call HistogramSynchronization to collect histograms from all renderers and 195 // then call OnHistogramSynchronizationDone to continue processing. 196 DCHECK(!waiting_for_collect_final_metrics_step_); 197 waiting_for_collect_final_metrics_step_ = true; 198 199 base::Closure callback = 200 base::Bind(&ChromeMetricsServiceClient::OnMemoryDetailCollectionDone, 201 weak_ptr_factory_.GetWeakPtr()); 202 203 scoped_refptr<MetricsMemoryDetails> details( 204 new MetricsMemoryDetails(callback)); 205 details->StartFetch(MemoryDetails::UPDATE_USER_METRICS); 206 207 // Collect WebCore cache information to put into a histogram. 208 for (content::RenderProcessHost::iterator i( 209 content::RenderProcessHost::AllHostsIterator()); 210 !i.IsAtEnd(); i.Advance()) { 211 i.GetCurrentValue()->Send(new ChromeViewMsg_GetCacheResourceStats()); 212 } 213} 214 215void ChromeMetricsServiceClient::OnMemoryDetailCollectionDone() { 216 DCHECK(thread_checker_.CalledOnValidThread()); 217 218 // This function should only be called as the callback from an ansynchronous 219 // step. 220 DCHECK(waiting_for_collect_final_metrics_step_); 221 222 // Create a callback_task for OnHistogramSynchronizationDone. 223 base::Closure callback = base::Bind( 224 &ChromeMetricsServiceClient::OnHistogramSynchronizationDone, 225 weak_ptr_factory_.GetWeakPtr()); 226 227 base::TimeDelta timeout = 228 base::TimeDelta::FromMilliseconds(kMaxHistogramGatheringWaitDuration); 229 230 DCHECK_EQ(num_async_histogram_fetches_in_progress_, 0); 231 232#if defined(OS_ANDROID) 233 // Android has no service process. 234 num_async_histogram_fetches_in_progress_ = 1; 235#else // OS_ANDROID 236 num_async_histogram_fetches_in_progress_ = 2; 237 // Run requests to service and content in parallel. 238 if (!ServiceProcessControl::GetInstance()->GetHistograms(callback, timeout)) { 239 // Assume |num_async_histogram_fetches_in_progress_| is not changed by 240 // |GetHistograms()|. 241 DCHECK_EQ(num_async_histogram_fetches_in_progress_, 2); 242 // Assign |num_async_histogram_fetches_in_progress_| above and decrement it 243 // here to make code work even if |GetHistograms()| fired |callback|. 244 --num_async_histogram_fetches_in_progress_; 245 } 246#endif // OS_ANDROID 247 248 // Set up the callback to task to call after we receive histograms from all 249 // child processes. |timeout| specifies how long to wait before absolutely 250 // calling us back on the task. 251 content::FetchHistogramsAsynchronously(base::MessageLoop::current(), callback, 252 timeout); 253} 254 255void ChromeMetricsServiceClient::OnHistogramSynchronizationDone() { 256 DCHECK(thread_checker_.CalledOnValidThread()); 257 258 // This function should only be called as the callback from an ansynchronous 259 // step. 260 DCHECK(waiting_for_collect_final_metrics_step_); 261 DCHECK_GT(num_async_histogram_fetches_in_progress_, 0); 262 263 // Check if all expected requests finished. 264 if (--num_async_histogram_fetches_in_progress_ > 0) 265 return; 266 267 waiting_for_collect_final_metrics_step_ = false; 268 collect_final_metrics_done_callback_.Run(); 269} 270 271void ChromeMetricsServiceClient::RecordCommandLineMetrics() { 272 // Get stats on use of command line. 273 const CommandLine* command_line(CommandLine::ForCurrentProcess()); 274 size_t common_commands = 0; 275 if (command_line->HasSwitch(switches::kUserDataDir)) { 276 ++common_commands; 277 UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineDatDirCount", 1); 278 } 279 280 if (command_line->HasSwitch(switches::kApp)) { 281 ++common_commands; 282 UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineAppModeCount", 1); 283 } 284 285 // TODO(rohitrao): Should these be logged on iOS as well? 286 // http://crbug.com/375794 287 size_t switch_count = command_line->GetSwitches().size(); 288 UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineFlagCount", switch_count); 289 UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineUncommonFlagCount", 290 switch_count - common_commands); 291} 292 293void ChromeMetricsServiceClient::RegisterForNotifications() { 294 registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED, 295 content::NotificationService::AllBrowserContextsAndSources()); 296 registrar_.Add(this, chrome::NOTIFICATION_BROWSER_CLOSED, 297 content::NotificationService::AllSources()); 298 registrar_.Add(this, chrome::NOTIFICATION_TAB_PARENTED, 299 content::NotificationService::AllSources()); 300 registrar_.Add(this, chrome::NOTIFICATION_TAB_CLOSING, 301 content::NotificationService::AllSources()); 302 registrar_.Add(this, content::NOTIFICATION_LOAD_START, 303 content::NotificationService::AllSources()); 304 registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, 305 content::NotificationService::AllSources()); 306 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, 307 content::NotificationService::AllSources()); 308 registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG, 309 content::NotificationService::AllSources()); 310 registrar_.Add(this, chrome::NOTIFICATION_OMNIBOX_OPENED_URL, 311 content::NotificationService::AllSources()); 312} 313 314void ChromeMetricsServiceClient::Observe( 315 int type, 316 const content::NotificationSource& source, 317 const content::NotificationDetails& details) { 318 DCHECK(thread_checker_.CalledOnValidThread()); 319 320 switch (type) { 321 case chrome::NOTIFICATION_BROWSER_OPENED: 322 case chrome::NOTIFICATION_BROWSER_CLOSED: 323 case chrome::NOTIFICATION_OMNIBOX_OPENED_URL: 324 case chrome::NOTIFICATION_TAB_PARENTED: 325 case chrome::NOTIFICATION_TAB_CLOSING: 326 case content::NOTIFICATION_LOAD_STOP: 327 case content::NOTIFICATION_LOAD_START: 328 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: 329 case content::NOTIFICATION_RENDER_WIDGET_HOST_HANG: 330 metrics_service_->OnApplicationNotIdle(); 331 break; 332 333 default: 334 NOTREACHED(); 335 } 336} 337 338void ChromeMetricsServiceClient::StartGatheringMetrics( 339 const base::Closure& done_callback) { 340// TODO(blundell): Move all metrics gathering tasks from MetricsService to 341// here. 342#if defined(OS_CHROMEOS) 343 chromeos_metrics_provider_->InitTaskGetHardwareClass(done_callback); 344#else 345 done_callback.Run(); 346#endif 347} 348 349#if defined(OS_WIN) 350void ChromeMetricsServiceClient::CountBrowserCrashDumpAttempts() { 351 // Open the registry key for iteration. 352 base::win::RegKey regkey; 353 if (regkey.Open(HKEY_CURRENT_USER, 354 chrome::kBrowserCrashDumpAttemptsRegistryPath, 355 KEY_ALL_ACCESS) != ERROR_SUCCESS) { 356 return; 357 } 358 359 // The values we're interested in counting are all prefixed with the version. 360 base::string16 chrome_version(base::ASCIIToUTF16(chrome::kChromeVersion)); 361 362 // Track a list of values to delete. We don't modify the registry key while 363 // we're iterating over its values. 364 typedef std::vector<base::string16> StringVector; 365 StringVector to_delete; 366 367 // Iterate over the values in the key counting dumps with and without crashes. 368 // We directly walk the values instead of using RegistryValueIterator in order 369 // to read all of the values as DWORDS instead of strings. 370 base::string16 name; 371 DWORD value = 0; 372 int dumps_with_crash = 0; 373 int dumps_with_no_crash = 0; 374 for (int i = regkey.GetValueCount() - 1; i >= 0; --i) { 375 if (regkey.GetValueNameAt(i, &name) == ERROR_SUCCESS && 376 StartsWith(name, chrome_version, false) && 377 regkey.ReadValueDW(name.c_str(), &value) == ERROR_SUCCESS) { 378 to_delete.push_back(name); 379 if (value == 0) 380 ++dumps_with_no_crash; 381 else 382 ++dumps_with_crash; 383 } 384 } 385 386 // Delete the registry keys we've just counted. 387 for (StringVector::iterator i = to_delete.begin(); i != to_delete.end(); ++i) 388 regkey.DeleteValue(i->c_str()); 389 390 // Capture the histogram samples. 391 if (dumps_with_crash != 0) 392 UMA_HISTOGRAM_COUNTS("Chrome.BrowserDumpsWithCrash", dumps_with_crash); 393 if (dumps_with_no_crash != 0) 394 UMA_HISTOGRAM_COUNTS("Chrome.BrowserDumpsWithNoCrash", dumps_with_no_crash); 395 int total_dumps = dumps_with_crash + dumps_with_no_crash; 396 if (total_dumps != 0) 397 UMA_HISTOGRAM_COUNTS("Chrome.BrowserCrashDumpAttempts", total_dumps); 398} 399#endif // defined(OS_WIN) 400