chrome_stability_metrics_provider.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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
129void ChromeStabilityMetricsProvider::ClearSavedStabilityMetrics() {
130  PrefService* local_state = g_browser_process->local_state();
131
132  // Clear all the prefs used in this class in UMA reports (which doesn't
133  // include |kUninstallMetricsPageLoadCount| as it's not sent up by UMA).
134  local_state->SetInteger(prefs::kStabilityChildProcessCrashCount, 0);
135  local_state->SetInteger(prefs::kStabilityExtensionRendererCrashCount, 0);
136  local_state->SetInteger(prefs::kStabilityPageLoadCount, 0);
137  local_state->SetInteger(prefs::kStabilityRendererCrashCount, 0);
138  local_state->SetInteger(prefs::kStabilityRendererHangCount, 0);
139}
140
141// static
142void ChromeStabilityMetricsProvider::RegisterPrefs(
143    PrefRegistrySimple* registry) {
144  registry->RegisterIntegerPref(prefs::kStabilityChildProcessCrashCount, 0);
145  registry->RegisterIntegerPref(prefs::kStabilityExtensionRendererCrashCount,
146                                0);
147  registry->RegisterIntegerPref(prefs::kStabilityPageLoadCount, 0);
148  registry->RegisterIntegerPref(prefs::kStabilityRendererCrashCount, 0);
149  registry->RegisterIntegerPref(prefs::kStabilityRendererHangCount, 0);
150
151  registry->RegisterInt64Pref(prefs::kUninstallMetricsPageLoadCount, 0);
152}
153
154void ChromeStabilityMetricsProvider::Observe(
155    int type,
156    const content::NotificationSource& source,
157    const content::NotificationDetails& details) {
158  switch (type) {
159    case content::NOTIFICATION_LOAD_START: {
160      content::NavigationController* controller =
161          content::Source<content::NavigationController>(source).ptr();
162      content::WebContents* web_contents = controller->GetWebContents();
163      LogLoadStarted(web_contents);
164      break;
165    }
166
167    case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: {
168      content::RenderProcessHost::RendererClosedDetails* process_details =
169          content::Details<content::RenderProcessHost::RendererClosedDetails>(
170              details).ptr();
171      content::RenderProcessHost* host =
172          content::Source<content::RenderProcessHost>(source).ptr();
173      LogRendererCrash(
174          host, process_details->status, process_details->exit_code);
175      break;
176    }
177
178    case content::NOTIFICATION_RENDER_WIDGET_HOST_HANG:
179      LogRendererHang();
180      break;
181
182    default:
183      NOTREACHED();
184      break;
185  }
186}
187
188void ChromeStabilityMetricsProvider::BrowserChildProcessCrashed(
189    const content::ChildProcessData& data) {
190#if defined(ENABLE_PLUGINS)
191  // Exclude plugin crashes from the count below because we report them via
192  // a separate UMA metric.
193  if (PluginMetricsProvider::IsPluginProcess(data.process_type))
194    return;
195#endif
196
197  IncrementPrefValue(prefs::kStabilityChildProcessCrashCount);
198}
199
200void ChromeStabilityMetricsProvider::LogLoadStarted(
201    content::WebContents* web_contents) {
202  content::RecordAction(base::UserMetricsAction("PageLoad"));
203  // TODO(asvitkine): Check if this is used for anything and if not, remove.
204  LOCAL_HISTOGRAM_BOOLEAN("Chrome.UmaPageloadCounter", true);
205  IncrementPrefValue(prefs::kStabilityPageLoadCount);
206  IncrementLongPrefsValue(prefs::kUninstallMetricsPageLoadCount);
207  // We need to save the prefs, as page load count is a critical stat, and it
208  // might be lost due to a crash :-(.
209}
210
211void ChromeStabilityMetricsProvider::LogRendererCrash(
212    content::RenderProcessHost* host,
213    base::TerminationStatus status,
214    int exit_code) {
215  bool was_extension_process = false;
216#if defined(ENABLE_EXTENSIONS)
217  was_extension_process =
218      extensions::ProcessMap::Get(host->GetBrowserContext())->Contains(
219          host->GetID());
220#endif
221  if (status == base::TERMINATION_STATUS_PROCESS_CRASHED ||
222      status == base::TERMINATION_STATUS_ABNORMAL_TERMINATION) {
223    if (was_extension_process) {
224      IncrementPrefValue(prefs::kStabilityExtensionRendererCrashCount);
225
226      UMA_HISTOGRAM_SPARSE_SLOWLY("CrashExitCodes.Extension",
227                                  MapCrashExitCodeForHistogram(exit_code));
228    } else {
229      IncrementPrefValue(prefs::kStabilityRendererCrashCount);
230
231      UMA_HISTOGRAM_SPARSE_SLOWLY("CrashExitCodes.Renderer",
232                                  MapCrashExitCodeForHistogram(exit_code));
233    }
234
235    UMA_HISTOGRAM_PERCENTAGE("BrowserRenderProcessHost.ChildCrashes",
236                             was_extension_process ? 2 : 1);
237  } else if (status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED) {
238    UMA_HISTOGRAM_PERCENTAGE("BrowserRenderProcessHost.ChildKills",
239                             was_extension_process ? 2 : 1);
240  } else if (status == base::TERMINATION_STATUS_STILL_RUNNING) {
241    UMA_HISTOGRAM_PERCENTAGE("BrowserRenderProcessHost.DisconnectedAlive",
242                             was_extension_process ? 2 : 1);
243  }
244}
245
246void ChromeStabilityMetricsProvider::LogRendererHang() {
247  IncrementPrefValue(prefs::kStabilityRendererHangCount);
248}
249