blob: 2082442849181eec5c2b04901d9720e26d8d9b7c [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/metrics/chrome_metrics_service_client.h"
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram.h"
#include "base/prefs/pref_registry_simple.h"
#include "base/prefs/pref_service.h"
#include "base/strings/string16.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/platform_thread.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/google/google_brand.h"
#include "chrome/browser/metrics/chrome_stability_metrics_provider.h"
#include "chrome/browser/metrics/drive_metrics_provider.h"
#include "chrome/browser/metrics/omnibox_metrics_provider.h"
#include "chrome/browser/ui/browser_otr_state.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/chrome_version_info.h"
#include "chrome/common/crash_keys.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/render_messages.h"
#include "components/metrics/call_stack_profile_metrics_provider.h"
#include "components/metrics/gpu/gpu_metrics_provider.h"
#include "components/metrics/metrics_service.h"
#include "components/metrics/net/net_metrics_log_uploader.h"
#include "components/metrics/net/network_metrics_provider.h"
#include "components/metrics/profiler/profiler_metrics_provider.h"
#include "components/metrics/profiler/tracking_synchronizer.h"
#include "components/metrics/url_constants.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/histogram_fetcher.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/render_process_host.h"
#if defined(OS_ANDROID)
#include "chrome/browser/metrics/android_metrics_provider.h"
#endif
#if defined(ENABLE_PRINT_PREVIEW)
#include "chrome/browser/service_process/service_process_control.h"
#endif
#if defined(ENABLE_EXTENSIONS)
#include "chrome/browser/metrics/extensions_metrics_provider.h"
#endif
#if defined(ENABLE_PLUGINS)
#include "chrome/browser/metrics/plugin_metrics_provider.h"
#endif
#if defined(OS_CHROMEOS)
#include "chrome/browser/metrics/chromeos_metrics_provider.h"
#include "chrome/browser/metrics/signin_status_metrics_provider_chromeos.h"
#endif
#if defined(OS_WIN)
#include <windows.h>
#include "base/win/registry.h"
#include "chrome/browser/metrics/google_update_metrics_provider_win.h"
#include "components/browser_watcher/watcher_metrics_provider_win.h"
#endif
#if !defined(OS_CHROMEOS) && !defined(OS_IOS)
#include "chrome/browser/metrics/signin_status_metrics_provider.h"
#endif // !defined(OS_CHROMEOS) && !defined(OS_IOS)
namespace {
// This specifies the amount of time to wait for all renderers to send their
// data.
const int kMaxHistogramGatheringWaitDuration = 60000; // 60 seconds.
metrics::SystemProfileProto::Channel AsProtobufChannel(
chrome::VersionInfo::Channel channel) {
switch (channel) {
case chrome::VersionInfo::CHANNEL_UNKNOWN:
return metrics::SystemProfileProto::CHANNEL_UNKNOWN;
case chrome::VersionInfo::CHANNEL_CANARY:
return metrics::SystemProfileProto::CHANNEL_CANARY;
case chrome::VersionInfo::CHANNEL_DEV:
return metrics::SystemProfileProto::CHANNEL_DEV;
case chrome::VersionInfo::CHANNEL_BETA:
return metrics::SystemProfileProto::CHANNEL_BETA;
case chrome::VersionInfo::CHANNEL_STABLE:
return metrics::SystemProfileProto::CHANNEL_STABLE;
}
NOTREACHED();
return metrics::SystemProfileProto::CHANNEL_UNKNOWN;
}
// Standard interval between log uploads, in seconds.
#if defined(OS_ANDROID) || defined(OS_IOS)
const int kStandardUploadIntervalSeconds = 5 * 60; // Five minutes.
const int kStandardUploadIntervalCellularSeconds = 15 * 60; // Fifteen minutes.
#else
const int kStandardUploadIntervalSeconds = 30 * 60; // Thirty minutes.
#endif
#if defined(OS_ANDROID) || defined(OS_IOS)
// Returns true if the user is assigned to the experiment group for enabled
// cellular uploads.
bool IsCellularEnabledByExperiment() {
const std::string group_name =
base::FieldTrialList::FindFullName("UMA_EnableCellularLogUpload");
return group_name == "Enabled";
}
#endif
} // namespace
ChromeMetricsServiceClient::ChromeMetricsServiceClient(
metrics::MetricsStateManager* state_manager)
: metrics_state_manager_(state_manager),
chromeos_metrics_provider_(nullptr),
waiting_for_collect_final_metrics_step_(false),
num_async_histogram_fetches_in_progress_(0),
profiler_metrics_provider_(nullptr),
#if defined(ENABLE_PLUGINS)
plugin_metrics_provider_(nullptr),
#endif
#if defined(OS_WIN)
google_update_metrics_provider_(nullptr),
#endif
drive_metrics_provider_(nullptr),
weak_ptr_factory_(this) {
DCHECK(thread_checker_.CalledOnValidThread());
RecordCommandLineMetrics();
RegisterForNotifications();
#if defined(OS_WIN)
CountBrowserCrashDumpAttempts();
#endif // defined(OS_WIN)
}
ChromeMetricsServiceClient::~ChromeMetricsServiceClient() {
DCHECK(thread_checker_.CalledOnValidThread());
}
// static
scoped_ptr<ChromeMetricsServiceClient> ChromeMetricsServiceClient::Create(
metrics::MetricsStateManager* state_manager,
PrefService* local_state) {
// Perform two-phase initialization so that |client->metrics_service_| only
// receives pointers to fully constructed objects.
scoped_ptr<ChromeMetricsServiceClient> client(
new ChromeMetricsServiceClient(state_manager));
client->Initialize();
return client.Pass();
}
// static
void ChromeMetricsServiceClient::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterInt64Pref(prefs::kUninstallLastLaunchTimeSec, 0);
registry->RegisterInt64Pref(prefs::kUninstallLastObservedRunTimeSec, 0);
metrics::MetricsService::RegisterPrefs(registry);
ChromeStabilityMetricsProvider::RegisterPrefs(registry);
#if defined(OS_ANDROID)
AndroidMetricsProvider::RegisterPrefs(registry);
#endif // defined(OS_ANDROID)
#if defined(ENABLE_PLUGINS)
PluginMetricsProvider::RegisterPrefs(registry);
#endif // defined(ENABLE_PLUGINS)
}
void ChromeMetricsServiceClient::SetMetricsClientId(
const std::string& client_id) {
crash_keys::SetCrashClientIdFromGUID(client_id);
}
bool ChromeMetricsServiceClient::IsOffTheRecordSessionActive() {
return chrome::IsOffTheRecordSessionActive();
}
int32 ChromeMetricsServiceClient::GetProduct() {
return metrics::ChromeUserMetricsExtension::CHROME;
}
std::string ChromeMetricsServiceClient::GetApplicationLocale() {
return g_browser_process->GetApplicationLocale();
}
bool ChromeMetricsServiceClient::GetBrand(std::string* brand_code) {
return google_brand::GetBrand(brand_code);
}
metrics::SystemProfileProto::Channel ChromeMetricsServiceClient::GetChannel() {
return AsProtobufChannel(chrome::VersionInfo::GetChannel());
}
std::string ChromeMetricsServiceClient::GetVersionString() {
chrome::VersionInfo version_info;
std::string version = version_info.Version();
#if defined(ARCH_CPU_64_BITS)
version += "-64";
#endif // defined(ARCH_CPU_64_BITS)
if (!version_info.IsOfficialBuild())
version.append("-devel");
return version;
}
void ChromeMetricsServiceClient::OnLogUploadComplete() {
// Collect network stats after each UMA upload.
network_stats_uploader_.CollectAndReportNetworkStats();
}
void ChromeMetricsServiceClient::StartGatheringMetrics(
const base::Closure& done_callback) {
finished_gathering_initial_metrics_callback_ = done_callback;
base::Closure got_hardware_class_callback =
base::Bind(&ChromeMetricsServiceClient::OnInitTaskGotHardwareClass,
weak_ptr_factory_.GetWeakPtr());
#if defined(OS_CHROMEOS)
chromeos_metrics_provider_->InitTaskGetHardwareClass(
got_hardware_class_callback);
#else
got_hardware_class_callback.Run();
#endif // defined(OS_CHROMEOS)
}
void ChromeMetricsServiceClient::CollectFinalMetrics(
const base::Closure& done_callback) {
DCHECK(thread_checker_.CalledOnValidThread());
collect_final_metrics_done_callback_ = done_callback;
// Begin the multi-step process of collecting memory usage histograms:
// First spawn a task to collect the memory details; when that task is
// finished, it will call OnMemoryDetailCollectionDone. That will in turn
// call HistogramSynchronization to collect histograms from all renderers and
// then call OnHistogramSynchronizationDone to continue processing.
DCHECK(!waiting_for_collect_final_metrics_step_);
waiting_for_collect_final_metrics_step_ = true;
base::Closure callback =
base::Bind(&ChromeMetricsServiceClient::OnMemoryDetailCollectionDone,
weak_ptr_factory_.GetWeakPtr());
scoped_refptr<MetricsMemoryDetails> details(
new MetricsMemoryDetails(callback, &memory_growth_tracker_));
details->StartFetch(MemoryDetails::FROM_CHROME_ONLY);
// Collect WebCore cache information to put into a histogram.
for (content::RenderProcessHost::iterator i(
content::RenderProcessHost::AllHostsIterator());
!i.IsAtEnd(); i.Advance()) {
i.GetCurrentValue()->Send(new ChromeViewMsg_GetCacheResourceStats());
}
}
scoped_ptr<metrics::MetricsLogUploader>
ChromeMetricsServiceClient::CreateUploader(
const base::Callback<void(int)>& on_upload_complete) {
return scoped_ptr<metrics::MetricsLogUploader>(
new metrics::NetMetricsLogUploader(
g_browser_process->system_request_context(),
metrics::kDefaultMetricsServerUrl,
metrics::kDefaultMetricsMimeType,
on_upload_complete));
}
base::TimeDelta ChromeMetricsServiceClient::GetStandardUploadInterval() {
#if defined(OS_ANDROID) || defined(OS_IOS)
bool is_cellular = false;
cellular_callback_.Run(&is_cellular);
if (is_cellular && IsCellularEnabledByExperiment())
return base::TimeDelta::FromSeconds(kStandardUploadIntervalCellularSeconds);
#endif
return base::TimeDelta::FromSeconds(kStandardUploadIntervalSeconds);
}
base::string16 ChromeMetricsServiceClient::GetRegistryBackupKey() {
#if defined(OS_WIN)
return L"Software\\" PRODUCT_STRING_PATH L"\\StabilityMetrics";
#else
return base::string16();
#endif
}
void ChromeMetricsServiceClient::LogPluginLoadingError(
const base::FilePath& plugin_path) {
#if defined(ENABLE_PLUGINS)
plugin_metrics_provider_->LogPluginLoadingError(plugin_path);
#else
NOTREACHED();
#endif // defined(ENABLE_PLUGINS)
}
void ChromeMetricsServiceClient::Initialize() {
metrics_service_.reset(new metrics::MetricsService(
metrics_state_manager_, this, g_browser_process->local_state()));
// Register metrics providers.
#if defined(ENABLE_EXTENSIONS)
metrics_service_->RegisterMetricsProvider(
scoped_ptr<metrics::MetricsProvider>(
new ExtensionsMetricsProvider(metrics_state_manager_)));
#endif
scoped_ptr<metrics::NetworkMetricsProvider> network_metrics_provider(
new metrics::NetworkMetricsProvider(
content::BrowserThread::GetBlockingPool()));
cellular_callback_ = network_metrics_provider->GetConnectionCallback();
metrics_service_->RegisterMetricsProvider(network_metrics_provider.Pass());
metrics_service_->RegisterMetricsProvider(
scoped_ptr<metrics::MetricsProvider>(new OmniboxMetricsProvider));
metrics_service_->RegisterMetricsProvider(
scoped_ptr<metrics::MetricsProvider>(new ChromeStabilityMetricsProvider));
metrics_service_->RegisterMetricsProvider(
scoped_ptr<metrics::MetricsProvider>(new metrics::GPUMetricsProvider));
drive_metrics_provider_ = new DriveMetricsProvider;
metrics_service_->RegisterMetricsProvider(
scoped_ptr<metrics::MetricsProvider>(drive_metrics_provider_));
profiler_metrics_provider_ =
new metrics::ProfilerMetricsProvider(cellular_callback_);
metrics_service_->RegisterMetricsProvider(
scoped_ptr<metrics::MetricsProvider>(profiler_metrics_provider_));
metrics_service_->RegisterMetricsProvider(
scoped_ptr<metrics::MetricsProvider>(
new metrics::CallStackProfileMetricsProvider));
#if defined(OS_ANDROID)
metrics_service_->RegisterMetricsProvider(
scoped_ptr<metrics::MetricsProvider>(
new AndroidMetricsProvider(g_browser_process->local_state())));
#endif // defined(OS_ANDROID)
#if defined(OS_WIN)
google_update_metrics_provider_ = new GoogleUpdateMetricsProviderWin;
metrics_service_->RegisterMetricsProvider(
scoped_ptr<metrics::MetricsProvider>(google_update_metrics_provider_));
// Report exit funnels for canary and dev only.
bool report_exit_funnels = false;
switch (chrome::VersionInfo::GetChannel()) {
case chrome::VersionInfo::CHANNEL_CANARY:
case chrome::VersionInfo::CHANNEL_DEV:
report_exit_funnels = true;
break;
}
metrics_service_->RegisterMetricsProvider(
scoped_ptr<metrics::MetricsProvider>(
new browser_watcher::WatcherMetricsProviderWin(
chrome::kBrowserExitCodesRegistryPath, report_exit_funnels)));
#endif // defined(OS_WIN)
#if defined(ENABLE_PLUGINS)
plugin_metrics_provider_ =
new PluginMetricsProvider(g_browser_process->local_state());
metrics_service_->RegisterMetricsProvider(
scoped_ptr<metrics::MetricsProvider>(plugin_metrics_provider_));
#endif // defined(ENABLE_PLUGINS)
#if defined(OS_CHROMEOS)
ChromeOSMetricsProvider* chromeos_metrics_provider =
new ChromeOSMetricsProvider;
chromeos_metrics_provider_ = chromeos_metrics_provider;
metrics_service_->RegisterMetricsProvider(
scoped_ptr<metrics::MetricsProvider>(chromeos_metrics_provider));
SigninStatusMetricsProviderChromeOS* signin_metrics_provider_cros =
new SigninStatusMetricsProviderChromeOS;
metrics_service_->RegisterMetricsProvider(
scoped_ptr<metrics::MetricsProvider>(signin_metrics_provider_cros));
#endif // defined(OS_CHROMEOS)
#if !defined(OS_CHROMEOS) && !defined(OS_IOS)
metrics_service_->RegisterMetricsProvider(
scoped_ptr<metrics::MetricsProvider>(
SigninStatusMetricsProvider::CreateInstance()));
#endif // !defined(OS_CHROMEOS) && !defined(OS_IOS)
}
void ChromeMetricsServiceClient::OnInitTaskGotHardwareClass() {
const base::Closure got_plugin_info_callback =
base::Bind(&ChromeMetricsServiceClient::OnInitTaskGotPluginInfo,
weak_ptr_factory_.GetWeakPtr());
#if defined(ENABLE_PLUGINS)
plugin_metrics_provider_->GetPluginInformation(got_plugin_info_callback);
#else
got_plugin_info_callback.Run();
#endif // defined(ENABLE_PLUGINS)
}
void ChromeMetricsServiceClient::OnInitTaskGotPluginInfo() {
const base::Closure got_metrics_callback =
base::Bind(&ChromeMetricsServiceClient::OnInitTaskGotGoogleUpdateData,
weak_ptr_factory_.GetWeakPtr());
#if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
google_update_metrics_provider_->GetGoogleUpdateData(got_metrics_callback);
#else
got_metrics_callback.Run();
#endif // defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
}
void ChromeMetricsServiceClient::OnInitTaskGotGoogleUpdateData() {
// Start the next part of the init task: fetching performance data. This will
// call into |FinishedReceivingProfilerData()| when the task completes.
metrics::TrackingSynchronizer::FetchProfilerDataAsynchronously(
weak_ptr_factory_.GetWeakPtr());
}
void ChromeMetricsServiceClient::ReceivedProfilerData(
const tracked_objects::ProcessDataSnapshot& process_data,
int process_type) {
profiler_metrics_provider_->RecordProfilerData(process_data, process_type);
}
void ChromeMetricsServiceClient::FinishedReceivingProfilerData() {
drive_metrics_provider_->GetDriveMetrics(
finished_gathering_initial_metrics_callback_);
}
void ChromeMetricsServiceClient::OnMemoryDetailCollectionDone() {
DCHECK(thread_checker_.CalledOnValidThread());
// This function should only be called as the callback from an ansynchronous
// step.
DCHECK(waiting_for_collect_final_metrics_step_);
// Create a callback_task for OnHistogramSynchronizationDone.
base::Closure callback = base::Bind(
&ChromeMetricsServiceClient::OnHistogramSynchronizationDone,
weak_ptr_factory_.GetWeakPtr());
base::TimeDelta timeout =
base::TimeDelta::FromMilliseconds(kMaxHistogramGatheringWaitDuration);
DCHECK_EQ(num_async_histogram_fetches_in_progress_, 0);
#if !defined(ENABLE_PRINT_PREVIEW)
num_async_histogram_fetches_in_progress_ = 1;
#else // !ENABLE_PRINT_PREVIEW
num_async_histogram_fetches_in_progress_ = 2;
// Run requests to service and content in parallel.
if (!ServiceProcessControl::GetInstance()->GetHistograms(callback, timeout)) {
// Assume |num_async_histogram_fetches_in_progress_| is not changed by
// |GetHistograms()|.
DCHECK_EQ(num_async_histogram_fetches_in_progress_, 2);
// Assign |num_async_histogram_fetches_in_progress_| above and decrement it
// here to make code work even if |GetHistograms()| fired |callback|.
--num_async_histogram_fetches_in_progress_;
}
#endif // !ENABLE_PRINT_PREVIEW
// Set up the callback to task to call after we receive histograms from all
// child processes. |timeout| specifies how long to wait before absolutely
// calling us back on the task.
content::FetchHistogramsAsynchronously(base::MessageLoop::current(), callback,
timeout);
}
void ChromeMetricsServiceClient::OnHistogramSynchronizationDone() {
DCHECK(thread_checker_.CalledOnValidThread());
// This function should only be called as the callback from an ansynchronous
// step.
DCHECK(waiting_for_collect_final_metrics_step_);
DCHECK_GT(num_async_histogram_fetches_in_progress_, 0);
// Check if all expected requests finished.
if (--num_async_histogram_fetches_in_progress_ > 0)
return;
waiting_for_collect_final_metrics_step_ = false;
collect_final_metrics_done_callback_.Run();
}
void ChromeMetricsServiceClient::RecordCommandLineMetrics() {
// Get stats on use of command line.
const base::CommandLine* command_line(base::CommandLine::ForCurrentProcess());
size_t common_commands = 0;
if (command_line->HasSwitch(switches::kUserDataDir)) {
++common_commands;
UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineDatDirCount", 1);
}
if (command_line->HasSwitch(switches::kApp)) {
++common_commands;
UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineAppModeCount", 1);
}
// TODO(rohitrao): Should these be logged on iOS as well?
// http://crbug.com/375794
size_t switch_count = command_line->GetSwitches().size();
UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineFlagCount", switch_count);
UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineUncommonFlagCount",
switch_count - common_commands);
}
void ChromeMetricsServiceClient::RegisterForNotifications() {
registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED,
content::NotificationService::AllBrowserContextsAndSources());
registrar_.Add(this, chrome::NOTIFICATION_BROWSER_CLOSED,
content::NotificationService::AllSources());
registrar_.Add(this, chrome::NOTIFICATION_TAB_PARENTED,
content::NotificationService::AllSources());
registrar_.Add(this, chrome::NOTIFICATION_TAB_CLOSING,
content::NotificationService::AllSources());
registrar_.Add(this, content::NOTIFICATION_LOAD_START,
content::NotificationService::AllSources());
registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
content::NotificationService::AllSources());
registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
content::NotificationService::AllSources());
registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG,
content::NotificationService::AllSources());
registrar_.Add(this, chrome::NOTIFICATION_OMNIBOX_OPENED_URL,
content::NotificationService::AllSources());
}
void ChromeMetricsServiceClient::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK(thread_checker_.CalledOnValidThread());
switch (type) {
case chrome::NOTIFICATION_BROWSER_OPENED:
case chrome::NOTIFICATION_BROWSER_CLOSED:
case chrome::NOTIFICATION_OMNIBOX_OPENED_URL:
case chrome::NOTIFICATION_TAB_PARENTED:
case chrome::NOTIFICATION_TAB_CLOSING:
case content::NOTIFICATION_LOAD_STOP:
case content::NOTIFICATION_LOAD_START:
case content::NOTIFICATION_RENDERER_PROCESS_CLOSED:
case content::NOTIFICATION_RENDER_WIDGET_HOST_HANG:
metrics_service_->OnApplicationNotIdle();
break;
default:
NOTREACHED();
}
}
#if defined(OS_WIN)
void ChromeMetricsServiceClient::CountBrowserCrashDumpAttempts() {
// Open the registry key for iteration.
base::win::RegKey regkey;
if (regkey.Open(HKEY_CURRENT_USER,
chrome::kBrowserCrashDumpAttemptsRegistryPath,
KEY_ALL_ACCESS) != ERROR_SUCCESS) {
return;
}
// The values we're interested in counting are all prefixed with the version.
base::string16 chrome_version(base::ASCIIToUTF16(chrome::kChromeVersion));
// Track a list of values to delete. We don't modify the registry key while
// we're iterating over its values.
typedef std::vector<base::string16> StringVector;
StringVector to_delete;
// Iterate over the values in the key counting dumps with and without crashes.
// We directly walk the values instead of using RegistryValueIterator in order
// to read all of the values as DWORDS instead of strings.
base::string16 name;
DWORD value = 0;
int dumps_with_crash = 0;
int dumps_with_no_crash = 0;
for (int i = regkey.GetValueCount() - 1; i >= 0; --i) {
if (regkey.GetValueNameAt(i, &name) == ERROR_SUCCESS &&
StartsWith(name, chrome_version, false) &&
regkey.ReadValueDW(name.c_str(), &value) == ERROR_SUCCESS) {
to_delete.push_back(name);
if (value == 0)
++dumps_with_no_crash;
else
++dumps_with_crash;
}
}
// Delete the registry keys we've just counted.
for (StringVector::iterator i = to_delete.begin(); i != to_delete.end(); ++i)
regkey.DeleteValue(i->c_str());
// Capture the histogram samples.
if (dumps_with_crash != 0)
UMA_HISTOGRAM_COUNTS("Chrome.BrowserDumpsWithCrash", dumps_with_crash);
if (dumps_with_no_crash != 0)
UMA_HISTOGRAM_COUNTS("Chrome.BrowserDumpsWithNoCrash", dumps_with_no_crash);
int total_dumps = dumps_with_crash + dumps_with_no_crash;
if (total_dumps != 0)
UMA_HISTOGRAM_COUNTS("Chrome.BrowserCrashDumpAttempts", total_dumps);
}
#endif // defined(OS_WIN)