// Copyright 2015 The Chromium Authors // 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 #include #include "base/check.h" #include "base/containers/contains.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/notreached.h" #include "base/strings/strcat.h" #include "base/system/sys_info.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 BUILDFLAG(IS_WIN) #include // Needed for STATUS_* codes #endif #if BUILDFLAG(IS_ANDROID) #include "base/android/application_status_listener.h" #endif namespace metrics { namespace { #if !BUILDFLAG(IS_ANDROID) // 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 BUILDFLAG(IS_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(exit_code) == STATUS_GUARD_PAGE_VIOLATION) return 0x1FCF7EC3; // Randomly picked number. #endif return std::abs(exit_code); } #endif // !BUILDFLAG(IS_ANDROID) #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) const char* HostedContentTypeToString( RendererHostedContentType hosted_content_type) { switch (hosted_content_type) { case metrics::RendererHostedContentType::kExtension: return "Extension"; case metrics::RendererHostedContentType::kForegroundMainFrame: return "ForegroundMainFrame"; case metrics::RendererHostedContentType::kForegroundSubframe: return "ForegroundSubframe"; case metrics::RendererHostedContentType::kBackgroundFrame: return "BackgroundFrame"; case metrics::RendererHostedContentType::kInactiveFrame: return "InactiveFrame"; case metrics::RendererHostedContentType::kNoFrameOrExtension: return "NoFrameOrExtension"; } } void RecordRendererAbnormalTerminationByHostedContentType( RendererHostedContentType hosted_content_type, base::TerminationStatus status) { if (status == base::TERMINATION_STATUS_NORMAL_TERMINATION) { return; } base::UmaHistogramEnumeration( "Stability.RendererAbnormalTermination2.HostedContentType", hosted_content_type); base::UmaHistogramEnumeration( base::StrCat({"Stability.RendererAbnormalTermination2.", HostedContentTypeToString(hosted_content_type)}), status, base::TERMINATION_STATUS_MAX_ENUM); } #endif // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) } // namespace StabilityMetricsHelper::StabilityMetricsHelper(PrefService* local_state) : local_state_(local_state) { DCHECK(local_state_); } StabilityMetricsHelper::~StabilityMetricsHelper() {} #if BUILDFLAG(IS_ANDROID) 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::kStabilityRendererLaunchCount); if (count) { stability_proto->set_renderer_launch_count(count); local_state_->SetInteger(prefs::kStabilityRendererLaunchCount, 0); } } void StabilityMetricsHelper::ClearSavedStabilityMetrics() { local_state_->SetInteger(prefs::kStabilityPageLoadCount, 0); local_state_->SetInteger(prefs::kStabilityRendererLaunchCount, 0); } #endif // BUILDFLAG(IS_ANDROID) // static void StabilityMetricsHelper::RegisterPrefs(PrefRegistrySimple* registry) { #if BUILDFLAG(IS_ANDROID) registry->RegisterIntegerPref(prefs::kStabilityPageLoadCount, 0); registry->RegisterIntegerPref(prefs::kStabilityRendererLaunchCount, 0); #endif // BUILDFLAG(IS_ANDROID) } void StabilityMetricsHelper::IncreaseRendererCrashCount() { RecordStabilityEvent(StabilityEventType::kRendererCrash); } void StabilityMetricsHelper::IncreaseGpuCrashCount() { RecordStabilityEvent(StabilityEventType::kGpuCrash); } void StabilityMetricsHelper::BrowserUtilityProcessLaunched( const std::string& metrics_name) { uint32_t hash = variations::HashName(metrics_name); base::UmaHistogramSparse("ChildProcess.Launched.UtilityProcessHash", hash); RecordStabilityEvent(StabilityEventType::kUtilityLaunch); } void StabilityMetricsHelper::BrowserUtilityProcessCrashed( const std::string& metrics_name, int exit_code) { uint32_t hash = variations::HashName(metrics_name); base::UmaHistogramSparse("ChildProcess.Crashed.UtilityProcessHash", hash); base::UmaHistogramSparse("ChildProcess.Crashed.UtilityProcessExitCode", exit_code); RecordStabilityEvent(StabilityEventType::kUtilityCrash); } void StabilityMetricsHelper::BrowserUtilityProcessLaunchFailed( const std::string& metrics_name, int launch_error_code #if BUILDFLAG(IS_WIN) , DWORD last_error #endif ) { uint32_t hash = variations::HashName(metrics_name); base::UmaHistogramSparse("ChildProcess.LaunchFailed.UtilityProcessHash", hash); base::UmaHistogramSparse("ChildProcess.LaunchFailed.UtilityProcessErrorCode", launch_error_code); #if BUILDFLAG(IS_WIN) base::UmaHistogramSparse("ChildProcess.LaunchFailed.WinLastError", last_error); #endif // TODO(wfh): Decide if this utility process launch failure should also // trigger a Stability Event. } void StabilityMetricsHelper::LogLoadStarted() { #if BUILDFLAG(IS_ANDROID) IncrementPrefValue(prefs::kStabilityPageLoadCount); #endif RecordStabilityEvent(StabilityEventType::kPageLoad); } #if BUILDFLAG(IS_IOS) void StabilityMetricsHelper::LogRendererCrash() { // The actual exit code isn't provided on iOS; use a dummy value. constexpr int kDummyExitCode = 105; LogRendererCrashImpl(CoarseRendererType::kRenderer, kDummyExitCode); } #elif !BUILDFLAG(IS_ANDROID) void StabilityMetricsHelper::LogRendererCrash( RendererHostedContentType hosted_content_type, base::TerminationStatus status, int exit_code) { RecordRendererAbnormalTerminationByHostedContentType(hosted_content_type, status); CoarseRendererType coarse_renderer_type = hosted_content_type == RendererHostedContentType::kExtension ? CoarseRendererType::kExtension : CoarseRendererType::kRenderer; 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: LogRendererCrashImpl(coarse_renderer_type, exit_code); break; #if BUILDFLAG(IS_CHROMEOS) case base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM: base::UmaHistogramEnumeration("BrowserRenderProcessHost.ChildKills.OOM", coarse_renderer_type); [[fallthrough]]; #endif // BUILDFLAG(IS_CHROMEOS) case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: base::UmaHistogramEnumeration("BrowserRenderProcessHost.ChildKills", coarse_renderer_type); break; case base::TERMINATION_STATUS_STILL_RUNNING: base::UmaHistogramEnumeration( "BrowserRenderProcessHost.DisconnectedAlive", coarse_renderer_type); break; case base::TERMINATION_STATUS_LAUNCH_FAILED: // TODO(rkaplow): See if we can remove this histogram as we have // Stability.Counts2 which has the same metrics. base::UmaHistogramEnumeration( "BrowserRenderProcessHost.ChildLaunchFailures", coarse_renderer_type); base::UmaHistogramSparse( "BrowserRenderProcessHost.ChildLaunchFailureCodes", exit_code); RecordStabilityEvent( hosted_content_type == RendererHostedContentType::kExtension ? StabilityEventType::kExtensionRendererFailedLaunch : StabilityEventType::kRendererFailedLaunch); break; #if BUILDFLAG(IS_WIN) case base::TERMINATION_STATUS_INTEGRITY_FAILURE: base::UmaHistogramEnumeration( "BrowserRenderProcessHost.ChildCodeIntegrityFailures", coarse_renderer_type); break; #endif case base::TERMINATION_STATUS_MAX_ENUM: NOTREACHED(); break; } } #endif // !BUILDFLAG(IS_ANDROID) void StabilityMetricsHelper::LogRendererLaunched(bool was_extension_process) { auto metric = was_extension_process ? StabilityEventType::kExtensionRendererLaunch : StabilityEventType::kRendererLaunch; RecordStabilityEvent(metric); #if BUILDFLAG(IS_ANDROID) if (!was_extension_process) IncrementPrefValue(prefs::kStabilityRendererLaunchCount); #endif // BUILDFLAG(IS_ANDROID) } void StabilityMetricsHelper::IncrementPrefValue(const char* path) { int value = local_state_->GetInteger(path); local_state_->SetInteger(path, value + 1); } // static void StabilityMetricsHelper::RecordStabilityEvent( StabilityEventType stability_event_type) { UMA_STABILITY_HISTOGRAM_ENUMERATION("Stability.Counts2", stability_event_type); } #if !BUILDFLAG(IS_ANDROID) void StabilityMetricsHelper::LogRendererCrashImpl( CoarseRendererType renderer_type, int exit_code) { if (renderer_type == CoarseRendererType::kExtension) { #if !BUILDFLAG(ENABLE_EXTENSIONS) NOTREACHED(); #endif RecordStabilityEvent(StabilityEventType::kExtensionCrash); base::UmaHistogramSparse("CrashExitCodes.Extension", MapCrashExitCodeForHistogram(exit_code)); } else { IncreaseRendererCrashCount(); base::UmaHistogramSparse("CrashExitCodes.Renderer", MapCrashExitCodeForHistogram(exit_code)); } base::UmaHistogramEnumeration("BrowserRenderProcessHost.ChildCrashes", renderer_type); } #endif // !BUILDFLAG(IS_ANDROID) } // namespace metrics