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