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