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
33 namespace {
34
IncrementPrefValue(const char * path)35 void 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
IncrementLongPrefsValue(const char * path)42 void 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).
MapCrashExitCodeForHistogram(int exit_code)51 int 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
ChromeStabilityMetricsProvider()65 ChromeStabilityMetricsProvider::ChromeStabilityMetricsProvider() {
66 BrowserChildProcessObserver::Add(this);
67 }
68
~ChromeStabilityMetricsProvider()69 ChromeStabilityMetricsProvider::~ChromeStabilityMetricsProvider() {
70 BrowserChildProcessObserver::Remove(this);
71 }
72
OnRecordingEnabled()73 void 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
OnRecordingDisabled()85 void ChromeStabilityMetricsProvider::OnRecordingDisabled() {
86 registrar_.RemoveAll();
87 }
88
ProvideStabilityMetrics(metrics::SystemProfileProto * system_profile_proto)89 void 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
RegisterPrefs(PrefRegistrySimple * registry)127 void 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
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)138 void 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
BrowserChildProcessCrashed(const content::ChildProcessData & data)172 void 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
LogLoadStarted(content::WebContents * web_contents)184 void 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
LogRendererCrash(content::RenderProcessHost * host,base::TerminationStatus status,int exit_code)194 void 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
LogRendererHang()226 void ChromeStabilityMetricsProvider::LogRendererHang() {
227 IncrementPrefValue(prefs::kStabilityRendererHangCount);
228 }
229