// 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 "components/memory_pressure/system_memory_pressure_evaluator_linux.h" #include "base/bind.h" #include "base/numerics/safe_conversions.h" #include "base/process/process_metrics.h" #include "base/task/single_thread_task_runner.h" #include "base/task/task_traits.h" #include "base/threading/sequenced_task_runner_handle.h" #include "base/time/time.h" #include "components/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 memory_pressure { namespace os_linux { const base::TimeDelta SystemMemoryPressureEvaluator::kMemorySamplingPeriod = base::Seconds(5); const base::TimeDelta SystemMemoryPressureEvaluator::kModeratePressureCooldown = base::Seconds(10); const int SystemMemoryPressureEvaluator::kDefaultModerateThresholdPc = 75; const int SystemMemoryPressureEvaluator::kDefaultCriticalThresholdPc = 85; SystemMemoryPressureEvaluator::SystemMemoryPressureEvaluator( std::unique_ptr voter) : memory_pressure::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 voter) : memory_pressure::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, kMemorySamplingPeriod, 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 / kMemorySamplingPeriod; 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; // The computation of the different thresholds assumes that // SystemMemoryInfoKB::total is stored as an integer and so the result of // |static_cast(mem_info.total) * (100 - kThresholdPc)| won't // overflow. static_assert( std::is_same::value, "SystemMemoryInfoKB::total is expected to be stored as an integer."); critical_threshold_mb_ = base::checked_cast( static_cast(mem_info.total) * (100 - kDefaultCriticalThresholdPc) / 100 / kKiBperMiB); moderate_threshold_mb_ = base::checked_cast( static_cast(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 memory_pressure