• 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 
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 
36 namespace {
37 
IncrementPrefValue(const char * path)38 void 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 
IncrementLongPrefsValue(const char * path)45 void 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).
MapCrashExitCodeForHistogram(int exit_code)54 int 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 
ChromeStabilityMetricsProvider()68 ChromeStabilityMetricsProvider::ChromeStabilityMetricsProvider() {
69   BrowserChildProcessObserver::Add(this);
70 }
71 
~ChromeStabilityMetricsProvider()72 ChromeStabilityMetricsProvider::~ChromeStabilityMetricsProvider() {
73   BrowserChildProcessObserver::Remove(this);
74 }
75 
OnRecordingEnabled()76 void 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 
OnRecordingDisabled()88 void ChromeStabilityMetricsProvider::OnRecordingDisabled() {
89   registrar_.RemoveAll();
90 }
91 
ProvideStabilityMetrics(metrics::SystemProfileProto * system_profile_proto)92 void 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 
ClearSavedStabilityMetrics()129 void 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
RegisterPrefs(PrefRegistrySimple * registry)142 void 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 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)154 void 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 
BrowserChildProcessCrashed(const content::ChildProcessData & data)188 void 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 
LogLoadStarted(content::WebContents * web_contents)200 void 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 
LogRendererCrash(content::RenderProcessHost * host,base::TerminationStatus status,int exit_code)211 void 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 
LogRendererHang()246 void ChromeStabilityMetricsProvider::LogRendererHang() {
247   IncrementPrefValue(prefs::kStabilityRendererHangCount);
248 }
249