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