blob: c786799518dec2e51fd3cbca22cfc2e11c0da30c [file] [log] [blame]
// Copyright 2017 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 "chrome/browser/memory/swap_thrashing_monitor.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/time/time.h"
#if defined(OS_WIN)
#include "chrome/browser/memory/swap_thrashing_monitor_delegate_win.h"
#endif
namespace features {
// Feature flag controlling whether or not this monitor should be enabled.
const base::Feature kSwapThrashingMonitor{"SwapThrashingMonitor",
base::FEATURE_DISABLED_BY_DEFAULT};
} // namespace features
namespace memory {
namespace {
constexpr base::TimeDelta kSamplingInterval = base::TimeDelta::FromSeconds(2);
// Enumeration of UMA swap thrashing levels. This needs to be kept in sync with
// tools/metrics/histograms/enums.xml and the swap_thrashing levels defined in
// swap_thrashing_common.h.
//
// These values are persisted to logs, and should therefore never be renumbered
// nor reused
enum SwapThrashingLevelUMA {
UMA_SWAP_THRASHING_LEVEL_NONE = 0,
UMA_SWAP_THRASHING_LEVEL_SUSPECTED = 1,
UMA_SWAP_THRASHING_LEVEL_CONFIRMED = 2,
// This must be the last value in the enum.
UMA_SWAP_THRASHING_LEVEL_COUNT,
};
// Enumeration of UMA swap thrashing level changes. This needs to be kept in
// sync with tools/metrics/histograms/enums.xml.
//
// These values are persisted to logs, and should therefore never be renumbered
// nor reused
enum SwapThrashingLevelChangesUMA {
UMA_SWAP_THRASHING_LEVEL_CHANGE_NONE_TO_SUSPECTED = 0,
UMA_SWAP_THRASHING_LEVEL_CHANGE_SUSPECTED_TO_CONFIRMED = 1,
UMA_SWAP_THRASHING_LEVEL_CHANGE_CONFIRMED_TO_SUSPECTED = 2,
UMA_SWAP_THRASHING_LEVEL_CHANGE_SUSPECTED_TO_NONE = 3,
// This must be the last value in the enum.
UMA_SWAP_THRASHING_LEVEL_CHANGE_COUNT,
};
// Converts a swap thrashing level to an UMA enumeration value.
SwapThrashingLevelUMA SwapThrashingLevelToUmaEnumValue(
SwapThrashingLevel level) {
switch (level) {
case SwapThrashingLevel::SWAP_THRASHING_LEVEL_NONE:
return UMA_SWAP_THRASHING_LEVEL_NONE;
case SwapThrashingLevel::SWAP_THRASHING_LEVEL_SUSPECTED:
return UMA_SWAP_THRASHING_LEVEL_SUSPECTED;
case SwapThrashingLevel::SWAP_THRASHING_LEVEL_CONFIRMED:
return UMA_SWAP_THRASHING_LEVEL_CONFIRMED;
}
NOTREACHED();
return UMA_SWAP_THRASHING_LEVEL_NONE;
}
} // namespace
// static
void SwapThrashingMonitor::Initialize() {
GetInstance();
}
SwapThrashingMonitor* SwapThrashingMonitor::GetInstance() {
static SwapThrashingMonitor* instance = new SwapThrashingMonitor();
return instance;
}
SwapThrashingMonitor::SwapThrashingMonitor()
: current_swap_thrashing_level_(
SwapThrashingLevel::SWAP_THRASHING_LEVEL_NONE) {
DCHECK(base::FeatureList::IsEnabled(features::kSwapThrashingMonitor));
#if defined(OS_WIN)
delegate_ = std::make_unique<SwapThrashingMonitorDelegateWin>();
#else
delegate_ = std::make_unique<SwapThrashingMonitorDelegate>();
#endif
StartObserving();
}
SwapThrashingMonitor::~SwapThrashingMonitor() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
SwapThrashingLevel SwapThrashingMonitor::GetCurrentSwapThrashingLevel() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return current_swap_thrashing_level_;
}
void SwapThrashingMonitor::CheckSwapThrashingPressureAndRecordStatistics() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
SwapThrashingLevel swap_thrashing_level =
delegate_->SampleAndCalculateSwapThrashingLevel();
// Record the state changes.
if (swap_thrashing_level != current_swap_thrashing_level_) {
SwapThrashingLevelChangesUMA level_change =
UMA_SWAP_THRASHING_LEVEL_CHANGE_COUNT;
switch (current_swap_thrashing_level_) {
case SwapThrashingLevel::SWAP_THRASHING_LEVEL_NONE: {
DCHECK_EQ(SwapThrashingLevel::SWAP_THRASHING_LEVEL_SUSPECTED,
swap_thrashing_level);
level_change = UMA_SWAP_THRASHING_LEVEL_CHANGE_NONE_TO_SUSPECTED;
break;
}
case SwapThrashingLevel::SWAP_THRASHING_LEVEL_SUSPECTED: {
if (swap_thrashing_level ==
SwapThrashingLevel::SWAP_THRASHING_LEVEL_NONE) {
level_change = UMA_SWAP_THRASHING_LEVEL_CHANGE_SUSPECTED_TO_NONE;
} else {
level_change = UMA_SWAP_THRASHING_LEVEL_CHANGE_SUSPECTED_TO_CONFIRMED;
}
break;
}
case SwapThrashingLevel::SWAP_THRASHING_LEVEL_CONFIRMED: {
DCHECK_EQ(SwapThrashingLevel::SWAP_THRASHING_LEVEL_SUSPECTED,
swap_thrashing_level);
level_change = UMA_SWAP_THRASHING_LEVEL_CHANGE_CONFIRMED_TO_SUSPECTED;
break;
}
default:
break;
}
UMA_HISTOGRAM_ENUMERATION("Memory.Experimental.SwapThrashingLevelChanges",
level_change,
UMA_SWAP_THRASHING_LEVEL_CHANGE_COUNT);
}
current_swap_thrashing_level_ = swap_thrashing_level;
UMA_HISTOGRAM_ENUMERATION(
"Memory.Experimental.SwapThrashingLevel",
SwapThrashingLevelToUmaEnumValue(current_swap_thrashing_level_),
UMA_SWAP_THRASHING_LEVEL_COUNT);
}
void SwapThrashingMonitor::StartObserving() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// TODO(sebmarchand): Determine if the system is using on-disk swap, abort if
// it isn't as there won't be any swap-paging to observe (on-disk swap could
// later become available if the user turn it on but this case is rare that
// it's safe to ignore it). See crbug.com/779309.
check_timer_.Start(
FROM_HERE, kSamplingInterval,
base::Bind(
&SwapThrashingMonitor::CheckSwapThrashingPressureAndRecordStatistics,
base::Unretained(this)));
}
} // namespace memory