blob: 517fc71fc86ca976cb1a5ea05024d88914b7012b [file] [log] [blame]
// 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