Index: chrome/browser/memory/tab_manager_delegate_chromeos.cc |
diff --git a/chrome/browser/memory/tab_manager_delegate_chromeos.cc b/chrome/browser/memory/tab_manager_delegate_chromeos.cc |
deleted file mode 100644 |
index 5a075394a8875665e907d7082db72e3822fdb587..0000000000000000000000000000000000000000 |
--- a/chrome/browser/memory/tab_manager_delegate_chromeos.cc |
+++ /dev/null |
@@ -1,776 +0,0 @@ |
-// 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 "chrome/browser/memory/tab_manager_delegate_chromeos.h" |
- |
-#include <math.h> |
-#include <stdint.h> |
- |
-#include <algorithm> |
-#include <map> |
-#include <vector> |
- |
-#include "ash/shell.h" |
-#include "base/bind.h" |
-#include "base/command_line.h" |
-#include "base/files/file_path.h" |
-#include "base/files/file_util.h" |
-#include "base/memory/memory_pressure_monitor_chromeos.h" |
-#include "base/metrics/histogram_macros.h" |
-#include "base/process/process_handle.h" // kNullProcessHandle. |
-#include "base/process/process_metrics.h" |
-#include "base/strings/string16.h" |
-#include "base/strings/string_number_conversions.h" |
-#include "base/strings/string_util.h" |
-#include "base/strings/utf_string_conversions.h" |
-#include "base/time/time.h" |
-#include "chrome/browser/chromeos/arc/process/arc_process.h" |
-#include "chrome/browser/chromeos/arc/process/arc_process_service.h" |
-#include "chrome/browser/memory/memory_kills_monitor.h" |
-#include "chrome/browser/memory/tab_stats.h" |
-#include "chrome/browser/ui/browser.h" |
-#include "chrome/browser/ui/browser_list.h" |
-#include "chrome/browser/ui/tabs/tab_strip_model.h" |
-#include "chrome/common/chrome_constants.h" |
-#include "chrome/common/chrome_features.h" |
-#include "chromeos/dbus/dbus_thread_manager.h" |
-#include "components/arc/arc_bridge_service.h" |
-#include "components/arc/arc_service_manager.h" |
-#include "components/arc/arc_util.h" |
-#include "components/device_event_log/device_event_log.h" |
-#include "content/public/browser/browser_thread.h" |
-#include "content/public/browser/notification_service.h" |
-#include "content/public/browser/notification_types.h" |
-#include "content/public/browser/render_process_host.h" |
-#include "content/public/browser/render_widget_host.h" |
-#include "content/public/browser/zygote_host_linux.h" |
-#include "ui/wm/public/activation_client.h" |
- |
-using base::ProcessHandle; |
-using base::TimeDelta; |
-using base::TimeTicks; |
-using content::BrowserThread; |
- |
-namespace memory { |
-namespace { |
- |
-// When switching to a new tab the tab's renderer's OOM score needs to be |
-// updated to reflect its front-most status and protect it from discard. |
-// However, doing this immediately might slow down tab switch time, so wait |
-// a little while before doing the adjustment. |
-const int kFocusedProcessScoreAdjustIntervalMs = 500; |
- |
-wm::ActivationClient* GetActivationClient() { |
- if (!ash::Shell::HasInstance()) |
- return nullptr; |
- return wm::GetActivationClient(ash::Shell::GetPrimaryRootWindow()); |
-} |
- |
-bool IsArcMemoryManagementEnabled() { |
- return base::FeatureList::IsEnabled(features::kArcMemoryManagement); |
-} |
- |
-void OnSetOomScoreAdj(bool success, const std::string& output) { |
- VLOG(2) << "OnSetOomScoreAdj " << success << " " << output; |
- if (!success) |
- LOG(ERROR) << "Set OOM score error: " << output; |
- else if (!output.empty()) |
- LOG(WARNING) << "Set OOM score: " << output; |
-} |
- |
-} // namespace |
- |
-// static |
-const int TabManagerDelegate::kLowestOomScore = -1000; |
- |
-std::ostream& operator<<(std::ostream& os, const ProcessType& type) { |
- switch (type) { |
- case ProcessType::FOCUSED_TAB: |
- return os << "FOCUSED_TAB"; |
- case ProcessType::FOCUSED_APP: |
- return os << "FOCUSED_APP"; |
- case ProcessType::IMPORTANT_APP: |
- return os << "IMPORTANT_APP"; |
- case ProcessType::BACKGROUND_TAB: |
- return os << "BACKGROUND_TAB"; |
- case ProcessType::BACKGROUND_APP: |
- return os << "BACKGROUND_APP"; |
- case ProcessType::UNKNOWN_TYPE: |
- return os << "UNKNOWN_TYPE"; |
- default: |
- return os << "NOT_IMPLEMENTED_ERROR"; |
- } |
- return os; |
-} |
- |
-// TabManagerDelegate::Candidate implementation. |
-std::ostream& operator<<( |
- std::ostream& out, const TabManagerDelegate::Candidate& candidate) { |
- if (candidate.app()) { |
- out << "app " << *candidate.app(); |
- } else if (candidate.tab()) { |
- const TabStats* const& tab = candidate.tab(); |
- out << "tab " << tab->title << ", renderer_handle: " << tab->renderer_handle |
- << ", oom_score: " << tab->oom_score |
- << ", is_discarded: " << tab->is_discarded |
- << ", discard_count: " << tab->discard_count |
- << ", last_active: " << tab->last_active; |
- } |
- out << ", process_type " << candidate.process_type(); |
- return out; |
-} |
- |
-TabManagerDelegate::Candidate& TabManagerDelegate::Candidate::operator=( |
- TabManagerDelegate::Candidate&& other) { |
- tab_ = other.tab_; |
- app_ = other.app_; |
- process_type_ = other.process_type_; |
- return *this; |
-} |
- |
-bool TabManagerDelegate::Candidate::operator<( |
- const TabManagerDelegate::Candidate& rhs) const { |
- if (process_type() != rhs.process_type()) |
- return process_type() < rhs.process_type(); |
- if (app() && rhs.app()) |
- return *app() < *rhs.app(); |
- if (tab() && rhs.tab()) |
- return TabManager::CompareTabStats(*tab(), *rhs.tab()); |
- // Impossible case. If app and tab are mixed in one process type, favor |
- // apps. |
- NOTREACHED() << "Undefined comparison between apps and tabs: process_type=" |
- << process_type(); |
- return app(); |
-} |
- |
-ProcessType TabManagerDelegate::Candidate::GetProcessTypeInternal() const { |
- if (app()) { |
- if (app()->is_focused()) |
- return ProcessType::FOCUSED_APP; |
- if (app()->IsImportant()) |
- return ProcessType::IMPORTANT_APP; |
- return ProcessType::BACKGROUND_APP; |
- } |
- if (tab()) { |
- if (tab()->is_selected) |
- return ProcessType::FOCUSED_TAB; |
- return ProcessType::BACKGROUND_TAB; |
- } |
- NOTREACHED() << "Unexpected process type"; |
- return ProcessType::UNKNOWN_TYPE; |
-} |
- |
-// Holds the info of a newly focused tab or app window. The focused process is |
-// set to highest priority (lowest OOM score), but not immediately. To avoid |
-// redundant settings the OOM score adjusting only happens after a timeout. If |
-// the process loses focus before the timeout, the adjustment is canceled. |
-class TabManagerDelegate::FocusedProcess { |
- public: |
- static const int kInvalidArcAppNspid = 0; |
- |
- FocusedProcess() { Reset(); } |
- |
- void SetTabPid(const base::ProcessHandle pid) { |
- pid_ = pid; |
- nspid_ = kInvalidArcAppNspid; |
- } |
- |
- void SetArcAppNspid(const int nspid) { |
- pid_ = base::kNullProcessHandle; |
- nspid_ = nspid; |
- } |
- |
- base::ProcessHandle GetTabPid() const { return pid_; } |
- |
- int GetArcAppNspid() const { return nspid_; } |
- |
- // Checks whether the containing instance is an ARC app. If so it resets the |
- // data and returns true. Useful when canceling an ongoing OOM score setting |
- // for a focused ARC app because the focus has been shifted away shortly. |
- bool ResetIfIsArcApp() { |
- if (nspid_ != kInvalidArcAppNspid) { |
- Reset(); |
- return true; |
- } |
- return false; |
- } |
- |
- private: |
- void Reset() { |
- pid_ = base::kNullProcessHandle; |
- nspid_ = kInvalidArcAppNspid; |
- } |
- |
- // The focused app could be a Chrome tab or an Android app, but not both. |
- // At most one of them contains a valid value at any time. |
- |
- // If a chrome tab. |
- base::ProcessHandle pid_; |
- // If an Android app. |
- int nspid_; |
-}; |
- |
-// TabManagerDelegate::MemoryStat implementation. |
- |
-// static |
-int TabManagerDelegate::MemoryStat::ReadIntFromFile( |
- const char* file_name, const int default_val) { |
- std::string file_string; |
- if (!base::ReadFileToString(base::FilePath(file_name), &file_string)) { |
- LOG(WARNING) << "Unable to read file" << file_name; |
- return default_val; |
- } |
- int val = default_val; |
- if (!base::StringToInt( |
- base::TrimWhitespaceASCII(file_string, base::TRIM_TRAILING), |
- &val)) { |
- LOG(WARNING) << "Unable to parse string" << file_string; |
- return default_val; |
- } |
- return val; |
-} |
- |
-// static |
-int TabManagerDelegate::MemoryStat::LowMemoryMarginKB() { |
- static const int kDefaultLowMemoryMarginMb = 50; |
- static const char kLowMemoryMarginConfig[] = |
- "/sys/kernel/mm/chromeos-low_mem/margin"; |
- return ReadIntFromFile( |
- kLowMemoryMarginConfig, kDefaultLowMemoryMarginMb) * 1024; |
-} |
- |
-// The logic of available memory calculation is copied from |
-// _is_low_mem_situation() in kernel file include/linux/low-mem-notify.h. |
-// Maybe we should let kernel report the number directly. |
-int TabManagerDelegate::MemoryStat::TargetMemoryToFreeKB() { |
- static const int kRamVsSwapWeight = 4; |
- static const char kMinFilelistConfig[] = "/proc/sys/vm/min_filelist_kbytes"; |
- static const char kMinFreeKbytes[] = "/proc/sys/vm/min_free_kbytes"; |
- |
- base::SystemMemoryInfoKB system_mem; |
- base::GetSystemMemoryInfo(&system_mem); |
- const int file_mem_kb = system_mem.active_file + system_mem.inactive_file; |
- const int min_filelist_kb = ReadIntFromFile(kMinFilelistConfig, 0); |
- const int min_free_kb = ReadIntFromFile(kMinFreeKbytes, 0); |
- // Calculate current available memory in system. |
- // File-backed memory should be easy to reclaim, unless they're dirty. |
- // TODO(cylee): On ChromeOS, kernel reports low memory condition when |
- // available memory is low. The following formula duplicates the logic in |
- // kernel to calculate how much memory should be released. In the future, |
- // kernel should try to report the amount of memory to release directly to |
- // eliminate the duplication here. |
- const int available_mem_kb = system_mem.free + |
- file_mem_kb - system_mem.dirty - min_filelist_kb + |
- system_mem.swap_free / kRamVsSwapWeight - |
- min_free_kb; |
- |
- return LowMemoryMarginKB() - available_mem_kb; |
-} |
- |
-int TabManagerDelegate::MemoryStat::EstimatedMemoryFreedKB( |
- base::ProcessHandle pid) { |
- std::unique_ptr<base::ProcessMetrics> process_metrics( |
- base::ProcessMetrics::CreateProcessMetrics(pid)); |
- base::WorkingSetKBytes mem_usage; |
- process_metrics->GetWorkingSetKBytes(&mem_usage); |
- return mem_usage.priv; |
-} |
- |
-TabManagerDelegate::TabManagerDelegate( |
- const base::WeakPtr<TabManager>& tab_manager) |
- : TabManagerDelegate(tab_manager, new MemoryStat()) { |
-} |
- |
-TabManagerDelegate::TabManagerDelegate( |
- const base::WeakPtr<TabManager>& tab_manager, |
- TabManagerDelegate::MemoryStat* mem_stat) |
- : tab_manager_(tab_manager), |
- focused_process_(new FocusedProcess()), |
- mem_stat_(mem_stat), |
- weak_ptr_factory_(this) { |
- registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, |
- content::NotificationService::AllBrowserContextsAndSources()); |
- registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, |
- content::NotificationService::AllBrowserContextsAndSources()); |
- registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, |
- content::NotificationService::AllBrowserContextsAndSources()); |
- auto* activation_client = GetActivationClient(); |
- if (activation_client) |
- activation_client->AddObserver(this); |
- BrowserList::GetInstance()->AddObserver(this); |
-} |
- |
-TabManagerDelegate::~TabManagerDelegate() { |
- BrowserList::GetInstance()->RemoveObserver(this); |
- auto* activation_client = GetActivationClient(); |
- if (activation_client) |
- activation_client->RemoveObserver(this); |
-} |
- |
-void TabManagerDelegate::OnBrowserSetLastActive(Browser* browser) { |
- // Set OOM score to the selected tab when a browser window is activated. |
- // content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED didn't catch the |
- // case (like when switching focus between 2 browser windows) so we need to |
- // handle it here. |
- TabStripModel* tab_strip_model = browser->tab_strip_model(); |
- int selected_index = tab_strip_model->active_index(); |
- content::WebContents* contents = |
- tab_strip_model->GetWebContentsAt(selected_index); |
- if (!contents) |
- return; |
- |
- base::ProcessHandle pid = contents->GetRenderProcessHost()->GetHandle(); |
- AdjustFocusedTabScore(pid); |
-} |
- |
-void TabManagerDelegate::OnWindowActivated( |
- wm::ActivationChangeObserver::ActivationReason reason, |
- aura::Window* gained_active, |
- aura::Window* lost_active) { |
- if (arc::IsArcAppWindow(gained_active)) { |
- // Currently there is no way to know which app is displayed in the ARC |
- // window, so schedule an early adjustment for all processes to reflect |
- // the change. |
- // Put a dummy FocusedProcess with nspid = kInvalidArcAppNspid for now to |
- // indicate the focused process is an arc app. |
- // TODO(cylee): Fix it when we have nspid info in ARC windows. |
- focused_process_->SetArcAppNspid(FocusedProcess::kInvalidArcAppNspid); |
- // If the timer is already running (possibly for a tab), it'll be reset |
- // here. |
- focus_process_score_adjust_timer_.Start( |
- FROM_HERE, |
- TimeDelta::FromMilliseconds(kFocusedProcessScoreAdjustIntervalMs), |
- this, &TabManagerDelegate::ScheduleEarlyOomPrioritiesAdjustment); |
- } |
- if (arc::IsArcAppWindow(lost_active)) { |
- // Do not bother adjusting OOM score if the ARC window is deactivated |
- // shortly. |
- if (focused_process_->ResetIfIsArcApp() && |
- focus_process_score_adjust_timer_.IsRunning()) |
- focus_process_score_adjust_timer_.Stop(); |
- } |
-} |
- |
-void TabManagerDelegate::ScheduleEarlyOomPrioritiesAdjustment() { |
- DCHECK_CURRENTLY_ON(BrowserThread::UI); |
- if (tab_manager_) { |
- AdjustOomPriorities(tab_manager_->GetUnsortedTabStats()); |
- } |
-} |
- |
-// If able to get the list of ARC procsses, prioritize tabs and apps as a whole. |
-// Otherwise try to kill tabs only. |
-void TabManagerDelegate::LowMemoryKill( |
- const TabStatsList& tab_list) { |
- arc::ArcProcessService* arc_process_service = arc::ArcProcessService::Get(); |
- if (arc_process_service && |
- arc_process_service->RequestAppProcessList( |
- base::Bind(&TabManagerDelegate::LowMemoryKillImpl, |
- weak_ptr_factory_.GetWeakPtr(), tab_list))) { |
- // LowMemoryKillImpl will be called asynchronously so nothing left to do. |
- return; |
- } |
- // If the list of ARC processes is not available, call LowMemoryKillImpl |
- // synchronously with an empty list of apps. |
- std::vector<arc::ArcProcess> dummy_apps; |
- LowMemoryKillImpl(tab_list, dummy_apps); |
-} |
- |
-int TabManagerDelegate::GetCachedOomScore(ProcessHandle process_handle) { |
- DCHECK_CURRENTLY_ON(BrowserThread::UI); |
- auto it = oom_score_map_.find(process_handle); |
- if (it != oom_score_map_.end()) { |
- return it->second; |
- } |
- // An impossible value for oom_score_adj. |
- return -1001; |
-} |
- |
-void TabManagerDelegate::OnFocusTabScoreAdjustmentTimeout() { |
- DCHECK_CURRENTLY_ON(BrowserThread::UI); |
- base::ProcessHandle pid = focused_process_->GetTabPid(); |
- // The focused process doesn't render a tab. Could happen when the focus |
- // just switched to an ARC app before the timeout. We can not avoid the race. |
- if (pid == base::kNullProcessHandle) |
- return; |
- |
- // Update the OOM score cache. |
- oom_score_map_[pid] = chrome::kLowestRendererOomScore; |
- |
- // Sets OOM score. |
- VLOG(3) << "Set OOM score " << chrome::kLowestRendererOomScore |
- << " for focused tab " << pid; |
- std::map<int, int> dict; |
- dict[pid] = chrome::kLowestRendererOomScore; |
- GetDebugDaemonClient()->SetOomScoreAdj(dict, base::Bind(&OnSetOomScoreAdj)); |
-} |
- |
-void TabManagerDelegate::AdjustFocusedTabScore(base::ProcessHandle pid) { |
- DCHECK_CURRENTLY_ON(BrowserThread::UI); |
- |
- // Clear running timer if one was set for a previous focused tab/app. |
- if (focus_process_score_adjust_timer_.IsRunning()) |
- focus_process_score_adjust_timer_.Stop(); |
- focused_process_->SetTabPid(pid); |
- |
- // If the currently focused tab already has a lower score, do not |
- // set it. This can happen in case the newly focused tab is script |
- // connected to the previous tab. |
- ProcessScoreMap::iterator it = oom_score_map_.find(pid); |
- const bool not_lowest_score = (it == oom_score_map_.end() || |
- it->second != chrome::kLowestRendererOomScore); |
- |
- if (not_lowest_score) { |
- // By starting a timer we guarantee that the tab is focused for |
- // certain amount of time. Secondly, it also does not add overhead |
- // to the tab switching time. |
- // If there's an existing running timer (could be for ARC app), it |
- // would be replaced by a new task. |
- focus_process_score_adjust_timer_.Start( |
- FROM_HERE, |
- TimeDelta::FromMilliseconds(kFocusedProcessScoreAdjustIntervalMs), |
- this, &TabManagerDelegate::OnFocusTabScoreAdjustmentTimeout); |
- } |
-} |
- |
-void TabManagerDelegate::Observe(int type, |
- const content::NotificationSource& source, |
- const content::NotificationDetails& details) { |
- switch (type) { |
- case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: |
- case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: { |
- content::RenderProcessHost* host = |
- content::Source<content::RenderProcessHost>(source).ptr(); |
- oom_score_map_.erase(host->GetHandle()); |
- // Coming here we know that a renderer was just killed and memory should |
- // come back into the pool. However - the memory pressure observer did |
- // not yet update its status and therefore we ask it to redo the |
- // measurement, calling us again if we have to release more. |
- // Note: We do not only accelerate the discarding speed by doing another |
- // check in short succession - we also accelerate it because the timer |
- // driven MemoryPressureMonitor will continue to produce timed events |
- // on top. So the longer the cleanup phase takes, the more tabs will |
- // get discarded in parallel. |
- base::chromeos::MemoryPressureMonitor* monitor = |
- base::chromeos::MemoryPressureMonitor::Get(); |
- if (monitor) |
- monitor->ScheduleEarlyCheck(); |
- break; |
- } |
- case content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED: { |
- bool visible = *content::Details<bool>(details).ptr(); |
- if (visible) { |
- content::RenderProcessHost* render_host = |
- content::Source<content::RenderWidgetHost>(source) |
- .ptr() |
- ->GetProcess(); |
- AdjustFocusedTabScore(render_host->GetHandle()); |
- } |
- // Do not handle the "else" case when it changes to invisible because |
- // 1. The behavior is a bit awkward in that when switching from tab A to |
- // tab B, the event "invisible of B" comes after "visible of A". It can |
- // cause problems when the 2 tabs have the same content (e.g., New Tab |
- // Page). To be more clear, if we try to cancel the timer when losing |
- // focus it may cancel the timer for the same renderer process. |
- // 2. When another window is launched on top of an existing browser |
- // window, the selected tab in the existing browser didn't receive this |
- // event, so an attempt to cancel timer in this case doesn't work. |
- break; |
- } |
- default: |
- NOTREACHED() << "Received unexpected notification"; |
- break; |
- } |
-} |
- |
-// Here we collect most of the information we need to sort the existing |
-// renderers in priority order, and hand out oom_score_adj scores based on that |
-// sort order. |
-// |
-// Things we need to collect on the browser thread (because |
-// TabStripModel isn't thread safe): |
-// 1) whether or not a tab is pinned |
-// 2) last time a tab was selected |
-// 3) is the tab currently selected |
-void TabManagerDelegate::AdjustOomPriorities(const TabStatsList& tab_list) { |
- if (IsArcMemoryManagementEnabled()) { |
- arc::ArcProcessService* arc_process_service = arc::ArcProcessService::Get(); |
- if (arc_process_service && |
- arc_process_service->RequestAppProcessList( |
- base::Bind(&TabManagerDelegate::AdjustOomPrioritiesImpl, |
- weak_ptr_factory_.GetWeakPtr(), tab_list))) { |
- return; |
- } |
- } |
- // Pass in a dummy list if unable to get ARC processes. |
- AdjustOomPrioritiesImpl(tab_list, std::vector<arc::ArcProcess>()); |
-} |
- |
-// Excludes persistent ARC apps, but still preserves active chrome tabs and |
-// focused ARC apps. The latter ones should not be killed by TabManager here, |
-// but we want to adjust their oom_score_adj. |
-// static |
-std::vector<TabManagerDelegate::Candidate> |
-TabManagerDelegate::GetSortedCandidates( |
- const TabStatsList& tab_list, |
- const std::vector<arc::ArcProcess>& arc_processes) { |
- std::vector<Candidate> candidates; |
- candidates.reserve(tab_list.size() + arc_processes.size()); |
- |
- for (const auto& tab : tab_list) { |
- candidates.emplace_back(&tab); |
- } |
- |
- for (const auto& app : arc_processes) { |
- candidates.emplace_back(&app); |
- } |
- |
- // Sort candidates according to priority. |
- std::sort(candidates.begin(), candidates.end()); |
- |
- return candidates; |
-} |
- |
-bool TabManagerDelegate::IsRecentlyKilledArcProcess( |
- const std::string& process_name, |
- const TimeTicks& now) { |
- const auto it = recently_killed_arc_processes_.find(process_name); |
- if (it == recently_killed_arc_processes_.end()) |
- return false; |
- return (now - it->second) <= GetArcRespawnKillDelay(); |
-} |
- |
-bool TabManagerDelegate::KillArcProcess(const int nspid) { |
- auto* arc_service_manager = arc::ArcServiceManager::Get(); |
- if (!arc_service_manager) |
- return false; |
- |
- auto* arc_process_instance = ARC_GET_INSTANCE_FOR_METHOD( |
- arc_service_manager->arc_bridge_service()->process(), KillProcess); |
- if (!arc_process_instance) |
- return false; |
- |
- arc_process_instance->KillProcess(nspid, "LowMemoryKill"); |
- return true; |
-} |
- |
-bool TabManagerDelegate::KillTab(int64_t tab_id) { |
- // Check |tab_manager_| is alive before taking tabs into consideration. |
- return tab_manager_ && |
- tab_manager_->CanDiscardTab(tab_id) && |
- tab_manager_->DiscardTabById(tab_id); |
-} |
- |
- |
-chromeos::DebugDaemonClient* TabManagerDelegate::GetDebugDaemonClient() { |
- return chromeos::DBusThreadManager::Get()->GetDebugDaemonClient(); |
-} |
- |
-void TabManagerDelegate::LowMemoryKillImpl( |
- const TabStatsList& tab_list, |
- const std::vector<arc::ArcProcess>& arc_processes) { |
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
- VLOG(2) << "LowMemoryKillImpl"; |
- |
- const std::vector<TabManagerDelegate::Candidate> candidates = |
- GetSortedCandidates(tab_list, arc_processes); |
- |
- int target_memory_to_free_kb = mem_stat_->TargetMemoryToFreeKB(); |
- const TimeTicks now = TimeTicks::Now(); |
- |
- MEMORY_LOG(ERROR) << "List of low memory kill candidates " |
- "(sorted from low priority to high priority):"; |
- for (auto it = candidates.rbegin(); it != candidates.rend(); ++it) { |
- MEMORY_LOG(ERROR) << *it; |
- } |
- // Kill processes until the estimated amount of freed memory is sufficient to |
- // bring the system memory back to a normal level. |
- // The list is sorted by descending importance, so we go through the list |
- // backwards. |
- for (auto it = candidates.rbegin(); it != candidates.rend(); ++it) { |
- MEMORY_LOG(ERROR) << "Target memory to free: " << target_memory_to_free_kb |
- << " KB"; |
- if (target_memory_to_free_kb <= 0) |
- break; |
- // Never kill selected tab, foreground app, and important apps regardless of |
- // whether they're in the active window. Since the user experience would be |
- // bad. |
- ProcessType process_type = it->process_type(); |
- if (process_type <= ProcessType::IMPORTANT_APP) { |
- MEMORY_LOG(ERROR) << "Skipped killing " << it->app()->process_name(); |
- continue; |
- } |
- if (it->app()) { |
- if (IsRecentlyKilledArcProcess(it->app()->process_name(), now)) { |
- MEMORY_LOG(ERROR) << "Avoided killing " << it->app()->process_name() |
- << " too often"; |
- continue; |
- } |
- int estimated_memory_freed_kb = |
- mem_stat_->EstimatedMemoryFreedKB(it->app()->pid()); |
- if (KillArcProcess(it->app()->nspid())) { |
- recently_killed_arc_processes_[it->app()->process_name()] = now; |
- target_memory_to_free_kb -= estimated_memory_freed_kb; |
- MemoryKillsMonitor::LogLowMemoryKill("APP", estimated_memory_freed_kb); |
- MEMORY_LOG(ERROR) << "Killed app " << it->app()->process_name() << " (" |
- << it->app()->pid() << ")" |
- << ", estimated " << estimated_memory_freed_kb |
- << " KB freed"; |
- } else { |
- MEMORY_LOG(ERROR) << "Failed to kill " << it->app()->process_name(); |
- } |
- } else { |
- int64_t tab_id = it->tab()->tab_contents_id; |
- // The estimation is problematic since multiple tabs may share the same |
- // process, while the calculation counts memory used by the whole process. |
- // So |estimated_memory_freed_kb| is an over-estimation. |
- int estimated_memory_freed_kb = |
- mem_stat_->EstimatedMemoryFreedKB(it->tab()->renderer_handle); |
- if (KillTab(tab_id)) { |
- target_memory_to_free_kb -= estimated_memory_freed_kb; |
- MemoryKillsMonitor::LogLowMemoryKill("TAB", estimated_memory_freed_kb); |
- MEMORY_LOG(ERROR) << "Killed tab " << it->tab()->title << " (" |
- << it->tab()->renderer_handle << "), estimated " |
- << estimated_memory_freed_kb << " KB freed"; |
- } |
- } |
- } |
- if (target_memory_to_free_kb > 0) { |
- MEMORY_LOG(ERROR) |
- << "Unable to kill enough candidates to meet target_memory_to_free_kb "; |
- } |
-} |
- |
-void TabManagerDelegate::AdjustOomPrioritiesImpl( |
- const TabStatsList& tab_list, |
- const std::vector<arc::ArcProcess>& arc_processes) { |
- std::vector<TabManagerDelegate::Candidate> candidates; |
- std::vector<TabManagerDelegate::Candidate> apps_non_killable; |
- |
- // Least important first. |
- auto all_candidates = GetSortedCandidates(tab_list, arc_processes); |
- for (auto& candidate : all_candidates) { |
- // TODO(cylee|yusukes): Consider using IsImportant() instead of |
- // IsKernelKillable() for simplicity. |
- // TODO(cylee): Also consider protecting FOCUSED_TAB from the kernel OOM |
- // killer so that Chrome and the kernel do the same regarding OOM handling. |
- if (!candidate.app() || candidate.app()->IsKernelKillable()) { |
- // Add tabs and killable apps to |candidates|. |
- candidates.emplace_back(std::move(candidate)); |
- } else { |
- // Add non-killable apps to |apps_non_killable|. |
- apps_non_killable.emplace_back(std::move(candidate)); |
- } |
- } |
- |
- // Now we assign priorities based on the sorted list. We're assigning |
- // priorities in the range of kLowestRendererOomScore to |
- // kHighestRendererOomScore (defined in chrome_constants.h). oom_score_adj |
- // takes values from -1000 to 1000. Negative values are reserved for system |
- // processes, and we want to give some room below the range we're using to |
- // allow for things that want to be above the renderers in priority, so the |
- // defined range gives us some variation in priority without taking up the |
- // whole range. In the end, however, it's a pretty arbitrary range to use. |
- // Higher values are more likely to be killed by the OOM killer. |
- |
- // Break the processes into 2 parts. This is to help lower the chance of |
- // altering OOM score for many processes on any small change. |
- int range_middle = |
- (chrome::kLowestRendererOomScore + chrome::kHighestRendererOomScore) / 2; |
- |
- // Find some pivot point. For now processes with priority >= CHROME_INTERNAL |
- // are prone to be affected by LRU change. Taking them as "high priority" |
- // processes. |
- auto lower_priority_part = candidates.end(); |
- for (auto it = candidates.begin(); it != candidates.end(); ++it) { |
- if (it->process_type() >= ProcessType::BACKGROUND_APP) { |
- lower_priority_part = it; |
- break; |
- } |
- } |
- |
- ProcessScoreMap new_map; |
- |
- // Make the apps non-killable. |
- if (!apps_non_killable.empty()) |
- SetOomScore(apps_non_killable, kLowestOomScore, &new_map); |
- |
- // Higher priority part. |
- DistributeOomScoreInRange(candidates.begin(), lower_priority_part, |
- chrome::kLowestRendererOomScore, range_middle, |
- &new_map); |
- // Lower priority part. |
- DistributeOomScoreInRange(lower_priority_part, candidates.end(), range_middle, |
- chrome::kHighestRendererOomScore, &new_map); |
- |
- oom_score_map_.swap(new_map); |
-} |
- |
-void TabManagerDelegate::SetOomScore( |
- const std::vector<TabManagerDelegate::Candidate>& candidates, |
- int score, |
- ProcessScoreMap* new_map) { |
- DistributeOomScoreInRange(candidates.begin(), candidates.end(), score, score, |
- new_map); |
-} |
- |
-void TabManagerDelegate::DistributeOomScoreInRange( |
- std::vector<TabManagerDelegate::Candidate>::const_iterator begin, |
- std::vector<TabManagerDelegate::Candidate>::const_iterator end, |
- int range_begin, |
- int range_end, |
- ProcessScoreMap* new_map) { |
- // Processes whose OOM scores should be updated. Ignore duplicated pids but |
- // the last occurrence. |
- std::map<base::ProcessHandle, int32_t> oom_scores_to_change; |
- |
- // Though there might be duplicate process handles, it doesn't matter to |
- // overestimate the number of processes here since the we don't need to |
- // use up the full range. |
- int num = (end - begin); |
- const float priority_increment = |
- static_cast<float>(range_end - range_begin) / num; |
- |
- float priority = range_begin; |
- for (auto cur = begin; cur != end; ++cur) { |
- const int score = round(priority); |
- |
- base::ProcessHandle pid = base::kNullProcessHandle; |
- if (cur->app()) { |
- pid = cur->app()->pid(); |
- } else { |
- pid = cur->tab()->renderer_handle; |
- // 1. tab_list contains entries for already-discarded tabs. If the PID |
- // (renderer_handle) is zero, we don't need to adjust the oom_score. |
- // 2. Only add unseen process handle so if there's multiple tab maps to |
- // the same process, the process is set to an OOM score based on its "most |
- // important" tab. |
- if (pid == base::kNullProcessHandle || |
- new_map->find(pid) != new_map->end()) |
- continue; |
- } |
- |
- if (pid == base::kNullProcessHandle) |
- continue; |
- |
- // Update the to-be-cached OOM score map. Use pid as map keys so it's |
- // globally unique. |
- (*new_map)[pid] = score; |
- |
- // Need to update OOM score if the calculated score is different from |
- // current cached score. |
- if (oom_score_map_[pid] != score) { |
- VLOG(3) << "Update OOM score " << score << " for " << *cur; |
- oom_scores_to_change[pid] = static_cast<int32_t>(score); |
- } |
- priority += priority_increment; |
- } |
- |
- if (oom_scores_to_change.size()) { |
- GetDebugDaemonClient()->SetOomScoreAdj( |
- oom_scores_to_change, base::Bind(&OnSetOomScoreAdj)); |
- } |
-} |
- |
-} // namespace memory |