chrome_metrics_service_client.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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 "base/bind.h"
8#include "base/callback.h"
9#include "base/command_line.h"
10#include "base/logging.h"
11#include "base/metrics/histogram.h"
12#include "base/strings/string16.h"
13#include "base/strings/string_util.h"
14#include "base/strings/utf_string_conversions.h"
15#include "base/threading/platform_thread.h"
16#include "base/time/time.h"
17#include "chrome/browser/browser_process.h"
18#include "chrome/browser/chrome_notification_types.h"
19#include "chrome/browser/google/google_util.h"
20#include "chrome/browser/memory_details.h"
21#include "chrome/browser/metrics/extensions_metrics_provider.h"
22#include "chrome/browser/metrics/metrics_service.h"
23#include "chrome/browser/ui/browser_otr_state.h"
24#include "chrome/common/chrome_constants.h"
25#include "chrome/common/chrome_switches.h"
26#include "chrome/common/chrome_version_info.h"
27#include "chrome/common/crash_keys.h"
28#include "chrome/common/render_messages.h"
29#include "content/public/browser/browser_thread.h"
30#include "content/public/browser/histogram_fetcher.h"
31#include "content/public/browser/notification_service.h"
32#include "content/public/browser/render_process_host.h"
33
34#if !defined(OS_ANDROID)
35#include "chrome/browser/service_process/service_process_control.h"
36#endif
37
38#if defined(OS_CHROMEOS)
39#include "chrome/browser/metrics/chromeos_metrics_provider.h"
40#endif
41
42#if defined(OS_WIN)
43#include <windows.h>
44#include "base/win/registry.h"
45#include "chrome/browser/metrics/google_update_metrics_provider_win.h"
46#endif
47
48namespace {
49
50// This specifies the amount of time to wait for all renderers to send their
51// data.
52const int kMaxHistogramGatheringWaitDuration = 60000;  // 60 seconds.
53
54metrics::SystemProfileProto::Channel AsProtobufChannel(
55    chrome::VersionInfo::Channel channel) {
56  switch (channel) {
57    case chrome::VersionInfo::CHANNEL_UNKNOWN:
58      return metrics::SystemProfileProto::CHANNEL_UNKNOWN;
59    case chrome::VersionInfo::CHANNEL_CANARY:
60      return metrics::SystemProfileProto::CHANNEL_CANARY;
61    case chrome::VersionInfo::CHANNEL_DEV:
62      return metrics::SystemProfileProto::CHANNEL_DEV;
63    case chrome::VersionInfo::CHANNEL_BETA:
64      return metrics::SystemProfileProto::CHANNEL_BETA;
65    case chrome::VersionInfo::CHANNEL_STABLE:
66      return metrics::SystemProfileProto::CHANNEL_STABLE;
67  }
68  NOTREACHED();
69  return metrics::SystemProfileProto::CHANNEL_UNKNOWN;
70}
71
72// Handles asynchronous fetching of memory details.
73// Will run the provided task after finished.
74class MetricsMemoryDetails : public MemoryDetails {
75 public:
76  explicit MetricsMemoryDetails(const base::Closure& callback)
77      : callback_(callback) {}
78
79  virtual void OnDetailsAvailable() OVERRIDE {
80    base::MessageLoop::current()->PostTask(FROM_HERE, callback_);
81  }
82
83 private:
84  virtual ~MetricsMemoryDetails() {}
85
86  base::Closure callback_;
87
88  DISALLOW_COPY_AND_ASSIGN(MetricsMemoryDetails);
89};
90
91}  // namespace
92
93ChromeMetricsServiceClient::ChromeMetricsServiceClient(
94    metrics::MetricsStateManager* state_manager)
95    : metrics_state_manager_(state_manager),
96      chromeos_metrics_provider_(NULL),
97      waiting_for_collect_final_metrics_step_(false),
98      num_async_histogram_fetches_in_progress_(0),
99      weak_ptr_factory_(this) {
100  DCHECK(thread_checker_.CalledOnValidThread());
101  RecordCommandLineMetrics();
102  RegisterForNotifications();
103
104#if defined(OS_WIN)
105  CountBrowserCrashDumpAttempts();
106#endif  // defined(OS_WIN)
107}
108
109ChromeMetricsServiceClient::~ChromeMetricsServiceClient() {
110  DCHECK(thread_checker_.CalledOnValidThread());
111}
112
113// static
114scoped_ptr<ChromeMetricsServiceClient> ChromeMetricsServiceClient::Create(
115    metrics::MetricsStateManager* state_manager,
116    PrefService* local_state) {
117  // Perform two-phase initialization so that |client->metrics_service_| only
118  // receives pointers to fully constructed objects.
119  scoped_ptr<ChromeMetricsServiceClient> client(
120      new ChromeMetricsServiceClient(state_manager));
121  client->Initialize();
122
123  return client.Pass();
124}
125
126void ChromeMetricsServiceClient::Initialize() {
127  metrics_service_.reset(new MetricsService(
128      metrics_state_manager_, this, g_browser_process->local_state()));
129
130  // Register metrics providers.
131  metrics_service_->RegisterMetricsProvider(
132      scoped_ptr<metrics::MetricsProvider>(
133          new ExtensionsMetricsProvider(metrics_state_manager_)));
134
135#if defined(OS_CHROMEOS)
136  ChromeOSMetricsProvider* chromeos_metrics_provider =
137      new ChromeOSMetricsProvider;
138  chromeos_metrics_provider_ = chromeos_metrics_provider;
139  metrics_service_->RegisterMetricsProvider(
140      scoped_ptr<metrics::MetricsProvider>(chromeos_metrics_provider));
141#endif
142}
143
144void ChromeMetricsServiceClient::SetClientID(const std::string& client_id) {
145  crash_keys::SetClientID(client_id);
146}
147
148bool ChromeMetricsServiceClient::IsOffTheRecordSessionActive() {
149  return !chrome::IsOffTheRecordSessionActive();
150}
151
152std::string ChromeMetricsServiceClient::GetApplicationLocale() {
153  return g_browser_process->GetApplicationLocale();
154}
155
156bool ChromeMetricsServiceClient::GetBrand(std::string* brand_code) {
157  return google_util::GetBrand(brand_code);
158}
159
160metrics::SystemProfileProto::Channel ChromeMetricsServiceClient::GetChannel() {
161  return AsProtobufChannel(chrome::VersionInfo::GetChannel());
162}
163
164std::string ChromeMetricsServiceClient::GetVersionString() {
165  chrome::VersionInfo version_info;
166  if (!version_info.is_valid()) {
167    NOTREACHED();
168    return std::string();
169  }
170
171  std::string version = version_info.Version();
172#if defined(ARCH_CPU_64_BITS)
173  version += "-64";
174#endif  // defined(ARCH_CPU_64_BITS)
175  if (!version_info.IsOfficialBuild())
176    version.append("-devel");
177  return version;
178}
179
180void ChromeMetricsServiceClient::OnLogUploadComplete() {
181  // Collect network stats after each UMA upload.
182  network_stats_uploader_.CollectAndReportNetworkStats();
183}
184
185void ChromeMetricsServiceClient::CollectFinalMetrics(
186    const base::Closure& done_callback) {
187  DCHECK(thread_checker_.CalledOnValidThread());
188
189  collect_final_metrics_done_callback_ = done_callback;
190
191  // Begin the multi-step process of collecting memory usage histograms:
192  // First spawn a task to collect the memory details; when that task is
193  // finished, it will call OnMemoryDetailCollectionDone. That will in turn
194  // call HistogramSynchronization to collect histograms from all renderers and
195  // then call OnHistogramSynchronizationDone to continue processing.
196  DCHECK(!waiting_for_collect_final_metrics_step_);
197  waiting_for_collect_final_metrics_step_ = true;
198
199  base::Closure callback =
200      base::Bind(&ChromeMetricsServiceClient::OnMemoryDetailCollectionDone,
201                 weak_ptr_factory_.GetWeakPtr());
202
203  scoped_refptr<MetricsMemoryDetails> details(
204      new MetricsMemoryDetails(callback));
205  details->StartFetch(MemoryDetails::UPDATE_USER_METRICS);
206
207  // Collect WebCore cache information to put into a histogram.
208  for (content::RenderProcessHost::iterator i(
209          content::RenderProcessHost::AllHostsIterator());
210       !i.IsAtEnd(); i.Advance()) {
211    i.GetCurrentValue()->Send(new ChromeViewMsg_GetCacheResourceStats());
212  }
213}
214
215void ChromeMetricsServiceClient::OnMemoryDetailCollectionDone() {
216  DCHECK(thread_checker_.CalledOnValidThread());
217
218  // This function should only be called as the callback from an ansynchronous
219  // step.
220  DCHECK(waiting_for_collect_final_metrics_step_);
221
222  // Create a callback_task for OnHistogramSynchronizationDone.
223  base::Closure callback = base::Bind(
224      &ChromeMetricsServiceClient::OnHistogramSynchronizationDone,
225      weak_ptr_factory_.GetWeakPtr());
226
227  base::TimeDelta timeout =
228      base::TimeDelta::FromMilliseconds(kMaxHistogramGatheringWaitDuration);
229
230  DCHECK_EQ(num_async_histogram_fetches_in_progress_, 0);
231
232#if defined(OS_ANDROID)
233  // Android has no service process.
234  num_async_histogram_fetches_in_progress_ = 1;
235#else  // OS_ANDROID
236  num_async_histogram_fetches_in_progress_ = 2;
237  // Run requests to service and content in parallel.
238  if (!ServiceProcessControl::GetInstance()->GetHistograms(callback, timeout)) {
239    // Assume |num_async_histogram_fetches_in_progress_| is not changed by
240    // |GetHistograms()|.
241    DCHECK_EQ(num_async_histogram_fetches_in_progress_, 2);
242    // Assign |num_async_histogram_fetches_in_progress_| above and decrement it
243    // here to make code work even if |GetHistograms()| fired |callback|.
244    --num_async_histogram_fetches_in_progress_;
245  }
246#endif  // OS_ANDROID
247
248  // Set up the callback to task to call after we receive histograms from all
249  // child processes. |timeout| specifies how long to wait before absolutely
250  // calling us back on the task.
251  content::FetchHistogramsAsynchronously(base::MessageLoop::current(), callback,
252                                         timeout);
253}
254
255void ChromeMetricsServiceClient::OnHistogramSynchronizationDone() {
256  DCHECK(thread_checker_.CalledOnValidThread());
257
258  // This function should only be called as the callback from an ansynchronous
259  // step.
260  DCHECK(waiting_for_collect_final_metrics_step_);
261  DCHECK_GT(num_async_histogram_fetches_in_progress_, 0);
262
263  // Check if all expected requests finished.
264  if (--num_async_histogram_fetches_in_progress_ > 0)
265    return;
266
267  waiting_for_collect_final_metrics_step_ = false;
268  collect_final_metrics_done_callback_.Run();
269}
270
271void ChromeMetricsServiceClient::RecordCommandLineMetrics() {
272  // Get stats on use of command line.
273  const CommandLine* command_line(CommandLine::ForCurrentProcess());
274  size_t common_commands = 0;
275  if (command_line->HasSwitch(switches::kUserDataDir)) {
276    ++common_commands;
277    UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineDatDirCount", 1);
278  }
279
280  if (command_line->HasSwitch(switches::kApp)) {
281    ++common_commands;
282    UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineAppModeCount", 1);
283  }
284
285  // TODO(rohitrao): Should these be logged on iOS as well?
286  // http://crbug.com/375794
287  size_t switch_count = command_line->GetSwitches().size();
288  UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineFlagCount", switch_count);
289  UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineUncommonFlagCount",
290                           switch_count - common_commands);
291}
292
293void ChromeMetricsServiceClient::RegisterForNotifications() {
294  registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED,
295                 content::NotificationService::AllBrowserContextsAndSources());
296  registrar_.Add(this, chrome::NOTIFICATION_BROWSER_CLOSED,
297                 content::NotificationService::AllSources());
298  registrar_.Add(this, chrome::NOTIFICATION_TAB_PARENTED,
299                 content::NotificationService::AllSources());
300  registrar_.Add(this, chrome::NOTIFICATION_TAB_CLOSING,
301                 content::NotificationService::AllSources());
302  registrar_.Add(this, content::NOTIFICATION_LOAD_START,
303                 content::NotificationService::AllSources());
304  registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
305                 content::NotificationService::AllSources());
306  registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
307                 content::NotificationService::AllSources());
308  registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG,
309                 content::NotificationService::AllSources());
310  registrar_.Add(this, chrome::NOTIFICATION_OMNIBOX_OPENED_URL,
311                 content::NotificationService::AllSources());
312}
313
314void ChromeMetricsServiceClient::Observe(
315    int type,
316    const content::NotificationSource& source,
317    const content::NotificationDetails& details) {
318  DCHECK(thread_checker_.CalledOnValidThread());
319
320  switch (type) {
321    case chrome::NOTIFICATION_BROWSER_OPENED:
322    case chrome::NOTIFICATION_BROWSER_CLOSED:
323    case chrome::NOTIFICATION_OMNIBOX_OPENED_URL:
324    case chrome::NOTIFICATION_TAB_PARENTED:
325    case chrome::NOTIFICATION_TAB_CLOSING:
326    case content::NOTIFICATION_LOAD_STOP:
327    case content::NOTIFICATION_LOAD_START:
328    case content::NOTIFICATION_RENDERER_PROCESS_CLOSED:
329    case content::NOTIFICATION_RENDER_WIDGET_HOST_HANG:
330      metrics_service_->OnApplicationNotIdle();
331      break;
332
333    default:
334      NOTREACHED();
335  }
336}
337
338void ChromeMetricsServiceClient::StartGatheringMetrics(
339    const base::Closure& done_callback) {
340// TODO(blundell): Move all metrics gathering tasks from MetricsService to
341// here.
342#if defined(OS_CHROMEOS)
343  chromeos_metrics_provider_->InitTaskGetHardwareClass(done_callback);
344#else
345  done_callback.Run();
346#endif
347}
348
349#if defined(OS_WIN)
350void ChromeMetricsServiceClient::CountBrowserCrashDumpAttempts() {
351  // Open the registry key for iteration.
352  base::win::RegKey regkey;
353  if (regkey.Open(HKEY_CURRENT_USER,
354                  chrome::kBrowserCrashDumpAttemptsRegistryPath,
355                  KEY_ALL_ACCESS) != ERROR_SUCCESS) {
356    return;
357  }
358
359  // The values we're interested in counting are all prefixed with the version.
360  base::string16 chrome_version(base::ASCIIToUTF16(chrome::kChromeVersion));
361
362  // Track a list of values to delete. We don't modify the registry key while
363  // we're iterating over its values.
364  typedef std::vector<base::string16> StringVector;
365  StringVector to_delete;
366
367  // Iterate over the values in the key counting dumps with and without crashes.
368  // We directly walk the values instead of using RegistryValueIterator in order
369  // to read all of the values as DWORDS instead of strings.
370  base::string16 name;
371  DWORD value = 0;
372  int dumps_with_crash = 0;
373  int dumps_with_no_crash = 0;
374  for (int i = regkey.GetValueCount() - 1; i >= 0; --i) {
375    if (regkey.GetValueNameAt(i, &name) == ERROR_SUCCESS &&
376        StartsWith(name, chrome_version, false) &&
377        regkey.ReadValueDW(name.c_str(), &value) == ERROR_SUCCESS) {
378      to_delete.push_back(name);
379      if (value == 0)
380        ++dumps_with_no_crash;
381      else
382        ++dumps_with_crash;
383    }
384  }
385
386  // Delete the registry keys we've just counted.
387  for (StringVector::iterator i = to_delete.begin(); i != to_delete.end(); ++i)
388    regkey.DeleteValue(i->c_str());
389
390  // Capture the histogram samples.
391  if (dumps_with_crash != 0)
392    UMA_HISTOGRAM_COUNTS("Chrome.BrowserDumpsWithCrash", dumps_with_crash);
393  if (dumps_with_no_crash != 0)
394    UMA_HISTOGRAM_COUNTS("Chrome.BrowserDumpsWithNoCrash", dumps_with_no_crash);
395  int total_dumps = dumps_with_crash + dumps_with_no_crash;
396  if (total_dumps != 0)
397    UMA_HISTOGRAM_COUNTS("Chrome.BrowserCrashDumpAttempts", total_dumps);
398}
399#endif  // defined(OS_WIN)
400