chrome_stability_metrics_provider.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_stability_metrics_provider.h"
6
7#include <vector>
8
9#include "base/logging.h"
10#include "base/metrics/histogram.h"
11#include "base/metrics/sparse_histogram.h"
12#include "base/prefs/pref_registry_simple.h"
13#include "base/prefs/pref_service.h"
14#include "chrome/browser/browser_process.h"
15#include "chrome/browser/chrome_notification_types.h"
16#include "chrome/common/pref_names.h"
17#include "components/metrics/proto/system_profile.pb.h"
18#include "content/public/browser/child_process_data.h"
19#include "content/public/browser/notification_service.h"
20#include "content/public/browser/render_process_host.h"
21#include "content/public/browser/user_metrics.h"
22#include "content/public/browser/web_contents.h"
23#include "extensions/browser/process_map.h"
24
25#if defined(ENABLE_PLUGINS)
26#include "chrome/browser/metrics/plugin_metrics_provider.h"
27#endif
28
29#if defined(OS_WIN)
30#include <windows.h>  // Needed for STATUS_* codes
31#endif
32
33namespace {
34
35void IncrementPrefValue(const char* path) {
36  PrefService* pref = g_browser_process->local_state();
37  DCHECK(pref);
38  int value = pref->GetInteger(path);
39  pref->SetInteger(path, value + 1);
40}
41
42void IncrementLongPrefsValue(const char* path) {
43  PrefService* pref = g_browser_process->local_state();
44  DCHECK(pref);
45  int64 value = pref->GetInt64(path);
46  pref->SetInt64(path, value + 1);
47}
48
49// Converts an exit code into something that can be inserted into our
50// histograms (which expect non-negative numbers less than MAX_INT).
51int MapCrashExitCodeForHistogram(int exit_code) {
52#if defined(OS_WIN)
53  // Since |abs(STATUS_GUARD_PAGE_VIOLATION) == MAX_INT| it causes problems in
54  // histograms.cc. Solve this by remapping it to a smaller value, which
55  // hopefully doesn't conflict with other codes.
56  if (exit_code == STATUS_GUARD_PAGE_VIOLATION)
57    return 0x1FCF7EC3;  // Randomly picked number.
58#endif
59
60  return std::abs(exit_code);
61}
62
63}  // namespace
64
65ChromeStabilityMetricsProvider::ChromeStabilityMetricsProvider() {
66  BrowserChildProcessObserver::Add(this);
67}
68
69ChromeStabilityMetricsProvider::~ChromeStabilityMetricsProvider() {
70  BrowserChildProcessObserver::Remove(this);
71}
72
73void ChromeStabilityMetricsProvider::OnRecordingEnabled() {
74  registrar_.Add(this,
75                 content::NOTIFICATION_LOAD_START,
76                 content::NotificationService::AllSources());
77  registrar_.Add(this,
78                 content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
79                 content::NotificationService::AllSources());
80  registrar_.Add(this,
81                 content::NOTIFICATION_RENDER_WIDGET_HOST_HANG,
82                 content::NotificationService::AllSources());
83}
84
85void ChromeStabilityMetricsProvider::OnRecordingDisabled() {
86  registrar_.RemoveAll();
87}
88
89void ChromeStabilityMetricsProvider::ProvideStabilityMetrics(
90    metrics::SystemProfileProto* system_profile_proto) {
91  PrefService* pref = g_browser_process->local_state();
92  metrics::SystemProfileProto_Stability* stability_proto =
93      system_profile_proto->mutable_stability();
94
95  int count = pref->GetInteger(prefs::kStabilityPageLoadCount);
96  if (count) {
97    stability_proto->set_page_load_count(count);
98    pref->SetInteger(prefs::kStabilityPageLoadCount, 0);
99  }
100
101  count = pref->GetInteger(prefs::kStabilityChildProcessCrashCount);
102  if (count) {
103    stability_proto->set_child_process_crash_count(count);
104    pref->SetInteger(prefs::kStabilityChildProcessCrashCount, 0);
105  }
106
107  count = pref->GetInteger(prefs::kStabilityRendererCrashCount);
108  if (count) {
109    stability_proto->set_renderer_crash_count(count);
110    pref->SetInteger(prefs::kStabilityRendererCrashCount, 0);
111  }
112
113  count = pref->GetInteger(prefs::kStabilityExtensionRendererCrashCount);
114  if (count) {
115    stability_proto->set_extension_renderer_crash_count(count);
116    pref->SetInteger(prefs::kStabilityExtensionRendererCrashCount, 0);
117  }
118
119  count = pref->GetInteger(prefs::kStabilityRendererHangCount);
120  if (count) {
121    stability_proto->set_renderer_hang_count(count);
122    pref->SetInteger(prefs::kStabilityRendererHangCount, 0);
123  }
124}
125
126// static
127void ChromeStabilityMetricsProvider::RegisterPrefs(
128    PrefRegistrySimple* registry) {
129  registry->RegisterIntegerPref(prefs::kStabilityPageLoadCount, 0);
130  registry->RegisterIntegerPref(prefs::kStabilityRendererCrashCount, 0);
131  registry->RegisterIntegerPref(prefs::kStabilityExtensionRendererCrashCount,
132                                0);
133  registry->RegisterIntegerPref(prefs::kStabilityRendererHangCount, 0);
134  registry->RegisterIntegerPref(prefs::kStabilityChildProcessCrashCount, 0);
135  registry->RegisterInt64Pref(prefs::kUninstallMetricsPageLoadCount, 0);
136}
137
138void ChromeStabilityMetricsProvider::Observe(
139    int type,
140    const content::NotificationSource& source,
141    const content::NotificationDetails& details) {
142  switch (type) {
143    case content::NOTIFICATION_LOAD_START: {
144      content::NavigationController* controller =
145          content::Source<content::NavigationController>(source).ptr();
146      content::WebContents* web_contents = controller->GetWebContents();
147      LogLoadStarted(web_contents);
148      break;
149    }
150
151    case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: {
152      content::RenderProcessHost::RendererClosedDetails* process_details =
153          content::Details<content::RenderProcessHost::RendererClosedDetails>(
154              details).ptr();
155      content::RenderProcessHost* host =
156          content::Source<content::RenderProcessHost>(source).ptr();
157      LogRendererCrash(
158          host, process_details->status, process_details->exit_code);
159      break;
160    }
161
162    case content::NOTIFICATION_RENDER_WIDGET_HOST_HANG:
163      LogRendererHang();
164      break;
165
166    default:
167      NOTREACHED();
168      break;
169  }
170}
171
172void ChromeStabilityMetricsProvider::BrowserChildProcessCrashed(
173    const content::ChildProcessData& data) {
174#if defined(ENABLE_PLUGINS)
175  // Exclude plugin crashes from the count below because we report them via
176  // a separate UMA metric.
177  if (PluginMetricsProvider::IsPluginProcess(data.process_type))
178    return;
179#endif
180
181  IncrementPrefValue(prefs::kStabilityChildProcessCrashCount);
182}
183
184void ChromeStabilityMetricsProvider::LogLoadStarted(
185    content::WebContents* web_contents) {
186  content::RecordAction(base::UserMetricsAction("PageLoad"));
187  HISTOGRAM_ENUMERATION("Chrome.UmaPageloadCounter", 1, 2);
188  IncrementPrefValue(prefs::kStabilityPageLoadCount);
189  IncrementLongPrefsValue(prefs::kUninstallMetricsPageLoadCount);
190  // We need to save the prefs, as page load count is a critical stat, and it
191  // might be lost due to a crash :-(.
192}
193
194void ChromeStabilityMetricsProvider::LogRendererCrash(
195    content::RenderProcessHost* host,
196    base::TerminationStatus status,
197    int exit_code) {
198  bool was_extension_process =
199      extensions::ProcessMap::Get(host->GetBrowserContext())
200          ->Contains(host->GetID());
201  if (status == base::TERMINATION_STATUS_PROCESS_CRASHED ||
202      status == base::TERMINATION_STATUS_ABNORMAL_TERMINATION) {
203    if (was_extension_process) {
204      IncrementPrefValue(prefs::kStabilityExtensionRendererCrashCount);
205
206      UMA_HISTOGRAM_SPARSE_SLOWLY("CrashExitCodes.Extension",
207                                  MapCrashExitCodeForHistogram(exit_code));
208    } else {
209      IncrementPrefValue(prefs::kStabilityRendererCrashCount);
210
211      UMA_HISTOGRAM_SPARSE_SLOWLY("CrashExitCodes.Renderer",
212                                  MapCrashExitCodeForHistogram(exit_code));
213    }
214
215    UMA_HISTOGRAM_PERCENTAGE("BrowserRenderProcessHost.ChildCrashes",
216                             was_extension_process ? 2 : 1);
217  } else if (status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED) {
218    UMA_HISTOGRAM_PERCENTAGE("BrowserRenderProcessHost.ChildKills",
219                             was_extension_process ? 2 : 1);
220  } else if (status == base::TERMINATION_STATUS_STILL_RUNNING) {
221    UMA_HISTOGRAM_PERCENTAGE("BrowserRenderProcessHost.DisconnectedAlive",
222                             was_extension_process ? 2 : 1);
223  }
224}
225
226void ChromeStabilityMetricsProvider::LogRendererHang() {
227  IncrementPrefValue(prefs::kStabilityRendererHangCount);
228}
229