Commit 4d1357d6 authored by chrisha's avatar chrisha Committed by Commit bot

Remove memory_pressure component.

This was part of an aborted refactor, which was abandoned due to memory coordinator work (which has deprecated the memory pressure subsystem).
When memory coordinator is finished all uses of memory pressure will be migrated to it.

BUG=
TBR=sdefresne@chromium.org, halliwell@chromium.org

Review-Url: https://codereview.chromium.org/2874553004
Cr-Commit-Position: refs/heads/master@{#471027}
parent fa567892
...@@ -22,7 +22,6 @@ source_set("hang_util") { ...@@ -22,7 +22,6 @@ source_set("hang_util") {
] ]
deps = [ deps = [
"//base", "//base",
"//components/memory_pressure",
] ]
libs = [ "pdh.lib" ] # Used by system_load_estimator.h. libs = [ "pdh.lib" ] # Used by system_load_estimator.h.
ldflags = [ "/DELAYLOAD:pdh.dll" ] # Only used on report capture. ldflags = [ "/DELAYLOAD:pdh.dll" ] # Only used on report capture.
......
...@@ -3,6 +3,5 @@ include_rules = [ ...@@ -3,6 +3,5 @@ include_rules = [
"+chrome/install_static", "+chrome/install_static",
"+components/browser_watcher", "+components/browser_watcher",
"+components/crash", "+components/crash",
"+components/memory_pressure",
"+testing/gtest", "+testing/gtest",
] ]
...@@ -13,8 +13,6 @@ namespace chromecast { ...@@ -13,8 +13,6 @@ namespace chromecast {
// Memory pressure monitor for Cast: polls for current memory // Memory pressure monitor for Cast: polls for current memory
// usage periodically and sends memory pressure notifications. // usage periodically and sends memory pressure notifications.
// TODO(halliwell): switch to //components/memory_pressure when that's
// ready.
class CastMemoryPressureMonitor : public base::MemoryPressureMonitor { class CastMemoryPressureMonitor : public base::MemoryPressureMonitor {
public: public:
CastMemoryPressureMonitor(); CastMemoryPressureMonitor();
......
...@@ -98,7 +98,6 @@ test("components_unittests") { ...@@ -98,7 +98,6 @@ test("components_unittests") {
"//components/language_usage_metrics:unit_tests", "//components/language_usage_metrics:unit_tests",
"//components/leveldb_proto:unit_tests", "//components/leveldb_proto:unit_tests",
"//components/login:unit_tests", "//components/login:unit_tests",
"//components/memory_pressure:unit_tests",
"//components/metrics:unit_tests", "//components/metrics:unit_tests",
"//components/mime_util:unit_tests", "//components/mime_util:unit_tests",
"//components/navigation_metrics:unit_tests", "//components/navigation_metrics:unit_tests",
......
# 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.
static_library("memory_pressure") {
sources = [
"direct_memory_pressure_calculator.h",
"direct_memory_pressure_calculator_linux.cc",
"direct_memory_pressure_calculator_linux.h",
"direct_memory_pressure_calculator_win.cc",
"direct_memory_pressure_calculator_win.h",
"filtered_memory_pressure_calculator.cc",
"filtered_memory_pressure_calculator.h",
"memory_pressure_calculator.h",
"memory_pressure_listener.cc",
"memory_pressure_listener.h",
"memory_pressure_monitor.cc",
"memory_pressure_monitor.h",
"memory_pressure_stats_collector.cc",
"memory_pressure_stats_collector.h",
]
deps = [
"//base",
]
}
source_set("unit_tests") {
testonly = true
sources = [
"direct_memory_pressure_calculator_linux_unittest.cc",
"direct_memory_pressure_calculator_win_unittest.cc",
"filtered_memory_pressure_calculator_unittest.cc",
"memory_pressure_monitor_unittest.cc",
"memory_pressure_stats_collector_unittest.cc",
"test_memory_pressure_calculator.cc",
"test_memory_pressure_calculator.h",
]
deps = [
":memory_pressure",
"//base/test:test_support",
"//testing/gmock",
"//testing/gtest",
]
}
chrisha@chromium.org
shrike@chromium.org
skuhne@chromium.org
// 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.
#ifndef COMPONENTS_MEMORY_PRESSURE_DIRECT_MEMORY_PRESSURE_CALCULATOR_H_
#define COMPONENTS_MEMORY_PRESSURE_DIRECT_MEMORY_PRESSURE_CALCULATOR_H_
#if defined(OS_LINUX)
#include "components/memory_pressure/direct_memory_pressure_calculator_linux.h"
#elsif defined(OS_WIN)
#include "components/memory_pressure/direct_memory_pressure_calculator_win.h"
#endif // OS_LINUX
#endif // COMPONENTS_MEMORY_PRESSURE_DIRECT_MEMORY_PRESSURE_CALCULATOR_H_
// Copyright 2016 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/memory_pressure/direct_memory_pressure_calculator_linux.h"
#include "base/files/file_util.h"
#include "base/process/process_metrics.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/sys_info.h"
#include "base/threading/thread_restrictions.h"
namespace memory_pressure {
namespace {
const int kKiBperMiB = 1024;
// Used to calculate a moving average of faults/sec. Our sample times are
// inconsistent because MemoryPressureMonitor calls
// CalculateCurrentPressureLevel more frequently when memory pressure is high,
// and because we're measuring CPU time instead of real time. So our
// exponentially weighted moving average is generalized to a low-pass filter.
//
// Represents the amount of CPU time, in seconds, for a sample to be 50%
// forgotten in our moving average. Systems with more CPUs that are under high
// load will have a moving average that changes more quickly than a system with
// fewer CPUs under the same load. Do not normalize based on the number of CPUs
// because this behavior is accurate: if a system with a large number of CPUs
// is getting lots of work done without page faulting, it must not be under
// memory pressure.
//
// TODO(thomasanderson): Experimentally determine the correct value for this
// constant.
const double kLowPassHalfLife = 30.0;
// Returns the amount of memory that is available for use right now, or that can
// be easily reclaimed by the OS, in MBs.
int GetAvailableSystemMemoryMiB(const base::SystemMemoryInfoKB* mem_info) {
return mem_info->available
? mem_info->available / kKiBperMiB
: (mem_info->free + mem_info->buffers + mem_info->cached) /
kKiBperMiB;
}
} // namespace
// Thresholds at which we consider the system being under moderate/critical
// memory pressure. They represent the percentage of system memory in use.
const int DirectMemoryPressureCalculator::kDefaultModerateThresholdPc = 70;
const int DirectMemoryPressureCalculator::kDefaultCriticalThresholdPc = 90;
DirectMemoryPressureCalculator::DirectMemoryPressureCalculator()
: moderate_threshold_mb_(0),
critical_threshold_mb_(0) {
InferThresholds();
InitPageFaultMonitor();
}
DirectMemoryPressureCalculator::DirectMemoryPressureCalculator(
int moderate_threshold_mb,
int critical_threshold_mb)
: moderate_threshold_mb_(moderate_threshold_mb),
critical_threshold_mb_(critical_threshold_mb) {
DCHECK_GE(moderate_threshold_mb_, critical_threshold_mb_);
DCHECK_LE(0, critical_threshold_mb_);
InitPageFaultMonitor();
}
DirectMemoryPressureCalculator::MemoryPressureLevel
DirectMemoryPressureCalculator::PressureCausedByThrashing(
const base::SystemMemoryInfoKB& mem_info) {
base::TimeDelta new_user_exec_time = GetUserCpuTimeSinceBoot();
if (new_user_exec_time == base::TimeDelta())
return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
uint64_t new_major_page_faults = mem_info.pgmajfault;
if (new_user_exec_time != base::TimeDelta() && new_major_page_faults &&
(new_user_exec_time - last_user_exec_time_) != base::TimeDelta()) {
double delta_user_exec_time =
(new_user_exec_time - last_user_exec_time_).InSecondsF();
double delta_major_page_faults =
new_major_page_faults - last_major_page_faults_;
double sampled_faults_per_second =
delta_major_page_faults / delta_user_exec_time;
double adjusted_ewma_coefficient =
1 - exp2(-delta_user_exec_time / low_pass_half_life_seconds_);
current_faults_per_second_ =
adjusted_ewma_coefficient * sampled_faults_per_second +
(1 - adjusted_ewma_coefficient) * current_faults_per_second_;
last_user_exec_time_ = new_user_exec_time;
last_major_page_faults_ = new_major_page_faults;
}
if (current_faults_per_second_ >
critical_multiplier_ * AverageFaultsPerSecond()) {
return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
}
if (current_faults_per_second_ >
moderate_multiplier_ * AverageFaultsPerSecond()) {
return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
}
return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
}
DirectMemoryPressureCalculator::MemoryPressureLevel
DirectMemoryPressureCalculator::PressureCausedByOOM(
const base::SystemMemoryInfoKB& mem_info) {
int phys_free = GetAvailableSystemMemoryMiB(&mem_info);
if (phys_free <= critical_threshold_mb_)
return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
if (phys_free <= moderate_threshold_mb_)
return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
}
DirectMemoryPressureCalculator::MemoryPressureLevel
DirectMemoryPressureCalculator::CalculateCurrentPressureLevel() {
base::SystemMemoryInfoKB mem_info = {};
if (!GetSystemMemoryInfo(&mem_info))
return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
return std::max(PressureCausedByThrashing(mem_info),
PressureCausedByOOM(mem_info));
}
bool DirectMemoryPressureCalculator::GetSystemMemoryInfo(
base::SystemMemoryInfoKB* mem_info) const {
return base::GetSystemMemoryInfo(mem_info);
}
base::TimeDelta DirectMemoryPressureCalculator::GetUserCpuTimeSinceBoot()
const {
return base::GetUserCpuTimeSinceBoot();
}
void DirectMemoryPressureCalculator::InferThresholds() {
base::SystemMemoryInfoKB mem_info = {};
if (!GetSystemMemoryInfo(&mem_info))
return;
moderate_threshold_mb_ =
mem_info.total * (100 - kDefaultModerateThresholdPc) / 100 / kKiBperMiB;
critical_threshold_mb_ =
mem_info.total * (100 - kDefaultCriticalThresholdPc) / 100 / kKiBperMiB;
}
void DirectMemoryPressureCalculator::InitPageFaultMonitor() {
low_pass_half_life_seconds_ = kLowPassHalfLife;
last_user_exec_time_ = GetUserCpuTimeSinceBoot();
base::SystemMemoryInfoKB mem_info = {};
last_major_page_faults_ =
GetSystemMemoryInfo(&mem_info) ? mem_info.pgmajfault : 0;
current_faults_per_second_ = AverageFaultsPerSecond();
}
double DirectMemoryPressureCalculator::AverageFaultsPerSecond() const {
return last_major_page_faults_ == 0
? last_user_exec_time_.InSecondsF() / last_major_page_faults_
: 0;
}
} // namespace memory_pressure
// Copyright 2016 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.
#ifndef COMPONENTS_MEMORY_PRESSURE_DIRECT_MEMORY_PRESSURE_CALCULATOR_LINUX_H_
#define COMPONENTS_MEMORY_PRESSURE_DIRECT_MEMORY_PRESSURE_CALCULATOR_LINUX_H_
#include "base/macros.h"
#include "base/process/process_metrics.h"
#include "components/memory_pressure/memory_pressure_calculator.h"
namespace base {
class TimeDelta;
}
namespace memory_pressure {
// OS-specific implementation of MemoryPressureCalculator. This is only defined
// and used on platforms that do not have native memory pressure signals
// (ChromeOS, Linux, Windows). OSes that do have native signals simply hook into
// the appropriate subsystem (Android, Mac OS X).
class DirectMemoryPressureCalculator : public MemoryPressureCalculator {
public:
// Exposed for unittesting. See .cc file for detailed discussion of these
// constants.
static const int kDefaultModerateThresholdPc;
static const int kDefaultCriticalThresholdPc;
// Default constructor. Will choose thresholds automatically based on the
// actual amount of system memory installed.
DirectMemoryPressureCalculator();
// Constructor with explicit memory thresholds. These represent the amount of
// free memory below which the applicable memory pressure state applies.
DirectMemoryPressureCalculator(int moderate_threshold_mb,
int critical_threshold_mb);
~DirectMemoryPressureCalculator() override {}
// Calculates the current pressure level.
MemoryPressureLevel CalculateCurrentPressureLevel() override;
int moderate_threshold_mb() const { return moderate_threshold_mb_; }
int critical_threshold_mb() const { return critical_threshold_mb_; }
private:
friend class TestDirectMemoryPressureCalculator;
MemoryPressureLevel PressureCausedByThrashing(
const base::SystemMemoryInfoKB& mem_info);
MemoryPressureLevel PressureCausedByOOM(
const base::SystemMemoryInfoKB& mem_info);
// Gets system memory status. This is virtual as a unittesting hook and by
// default this invokes base::GetSystemMemoryInfo.
virtual bool GetSystemMemoryInfo(base::SystemMemoryInfoKB* mem_info) const;
// We use CPU time instead of real time because this eliminates the variable
// of average system load. Consumer machines are idle most of the time, so if
// we used real time, the average faults/sec would be very low. As soon as
// the user loads anything, the instantaneous faults/sec would increase, and
// we would overreport the memory pressure.
//
// We also ignore system time because we don't want to include the time the OS
// takes to swap pages in/out on behalf of processes.
//
// Virtual as a unittesting hook.
virtual base::TimeDelta GetUserCpuTimeSinceBoot() const;
// Uses GetSystemMemoryInfo to automatically infer appropriate values for
// moderate_threshold_mb_ and critical_threshold_mb_.
void InferThresholds();
// Initialize the page fault monitor state so CalculateCurrentPressureLevel
// will have a delta to analyze.
virtual void InitPageFaultMonitor();
// Computes last_major_page_faults_/last_user_exec_time_ with some
// protection.
double AverageFaultsPerSecond() const;
// Threshold amounts of available memory that trigger pressure levels. See
// memory_pressure_monitor_win.cc for a discussion of reasonable values for
// these.
int moderate_threshold_mb_;
int critical_threshold_mb_;
// State needed by the hard page fault monitor. These values are assumed
// not to overflow.
base::TimeDelta last_user_exec_time_;
uint64_t last_major_page_faults_;
// An exponentially weighted moving average of the sampled faults/sec.
double current_faults_per_second_;
double low_pass_half_life_seconds_;
// |current_faults_per_second_| must be at least |kModerateMultiplier| *
// AverageFaultsPerSecond() to report moderate pressure, and similarly for
// critical pressure. Non-const members for unit testing.
//
// TODO(thomasanderson): Experimentally determine the correct value for these
// constants.
double moderate_multiplier_ = 5.0;
double critical_multiplier_ = 10.0;
DISALLOW_COPY_AND_ASSIGN(DirectMemoryPressureCalculator);
};
} // namespace memory_pressure
#endif // COMPONENTS_MEMORY_PRESSURE_DIRECT_MEMORY_PRESSURE_CALCULATOR_LINUX_H_
// 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/memory_pressure/direct_memory_pressure_calculator_linux.h"
#include "base/process/process_metrics.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace memory_pressure {
namespace {
const int kKBperMB = 1024;
const int kTotalMemoryInMB = 4096;
} // namespace
// This is out of the anonymous namespace space because it is a friend of
// DirectMemoryPressureCalculator.
class TestDirectMemoryPressureCalculator
: public DirectMemoryPressureCalculator {
public:
TestDirectMemoryPressureCalculator()
: DirectMemoryPressureCalculator(20, 10) {
// The values passed to the MemoryPressureCalculator constructor are dummy
// values that are immediately overwritten by InferTresholds.
// Simulate 4GB of RAM.
mem_info_.total = kTotalMemoryInMB * kKBperMB;
// Run InferThresholds using the test fixture's GetSystemMemoryStatus.
InferThresholds();
}
TestDirectMemoryPressureCalculator(int total_memory_mb,
int moderate_threshold_mb,
int critical_threshold_mb)
: DirectMemoryPressureCalculator(moderate_threshold_mb,
critical_threshold_mb) {
mem_info_.total = total_memory_mb * kKBperMB;
mem_info_.pgmajfault = 0;
}
void InitPageFaultMonitor() override {}
// Sets up the memory status to reflect the provided absolute memory left.
void SetMemoryFree(int phys_left_mb) {
// |total| is set in the constructor and not modified.
// Set the amount of free memory.
mem_info_.free = phys_left_mb * kKBperMB;
DCHECK_LT(mem_info_.free, mem_info_.total);
}
void SetNone() { SetMemoryFree(moderate_threshold_mb() + 1); }
void SetModerate() { SetMemoryFree(moderate_threshold_mb() - 1); }
void SetCritical() { SetMemoryFree(critical_threshold_mb() - 1); }
double GetEwma() { return current_faults_per_second_; }
// Set the next CPU time to be read.
void SetCpuTime(double cpu_time) {
user_cpu_time_ = base::TimeDelta::FromSecondsD(cpu_time);
}
// Set the next page faults value to be read.
void SetPageFaults(uint64_t page_faults) {
mem_info_.pgmajfault = page_faults;
}
void SetPageFaultMonitorState(double user_exec_time,
uint64_t major_page_faults,
double current_faults,
double low_pass_half_life,
double moderate_multiplier,
double critical_multiplier) {
last_user_exec_time_ = base::TimeDelta::FromSecondsD(user_exec_time);
last_major_page_faults_ = major_page_faults;
current_faults_per_second_ = current_faults;
low_pass_half_life_seconds_ = low_pass_half_life;
moderate_multiplier_ = moderate_multiplier;
critical_multiplier_ = critical_multiplier;
}
private:
bool GetSystemMemoryInfo(base::SystemMemoryInfoKB* mem_info) const override {
// Simply copy the memory information set by the test fixture.
*mem_info = mem_info_;
return true;
}
base::TimeDelta GetUserCpuTimeSinceBoot() const override {
return user_cpu_time_;
}
base::TimeDelta user_cpu_time_;
base::SystemMemoryInfoKB mem_info_;
};
class DirectMemoryPressureCalculatorTest : public testing::Test {
public:
void CalculateCurrentPressureLevelTest(
TestDirectMemoryPressureCalculator* calc) {
int mod = calc->moderate_threshold_mb();
calc->SetMemoryFree(mod + 1);
EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE,
calc->CalculateCurrentPressureLevel());
calc->SetNone();
EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE,
calc->CalculateCurrentPressureLevel());
calc->SetMemoryFree(mod);
EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
calc->CalculateCurrentPressureLevel());
calc->SetMemoryFree(mod - 1);
EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
calc->CalculateCurrentPressureLevel());
int crit = calc->critical_threshold_mb();
calc->SetMemoryFree(crit + 1);
EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
calc->CalculateCurrentPressureLevel());
calc->SetModerate();
EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
calc->CalculateCurrentPressureLevel());
EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
calc->CalculateCurrentPressureLevel());
calc->SetMemoryFree(crit);
EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
calc->CalculateCurrentPressureLevel());
calc->SetMemoryFree(crit - 1);
EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
calc->CalculateCurrentPressureLevel());
calc->SetCritical();
EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
calc->CalculateCurrentPressureLevel());
}
};
// Tests the fundamental direct calculation of memory pressure with automatic
// small-memory thresholds.
TEST_F(DirectMemoryPressureCalculatorTest,
CalculateCurrentMemoryPressureLevel) {
TestDirectMemoryPressureCalculator calc;
static const int kModerateMb =
(100 - DirectMemoryPressureCalculator::kDefaultModerateThresholdPc) *
kTotalMemoryInMB / 100;
static const int kCriticalMb =
(100 - DirectMemoryPressureCalculator::kDefaultCriticalThresholdPc) *
kTotalMemoryInMB / 100;
EXPECT_EQ(kModerateMb, calc.moderate_threshold_mb());
EXPECT_EQ(kCriticalMb, calc.critical_threshold_mb());
ASSERT_NO_FATAL_FAILURE(CalculateCurrentPressureLevelTest(&calc));
}
// Tests the fundamental direct calculation of memory pressure with manually
// specified threshold levels.
TEST_F(DirectMemoryPressureCalculatorTest,
CalculateCurrentMemoryPressureLevelCustom) {
static const int kSystemMb = 512;
static const int kModerateMb = 256;
static const int kCriticalMb = 128;
TestDirectMemoryPressureCalculator calc(kSystemMb, kModerateMb, kCriticalMb);
EXPECT_EQ(kModerateMb, calc.moderate_threshold_mb());
EXPECT_EQ(kCriticalMb, calc.critical_threshold_mb());
ASSERT_NO_FATAL_FAILURE(CalculateCurrentPressureLevelTest(&calc));
}
// Double-check the math of the Ewma portion of the page fault monitor.
TEST_F(DirectMemoryPressureCalculatorTest, Ewma) {
double half_life = 100.0;
double cpu_time = 0;
uint64_t page_faults = 1;
double ewma = 100.0;
TestDirectMemoryPressureCalculator calc;
calc.SetPageFaultMonitorState(cpu_time, page_faults, ewma, half_life, 0, 0);
// Advance by one half-life. The ewma should be cut in half.
calc.SetCpuTime(half_life);
calc.SetPageFaults(page_faults);
calc.CalculateCurrentPressureLevel();
ewma /= 2;
EXPECT_DOUBLE_EQ(ewma, calc.GetEwma());
// We should get the same result if we advance by increments of 10 up to 100.
ewma = 100.0;
calc.SetPageFaultMonitorState(cpu_time, page_faults, ewma, half_life, 0, 0);
static const int kIncrements = 10;
for(int i = 1; i <= kIncrements; i++) {
calc.SetCpuTime(i * half_life / kIncrements);
calc.SetPageFaults(page_faults);
calc.CalculateCurrentPressureLevel();
}
ewma /= 2;
EXPECT_DOUBLE_EQ(ewma, calc.GetEwma());
static const struct {
uint64_t delta_time;
uint64_t delta_faults;
double expected_ewma;
} kSamples[] = {
// 0.5*0.0 + 0.5*10 = 5.0
{ 1, 10, 5.0 },
// 0.5*5.0 + 0.5*10 = 7.5
{ 1, 10, 7.5 },
// 0.25*7.5 + 0.75*(0/2) = 1.875
{ 2, 0, 1.875 },
// 0.125*1.875 + 0.875*(420/3) = 122.734375
{ 3, 420, 122.734375 },
// 0.5*122.734375 + 0.5*24 = 73.3671875
{ 1, 24, 73.3671875 },
};
cpu_time = 1;
page_faults = 1;
ewma = 0.0;
half_life = 1.0;
calc.SetPageFaultMonitorState(cpu_time, page_faults, ewma, half_life, 0, 0);
for (size_t i = 0 ; i < arraysize(kSamples); i++) {
cpu_time += kSamples[i].delta_time;
page_faults += kSamples[i].delta_faults;
calc.SetCpuTime(cpu_time);
calc.SetPageFaults(page_faults);
calc.CalculateCurrentPressureLevel();
EXPECT_DOUBLE_EQ(kSamples[i].expected_ewma, calc.GetEwma());
}
}
} // namespace memory_pressure
// 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/memory_pressure/direct_memory_pressure_calculator_win.h"
#include "base/logging.h"
#include "base/process/process_metrics.h"
namespace memory_pressure {
#if defined(MEMORY_PRESSURE_IS_POLLING)
namespace {
const int kKBperMB = 1024;
} // namespace
// A system is considered 'high memory' if it has more than 1.5GB of system
// memory available for use by the memory manager (not reserved for hardware
// and drivers). This is a fuzzy version of the ~2GB discussed below.
const int DirectMemoryPressureCalculator::kLargeMemoryThresholdMb = 1536;
// These are the default thresholds used for systems with < ~2GB of physical
// memory. Such systems have been observed to always maintain ~100MB of
// available memory, paging until that is the case. To try to avoid paging a
// threshold slightly above this is chosen. The moderate threshold is slightly
// less grounded in reality and chosen as 2.5x critical.
const int
DirectMemoryPressureCalculator::kSmallMemoryDefaultModerateThresholdMb =
500;
const int
DirectMemoryPressureCalculator::kSmallMemoryDefaultCriticalThresholdMb =
200;
// These are the default thresholds used for systems with >= ~2GB of physical
// memory. Such systems have been observed to always maintain ~300MB of
// available memory, paging until that is the case.
const int
DirectMemoryPressureCalculator::kLargeMemoryDefaultModerateThresholdMb =
1000;
const int
DirectMemoryPressureCalculator::kLargeMemoryDefaultCriticalThresholdMb =
400;
DirectMemoryPressureCalculator::DirectMemoryPressureCalculator()
: moderate_threshold_mb_(0), critical_threshold_mb_(0) {
InferThresholds();
}
DirectMemoryPressureCalculator::DirectMemoryPressureCalculator(
int moderate_threshold_mb,
int critical_threshold_mb)
: moderate_threshold_mb_(moderate_threshold_mb),
critical_threshold_mb_(critical_threshold_mb) {
DCHECK_GE(moderate_threshold_mb_, critical_threshold_mb_);
DCHECK_LE(0, critical_threshold_mb_);
}
DirectMemoryPressureCalculator::MemoryPressureLevel
DirectMemoryPressureCalculator::CalculateCurrentPressureLevel() {
base::SystemMemoryInfoKB mem_info = {};
if (!GetSystemMemoryInfo(&mem_info))
return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
// How much system memory is actively available for use right now, in MBs.
int phys_free = mem_info.avail_phys / kKBperMB;
// TODO(chrisha): This should eventually care about address space pressure,
// but the browser process (where this is running) effectively never runs out
// of address space. Renderers occasionally do, but it does them no good to
// have the browser process monitor address space pressure. Long term,
// renderers should run their own address space pressure monitors and act
// accordingly, with the browser making cross-process decisions based on
// system memory pressure.
// Determine if the physical memory is under critical memory pressure.
if (phys_free <= critical_threshold_mb_)
return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
// Determine if the physical memory is under moderate memory pressure.
if (phys_free <= moderate_threshold_mb_)
return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
// No memory pressure was detected.
return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
}
bool DirectMemoryPressureCalculator::GetSystemMemoryInfo(
base::SystemMemoryInfoKB* mem_info) const {
return base::GetSystemMemoryInfo(mem_info);
}
void DirectMemoryPressureCalculator::InferThresholds() {
// Determine if the memory installed is 'large' or 'small'. Default to 'large'
// on failure, which uses more conservative thresholds.
bool large_memory = true;
base::SystemMemoryInfoKB mem_info = {};
if (GetSystemMemoryInfo(&mem_info)) {
large_memory = mem_info.total / kKBperMB >=
DirectMemoryPressureCalculator::kLargeMemoryThresholdMb;
}
if (large_memory) {
moderate_threshold_mb_ = kLargeMemoryDefaultModerateThresholdMb;
critical_threshold_mb_ = kLargeMemoryDefaultCriticalThresholdMb;
} else {
moderate_threshold_mb_ = kSmallMemoryDefaultModerateThresholdMb;
critical_threshold_mb_ = kSmallMemoryDefaultCriticalThresholdMb;
}
}
#endif // defined(MEMORY_PRESSURE_IS_POLLING)
} // namespace memory_pressure
// 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.
#ifndef COMPONENTS_MEMORY_PRESSURE_DIRECT_MEMORY_PRESSURE_CALCULATOR_WIN_H_
#define COMPONENTS_MEMORY_PRESSURE_DIRECT_MEMORY_PRESSURE_CALCULATOR_WIN_H_
#include "base/macros.h"
#include "base/process/process_metrics.h"
#include "build/build_config.h"
#include "components/memory_pressure/memory_pressure_calculator.h"
namespace memory_pressure {
#if defined(MEMORY_PRESSURE_IS_POLLING)
// OS-specific implementation of MemoryPressureCalculator. This is only defined
// and used on platforms that do not have native memory pressure signals
// (ChromeOS, Linux, Windows). OSes that do have native signals simply hook into
// the appropriate subsystem (Android, Mac OS X).
class DirectMemoryPressureCalculator : public MemoryPressureCalculator {
public:
// Exposed for unittesting. See .cc file for detailed discussion of these
// constants.
static const int kLargeMemoryThresholdMb;
static const int kSmallMemoryDefaultModerateThresholdMb;
static const int kSmallMemoryDefaultCriticalThresholdMb;
static const int kLargeMemoryDefaultModerateThresholdMb;
static const int kLargeMemoryDefaultCriticalThresholdMb;
// Default constructor. Will choose thresholds automatically based on the
// actual amount of system memory installed.
DirectMemoryPressureCalculator();
// Constructor with explicit memory thresholds. These represent the amount of
// free memory below which the applicable memory pressure state applies.
DirectMemoryPressureCalculator(int moderate_threshold_mb,
int critical_threshold_mb);
~DirectMemoryPressureCalculator() override {}
// Calculates the current pressure level.
MemoryPressureLevel CalculateCurrentPressureLevel() override;
int moderate_threshold_mb() const { return moderate_threshold_mb_; }
int critical_threshold_mb() const { return critical_threshold_mb_; }
private:
friend class TestDirectMemoryPressureCalculator;
// Gets system memory status. This is virtual as a unittesting hook and by
// default this invokes base::GetSystemMemoryInfo.
virtual bool GetSystemMemoryInfo(base::SystemMemoryInfoKB* mem_info) const;
// Uses GetSystemMemoryInfo to automatically infer appropriate values for
// moderate_threshold_mb_ and critical_threshold_mb_.
void InferThresholds();
// Threshold amounts of available memory that trigger pressure levels. See
// memory_pressure_monitor_win.cc for a discussion of reasonable values for
// these.
int moderate_threshold_mb_;
int critical_threshold_mb_;
DISALLOW_COPY_AND_ASSIGN(DirectMemoryPressureCalculator);
};
#endif // defined(MEMORY_PRESSURE_IS_POLLING)
} // namespace memory_pressure
#endif // COMPONENTS_MEMORY_PRESSURE_DIRECT_MEMORY_PRESSURE_CALCULATOR_WIN_H_
// 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/memory_pressure/direct_memory_pressure_calculator_win.h"
#include "base/logging.h"
#include "base/process/process_metrics.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace memory_pressure {
#if defined(MEMORY_PRESSURE_IS_POLLING)
namespace {
const int kKBperMB = 1024;
} // namespace
// This is out of the anonymous namespace space because it is a friend of
// DirectMemoryPressureCalculator.
class TestDirectMemoryPressureCalculator
: public DirectMemoryPressureCalculator {
public:
explicit TestDirectMemoryPressureCalculator(bool large_memory)
: DirectMemoryPressureCalculator(20, 10) {
// The values passed to the MemoryPressureCalculator constructor are dummy
// values that are immediately overwritten by InferTresholds.
// Generate a plausible amount of memory.
mem_info_.total = GenerateTotalMemoryMb(large_memory) * kKBperMB;
// Run InferThresholds using the test fixture's GetSystemMemoryStatus.
InferThresholds();
}
TestDirectMemoryPressureCalculator(int total_memory_mb,
int moderate_threshold_mb,
int critical_threshold_mb)
: DirectMemoryPressureCalculator(moderate_threshold_mb,
critical_threshold_mb) {
mem_info_.total = total_memory_mb * kKBperMB;
}
// Generates an amount of total memory that is consistent with the requested
// memory model.
static int GenerateTotalMemoryMb(bool large_memory) {
int total_mb = 64;
while (total_mb < kLargeMemoryThresholdMb)
total_mb *= 2;
if (large_memory)
return total_mb * 2;
return total_mb / 2;
}
// Sets up the memory status to reflect the provided absolute memory left.
void SetMemoryFree(int phys_left_mb) {
// |total| is set in the constructor and not modified.
// Set the amount of free memory.
mem_info_.avail_phys = phys_left_mb * kKBperMB;
DCHECK_LT(mem_info_.avail_phys, mem_info_.total);
}
void SetNone() { SetMemoryFree(moderate_threshold_mb() + 1); }
void SetModerate() { SetMemoryFree(moderate_threshold_mb() - 1); }
void SetCritical() { SetMemoryFree(critical_threshold_mb() - 1); }
private:
bool GetSystemMemoryInfo(base::SystemMemoryInfoKB* mem_info) const override {
// Simply copy the memory information set by the test fixture.
*mem_info = mem_info_;
return true;
}
base::SystemMemoryInfoKB mem_info_;
};
class DirectMemoryPressureCalculatorTest : public testing::Test {
public:
void CalculateCurrentPressureLevelTest(
TestDirectMemoryPressureCalculator* calc) {
int mod = calc->moderate_threshold_mb();
calc->SetMemoryFree(mod + 1);
EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE,
calc->CalculateCurrentPressureLevel());
calc->SetNone();
EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE,
calc->CalculateCurrentPressureLevel());
calc->SetMemoryFree(mod);
EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
calc->CalculateCurrentPressureLevel());
calc->SetMemoryFree(mod - 1);
EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
calc->CalculateCurrentPressureLevel());
int crit = calc->critical_threshold_mb();
calc->SetMemoryFree(crit + 1);
EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
calc->CalculateCurrentPressureLevel());
calc->SetModerate();
EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
calc->CalculateCurrentPressureLevel());
EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
calc->CalculateCurrentPressureLevel());
calc->SetMemoryFree(crit);
EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
calc->CalculateCurrentPressureLevel());
calc->SetMemoryFree(crit - 1);
EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
calc->CalculateCurrentPressureLevel());
calc->SetCritical();
EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
calc->CalculateCurrentPressureLevel());
}
};
// Tests the fundamental direct calculation of memory pressure with automatic
// small-memory thresholds.
TEST_F(DirectMemoryPressureCalculatorTest,
CalculateCurrentMemoryPressureLevelSmall) {
static const int kModerateMb =
DirectMemoryPressureCalculator::kSmallMemoryDefaultModerateThresholdMb;
static const int kCriticalMb =
DirectMemoryPressureCalculator::kSmallMemoryDefaultCriticalThresholdMb;
TestDirectMemoryPressureCalculator calc(false); // Small-memory model.
EXPECT_EQ(kModerateMb, calc.moderate_threshold_mb());
EXPECT_EQ(kCriticalMb, calc.critical_threshold_mb());
ASSERT_NO_FATAL_FAILURE(CalculateCurrentPressureLevelTest(&calc));
}
// Tests the fundamental direct calculation of memory pressure with automatic
// large-memory thresholds.
TEST_F(DirectMemoryPressureCalculatorTest,
CalculateCurrentMemoryPressureLevelLarge) {
static const int kModerateMb =
DirectMemoryPressureCalculator::kLargeMemoryDefaultModerateThresholdMb;
static const int kCriticalMb =
DirectMemoryPressureCalculator::kLargeMemoryDefaultCriticalThresholdMb;
TestDirectMemoryPressureCalculator calc(true); // Large-memory model.
EXPECT_EQ(kModerateMb, calc.moderate_threshold_mb());
EXPECT_EQ(kCriticalMb, calc.critical_threshold_mb());
ASSERT_NO_FATAL_FAILURE(CalculateCurrentPressureLevelTest(&calc));
}
// Tests the fundamental direct calculation of memory pressure with manually
// specified threshold levels.
TEST_F(DirectMemoryPressureCalculatorTest,
CalculateCurrentMemoryPressureLevelCustom) {
static const int kSystemMb = 512;
static const int kModerateMb = 256;
static const int kCriticalMb = 128;
TestDirectMemoryPressureCalculator calc(kSystemMb, kModerateMb, kCriticalMb);
EXPECT_EQ(kModerateMb, calc.moderate_threshold_mb());
EXPECT_EQ(kCriticalMb, calc.critical_threshold_mb());
ASSERT_NO_FATAL_FAILURE(CalculateCurrentPressureLevelTest(&calc));
}
#endif // defined(MEMORY_PRESSURE_IS_POLLING)
} // namespace memory_pressure
// 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/memory_pressure/filtered_memory_pressure_calculator.h"
#include <algorithm>
#include "base/time/tick_clock.h"
namespace memory_pressure {
#if defined(MEMORY_PRESSURE_IS_POLLING)
FilteredMemoryPressureCalculator::FilteredMemoryPressureCalculator(
MemoryPressureCalculator* pressure_calculator,
base::TickClock* tick_clock)
: pressure_calculator_(pressure_calculator),
tick_clock_(tick_clock),
current_pressure_level_(
MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE),
samples_taken_(false),
cooldown_in_progress_(false) {
DCHECK(pressure_calculator);
DCHECK(tick_clock);
}
FilteredMemoryPressureCalculator::~FilteredMemoryPressureCalculator() {
}
FilteredMemoryPressureCalculator::MemoryPressureLevel
FilteredMemoryPressureCalculator::CalculateCurrentPressureLevel() {
base::TimeTicks now = tick_clock_->NowTicks();
// Take a sample.
samples_taken_ = true;
last_sample_time_ = now;
MemoryPressureLevel level =
pressure_calculator_->CalculateCurrentPressureLevel();
// The pressure hasn't changed or has gone up. In either case this is the end
// of a cooldown period if one was in progress.
if (level >= current_pressure_level_) {
cooldown_in_progress_ = false;
current_pressure_level_ = level;
return level;
}
// The pressure has gone down, so apply cooldown hysteresis.
if (cooldown_in_progress_) {
cooldown_high_tide_ = std::max(cooldown_high_tide_, level);
// Get the cooldown period for the current level.
int cooldown_ms = kCriticalPressureCooldownPeriodMs;
if (current_pressure_level_ ==
MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE) {
cooldown_ms = kModeratePressureCooldownPeriodMs;
}
base::TimeDelta cooldown_period =
base::TimeDelta::FromMilliseconds(cooldown_ms);
if (now - cooldown_start_time_ >= cooldown_period) {
// The cooldown has completed successfully, so transition the pressure
// level.
cooldown_in_progress_ = false;
current_pressure_level_ = cooldown_high_tide_;
}
} else {
// Start a new cooldown period.
cooldown_in_progress_ = true;
cooldown_start_time_ = now;
cooldown_high_tide_ = level;
}
return current_pressure_level_;
}
#endif // defined(MEMORY_PRESSURE_IS_POLLING)
} // namespace memory_pressure
// 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.
#ifndef COMPONENTS_MEMORY_PRESSURE_FILTERED_MEMORY_PRESSURE_CALCULATOR_H_
#define COMPONENTS_MEMORY_PRESSURE_FILTERED_MEMORY_PRESSURE_CALCULATOR_H_
#include "components/memory_pressure/memory_pressure_calculator.h"
#include "base/macros.h"
#include "base/time/time.h"
namespace base {
class TickClock;
} // namespace base
namespace memory_pressure {
#if defined(MEMORY_PRESSURE_IS_POLLING)
// A utility class that provides rate-limiting and hysteresis on raw memory
// pressure calculations. This is identical across all platforms, but only used
// on those that do not have native memory pressure signals.
class FilteredMemoryPressureCalculator : public MemoryPressureCalculator {
public:
// The cooldown period when transitioning from critical to moderate/no memory
// pressure, or from moderate to none.
//
// These values were experimentally obtained during the initial ChromeOS only
// implementation of this feature. By spending a significant cooldown period
// at a higher pressure level more time is dedicated to freeing resources and
// less churn occurs in the MemoryPressureListener event stream.
enum : int { kCriticalPressureCooldownPeriodMs = 5000 };
enum : int { kModeratePressureCooldownPeriodMs = 5000 };
// The provided |pressure_calculator| and |tick_clock| must outlive this
// object.
FilteredMemoryPressureCalculator(
MemoryPressureCalculator* pressure_calculator,
base::TickClock* tick_clock);
~FilteredMemoryPressureCalculator() override;
// Calculates the current pressure level.
MemoryPressureLevel CalculateCurrentPressureLevel() override;
// Accessors for unittesting.
bool cooldown_in_progress() const { return cooldown_in_progress_; }
base::TimeTicks cooldown_start_time() const { return cooldown_start_time_; }
MemoryPressureLevel cooldown_high_tide() const { return cooldown_high_tide_; }
private:
friend class TestFilteredMemoryPressureCalculator;
// The delegate pressure calculator. Provided by the constructor.
MemoryPressureCalculator* pressure_calculator_;
// The delegate tick clock. Provided by the constructor.
base::TickClock* tick_clock_;
// The memory pressure currently being reported.
MemoryPressureLevel current_pressure_level_;
// The last time a sample was taken.
bool samples_taken_;
base::TimeTicks last_sample_time_;
// State of an ongoing cooldown period, if any. The high-tide line indicates
// the highest memory pressure level (*below* the current one) that was
// encountered during the cooldown period. This allows a cooldown to
// transition directly from critical to none if *no* moderate pressure signals
// were seen during the period, otherwise it forces it to pass through a
// moderate cooldown as well.
bool cooldown_in_progress_;
base::TimeTicks cooldown_start_time_;
MemoryPressureLevel cooldown_high_tide_;
DISALLOW_COPY_AND_ASSIGN(FilteredMemoryPressureCalculator);
};
#endif // defined(MEMORY_PRESSURE_IS_POLLING)
} // namespace memory_pressure
#endif // COMPONENTS_MEMORY_PRESSURE_FILTERED_MEMORY_PRESSURE_CALCULATOR_H_
// 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.
#ifndef COMPONENTS_MEMORY_PRESSURE_MEMORY_PRESSURE_CALCULATOR_H_
#define COMPONENTS_MEMORY_PRESSURE_MEMORY_PRESSURE_CALCULATOR_H_
#include "base/macros.h"
#include "components/memory_pressure/memory_pressure_listener.h"
namespace memory_pressure {
#if defined(MEMORY_PRESSURE_IS_POLLING)
// Interface for a utility class that calculates memory pressure. Only used on
// platforms without native memory pressure signals.
class MemoryPressureCalculator {
public:
using MemoryPressureLevel = MemoryPressureListener::MemoryPressureLevel;
MemoryPressureCalculator() {}
virtual ~MemoryPressureCalculator() {}
virtual MemoryPressureLevel CalculateCurrentPressureLevel() = 0;
private:
DISALLOW_COPY_AND_ASSIGN(MemoryPressureCalculator);
};
#endif // defined(MEMORY_PRESSURE_IS_POLLING)
} // namespace memory_pressure
#endif // COMPONENTS_MEMORY_PRESSURE_MEMORY_PRESSURE_CALCULATOR_H_
// 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/memory_pressure/memory_pressure_listener.h"
#include "base/lazy_instance.h"
#include "base/observer_list_threadsafe.h"
#include "base/trace_event/trace_event.h"
namespace memory_pressure {
namespace {
// ObserverListThreadSafe is RefCountedThreadSafe, this traits is needed
// to ensure the LazyInstance will hold a reference to it.
struct LeakyLazyObserverListTraits
: base::internal::LeakyLazyInstanceTraits<
base::ObserverListThreadSafe<MemoryPressureListener>> {
static base::ObserverListThreadSafe<MemoryPressureListener>* New(
void* instance) {
base::ObserverListThreadSafe<MemoryPressureListener>* ret =
base::internal::LeakyLazyInstanceTraits<base::ObserverListThreadSafe<
MemoryPressureListener>>::New(instance);
// Leaky.
ret->AddRef();
return ret;
}
};
base::LazyInstance<base::ObserverListThreadSafe<MemoryPressureListener>,
LeakyLazyObserverListTraits> g_observers =
LAZY_INSTANCE_INITIALIZER;
} // namespace
MemoryPressureListener::MemoryPressureListener(
const MemoryPressureListener::MemoryPressureCallback& callback)
: callback_(callback) {
g_observers.Get().AddObserver(this);
}
MemoryPressureListener::~MemoryPressureListener() {
g_observers.Get().RemoveObserver(this);
}
void MemoryPressureListener::Notify(MemoryPressureLevel memory_pressure_level) {
callback_.Run(memory_pressure_level);
}
// static
void MemoryPressureListener::NotifyMemoryPressure(
MemoryPressureLevel memory_pressure_level) {
DCHECK_NE(memory_pressure_level, MEMORY_PRESSURE_LEVEL_NONE);
TRACE_EVENT1("memory", "MemoryPressureListener::NotifyMemoryPressure",
"level", memory_pressure_level);
g_observers.Get().Notify(FROM_HERE, &MemoryPressureListener::Notify,
memory_pressure_level);
}
} // namespace memory_pressure
// 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.
// MemoryPressureListener provides static APIs for handling memory pressure on
// platforms that have such signals (Android, ChromeOS, Mac and Windows). When
// such signals are received the app will try to discard buffers that aren't
// deemed essential (individual modules will implement their own policy).
#ifndef COMPONENTS_MEMORY_PRESSURE_MEMORY_PRESSURE_LISTENER_H_
#define COMPONENTS_MEMORY_PRESSURE_MEMORY_PRESSURE_LISTENER_H_
#include "base/base_export.h"
#include "base/callback.h"
#include "base/macros.h"
#include "build/build_config.h"
// Other operating systems will be added to this as they are implemented.
#if defined(OS_WIN) || defined(OS_LINUX)
#define MEMORY_PRESSURE_IS_POLLING
#endif
namespace memory_pressure {
// To start listening, create a new instance, passing a callback to a
// function that takes a MemoryPressureLevel parameter. To stop listening,
// simply delete the listener object. The implementation guarantees
// that the callback will always be called on the thread that created
// the listener.
//
// Note that even on the same thread, the callback is not guaranteed to be
// called synchronously within the system memory pressure broadcast.
// Please see notes in MemoryPressureLevel enum below: some levels are
// absolutely critical, and if not enough memory is returned to the system,
// it'll potentially kill the app, and then later the app will have to be
// cold-started.
//
// Example:
//
// void OnMemoryPressure(MemoryPressureLevel memory_pressure_level) {
// ...
// }
//
// // Start listening.
// MemoryPressureListener* my_listener =
// new MemoryPressureListener(base::Bind(&OnMemoryPressure));
//
// ...
//
// // Stop listening.
// delete my_listener;
//
class MemoryPressureListener {
public:
// A Java counterpart will be generated for this enum.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base
enum MemoryPressureLevel {
// No problems, there is enough memory to use. This event is not sent via
// callback, but the enum is used in other places to find out the current
// state of the system.
MEMORY_PRESSURE_LEVEL_NONE = -1,
// Modules are advised to free buffers that are cheap to re-allocate and not
// immediately needed.
MEMORY_PRESSURE_LEVEL_MODERATE = 0,
// At this level, modules are advised to free all possible memory. The
// alternative is to be killed by the system, which means all memory will
// have to be re-created, plus the cost of a cold start.
MEMORY_PRESSURE_LEVEL_CRITICAL = 2,
};
typedef base::Callback<void(MemoryPressureLevel)> MemoryPressureCallback;
explicit MemoryPressureListener(
const MemoryPressureCallback& memory_pressure_callback);
~MemoryPressureListener();
// Intended for use by the platform specific implementation.
static void NotifyMemoryPressure(MemoryPressureLevel memory_pressure_level);
private:
void Notify(MemoryPressureLevel memory_pressure_level);
MemoryPressureCallback callback_;
DISALLOW_COPY_AND_ASSIGN(MemoryPressureListener);
};
} // namespace memory_pressure
#endif // COMPONENTS_MEMORY_PRESSURE_MEMORY_PRESSURE_LISTENER_H_
This diff is collapsed.
// Copyright 2016 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.
//
// Declares the MemoryPressureMonitor class. This is responsible for monitoring
// system-wide memory pressure and dispatching memory pressure signals to
// MemoryPressureListener. It is also responsible for rate limiting calls to the
// memory pressure subsytem and gathering statistics for UMA.
//
// The class has a few compile time differences depending on if
// MEMORY_PRESSURE_IS_POLLING is defined. For Windows, ChromeOS and Linux
// the implementation is polling so MEMORY_PRESSURE_IS_POLLING is defined. For
// Mac, iOS and Android it is not defined.
//
// The difference is that "polling" platforms have no native OS signals
// indicating memory pressure. These platforms implement
// DirectMemoryPressureCalculator, which is polled on a schedule to check for
// memory pressure changes. On non-polling platforms the OS provides a native
// signal. This signal is observed by the platform-specific implementation of
// MemoryPressureMonitorImpl.
//
// The memory pressure system periodically repeats memory pressure signals while
// under memory pressure (the interval varying depending on the pressure level).
// As such, even non-polling platforms require a scheduling mechanism for
// repeating notifications. Both implementations share this basic scheduling
// subsystem, and also leverage it to make timely UMA reports.
#ifndef COMPONENTS_MEMORY_PRESSURE_MEMORY_PRESSURE_MONITOR_H_
#define COMPONENTS_MEMORY_PRESSURE_MEMORY_PRESSURE_MONITOR_H_
#include <map>
#include <memory>
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/synchronization/lock.h"
#include "base/time/time.h"
#include "components/memory_pressure/memory_pressure_listener.h"
namespace base {
class TaskRunner;
class TickClock;
} // namespace
namespace memory_pressure {
class MemoryPressureCalculator;
class MemoryPressureStatsCollector;
#if !defined(MEMORY_PRESSURE_IS_POLLING)
// For non-polling platform specific implementation details. An instance of
// this class will be encapsulated in the monitor. It will received an injected
// callback that routes messages to OnMemoryPressureChanged.
class MemoryPressureMonitorImpl;
#endif
// A thread-safe class for directly querying and automatically monitoring
// memory pressure. When system memory pressure levels change this class is
// responsible for notifying MemoryPressureListeners, and periodically
// renotifying them as conditions persist. This class will do its periodic work
// on the thread on which it was created. However, it is safe to call
// GetCurrentPressureLevel from any thread.
//
// This class doesn't make use of base::RepeatingTimer as it leaves an orphaned
// scheduled task every time it is canceled. This can occur every time a memory
// pressure level transition occurs, which has no strict upper bound. Instead
// a collection of at most "number of memory pressure level" scheduled tasks
// is used, with these tasks being reused as transition levels are crossed and
// polling requirements change. See |scheduled_checks_| and
// "ScheduleTaskIfNeededLocked" for details.
class MemoryPressureMonitor {
public:
using MemoryPressureLevel = MemoryPressureListener::MemoryPressureLevel;
// A simple dispatch delegate as a testing seam. Makes unittests much simpler
// as they don't need to setup a multithreaded environment.
using DispatchCallback = base::Callback<void(MemoryPressureLevel)>;
#if defined(MEMORY_PRESSURE_IS_POLLING)
// The minimum time that must pass between successive polls. This enforces an
// upper bound on the rate of calls to the contained MemoryPressureCalculator.
// 100ms (10Hz) allows a relatively fast respsonse time for rapidly increasing
// memory usage, but limits the amount of work done in the calculator and
// stats collection.
enum : int { kMinimumTimeBetweenSamplesMs = 100 };
// On polling platforms this is required to be somewhat short in order to
// observe memory pressure changes as they occur.
enum : int { kDefaultPollingIntervalMs = 5000 };
#else
// On non-polling platforms this is only required for keeping statistics up to
// date so can be quite a long period.
enum : int { kDefaultPollingIntervalMs = 60000 };
#endif
// Renotification intervals, per pressure level. These are the same on all
// platforms. These act as an upper bound on the polling interval when under
// the corresponding memory pressure.
enum : int { kNotificationIntervalPressureModerateMs = 5000 };
enum : int { kNotificationIntervalPressureCriticalMs = 1000 };
#if defined(MEMORY_PRESSURE_IS_POLLING)
// Fully configurable constructor for polling platforms.
MemoryPressureMonitor(const scoped_refptr<base::TaskRunner>& task_runner,
base::TickClock* tick_clock,
MemoryPressureStatsCollector* stats_collector,
MemoryPressureCalculator* calculator,
const DispatchCallback& dispatch_callback);
#else
// Constructor for non-polling platforms.
MemoryPressureMonitor(const scoped_refptr<base::TaskRunner>& task_runner,
base::TickClock* tick_clock,
MemoryPressureStatsCollector* stats_collector,
const DispatchCallback& dispatch_callback,
MemoryPressureLevel initial_pressure_level);
#endif
~MemoryPressureMonitor();
// Returns the current memory pressure level. On polling platforms this may
// result in a forced calculation of the current pressure level (a cheap
// operation). Can be called from any thread.
MemoryPressureLevel GetCurrentPressureLevel();
// Schedules a memory pressure check to run soon. This can be called from any
// thread.
void CheckMemoryPressureSoon();
private:
// For unittesting.
friend class TestMemoryPressureMonitor;
#if !defined(MEMORY_PRESSURE_IS_POLLING)
// Notifications from the OS will be routed here by the contained
// MemoryPressureMonitorImpl instance. For statistics and renotification to
// work properly this must be notified of all pressure level changes, even
// those indicating a return to a state of no pressure.
void OnMemoryPressureChanged(MemoryPressureLevel level);
#endif
// Starts the memory pressure monitor. To be called in the constructor.
void Start();
// Checks memory pressure and updates stats. This is the entry point for all
// scheduled checks. Each scheduled check is assigned a |serial| id
// (monotonically increasing) which is used to tie the task to the time at
// which it was scheduled via the |scheduled_checks_| map.
void CheckPressureAndUpdateStats(int serial);
void CheckPressureAndUpdateStatsLocked(int serial);
// Ensures that a task is scheduled for renotification/recalculation/stats
// updating. Uses the |serial| id of the current task to determine the time at
// which the next scheduled check should run. Assumes |lock_| is held.
void ScheduleTaskIfNeededLocked(int serial);
// Schedules a task.
void ScheduleTaskLocked(base::TimeTicks when);
// A lock for synchronization.
base::Lock lock_;
// Injected dependencies.
// The task runner on which periodic pressure checks and statistics uploading
// are run.
scoped_refptr<base::TaskRunner> task_runner_;
// The tick clock in use. Used under |lock_|.
base::TickClock* tick_clock_;
// The stats collector in use. Used under |lock_|.
MemoryPressureStatsCollector* stats_collector_;
// The memory pressure calculator in use. Used under |lock_|.
MemoryPressureCalculator* calculator_;
// The dispatch callback to use.
DispatchCallback dispatch_callback_;
#if !defined(MEMORY_PRESSURE_IS_POLLING)
// On non-polling platforms this object is responsible for routing OS
// notifications to OnMemoryPressureChanged, and setting the initial pressure
// value. The OS specific implementation is responsible for allocating this
// object.
std::unique_ptr<MemoryPressureMonitorImpl> monitor_impl_;
#endif
// Object state.
// The pressure level as of the most recent poll or notification. Under
// |lock_|.
MemoryPressureLevel current_memory_pressure_level_;
#if defined(MEMORY_PRESSURE_IS_POLLING)
// Time of the last pressure check. Under |lock_|. Only needed for polling
// implementations.
base::TimeTicks last_check_;
#endif
// Time of the last pressure notification. Under |lock_|.
base::TimeTicks last_notification_;
// A map of scheduled pressure checks/statistics updates and their serial
// numbers. Under |lock_|.
std::map<int, base::TimeTicks> scheduled_checks_;
// The most recently assigned serial number for a pressure check. The number
// 0 will never be assigned but will instead be reserved for unscheduled
// checks initiated externally. Under |lock_|.
int serial_number_;
// Weak pointer factory to ourself used for posting delayed tasks to
// task_runner_.
base::WeakPtrFactory<MemoryPressureMonitor> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(MemoryPressureMonitor);
};
} // namespace memory_pressure
#endif // COMPONENTS_MEMORY_PRESSURE_MEMORY_PRESSURE_MONITOR_H_
// 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/memory_pressure/memory_pressure_stats_collector.h"
#include <stddef.h>
#include <stdint.h>
#include "base/macros.h"
#include "base/metrics/histogram_macros.h"
#include "base/time/tick_clock.h"
namespace memory_pressure {
namespace {
using MemoryPressureLevel = MemoryPressureListener::MemoryPressureLevel;
// Converts a memory pressure level to an UMA enumeration value.
MemoryPressureLevelUMA MemoryPressureLevelToUmaEnumValue(
MemoryPressureLevel level) {
switch (level) {
case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
return UMA_MEMORY_PRESSURE_LEVEL_NONE;
case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
return UMA_MEMORY_PRESSURE_LEVEL_MODERATE;
case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
return UMA_MEMORY_PRESSURE_LEVEL_CRITICAL;
}
NOTREACHED();
return UMA_MEMORY_PRESSURE_LEVEL_NONE;
}
// Converts an UMA enumeration value to a memory pressure level.
MemoryPressureLevel MemoryPressureLevelFromUmaEnumValue(
MemoryPressureLevelUMA level) {
switch (level) {
case UMA_MEMORY_PRESSURE_LEVEL_NONE:
return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
case UMA_MEMORY_PRESSURE_LEVEL_MODERATE:
return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
case UMA_MEMORY_PRESSURE_LEVEL_CRITICAL:
return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
case UMA_MEMORY_PRESSURE_LEVEL_COUNT:
NOTREACHED();
break;
}
NOTREACHED();
return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
}
} // namespace
MemoryPressureStatsCollector::MemoryPressureStatsCollector(
base::TickClock* tick_clock)
: tick_clock_(tick_clock),
last_pressure_level_(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) {
}
void MemoryPressureStatsCollector::UpdateStatistics(
MemoryPressureLevel current_pressure_level) {
base::TimeTicks now = tick_clock_->NowTicks();
// Special case: first call to the collector. Observations have just started
// so there's nothing to report.
if (last_update_time_.is_null()) {
last_pressure_level_ = current_pressure_level;
last_update_time_ = now;
return;
}
// If the pressure level has transitioned then report this.
if (last_pressure_level_ != current_pressure_level)
ReportLevelChange(last_pressure_level_, current_pressure_level);
// Increment the appropriate cumulative bucket.
int index = MemoryPressureLevelToUmaEnumValue(current_pressure_level);
unreported_cumulative_time_[index] += now - last_update_time_;
// Update last pressure related state.
last_pressure_level_ = current_pressure_level;
last_update_time_ = now;
// Make reports about the amount of time spent cumulatively at each level.
for (size_t i = 0; i < arraysize(unreported_cumulative_time_); ++i) {
// Report the largest number of whole seconds possible at this moment and
// carry around the rest for a future report.
if (unreported_cumulative_time_[i].is_zero())
continue;
int64_t seconds = unreported_cumulative_time_[i].InSeconds();
if (seconds == 0)
continue;
unreported_cumulative_time_[i] -= base::TimeDelta::FromSeconds(seconds);
ReportCumulativeTime(MemoryPressureLevelFromUmaEnumValue(
static_cast<MemoryPressureLevelUMA>(i)),
static_cast<int>(seconds));
}
}
// static
void MemoryPressureStatsCollector::ReportCumulativeTime(
MemoryPressureLevel pressure_level,
int seconds) {
// Use the more primitive STATIC_HISTOGRAM_POINTER_BLOCK macro because the
// simple UMA_HISTOGRAM macros don't expose 'AddCount' functionality.
STATIC_HISTOGRAM_POINTER_BLOCK(
"Memory.PressureLevel",
AddCount(MemoryPressureLevelToUmaEnumValue(pressure_level), seconds),
base::LinearHistogram::FactoryGet(
"Memory.PressureLevel", 1, UMA_MEMORY_PRESSURE_LEVEL_COUNT,
UMA_MEMORY_PRESSURE_LEVEL_COUNT + 1,
base::HistogramBase::kUmaTargetedHistogramFlag));
}
// static
void MemoryPressureStatsCollector::ReportLevelChange(
MemoryPressureLevel old_pressure_level,
MemoryPressureLevel new_pressure_level) {
// TODO(chrisha): Report Memory.PressureLevelChange when this code is in use.
}
} // namespace memory_pressure
// 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.
#ifndef COMPONENTS_MEMORY_PRESSURE_MEMORY_PRESSURE_STATS_COLLECTOR_H_
#define COMPONENTS_MEMORY_PRESSURE_MEMORY_PRESSURE_STATS_COLLECTOR_H_
#include "base/macros.h"
#include "base/time/time.h"
#include "components/memory_pressure/memory_pressure_listener.h"
namespace base {
class TickClock;
}
namespace memory_pressure {
// Enumeration of UMA memory pressure levels. The pressure level enum defined in
// MemoryPressureListener is non-contiguous. This enum is a contiguous version
// of that for use with UMA. Both it and histograms.xml must be kept in sync
// with the MemoryPressureListener enum. Included in the header so that
// UMA_MEMORY_PRESSURE_LEVEL_COUNT is available.
enum MemoryPressureLevelUMA {
UMA_MEMORY_PRESSURE_LEVEL_NONE = 0,
UMA_MEMORY_PRESSURE_LEVEL_MODERATE = 1,
UMA_MEMORY_PRESSURE_LEVEL_CRITICAL = 2,
// This must be the last value in the enum.
UMA_MEMORY_PRESSURE_LEVEL_COUNT,
};
// Enumeration of UMA pressure level changes. This needs to be kept in sync
// with histograms.xml and the memory pressure levels defined in
// MemoryPressureListener. Exposed for unittesting.
enum MemoryPressureLevelChangeUMA {
UMA_MEMORY_PRESSURE_LEVEL_CHANGE_NONE_TO_MODERATE = 0,
UMA_MEMORY_PRESSURE_LEVEL_CHANGE_NONE_TO_CRITICAL = 1,
UMA_MEMORY_PRESSURE_LEVEL_CHANGE_MODERATE_TO_CRITICAL = 2,
UMA_MEMORY_PRESSURE_LEVEL_CHANGE_CRITICAL_TO_MODERATE = 3,
UMA_MEMORY_PRESSURE_LEVEL_CHANGE_CRITICAL_TO_NONE = 4,
UMA_MEMORY_PRESSURE_LEVEL_CHANGE_MODERATE_TO_NONE = 5,
// This must be the last value in the enum.
UMA_MEMORY_PRESSURE_LEVEL_CHANGE_COUNT
};
// Class that is responsible for collecting and eventually reporting memory
// pressure statistics. Contributes to the "Memory.PressureLevel" and
// "Memory.PressureLevelChanges" histograms.
//
// On platforms with a polling memory pressure implementation the
// UpdateStatistics function will be invoked every time the pressure is polled.
// On non-polling platforms (Mac, Android) it will be invoked on a periodic
// timer, and at the moment of pressure level changes.
class MemoryPressureStatsCollector {
public:
using MemoryPressureLevel = MemoryPressureListener::MemoryPressureLevel;
// The provided |tick_clock| must outlive this class.
MemoryPressureStatsCollector(base::TickClock* tick_clock);
// This is to be called periodically to ensure that up to date statistics
// have been reported.
void UpdateStatistics(MemoryPressureLevel current_pressure_level);
private:
friend class TestMemoryPressureStatsCollector;
// Helper functions for delivering collected statistics.
static void ReportCumulativeTime(MemoryPressureLevel pressure_level,
int seconds);
static void ReportLevelChange(MemoryPressureLevel old_pressure_level,
MemoryPressureLevel new_pressure_level);
// The tick clock in use. This class is intended to be owned by a class with
// a tick clock, and ownership remains there. Also intended as a test seam.
base::TickClock* tick_clock_;
// Buckets of time that have been spent in different pressure levels, but
// not yet reported. At every call to UpdateStatistics these buckets will be
// drained of as many 'full' seconds of time as possible and reported via
// UMA. The remaining time (< 1000 ms) will be left in the bucket to be rolled
// into a later UMA report.
base::TimeDelta unreported_cumulative_time_[UMA_MEMORY_PRESSURE_LEVEL_COUNT];
// The last observed pressure level and the time at which it was observed, and
// the time when this pressure level started.
MemoryPressureLevel last_pressure_level_;
base::TimeTicks last_update_time_;
DISALLOW_COPY_AND_ASSIGN(MemoryPressureStatsCollector);
};
} // namespace memory_pressure
#endif // COMPONENTS_MEMORY_PRESSURE_MEMORY_PRESSURE_STATS_COLLECTOR_H_
// 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/memory_pressure/memory_pressure_stats_collector.h"
#include "base/test/histogram_tester.h"
#include "base/test/simple_test_tick_clock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace memory_pressure {
namespace {
// Histogram names.
const char kPressureLevel[] = "Memory.PressureLevel";
} // namespace
// Test version of the stats collector with a few extra accessors.
class TestMemoryPressureStatsCollector : public MemoryPressureStatsCollector {
public:
TestMemoryPressureStatsCollector(base::TickClock* tick_clock)
: MemoryPressureStatsCollector(tick_clock) {}
// Accessors.
base::TimeDelta unreported_cumulative_time(int i) const {
return unreported_cumulative_time_[i];
}
MemoryPressureLevel last_pressure_level() const {
return last_pressure_level_;
}
base::TimeTicks last_update_time() const { return last_update_time_; }
};
// Test fixture.
class MemoryPressureStatsCollectorTest : public testing::Test {
public:
MemoryPressureStatsCollectorTest() : collector_(&tick_clock_) {}
void Tick(int ms) {
tick_clock_.Advance(base::TimeDelta::FromMilliseconds(ms));
}
// Validates expectations on the amount of accumulated (and unreported)
// time (milliseconds) per pressure level.
void ExpectAccumulated(int none_ms, int moderate_ms, int critical_ms) {
EXPECT_EQ(base::TimeDelta::FromMilliseconds(none_ms),
collector_.unreported_cumulative_time(0)); // None.
EXPECT_EQ(base::TimeDelta::FromMilliseconds(moderate_ms),
collector_.unreported_cumulative_time(1)); // Moderate.
EXPECT_EQ(base::TimeDelta::FromMilliseconds(critical_ms),
collector_.unreported_cumulative_time(2)); // Critical.
}
// Validates expectations on the amount of reported time (seconds) per
// pressure level.
void ExpectReported(int none_s, int moderate_s, int critical_s) {
int total_s = none_s + moderate_s + critical_s;
// If the histogram should be empty then simply confirm that it doesn't
// yet exist.
if (total_s == 0) {
EXPECT_TRUE(histograms_.GetTotalCountsForPrefix(kPressureLevel).empty());
return;
}
histograms_.ExpectBucketCount(kPressureLevel, 0, none_s); // None.
histograms_.ExpectBucketCount(kPressureLevel, 1, moderate_s); // Moderate.
histograms_.ExpectBucketCount(kPressureLevel, 2, critical_s); // Critical.
histograms_.ExpectTotalCount(kPressureLevel, total_s);
}
base::SimpleTestTickClock tick_clock_;
TestMemoryPressureStatsCollector collector_;
base::HistogramTester histograms_;
};
TEST_F(MemoryPressureStatsCollectorTest, EndToEnd) {
// Upon construction no statistics should yet have been reported.
ExpectAccumulated(0, 0, 0);
ExpectReported(0, 0, 0);
// A first call should not invoke any reporting functions, but it should
// modify member variables.
Tick(500);
collector_.UpdateStatistics(
MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE);
EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE,
collector_.last_pressure_level());
EXPECT_EQ(tick_clock_.NowTicks(), collector_.last_update_time());
ExpectAccumulated(0, 0, 0);
ExpectReported(0, 0, 0);
// A subsequent call with the same pressure level should increment the
// cumulative time but not make a report, as less than one second of time
// has been accumulated.
Tick(500);
collector_.UpdateStatistics(
MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE);
EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE,
collector_.last_pressure_level());
EXPECT_EQ(tick_clock_.NowTicks(), collector_.last_update_time());
ExpectAccumulated(500, 0, 0);
ExpectReported(0, 0, 0);
// Yet another call and this time a report should be made, as one second
// of time has been accumulated. 500ms should remain unreported.
Tick(1000);
collector_.UpdateStatistics(
MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE);
EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE,
collector_.last_pressure_level());
EXPECT_EQ(tick_clock_.NowTicks(), collector_.last_update_time());
ExpectAccumulated(500, 0, 0);
ExpectReported(1, 0, 0);
// A subsequent call with a different pressure level should increment the
// cumulative time and make several reports.
Tick(2250);
collector_.UpdateStatistics(
MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
collector_.last_pressure_level());
EXPECT_EQ(tick_clock_.NowTicks(), collector_.last_update_time());
ExpectAccumulated(500, 250, 0);
ExpectReported(1, 2, 0);
}
} // namespace memory_pressure
// 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/memory_pressure/test_memory_pressure_calculator.h"
namespace memory_pressure {
#if defined(MEMORY_PRESSURE_IS_POLLING)
TestMemoryPressureCalculator::TestMemoryPressureCalculator()
: level_(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE), calls_(0) {}
TestMemoryPressureCalculator::TestMemoryPressureCalculator(
MemoryPressureLevel level)
: level_(level), calls_(0) {}
TestMemoryPressureCalculator::MemoryPressureLevel
TestMemoryPressureCalculator::CalculateCurrentPressureLevel() {
++calls_;
return level_;
}
void TestMemoryPressureCalculator::SetLevel(MemoryPressureLevel level) {
level_ = level;
}
void TestMemoryPressureCalculator::SetNone() {
level_ = MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
}
void TestMemoryPressureCalculator::SetModerate() {
level_ = MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
}
void TestMemoryPressureCalculator::SetCritical() {
level_ = MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
}
void TestMemoryPressureCalculator::ResetCalls() {
calls_ = 0;
}
#endif // defined(MEMORY_PRESSURE_IS_POLLING)
} // namespace memory_pressure
// 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.
#ifndef COMPONENTS_MEMORY_PRESSURE_TEST_MEMORY_PRESSURE_CALCULATOR_H_
#define COMPONENTS_MEMORY_PRESSURE_TEST_MEMORY_PRESSURE_CALCULATOR_H_
#include "base/macros.h"
#include "components/memory_pressure/memory_pressure_calculator.h"
namespace memory_pressure {
#if defined(MEMORY_PRESSURE_IS_POLLING)
// A mock memory pressure calculator for unittesting.
class TestMemoryPressureCalculator : public MemoryPressureCalculator {
public:
// Defaults to no pressure.
TestMemoryPressureCalculator();
explicit TestMemoryPressureCalculator(MemoryPressureLevel level);
~TestMemoryPressureCalculator() override {}
// MemoryPressureCalculator implementation.
MemoryPressureLevel CalculateCurrentPressureLevel() override;
// Sets the mock calculator to return the given pressure level.
void SetLevel(MemoryPressureLevel level);
// Sets the mock calculator to return no pressure.
void SetNone();
// Sets the mock calculator to return moderate pressure.
void SetModerate();
// Sets the mock calculator to return critical pressure.
void SetCritical();
// Resets the call counter to 'CalculateCurrentPressureLevel'.
void ResetCalls();
// Returns the number of calls to 'CalculateCurrentPressureLevel'.
int calls() const { return calls_; }
private:
MemoryPressureLevel level_;
int calls_;
DISALLOW_COPY_AND_ASSIGN(TestMemoryPressureCalculator);
};
#endif // defined(MEMORY_PRESSURE_IS_POLLING)
} // namespace memory_pressure
#endif // COMPONENTS_MEMORY_PRESSURE_TEST_MEMORY_PRESSURE_CALCULATOR_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment