Home | History | Annotate | Download | only in metrics
      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