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 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 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). 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 68 ChromeStabilityMetricsProvider::ChromeStabilityMetricsProvider() { 69 BrowserChildProcessObserver::Add(this); 70 } 71 72 ChromeStabilityMetricsProvider::~ChromeStabilityMetricsProvider() { 73 BrowserChildProcessObserver::Remove(this); 74 } 75 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 88 void ChromeStabilityMetricsProvider::OnRecordingDisabled() { 89 registrar_.RemoveAll(); 90 } 91 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 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 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 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 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 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 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 246 void ChromeStabilityMetricsProvider::LogRendererHang() { 247 IncrementPrefValue(prefs::kStabilityRendererHangCount); 248 } 249