• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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