| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/metrics/stability_metrics_helper.h" |
| |
| #include <stdint.h> |
| |
| #include <vector> |
| |
| #include "base/logging.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/metrics/user_metrics.h" |
| #include "build/build_config.h" |
| #include "build/buildflag.h" |
| #include "components/metrics/metrics_pref_names.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/variations/hashing.h" |
| #include "extensions/buildflags/buildflags.h" |
| #include "third_party/metrics_proto/system_profile.pb.h" |
| |
| #if defined(OS_WIN) |
| #include <windows.h> // Needed for STATUS_* codes |
| #endif |
| |
| #if defined(OS_CHROMEOS) |
| #include "components/metrics/system_memory_stats_recorder.h" |
| #endif |
| |
| namespace metrics { |
| |
| namespace { |
| |
| enum RendererType { |
| RENDERER_TYPE_RENDERER = 1, |
| RENDERER_TYPE_EXTENSION, |
| // NOTE: Add new action types only immediately above this line. Also, |
| // make sure the enum list in tools/metrics/histograms/histograms.xml is |
| // updated with any change in here. |
| RENDERER_TYPE_COUNT |
| }; |
| |
| // Converts an exit code into something that can be inserted into our |
| // histograms (which expect non-negative numbers less than MAX_INT). |
| int MapCrashExitCodeForHistogram(int exit_code) { |
| #if defined(OS_WIN) |
| // Since |abs(STATUS_GUARD_PAGE_VIOLATION) == MAX_INT| it causes problems in |
| // histograms.cc. Solve this by remapping it to a smaller value, which |
| // hopefully doesn't conflict with other codes. |
| if (static_cast<DWORD>(exit_code) == STATUS_GUARD_PAGE_VIOLATION) |
| return 0x1FCF7EC3; // Randomly picked number. |
| #endif |
| |
| return std::abs(exit_code); |
| } |
| |
| void RecordChildKills(RendererType histogram_type) { |
| UMA_HISTOGRAM_ENUMERATION("BrowserRenderProcessHost.ChildKills", |
| histogram_type, RENDERER_TYPE_COUNT); |
| } |
| |
| // Macro for logging the age of a crashed process. |
| // |
| // Notes: |
| // - IMPORTANT: When changing the constants below, please change the names of |
| // the histograms logged via UMA_HISTOGRAM_CRASHED_PROCESS_AGE. |
| // - 99th percentile of Memory.Experimental.Renderer.Uptime hovers around 17h. |
| // - |kCrashedProcessAgeMin| is as low as possible, so that we may with |
| // high-confidence categorize crashes that occur during early startup (e.g. |
| // crashes that end up with STATUS_DLL_INIT_FAILED or STATUS_DLL_NOT_FOUND). |
| // - Note that even with just 50 buckets, we still get narrow and accurate |
| // buckets at the lower end: 0ms, 1ms, 2ms, 3ms, 4-5ms, 6-8ms, 9-12ms, ... |
| constexpr auto kCrashedProcessAgeMin = base::TimeDelta::FromMilliseconds(1); |
| constexpr auto kCrashedProcessAgeMax = base::TimeDelta::FromHours(48); |
| constexpr uint32_t kCrashedProcessAgeCount = 50; |
| #define UMA_HISTOGRAM_CRASHED_PROCESS_AGE(histogram_name, uptime) \ |
| UMA_HISTOGRAM_CUSTOM_TIMES(histogram_name, uptime, kCrashedProcessAgeMin, \ |
| kCrashedProcessAgeMax, kCrashedProcessAgeCount) |
| |
| } // namespace |
| |
| StabilityMetricsHelper::StabilityMetricsHelper(PrefService* local_state) |
| : local_state_(local_state) { |
| DCHECK(local_state_); |
| } |
| |
| StabilityMetricsHelper::~StabilityMetricsHelper() {} |
| |
| void StabilityMetricsHelper::ProvideStabilityMetrics( |
| SystemProfileProto* system_profile_proto) { |
| SystemProfileProto_Stability* stability_proto = |
| system_profile_proto->mutable_stability(); |
| |
| int count = local_state_->GetInteger(prefs::kStabilityPageLoadCount); |
| if (count) { |
| stability_proto->set_page_load_count(count); |
| local_state_->SetInteger(prefs::kStabilityPageLoadCount, 0); |
| } |
| |
| count = local_state_->GetInteger(prefs::kStabilityChildProcessCrashCount); |
| if (count) { |
| stability_proto->set_child_process_crash_count(count); |
| local_state_->SetInteger(prefs::kStabilityChildProcessCrashCount, 0); |
| } |
| |
| count = local_state_->GetInteger(prefs::kStabilityRendererCrashCount); |
| if (count) { |
| stability_proto->set_renderer_crash_count(count); |
| local_state_->SetInteger(prefs::kStabilityRendererCrashCount, 0); |
| } |
| |
| count = local_state_->GetInteger(prefs::kStabilityRendererFailedLaunchCount); |
| if (count) { |
| stability_proto->set_renderer_failed_launch_count(count); |
| local_state_->SetInteger(prefs::kStabilityRendererFailedLaunchCount, 0); |
| } |
| |
| count = local_state_->GetInteger(prefs::kStabilityRendererLaunchCount); |
| if (count) { |
| stability_proto->set_renderer_launch_count(count); |
| local_state_->SetInteger(prefs::kStabilityRendererLaunchCount, 0); |
| } |
| |
| count = |
| local_state_->GetInteger(prefs::kStabilityExtensionRendererCrashCount); |
| if (count) { |
| stability_proto->set_extension_renderer_crash_count(count); |
| local_state_->SetInteger(prefs::kStabilityExtensionRendererCrashCount, 0); |
| } |
| |
| count = local_state_->GetInteger( |
| prefs::kStabilityExtensionRendererFailedLaunchCount); |
| if (count) { |
| stability_proto->set_extension_renderer_failed_launch_count(count); |
| local_state_->SetInteger( |
| prefs::kStabilityExtensionRendererFailedLaunchCount, 0); |
| } |
| |
| count = local_state_->GetInteger(prefs::kStabilityRendererHangCount); |
| if (count) { |
| stability_proto->set_renderer_hang_count(count); |
| local_state_->SetInteger(prefs::kStabilityRendererHangCount, 0); |
| } |
| |
| count = |
| local_state_->GetInteger(prefs::kStabilityExtensionRendererLaunchCount); |
| if (count) { |
| stability_proto->set_extension_renderer_launch_count(count); |
| local_state_->SetInteger(prefs::kStabilityExtensionRendererLaunchCount, 0); |
| } |
| } |
| |
| void StabilityMetricsHelper::ClearSavedStabilityMetrics() { |
| // Clear all the prefs used in this class in UMA reports (which doesn't |
| // include |kUninstallMetricsPageLoadCount| as it's not sent up by UMA). |
| local_state_->SetInteger(prefs::kStabilityChildProcessCrashCount, 0); |
| local_state_->SetInteger(prefs::kStabilityExtensionRendererCrashCount, 0); |
| local_state_->SetInteger(prefs::kStabilityExtensionRendererFailedLaunchCount, |
| 0); |
| local_state_->SetInteger(prefs::kStabilityExtensionRendererLaunchCount, 0); |
| local_state_->SetInteger(prefs::kStabilityPageLoadCount, 0); |
| local_state_->SetInteger(prefs::kStabilityRendererCrashCount, 0); |
| local_state_->SetInteger(prefs::kStabilityRendererFailedLaunchCount, 0); |
| local_state_->SetInteger(prefs::kStabilityRendererHangCount, 0); |
| local_state_->SetInteger(prefs::kStabilityRendererLaunchCount, 0); |
| } |
| |
| // static |
| void StabilityMetricsHelper::RegisterPrefs(PrefRegistrySimple* registry) { |
| registry->RegisterIntegerPref(prefs::kStabilityChildProcessCrashCount, 0); |
| registry->RegisterIntegerPref(prefs::kStabilityExtensionRendererCrashCount, |
| 0); |
| registry->RegisterIntegerPref( |
| prefs::kStabilityExtensionRendererFailedLaunchCount, 0); |
| registry->RegisterIntegerPref(prefs::kStabilityExtensionRendererLaunchCount, |
| 0); |
| registry->RegisterIntegerPref(prefs::kStabilityPageLoadCount, 0); |
| registry->RegisterIntegerPref(prefs::kStabilityRendererCrashCount, 0); |
| registry->RegisterIntegerPref(prefs::kStabilityRendererFailedLaunchCount, 0); |
| registry->RegisterIntegerPref(prefs::kStabilityRendererHangCount, 0); |
| registry->RegisterIntegerPref(prefs::kStabilityRendererLaunchCount, 0); |
| |
| registry->RegisterInt64Pref(prefs::kUninstallMetricsPageLoadCount, 0); |
| } |
| |
| void StabilityMetricsHelper::IncreaseRendererCrashCount() { |
| IncrementPrefValue(prefs::kStabilityRendererCrashCount); |
| } |
| |
| void StabilityMetricsHelper::BrowserUtilityProcessLaunched( |
| const std::string& metrics_name) { |
| uint32_t hash = variations::HashName(metrics_name); |
| base::UmaHistogramSparse("ChildProcess.Launched.UtilityProcessHash", hash); |
| } |
| |
| void StabilityMetricsHelper::BrowserUtilityProcessCrashed( |
| const std::string& metrics_name, |
| int exit_code) { |
| // TODO(wfh): there doesn't appear to be a good way to log these exit_codes |
| // without adding something into the stability proto, so for now only log the |
| // crash and if the numbers are high enough, logging exit codes can be added |
| // later. |
| uint32_t hash = variations::HashName(metrics_name); |
| base::UmaHistogramSparse("ChildProcess.Crashed.UtilityProcessHash", hash); |
| } |
| |
| void StabilityMetricsHelper::BrowserChildProcessCrashed() { |
| IncrementPrefValue(prefs::kStabilityChildProcessCrashCount); |
| } |
| |
| void StabilityMetricsHelper::LogLoadStarted(bool is_incognito) { |
| base::RecordAction(base::UserMetricsAction("PageLoad")); |
| if (is_incognito) |
| base::RecordAction(base::UserMetricsAction("PageLoadInIncognito")); |
| IncrementPrefValue(prefs::kStabilityPageLoadCount); |
| IncrementLongPrefsValue(prefs::kUninstallMetricsPageLoadCount); |
| // We need to save the prefs, as page load count is a critical stat, and it |
| // might be lost due to a crash :-(. |
| } |
| |
| void StabilityMetricsHelper::LogRendererCrash( |
| bool was_extension_process, |
| base::TerminationStatus status, |
| int exit_code, |
| base::Optional<base::TimeDelta> uptime) { |
| RendererType histogram_type = |
| was_extension_process ? RENDERER_TYPE_EXTENSION : RENDERER_TYPE_RENDERER; |
| |
| switch (status) { |
| case base::TERMINATION_STATUS_NORMAL_TERMINATION: |
| break; |
| case base::TERMINATION_STATUS_PROCESS_CRASHED: |
| case base::TERMINATION_STATUS_ABNORMAL_TERMINATION: |
| case base::TERMINATION_STATUS_OOM: |
| if (was_extension_process) { |
| #if !BUILDFLAG(ENABLE_EXTENSIONS) |
| NOTREACHED(); |
| #endif |
| IncrementPrefValue(prefs::kStabilityExtensionRendererCrashCount); |
| |
| base::UmaHistogramSparse("CrashExitCodes.Extension", |
| MapCrashExitCodeForHistogram(exit_code)); |
| if (uptime.has_value()) { |
| UMA_HISTOGRAM_CRASHED_PROCESS_AGE( |
| "Stability.CrashedProcessAge.Extension", uptime.value()); |
| } |
| } else { |
| IncrementPrefValue(prefs::kStabilityRendererCrashCount); |
| |
| base::UmaHistogramSparse("CrashExitCodes.Renderer", |
| MapCrashExitCodeForHistogram(exit_code)); |
| if (uptime.has_value()) { |
| UMA_HISTOGRAM_CRASHED_PROCESS_AGE( |
| "Stability.CrashedProcessAge.Renderer", uptime.value()); |
| } |
| } |
| |
| UMA_HISTOGRAM_ENUMERATION("BrowserRenderProcessHost.ChildCrashes", |
| histogram_type, RENDERER_TYPE_COUNT); |
| break; |
| case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: |
| RecordChildKills(histogram_type); |
| break; |
| #if defined(OS_ANDROID) |
| case base::TERMINATION_STATUS_OOM_PROTECTED: |
| // TODO(wfh): Check if this should be a Kill or a Crash on Android. |
| break; |
| #endif |
| #if defined(OS_CHROMEOS) |
| case base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM: |
| RecordChildKills(histogram_type); |
| UMA_HISTOGRAM_ENUMERATION("BrowserRenderProcessHost.ChildKills.OOM", |
| was_extension_process ? 2 : 1, 3); |
| RecordMemoryStats(was_extension_process |
| ? RECORD_MEMORY_STATS_EXTENSIONS_OOM_KILLED |
| : RECORD_MEMORY_STATS_CONTENTS_OOM_KILLED); |
| break; |
| #endif |
| case base::TERMINATION_STATUS_STILL_RUNNING: |
| UMA_HISTOGRAM_ENUMERATION("BrowserRenderProcessHost.DisconnectedAlive", |
| histogram_type, RENDERER_TYPE_COUNT); |
| break; |
| case base::TERMINATION_STATUS_LAUNCH_FAILED: |
| UMA_HISTOGRAM_ENUMERATION("BrowserRenderProcessHost.ChildLaunchFailures", |
| histogram_type, RENDERER_TYPE_COUNT); |
| base::UmaHistogramSparse( |
| "BrowserRenderProcessHost.ChildLaunchFailureCodes", exit_code); |
| if (was_extension_process) |
| IncrementPrefValue(prefs::kStabilityExtensionRendererFailedLaunchCount); |
| else |
| IncrementPrefValue(prefs::kStabilityRendererFailedLaunchCount); |
| break; |
| case base::TERMINATION_STATUS_MAX_ENUM: |
| NOTREACHED(); |
| break; |
| } |
| } |
| |
| void StabilityMetricsHelper::LogRendererLaunched(bool was_extension_process) { |
| if (was_extension_process) |
| IncrementPrefValue(prefs::kStabilityExtensionRendererLaunchCount); |
| else |
| IncrementPrefValue(prefs::kStabilityRendererLaunchCount); |
| } |
| |
| void StabilityMetricsHelper::IncrementPrefValue(const char* path) { |
| int value = local_state_->GetInteger(path); |
| local_state_->SetInteger(path, value + 1); |
| } |
| |
| void StabilityMetricsHelper::IncrementLongPrefsValue(const char* path) { |
| int64_t value = local_state_->GetInt64(path); |
| local_state_->SetInt64(path, value + 1); |
| } |
| |
| void StabilityMetricsHelper::LogRendererHang() { |
| IncrementPrefValue(prefs::kStabilityRendererHangCount); |
| } |
| |
| } // namespace metrics |