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