blob: 1878d8887c02f6110a932148c3a91a1940d25796 [file] [log] [blame]
// Copyright 2020 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 "base/util/memory_pressure/system_memory_pressure_evaluator_linux.h"
#include "base/bind.h"
#include "base/process/process_metrics.h"
#include "base/single_thread_task_runner.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/time/time.h"
#include "base/util/memory_pressure/multi_source_memory_pressure_monitor.h"
namespace {
constexpr int kKiBperMiB = 1024;
int GetAvailableSystemMemoryMiB(const base::SystemMemoryInfoKB& mem_info) {
// Use 'available' metric if is is present,
// if no (kernels < 3.14), let's make a rough evaluation using free physical
// memory plus buffers and caches (that OS can free in case of low memory
// state)
int mem_available =
mem_info.available ? mem_info.available
: (mem_info.free + mem_info.buffers + mem_info.cached);
// How much physical memory is actively available for use right now, in MBs.
return mem_available / kKiBperMiB;
}
} // namespace
namespace util {
namespace os_linux {
const base::TimeDelta SystemMemoryPressureEvaluator::kModeratePressureCooldown =
base::TimeDelta::FromSeconds(10);
const int SystemMemoryPressureEvaluator::kDefaultModerateThresholdPc = 75;
const int SystemMemoryPressureEvaluator::kDefaultCriticalThresholdPc = 85;
SystemMemoryPressureEvaluator::SystemMemoryPressureEvaluator(
std::unique_ptr<MemoryPressureVoter> voter)
: util::SystemMemoryPressureEvaluator(std::move(voter)),
moderate_pressure_repeat_count_(0) {
if (InferThresholds())
StartObserving();
}
SystemMemoryPressureEvaluator::SystemMemoryPressureEvaluator(
int moderate_threshold_mb,
int critical_threshold_mb,
std::unique_ptr<MemoryPressureVoter> voter)
: util::SystemMemoryPressureEvaluator(std::move(voter)),
moderate_threshold_mb_(moderate_threshold_mb),
critical_threshold_mb_(critical_threshold_mb),
moderate_pressure_repeat_count_(0) {
DCHECK_GE(moderate_threshold_mb_, critical_threshold_mb_);
DCHECK_GT(critical_threshold_mb_, 0);
StartObserving();
}
void SystemMemoryPressureEvaluator::StartObserving() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
timer_.Start(
FROM_HERE, base::MemoryPressureMonitor::kUMAMemoryPressureLevelPeriod,
base::BindRepeating(&SystemMemoryPressureEvaluator::CheckMemoryPressure,
base::Unretained(this)));
}
void SystemMemoryPressureEvaluator::StopObserving() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// If StartObserving failed, StopObserving will still get called.
timer_.Stop();
}
bool SystemMemoryPressureEvaluator::GetSystemMemoryInfo(
base::SystemMemoryInfoKB* mem_info) {
return base::GetSystemMemoryInfo(mem_info);
}
void SystemMemoryPressureEvaluator::CheckMemoryPressure() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Get the previous pressure level and update the current one.
MemoryPressureLevel old_vote = current_vote();
SetCurrentVote(CalculateCurrentPressureLevel());
// |notify| will be set to true if MemoryPressureListeners need to be
// notified of a memory pressure level state change.
bool notify = false;
switch (current_vote()) {
case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
break;
case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
if (old_vote != current_vote()) {
// This is a new transition to moderate pressure so notify.
moderate_pressure_repeat_count_ = 0;
notify = true;
} else {
// Already in moderate pressure, only notify if sustained over the
// cooldown period.
const int kModeratePressureCooldownCycles =
kModeratePressureCooldown /
base::MemoryPressureMonitor::kUMAMemoryPressureLevelPeriod;
if (++moderate_pressure_repeat_count_ ==
kModeratePressureCooldownCycles) {
moderate_pressure_repeat_count_ = 0;
notify = true;
}
}
break;
case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
// Always notify of critical pressure levels.
notify = true;
break;
}
SendCurrentVote(notify);
}
bool SystemMemoryPressureEvaluator::InferThresholds() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::SystemMemoryInfoKB mem_info;
if (!GetSystemMemoryInfo(&mem_info))
return false;
critical_threshold_mb_ =
mem_info.total * (100 - kDefaultCriticalThresholdPc) / 100 / kKiBperMiB;
moderate_threshold_mb_ =
mem_info.total * (100 - kDefaultModerateThresholdPc) / 100 / kKiBperMiB;
return true;
}
base::MemoryPressureListener::MemoryPressureLevel
SystemMemoryPressureEvaluator::CalculateCurrentPressureLevel() {
base::SystemMemoryInfoKB mem_info;
if (GetSystemMemoryInfo(&mem_info)) {
// How much system memory is actively available for use right now, in MBs.
int available = GetAvailableSystemMemoryMiB(mem_info);
// Determine if the available memory is under critical memory pressure.
if (available <= critical_threshold_mb_)
return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
// Determine if the available memory is under moderate memory pressure.
if (available <= moderate_threshold_mb_)
return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
}
// No memory pressure was detected.
return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
}
} // namespace os_linux
} // namespace util