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 <vector> 8 9 #include "base/bind.h" 10 #include "base/callback.h" 11 #include "base/command_line.h" 12 #include "base/files/file_path.h" 13 #include "base/logging.h" 14 #include "base/metrics/histogram.h" 15 #include "base/prefs/pref_registry_simple.h" 16 #include "base/prefs/pref_service.h" 17 #include "base/strings/string16.h" 18 #include "base/strings/string_util.h" 19 #include "base/strings/utf_string_conversions.h" 20 #include "base/threading/platform_thread.h" 21 #include "chrome/browser/browser_process.h" 22 #include "chrome/browser/chrome_notification_types.h" 23 #include "chrome/browser/google/google_brand.h" 24 #include "chrome/browser/metrics/chrome_stability_metrics_provider.h" 25 #include "chrome/browser/metrics/omnibox_metrics_provider.h" 26 #include "chrome/browser/ui/browser_otr_state.h" 27 #include "chrome/common/chrome_constants.h" 28 #include "chrome/common/chrome_switches.h" 29 #include "chrome/common/chrome_version_info.h" 30 #include "chrome/common/crash_keys.h" 31 #include "chrome/common/pref_names.h" 32 #include "chrome/common/render_messages.h" 33 #include "components/metrics/gpu/gpu_metrics_provider.h" 34 #include "components/metrics/metrics_service.h" 35 #include "components/metrics/net/net_metrics_log_uploader.h" 36 #include "components/metrics/net/network_metrics_provider.h" 37 #include "components/metrics/profiler/profiler_metrics_provider.h" 38 #include "components/metrics/profiler/tracking_synchronizer.h" 39 #include "content/public/browser/browser_thread.h" 40 #include "content/public/browser/histogram_fetcher.h" 41 #include "content/public/browser/notification_service.h" 42 #include "content/public/browser/render_process_host.h" 43 44 #if defined(OS_ANDROID) 45 #include "chrome/browser/metrics/android_metrics_provider.h" 46 #endif 47 48 #if defined(ENABLE_FULL_PRINTING) 49 #include "chrome/browser/service_process/service_process_control.h" 50 #endif 51 52 #if defined(ENABLE_EXTENSIONS) 53 #include "chrome/browser/metrics/extensions_metrics_provider.h" 54 #endif 55 56 #if defined(ENABLE_PLUGINS) 57 #include "chrome/browser/metrics/plugin_metrics_provider.h" 58 #endif 59 60 #if defined(OS_CHROMEOS) 61 #include "chrome/browser/metrics/chromeos_metrics_provider.h" 62 #endif 63 64 #if defined(OS_WIN) 65 #include <windows.h> 66 #include "base/win/registry.h" 67 #include "chrome/browser/metrics/google_update_metrics_provider_win.h" 68 #endif 69 70 #if !defined(OS_CHROMEOS) && !defined(OS_IOS) 71 #include "chrome/browser/metrics/signin_status_metrics_provider.h" 72 #endif 73 74 namespace { 75 76 // This specifies the amount of time to wait for all renderers to send their 77 // data. 78 const int kMaxHistogramGatheringWaitDuration = 60000; // 60 seconds. 79 80 metrics::SystemProfileProto::Channel AsProtobufChannel( 81 chrome::VersionInfo::Channel channel) { 82 switch (channel) { 83 case chrome::VersionInfo::CHANNEL_UNKNOWN: 84 return metrics::SystemProfileProto::CHANNEL_UNKNOWN; 85 case chrome::VersionInfo::CHANNEL_CANARY: 86 return metrics::SystemProfileProto::CHANNEL_CANARY; 87 case chrome::VersionInfo::CHANNEL_DEV: 88 return metrics::SystemProfileProto::CHANNEL_DEV; 89 case chrome::VersionInfo::CHANNEL_BETA: 90 return metrics::SystemProfileProto::CHANNEL_BETA; 91 case chrome::VersionInfo::CHANNEL_STABLE: 92 return metrics::SystemProfileProto::CHANNEL_STABLE; 93 } 94 NOTREACHED(); 95 return metrics::SystemProfileProto::CHANNEL_UNKNOWN; 96 } 97 98 // Handles asynchronous fetching of memory details. 99 // Will run the provided task after finished. 100 class MetricsMemoryDetails : public MemoryDetails { 101 public: 102 MetricsMemoryDetails( 103 const base::Closure& callback, 104 MemoryGrowthTracker* memory_growth_tracker) 105 : callback_(callback) { 106 SetMemoryGrowthTracker(memory_growth_tracker); 107 } 108 109 virtual void OnDetailsAvailable() OVERRIDE { 110 base::MessageLoop::current()->PostTask(FROM_HERE, callback_); 111 } 112 113 private: 114 virtual ~MetricsMemoryDetails() {} 115 116 base::Closure callback_; 117 118 DISALLOW_COPY_AND_ASSIGN(MetricsMemoryDetails); 119 }; 120 121 } // namespace 122 123 ChromeMetricsServiceClient::ChromeMetricsServiceClient( 124 metrics::MetricsStateManager* state_manager) 125 : metrics_state_manager_(state_manager), 126 chromeos_metrics_provider_(NULL), 127 waiting_for_collect_final_metrics_step_(false), 128 num_async_histogram_fetches_in_progress_(0), 129 weak_ptr_factory_(this) { 130 DCHECK(thread_checker_.CalledOnValidThread()); 131 RecordCommandLineMetrics(); 132 RegisterForNotifications(); 133 134 #if defined(OS_WIN) 135 CountBrowserCrashDumpAttempts(); 136 #endif // defined(OS_WIN) 137 } 138 139 ChromeMetricsServiceClient::~ChromeMetricsServiceClient() { 140 DCHECK(thread_checker_.CalledOnValidThread()); 141 } 142 143 // static 144 scoped_ptr<ChromeMetricsServiceClient> ChromeMetricsServiceClient::Create( 145 metrics::MetricsStateManager* state_manager, 146 PrefService* local_state) { 147 // Perform two-phase initialization so that |client->metrics_service_| only 148 // receives pointers to fully constructed objects. 149 scoped_ptr<ChromeMetricsServiceClient> client( 150 new ChromeMetricsServiceClient(state_manager)); 151 client->Initialize(); 152 153 return client.Pass(); 154 } 155 156 // static 157 void ChromeMetricsServiceClient::RegisterPrefs(PrefRegistrySimple* registry) { 158 registry->RegisterInt64Pref(prefs::kUninstallLastLaunchTimeSec, 0); 159 registry->RegisterInt64Pref(prefs::kUninstallLastObservedRunTimeSec, 0); 160 161 metrics::MetricsService::RegisterPrefs(registry); 162 ChromeStabilityMetricsProvider::RegisterPrefs(registry); 163 164 #if defined(OS_ANDROID) 165 AndroidMetricsProvider::RegisterPrefs(registry); 166 #endif // defined(OS_ANDROID) 167 168 #if defined(ENABLE_PLUGINS) 169 PluginMetricsProvider::RegisterPrefs(registry); 170 #endif // defined(ENABLE_PLUGINS) 171 } 172 173 void ChromeMetricsServiceClient::SetMetricsClientId( 174 const std::string& client_id) { 175 crash_keys::SetCrashClientIdFromGUID(client_id); 176 } 177 178 bool ChromeMetricsServiceClient::IsOffTheRecordSessionActive() { 179 return chrome::IsOffTheRecordSessionActive(); 180 } 181 182 std::string ChromeMetricsServiceClient::GetApplicationLocale() { 183 return g_browser_process->GetApplicationLocale(); 184 } 185 186 bool ChromeMetricsServiceClient::GetBrand(std::string* brand_code) { 187 return google_brand::GetBrand(brand_code); 188 } 189 190 metrics::SystemProfileProto::Channel ChromeMetricsServiceClient::GetChannel() { 191 return AsProtobufChannel(chrome::VersionInfo::GetChannel()); 192 } 193 194 std::string ChromeMetricsServiceClient::GetVersionString() { 195 chrome::VersionInfo version_info; 196 if (!version_info.is_valid()) { 197 NOTREACHED(); 198 return std::string(); 199 } 200 201 std::string version = version_info.Version(); 202 #if defined(ARCH_CPU_64_BITS) 203 version += "-64"; 204 #endif // defined(ARCH_CPU_64_BITS) 205 if (!version_info.IsOfficialBuild()) 206 version.append("-devel"); 207 return version; 208 } 209 210 void ChromeMetricsServiceClient::OnLogUploadComplete() { 211 // Collect network stats after each UMA upload. 212 network_stats_uploader_.CollectAndReportNetworkStats(); 213 } 214 215 void ChromeMetricsServiceClient::StartGatheringMetrics( 216 const base::Closure& done_callback) { 217 finished_gathering_initial_metrics_callback_ = done_callback; 218 base::Closure got_hardware_class_callback = 219 base::Bind(&ChromeMetricsServiceClient::OnInitTaskGotHardwareClass, 220 weak_ptr_factory_.GetWeakPtr()); 221 #if defined(OS_CHROMEOS) 222 chromeos_metrics_provider_->InitTaskGetHardwareClass( 223 got_hardware_class_callback); 224 #else 225 got_hardware_class_callback.Run(); 226 #endif // defined(OS_CHROMEOS) 227 } 228 229 void ChromeMetricsServiceClient::CollectFinalMetrics( 230 const base::Closure& done_callback) { 231 DCHECK(thread_checker_.CalledOnValidThread()); 232 233 collect_final_metrics_done_callback_ = done_callback; 234 235 // Begin the multi-step process of collecting memory usage histograms: 236 // First spawn a task to collect the memory details; when that task is 237 // finished, it will call OnMemoryDetailCollectionDone. That will in turn 238 // call HistogramSynchronization to collect histograms from all renderers and 239 // then call OnHistogramSynchronizationDone to continue processing. 240 DCHECK(!waiting_for_collect_final_metrics_step_); 241 waiting_for_collect_final_metrics_step_ = true; 242 243 base::Closure callback = 244 base::Bind(&ChromeMetricsServiceClient::OnMemoryDetailCollectionDone, 245 weak_ptr_factory_.GetWeakPtr()); 246 247 scoped_refptr<MetricsMemoryDetails> details( 248 new MetricsMemoryDetails(callback, &memory_growth_tracker_)); 249 details->StartFetch(MemoryDetails::UPDATE_USER_METRICS); 250 251 // Collect WebCore cache information to put into a histogram. 252 for (content::RenderProcessHost::iterator i( 253 content::RenderProcessHost::AllHostsIterator()); 254 !i.IsAtEnd(); i.Advance()) { 255 i.GetCurrentValue()->Send(new ChromeViewMsg_GetCacheResourceStats()); 256 } 257 } 258 259 scoped_ptr<metrics::MetricsLogUploader> 260 ChromeMetricsServiceClient::CreateUploader( 261 const std::string& server_url, 262 const std::string& mime_type, 263 const base::Callback<void(int)>& on_upload_complete) { 264 return scoped_ptr<metrics::MetricsLogUploader>( 265 new metrics::NetMetricsLogUploader( 266 g_browser_process->system_request_context(), server_url, mime_type, 267 on_upload_complete)); 268 } 269 270 base::string16 ChromeMetricsServiceClient::GetRegistryBackupKey() { 271 #if defined(OS_WIN) 272 return L"Software\\" PRODUCT_STRING_PATH L"\\StabilityMetrics"; 273 #else 274 return base::string16(); 275 #endif 276 } 277 278 void ChromeMetricsServiceClient::LogPluginLoadingError( 279 const base::FilePath& plugin_path) { 280 #if defined(ENABLE_PLUGINS) 281 plugin_metrics_provider_->LogPluginLoadingError(plugin_path); 282 #else 283 NOTREACHED(); 284 #endif // defined(ENABLE_PLUGINS) 285 } 286 287 void ChromeMetricsServiceClient::Initialize() { 288 metrics_service_.reset(new metrics::MetricsService( 289 metrics_state_manager_, this, g_browser_process->local_state())); 290 291 // Register metrics providers. 292 #if defined(ENABLE_EXTENSIONS) 293 metrics_service_->RegisterMetricsProvider( 294 scoped_ptr<metrics::MetricsProvider>( 295 new ExtensionsMetricsProvider(metrics_state_manager_))); 296 #endif 297 metrics_service_->RegisterMetricsProvider( 298 scoped_ptr<metrics::MetricsProvider>(new NetworkMetricsProvider( 299 content::BrowserThread::GetBlockingPool()))); 300 metrics_service_->RegisterMetricsProvider( 301 scoped_ptr<metrics::MetricsProvider>(new OmniboxMetricsProvider)); 302 metrics_service_->RegisterMetricsProvider( 303 scoped_ptr<metrics::MetricsProvider>(new ChromeStabilityMetricsProvider)); 304 metrics_service_->RegisterMetricsProvider( 305 scoped_ptr<metrics::MetricsProvider>(new metrics::GPUMetricsProvider())); 306 profiler_metrics_provider_ = new metrics::ProfilerMetricsProvider; 307 metrics_service_->RegisterMetricsProvider( 308 scoped_ptr<metrics::MetricsProvider>(profiler_metrics_provider_)); 309 310 #if defined(OS_ANDROID) 311 metrics_service_->RegisterMetricsProvider( 312 scoped_ptr<metrics::MetricsProvider>( 313 new AndroidMetricsProvider(g_browser_process->local_state()))); 314 #endif // defined(OS_ANDROID) 315 316 #if defined(OS_WIN) 317 google_update_metrics_provider_ = new GoogleUpdateMetricsProviderWin; 318 metrics_service_->RegisterMetricsProvider( 319 scoped_ptr<metrics::MetricsProvider>(google_update_metrics_provider_)); 320 #endif // defined(OS_WIN) 321 322 #if defined(ENABLE_PLUGINS) 323 plugin_metrics_provider_ = 324 new PluginMetricsProvider(g_browser_process->local_state()); 325 metrics_service_->RegisterMetricsProvider( 326 scoped_ptr<metrics::MetricsProvider>(plugin_metrics_provider_)); 327 #endif // defined(ENABLE_PLUGINS) 328 329 #if defined(OS_CHROMEOS) 330 ChromeOSMetricsProvider* chromeos_metrics_provider = 331 new ChromeOSMetricsProvider; 332 chromeos_metrics_provider_ = chromeos_metrics_provider; 333 metrics_service_->RegisterMetricsProvider( 334 scoped_ptr<metrics::MetricsProvider>(chromeos_metrics_provider)); 335 #endif // defined(OS_CHROMEOS) 336 337 #if !defined(OS_CHROMEOS) && !defined(OS_IOS) 338 metrics_service_->RegisterMetricsProvider( 339 scoped_ptr<metrics::MetricsProvider>( 340 SigninStatusMetricsProvider::CreateInstance())); 341 #endif 342 } 343 344 void ChromeMetricsServiceClient::OnInitTaskGotHardwareClass() { 345 const base::Closure got_plugin_info_callback = 346 base::Bind(&ChromeMetricsServiceClient::OnInitTaskGotPluginInfo, 347 weak_ptr_factory_.GetWeakPtr()); 348 349 #if defined(ENABLE_PLUGINS) 350 plugin_metrics_provider_->GetPluginInformation(got_plugin_info_callback); 351 #else 352 got_plugin_info_callback.Run(); 353 #endif // defined(ENABLE_PLUGINS) 354 } 355 356 void ChromeMetricsServiceClient::OnInitTaskGotPluginInfo() { 357 const base::Closure got_metrics_callback = 358 base::Bind(&ChromeMetricsServiceClient::OnInitTaskGotGoogleUpdateData, 359 weak_ptr_factory_.GetWeakPtr()); 360 361 #if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD) 362 google_update_metrics_provider_->GetGoogleUpdateData(got_metrics_callback); 363 #else 364 got_metrics_callback.Run(); 365 #endif // defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD) 366 } 367 368 void ChromeMetricsServiceClient::OnInitTaskGotGoogleUpdateData() { 369 // Start the next part of the init task: fetching performance data. This will 370 // call into |FinishedReceivingProfilerData()| when the task completes. 371 metrics::TrackingSynchronizer::FetchProfilerDataAsynchronously( 372 weak_ptr_factory_.GetWeakPtr()); 373 } 374 375 void ChromeMetricsServiceClient::ReceivedProfilerData( 376 const tracked_objects::ProcessDataSnapshot& process_data, 377 int process_type) { 378 profiler_metrics_provider_->RecordProfilerData(process_data, process_type); 379 } 380 381 void ChromeMetricsServiceClient::FinishedReceivingProfilerData() { 382 finished_gathering_initial_metrics_callback_.Run(); 383 } 384 385 void ChromeMetricsServiceClient::OnMemoryDetailCollectionDone() { 386 DCHECK(thread_checker_.CalledOnValidThread()); 387 388 // This function should only be called as the callback from an ansynchronous 389 // step. 390 DCHECK(waiting_for_collect_final_metrics_step_); 391 392 // Create a callback_task for OnHistogramSynchronizationDone. 393 base::Closure callback = base::Bind( 394 &ChromeMetricsServiceClient::OnHistogramSynchronizationDone, 395 weak_ptr_factory_.GetWeakPtr()); 396 397 base::TimeDelta timeout = 398 base::TimeDelta::FromMilliseconds(kMaxHistogramGatheringWaitDuration); 399 400 DCHECK_EQ(num_async_histogram_fetches_in_progress_, 0); 401 402 #if !defined(ENABLE_FULL_PRINTING) 403 num_async_histogram_fetches_in_progress_ = 1; 404 #else // !ENABLE_FULL_PRINTING 405 num_async_histogram_fetches_in_progress_ = 2; 406 // Run requests to service and content in parallel. 407 if (!ServiceProcessControl::GetInstance()->GetHistograms(callback, timeout)) { 408 // Assume |num_async_histogram_fetches_in_progress_| is not changed by 409 // |GetHistograms()|. 410 DCHECK_EQ(num_async_histogram_fetches_in_progress_, 2); 411 // Assign |num_async_histogram_fetches_in_progress_| above and decrement it 412 // here to make code work even if |GetHistograms()| fired |callback|. 413 --num_async_histogram_fetches_in_progress_; 414 } 415 #endif // !ENABLE_FULL_PRINTING 416 417 // Set up the callback to task to call after we receive histograms from all 418 // child processes. |timeout| specifies how long to wait before absolutely 419 // calling us back on the task. 420 content::FetchHistogramsAsynchronously(base::MessageLoop::current(), callback, 421 timeout); 422 } 423 424 void ChromeMetricsServiceClient::OnHistogramSynchronizationDone() { 425 DCHECK(thread_checker_.CalledOnValidThread()); 426 427 // This function should only be called as the callback from an ansynchronous 428 // step. 429 DCHECK(waiting_for_collect_final_metrics_step_); 430 DCHECK_GT(num_async_histogram_fetches_in_progress_, 0); 431 432 // Check if all expected requests finished. 433 if (--num_async_histogram_fetches_in_progress_ > 0) 434 return; 435 436 waiting_for_collect_final_metrics_step_ = false; 437 collect_final_metrics_done_callback_.Run(); 438 } 439 440 void ChromeMetricsServiceClient::RecordCommandLineMetrics() { 441 // Get stats on use of command line. 442 const CommandLine* command_line(CommandLine::ForCurrentProcess()); 443 size_t common_commands = 0; 444 if (command_line->HasSwitch(switches::kUserDataDir)) { 445 ++common_commands; 446 UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineDatDirCount", 1); 447 } 448 449 if (command_line->HasSwitch(switches::kApp)) { 450 ++common_commands; 451 UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineAppModeCount", 1); 452 } 453 454 // TODO(rohitrao): Should these be logged on iOS as well? 455 // http://crbug.com/375794 456 size_t switch_count = command_line->GetSwitches().size(); 457 UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineFlagCount", switch_count); 458 UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineUncommonFlagCount", 459 switch_count - common_commands); 460 } 461 462 void ChromeMetricsServiceClient::RegisterForNotifications() { 463 registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED, 464 content::NotificationService::AllBrowserContextsAndSources()); 465 registrar_.Add(this, chrome::NOTIFICATION_BROWSER_CLOSED, 466 content::NotificationService::AllSources()); 467 registrar_.Add(this, chrome::NOTIFICATION_TAB_PARENTED, 468 content::NotificationService::AllSources()); 469 registrar_.Add(this, chrome::NOTIFICATION_TAB_CLOSING, 470 content::NotificationService::AllSources()); 471 registrar_.Add(this, content::NOTIFICATION_LOAD_START, 472 content::NotificationService::AllSources()); 473 registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, 474 content::NotificationService::AllSources()); 475 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, 476 content::NotificationService::AllSources()); 477 registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG, 478 content::NotificationService::AllSources()); 479 registrar_.Add(this, chrome::NOTIFICATION_OMNIBOX_OPENED_URL, 480 content::NotificationService::AllSources()); 481 } 482 483 void ChromeMetricsServiceClient::Observe( 484 int type, 485 const content::NotificationSource& source, 486 const content::NotificationDetails& details) { 487 DCHECK(thread_checker_.CalledOnValidThread()); 488 489 switch (type) { 490 case chrome::NOTIFICATION_BROWSER_OPENED: 491 case chrome::NOTIFICATION_BROWSER_CLOSED: 492 case chrome::NOTIFICATION_OMNIBOX_OPENED_URL: 493 case chrome::NOTIFICATION_TAB_PARENTED: 494 case chrome::NOTIFICATION_TAB_CLOSING: 495 case content::NOTIFICATION_LOAD_STOP: 496 case content::NOTIFICATION_LOAD_START: 497 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: 498 case content::NOTIFICATION_RENDER_WIDGET_HOST_HANG: 499 metrics_service_->OnApplicationNotIdle(); 500 break; 501 502 default: 503 NOTREACHED(); 504 } 505 } 506 507 #if defined(OS_WIN) 508 void ChromeMetricsServiceClient::CountBrowserCrashDumpAttempts() { 509 // Open the registry key for iteration. 510 base::win::RegKey regkey; 511 if (regkey.Open(HKEY_CURRENT_USER, 512 chrome::kBrowserCrashDumpAttemptsRegistryPath, 513 KEY_ALL_ACCESS) != ERROR_SUCCESS) { 514 return; 515 } 516 517 // The values we're interested in counting are all prefixed with the version. 518 base::string16 chrome_version(base::ASCIIToUTF16(chrome::kChromeVersion)); 519 520 // Track a list of values to delete. We don't modify the registry key while 521 // we're iterating over its values. 522 typedef std::vector<base::string16> StringVector; 523 StringVector to_delete; 524 525 // Iterate over the values in the key counting dumps with and without crashes. 526 // We directly walk the values instead of using RegistryValueIterator in order 527 // to read all of the values as DWORDS instead of strings. 528 base::string16 name; 529 DWORD value = 0; 530 int dumps_with_crash = 0; 531 int dumps_with_no_crash = 0; 532 for (int i = regkey.GetValueCount() - 1; i >= 0; --i) { 533 if (regkey.GetValueNameAt(i, &name) == ERROR_SUCCESS && 534 StartsWith(name, chrome_version, false) && 535 regkey.ReadValueDW(name.c_str(), &value) == ERROR_SUCCESS) { 536 to_delete.push_back(name); 537 if (value == 0) 538 ++dumps_with_no_crash; 539 else 540 ++dumps_with_crash; 541 } 542 } 543 544 // Delete the registry keys we've just counted. 545 for (StringVector::iterator i = to_delete.begin(); i != to_delete.end(); ++i) 546 regkey.DeleteValue(i->c_str()); 547 548 // Capture the histogram samples. 549 if (dumps_with_crash != 0) 550 UMA_HISTOGRAM_COUNTS("Chrome.BrowserDumpsWithCrash", dumps_with_crash); 551 if (dumps_with_no_crash != 0) 552 UMA_HISTOGRAM_COUNTS("Chrome.BrowserDumpsWithNoCrash", dumps_with_no_crash); 553 int total_dumps = dumps_with_crash + dumps_with_no_crash; 554 if (total_dumps != 0) 555 UMA_HISTOGRAM_COUNTS("Chrome.BrowserCrashDumpAttempts", total_dumps); 556 } 557 #endif // defined(OS_WIN) 558