chrome_metrics_service_client.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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
74namespace {
75
76// This specifies the amount of time to wait for all renderers to send their
77// data.
78const int kMaxHistogramGatheringWaitDuration = 60000;  // 60 seconds.
79
80metrics::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.
100class 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
123ChromeMetricsServiceClient::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
139ChromeMetricsServiceClient::~ChromeMetricsServiceClient() {
140  DCHECK(thread_checker_.CalledOnValidThread());
141}
142
143// static
144scoped_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
157void 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
173void ChromeMetricsServiceClient::SetMetricsClientId(
174    const std::string& client_id) {
175  crash_keys::SetCrashClientIdFromGUID(client_id);
176}
177
178bool ChromeMetricsServiceClient::IsOffTheRecordSessionActive() {
179  return chrome::IsOffTheRecordSessionActive();
180}
181
182std::string ChromeMetricsServiceClient::GetApplicationLocale() {
183  return g_browser_process->GetApplicationLocale();
184}
185
186bool ChromeMetricsServiceClient::GetBrand(std::string* brand_code) {
187  return google_brand::GetBrand(brand_code);
188}
189
190metrics::SystemProfileProto::Channel ChromeMetricsServiceClient::GetChannel() {
191  return AsProtobufChannel(chrome::VersionInfo::GetChannel());
192}
193
194std::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
210void ChromeMetricsServiceClient::OnLogUploadComplete() {
211  // Collect network stats after each UMA upload.
212  network_stats_uploader_.CollectAndReportNetworkStats();
213}
214
215void 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
229void 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#if !defined(OS_CHROMEOS) && !defined(OS_IOS)
244  // Record the signin status histogram value.
245  signin_status_metrics_provider_->RecordSigninStatusHistogram();
246#endif
247
248  base::Closure callback =
249      base::Bind(&ChromeMetricsServiceClient::OnMemoryDetailCollectionDone,
250                 weak_ptr_factory_.GetWeakPtr());
251
252  scoped_refptr<MetricsMemoryDetails> details(
253      new MetricsMemoryDetails(callback, &memory_growth_tracker_));
254  details->StartFetch(MemoryDetails::UPDATE_USER_METRICS);
255
256  // Collect WebCore cache information to put into a histogram.
257  for (content::RenderProcessHost::iterator i(
258          content::RenderProcessHost::AllHostsIterator());
259       !i.IsAtEnd(); i.Advance()) {
260    i.GetCurrentValue()->Send(new ChromeViewMsg_GetCacheResourceStats());
261  }
262}
263
264scoped_ptr<metrics::MetricsLogUploader>
265ChromeMetricsServiceClient::CreateUploader(
266    const std::string& server_url,
267    const std::string& mime_type,
268    const base::Callback<void(int)>& on_upload_complete) {
269  return scoped_ptr<metrics::MetricsLogUploader>(
270      new metrics::NetMetricsLogUploader(
271          g_browser_process->system_request_context(), server_url, mime_type,
272          on_upload_complete));
273}
274
275base::string16 ChromeMetricsServiceClient::GetRegistryBackupKey() {
276#if defined(OS_WIN)
277  return L"Software\\" PRODUCT_STRING_PATH L"\\StabilityMetrics";
278#else
279  return base::string16();
280#endif
281}
282
283void ChromeMetricsServiceClient::LogPluginLoadingError(
284    const base::FilePath& plugin_path) {
285#if defined(ENABLE_PLUGINS)
286  plugin_metrics_provider_->LogPluginLoadingError(plugin_path);
287#else
288  NOTREACHED();
289#endif  // defined(ENABLE_PLUGINS)
290}
291
292void ChromeMetricsServiceClient::Initialize() {
293  metrics_service_.reset(new metrics::MetricsService(
294      metrics_state_manager_, this, g_browser_process->local_state()));
295
296  // Register metrics providers.
297#if defined(ENABLE_EXTENSIONS)
298  metrics_service_->RegisterMetricsProvider(
299      scoped_ptr<metrics::MetricsProvider>(
300          new ExtensionsMetricsProvider(metrics_state_manager_)));
301#endif
302  metrics_service_->RegisterMetricsProvider(
303      scoped_ptr<metrics::MetricsProvider>(new NetworkMetricsProvider(
304          content::BrowserThread::GetBlockingPool())));
305  metrics_service_->RegisterMetricsProvider(
306      scoped_ptr<metrics::MetricsProvider>(new OmniboxMetricsProvider));
307  metrics_service_->RegisterMetricsProvider(
308      scoped_ptr<metrics::MetricsProvider>(new ChromeStabilityMetricsProvider));
309  metrics_service_->RegisterMetricsProvider(
310      scoped_ptr<metrics::MetricsProvider>(new metrics::GPUMetricsProvider()));
311  profiler_metrics_provider_ = new metrics::ProfilerMetricsProvider;
312  metrics_service_->RegisterMetricsProvider(
313      scoped_ptr<metrics::MetricsProvider>(profiler_metrics_provider_));
314
315#if defined(OS_ANDROID)
316  metrics_service_->RegisterMetricsProvider(
317      scoped_ptr<metrics::MetricsProvider>(
318          new AndroidMetricsProvider(g_browser_process->local_state())));
319#endif  // defined(OS_ANDROID)
320
321#if defined(OS_WIN)
322  google_update_metrics_provider_ = new GoogleUpdateMetricsProviderWin;
323  metrics_service_->RegisterMetricsProvider(
324      scoped_ptr<metrics::MetricsProvider>(google_update_metrics_provider_));
325#endif  // defined(OS_WIN)
326
327#if defined(ENABLE_PLUGINS)
328  plugin_metrics_provider_ =
329      new PluginMetricsProvider(g_browser_process->local_state());
330  metrics_service_->RegisterMetricsProvider(
331      scoped_ptr<metrics::MetricsProvider>(plugin_metrics_provider_));
332#endif  // defined(ENABLE_PLUGINS)
333
334#if defined(OS_CHROMEOS)
335  ChromeOSMetricsProvider* chromeos_metrics_provider =
336      new ChromeOSMetricsProvider;
337  chromeos_metrics_provider_ = chromeos_metrics_provider;
338  metrics_service_->RegisterMetricsProvider(
339      scoped_ptr<metrics::MetricsProvider>(chromeos_metrics_provider));
340#endif  // defined(OS_CHROMEOS)
341
342#if !defined(OS_CHROMEOS) && !defined(OS_IOS)
343  signin_status_metrics_provider_ =
344      SigninStatusMetricsProvider::CreateInstance();
345  metrics_service_->RegisterMetricsProvider(
346      scoped_ptr<metrics::MetricsProvider>(signin_status_metrics_provider_));
347#endif
348}
349
350void ChromeMetricsServiceClient::OnInitTaskGotHardwareClass() {
351  const base::Closure got_plugin_info_callback =
352      base::Bind(&ChromeMetricsServiceClient::OnInitTaskGotPluginInfo,
353                 weak_ptr_factory_.GetWeakPtr());
354
355#if defined(ENABLE_PLUGINS)
356  plugin_metrics_provider_->GetPluginInformation(got_plugin_info_callback);
357#else
358  got_plugin_info_callback.Run();
359#endif  // defined(ENABLE_PLUGINS)
360}
361
362void ChromeMetricsServiceClient::OnInitTaskGotPluginInfo() {
363  const base::Closure got_metrics_callback =
364      base::Bind(&ChromeMetricsServiceClient::OnInitTaskGotGoogleUpdateData,
365                 weak_ptr_factory_.GetWeakPtr());
366
367#if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
368  google_update_metrics_provider_->GetGoogleUpdateData(got_metrics_callback);
369#else
370  got_metrics_callback.Run();
371#endif  // defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
372}
373
374void ChromeMetricsServiceClient::OnInitTaskGotGoogleUpdateData() {
375  // Start the next part of the init task: fetching performance data.  This will
376  // call into |FinishedReceivingProfilerData()| when the task completes.
377  metrics::TrackingSynchronizer::FetchProfilerDataAsynchronously(
378      weak_ptr_factory_.GetWeakPtr());
379}
380
381void ChromeMetricsServiceClient::ReceivedProfilerData(
382    const tracked_objects::ProcessDataSnapshot& process_data,
383    int process_type) {
384  profiler_metrics_provider_->RecordProfilerData(process_data, process_type);
385}
386
387void ChromeMetricsServiceClient::FinishedReceivingProfilerData() {
388  finished_gathering_initial_metrics_callback_.Run();
389}
390
391void ChromeMetricsServiceClient::OnMemoryDetailCollectionDone() {
392  DCHECK(thread_checker_.CalledOnValidThread());
393
394  // This function should only be called as the callback from an ansynchronous
395  // step.
396  DCHECK(waiting_for_collect_final_metrics_step_);
397
398  // Create a callback_task for OnHistogramSynchronizationDone.
399  base::Closure callback = base::Bind(
400      &ChromeMetricsServiceClient::OnHistogramSynchronizationDone,
401      weak_ptr_factory_.GetWeakPtr());
402
403  base::TimeDelta timeout =
404      base::TimeDelta::FromMilliseconds(kMaxHistogramGatheringWaitDuration);
405
406  DCHECK_EQ(num_async_histogram_fetches_in_progress_, 0);
407
408#if !defined(ENABLE_FULL_PRINTING)
409  num_async_histogram_fetches_in_progress_ = 1;
410#else  // !ENABLE_FULL_PRINTING
411  num_async_histogram_fetches_in_progress_ = 2;
412  // Run requests to service and content in parallel.
413  if (!ServiceProcessControl::GetInstance()->GetHistograms(callback, timeout)) {
414    // Assume |num_async_histogram_fetches_in_progress_| is not changed by
415    // |GetHistograms()|.
416    DCHECK_EQ(num_async_histogram_fetches_in_progress_, 2);
417    // Assign |num_async_histogram_fetches_in_progress_| above and decrement it
418    // here to make code work even if |GetHistograms()| fired |callback|.
419    --num_async_histogram_fetches_in_progress_;
420  }
421#endif  // !ENABLE_FULL_PRINTING
422
423  // Set up the callback to task to call after we receive histograms from all
424  // child processes. |timeout| specifies how long to wait before absolutely
425  // calling us back on the task.
426  content::FetchHistogramsAsynchronously(base::MessageLoop::current(), callback,
427                                         timeout);
428}
429
430void ChromeMetricsServiceClient::OnHistogramSynchronizationDone() {
431  DCHECK(thread_checker_.CalledOnValidThread());
432
433  // This function should only be called as the callback from an ansynchronous
434  // step.
435  DCHECK(waiting_for_collect_final_metrics_step_);
436  DCHECK_GT(num_async_histogram_fetches_in_progress_, 0);
437
438  // Check if all expected requests finished.
439  if (--num_async_histogram_fetches_in_progress_ > 0)
440    return;
441
442  waiting_for_collect_final_metrics_step_ = false;
443  collect_final_metrics_done_callback_.Run();
444}
445
446void ChromeMetricsServiceClient::RecordCommandLineMetrics() {
447  // Get stats on use of command line.
448  const CommandLine* command_line(CommandLine::ForCurrentProcess());
449  size_t common_commands = 0;
450  if (command_line->HasSwitch(switches::kUserDataDir)) {
451    ++common_commands;
452    UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineDatDirCount", 1);
453  }
454
455  if (command_line->HasSwitch(switches::kApp)) {
456    ++common_commands;
457    UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineAppModeCount", 1);
458  }
459
460  // TODO(rohitrao): Should these be logged on iOS as well?
461  // http://crbug.com/375794
462  size_t switch_count = command_line->GetSwitches().size();
463  UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineFlagCount", switch_count);
464  UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineUncommonFlagCount",
465                           switch_count - common_commands);
466}
467
468void ChromeMetricsServiceClient::RegisterForNotifications() {
469  registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED,
470                 content::NotificationService::AllBrowserContextsAndSources());
471  registrar_.Add(this, chrome::NOTIFICATION_BROWSER_CLOSED,
472                 content::NotificationService::AllSources());
473  registrar_.Add(this, chrome::NOTIFICATION_TAB_PARENTED,
474                 content::NotificationService::AllSources());
475  registrar_.Add(this, chrome::NOTIFICATION_TAB_CLOSING,
476                 content::NotificationService::AllSources());
477  registrar_.Add(this, content::NOTIFICATION_LOAD_START,
478                 content::NotificationService::AllSources());
479  registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
480                 content::NotificationService::AllSources());
481  registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
482                 content::NotificationService::AllSources());
483  registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG,
484                 content::NotificationService::AllSources());
485  registrar_.Add(this, chrome::NOTIFICATION_OMNIBOX_OPENED_URL,
486                 content::NotificationService::AllSources());
487}
488
489void ChromeMetricsServiceClient::Observe(
490    int type,
491    const content::NotificationSource& source,
492    const content::NotificationDetails& details) {
493  DCHECK(thread_checker_.CalledOnValidThread());
494
495  switch (type) {
496    case chrome::NOTIFICATION_BROWSER_OPENED:
497    case chrome::NOTIFICATION_BROWSER_CLOSED:
498    case chrome::NOTIFICATION_OMNIBOX_OPENED_URL:
499    case chrome::NOTIFICATION_TAB_PARENTED:
500    case chrome::NOTIFICATION_TAB_CLOSING:
501    case content::NOTIFICATION_LOAD_STOP:
502    case content::NOTIFICATION_LOAD_START:
503    case content::NOTIFICATION_RENDERER_PROCESS_CLOSED:
504    case content::NOTIFICATION_RENDER_WIDGET_HOST_HANG:
505      metrics_service_->OnApplicationNotIdle();
506      break;
507
508    default:
509      NOTREACHED();
510  }
511}
512
513#if defined(OS_WIN)
514void ChromeMetricsServiceClient::CountBrowserCrashDumpAttempts() {
515  // Open the registry key for iteration.
516  base::win::RegKey regkey;
517  if (regkey.Open(HKEY_CURRENT_USER,
518                  chrome::kBrowserCrashDumpAttemptsRegistryPath,
519                  KEY_ALL_ACCESS) != ERROR_SUCCESS) {
520    return;
521  }
522
523  // The values we're interested in counting are all prefixed with the version.
524  base::string16 chrome_version(base::ASCIIToUTF16(chrome::kChromeVersion));
525
526  // Track a list of values to delete. We don't modify the registry key while
527  // we're iterating over its values.
528  typedef std::vector<base::string16> StringVector;
529  StringVector to_delete;
530
531  // Iterate over the values in the key counting dumps with and without crashes.
532  // We directly walk the values instead of using RegistryValueIterator in order
533  // to read all of the values as DWORDS instead of strings.
534  base::string16 name;
535  DWORD value = 0;
536  int dumps_with_crash = 0;
537  int dumps_with_no_crash = 0;
538  for (int i = regkey.GetValueCount() - 1; i >= 0; --i) {
539    if (regkey.GetValueNameAt(i, &name) == ERROR_SUCCESS &&
540        StartsWith(name, chrome_version, false) &&
541        regkey.ReadValueDW(name.c_str(), &value) == ERROR_SUCCESS) {
542      to_delete.push_back(name);
543      if (value == 0)
544        ++dumps_with_no_crash;
545      else
546        ++dumps_with_crash;
547    }
548  }
549
550  // Delete the registry keys we've just counted.
551  for (StringVector::iterator i = to_delete.begin(); i != to_delete.end(); ++i)
552    regkey.DeleteValue(i->c_str());
553
554  // Capture the histogram samples.
555  if (dumps_with_crash != 0)
556    UMA_HISTOGRAM_COUNTS("Chrome.BrowserDumpsWithCrash", dumps_with_crash);
557  if (dumps_with_no_crash != 0)
558    UMA_HISTOGRAM_COUNTS("Chrome.BrowserDumpsWithNoCrash", dumps_with_no_crash);
559  int total_dumps = dumps_with_crash + dumps_with_no_crash;
560  if (total_dumps != 0)
561    UMA_HISTOGRAM_COUNTS("Chrome.BrowserCrashDumpAttempts", total_dumps);
562}
563#endif  // defined(OS_WIN)
564