// 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 "chromecast/browser/cast_system_memory_pressure_evaluator.h" #include #include "base/bind.h" #include "base/command_line.h" #include "base/location.h" #include "base/logging.h" #include "base/process/process_metrics.h" #include "base/strings/string_number_conversions.h" #include "base/task/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "chromecast/base/chromecast_switches.h" #include "chromecast/base/metrics/cast_metrics_helper.h" #define MAKE_SURE_THREAD(callback, ...) \ if (!task_runner_->BelongsToCurrentThread()) { \ task_runner_->PostTask( \ FROM_HERE, \ base::BindOnce(&CastSystemMemoryPressureEvaluator::callback, \ weak_ptr_factory_.GetWeakPtr(), ##__VA_ARGS__)); \ return; \ } namespace chromecast { namespace { // Memory thresholds (as fraction of total memory) for memory pressure levels. // See more detailed description of pressure heuristic in PollPressureLevel. // TODO(halliwell): tune thresholds based on data. constexpr float kCriticalMemoryFraction = 0.25f; constexpr float kModerateMemoryFraction = 0.4f; // Default Relaxed memory thresholds selectively applied to a few apps. constexpr float kRelaxedCriticalMemoryFraction = 0.1f; constexpr float kRelaxedModerateMemoryFraction = 0.2f; // Memory thresholds in MB for the simple heuristic based on 'free' memory. constexpr int kCriticalFreeMemoryKB = 20 * 1024; constexpr int kModerateFreeMemoryKB = 30 * 1024; constexpr int kPollingIntervalMS = 5000; int GetSystemReservedKb() { int rtn_kb_ = 0; const base::CommandLine* command_line(base::CommandLine::ForCurrentProcess()); base::StringToInt( command_line->GetSwitchValueASCII(switches::kMemPressureSystemReservedKb), &rtn_kb_); DCHECK(rtn_kb_ >= 0); return std::max(rtn_kb_, 0); } } // namespace CastSystemMemoryPressureEvaluator::CastSystemMemoryPressureEvaluator( std::unique_ptr voter) : memory_pressure::SystemMemoryPressureEvaluator(std::move(voter)), critical_memory_fraction_command_line_( GetSwitchValueDouble(switches::kCastMemoryPressureCriticalFraction, -1.0f)), moderate_memory_fraction_command_line_( GetSwitchValueDouble(switches::kCastMemoryPressureModerateFraction, -1.0f)), system_reserved_kb_(GetSystemReservedKb()), task_runner_(base::ThreadTaskRunnerHandle::Get()), weak_ptr_factory_(this) { relaxed_critical_memory_fraction_ = kRelaxedCriticalMemoryFraction; relaxed_moderate_memory_fraction_ = kRelaxedModerateMemoryFraction; critical_memory_fraction_ = critical_memory_fraction_command_line_; moderate_memory_fraction_ = moderate_memory_fraction_command_line_; // If the fractions from command line parameters are invalid they are subject // to adjustment. AdjustMemoryFractions(false); PollPressureLevel(); } CastSystemMemoryPressureEvaluator::~CastSystemMemoryPressureEvaluator() = default; void CastSystemMemoryPressureEvaluator::PollPressureLevel() { DCHECK(task_runner_->BelongsToCurrentThread()); base::MemoryPressureListener::MemoryPressureLevel level = base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; base::SystemMemoryInfoKB info; if (!base::GetSystemMemoryInfo(&info)) { LOG(ERROR) << "GetSystemMemoryInfo failed"; } else if (system_reserved_kb_ != 0 || info.available != 0) { // Preferred memory pressure heuristic: // 1. Use /proc/meminfo's MemAvailable if possible, fall back to estimate // of free + buffers + cached otherwise. const int total_available = (info.available != 0) ? info.available : (info.free + info.buffers + info.cached); // 2. Allow some memory to be 'reserved' on command line. const int available = total_available - system_reserved_kb_; const int total = info.total - system_reserved_kb_; DCHECK_GT(total, 0); const float ratio = available / static_cast(total); if (ratio < critical_memory_fraction_) level = base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL; else if (ratio < moderate_memory_fraction_) level = base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE; } else { // Backup method purely using 'free' memory. It may generate more // pressure events than necessary, since more memory may actually be free. if (info.free < kCriticalFreeMemoryKB) level = base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL; else if (info.free < kModerateFreeMemoryKB) level = base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE; } UpdateMemoryPressureLevel(level); task_runner_->PostDelayedTask( FROM_HERE, base::BindOnce(&CastSystemMemoryPressureEvaluator::PollPressureLevel, weak_ptr_factory_.GetWeakPtr()), base::Milliseconds(kPollingIntervalMS)); } void CastSystemMemoryPressureEvaluator::UpdateMemoryPressureLevel( base::MemoryPressureListener::MemoryPressureLevel new_level) { DCHECK(task_runner_->BelongsToCurrentThread()); auto old_vote = current_vote(); SetCurrentVote(new_level); SendCurrentVote(/* notify = */ current_vote() != base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE); if (old_vote == current_vote()) return; metrics::CastMetricsHelper::GetInstance()->RecordApplicationEventWithValue( "Memory.Pressure.LevelChange", new_level); } void CastSystemMemoryPressureEvaluator::AdjustMemoryFractions(bool relax) { DCHECK(task_runner_->BelongsToCurrentThread()); if (critical_memory_fraction_command_line_ < 0) { critical_memory_fraction_ = relax ? relaxed_critical_memory_fraction_ : kCriticalMemoryFraction; } if (moderate_memory_fraction_command_line_ < 0) { moderate_memory_fraction_ = relax ? relaxed_moderate_memory_fraction_ : kModerateMemoryFraction; } LOG(INFO) << __func__ << ": critical_memory_fraction_=" << critical_memory_fraction_ << ", moderate_memory_fraction_=" << moderate_memory_fraction_; } void CastSystemMemoryPressureEvaluator::ConfigRelaxMemoryPressureThresholds( float relaxed_critical_memory_fraction, float relaxed_moderate_memory_fraction) { MAKE_SURE_THREAD(ConfigRelaxMemoryPressureThresholds, relaxed_critical_memory_fraction, relaxed_moderate_memory_fraction); LOG(INFO) << __func__ << ", " << relaxed_critical_memory_fraction << ", " << relaxed_moderate_memory_fraction; if (relaxed_critical_memory_fraction > 0) { relaxed_critical_memory_fraction_ = relaxed_critical_memory_fraction; } if (relaxed_moderate_memory_fraction > 0) { relaxed_moderate_memory_fraction_ = relaxed_moderate_memory_fraction; } } void CastSystemMemoryPressureEvaluator::RelaxMemoryPressureThresholds( std::string requesting_app_session_id) { MAKE_SURE_THREAD(RelaxMemoryPressureThresholds, std::move(requesting_app_session_id)); apps_needing_relaxed_memory_pressure_thresholds_.insert( std::move(requesting_app_session_id)); AdjustMemoryFractions(true); } void CastSystemMemoryPressureEvaluator::RestoreMemoryPressureThresholds( const std::string& requesting_app_session_id) { MAKE_SURE_THREAD(RestoreMemoryPressureThresholds, requesting_app_session_id); apps_needing_relaxed_memory_pressure_thresholds_.erase( requesting_app_session_id); if (apps_needing_relaxed_memory_pressure_thresholds_.empty()) { AdjustMemoryFractions(false); } } } // namespace chromecast