| 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
|
| index 67d1f88d8ca6f75dd032aefe92e88c49f9db3a08..de53aaa038855d40801d1baf85ba51e279035b61 100644
|
| --- a/chrome/browser/memory/tab_manager_delegate_chromeos.cc
|
| +++ b/chrome/browser/memory/tab_manager_delegate_chromeos.cc
|
| @@ -4,8 +4,10 @@
|
|
|
| #include "chrome/browser/memory/tab_manager_delegate_chromeos.h"
|
|
|
| +#include <stdint.h>
|
| +
|
| #include <algorithm>
|
| -#include <set>
|
| +#include <map>
|
| #include <string>
|
| #include <vector>
|
|
|
| @@ -22,7 +24,6 @@
|
| #include "base/strings/string_number_conversions.h"
|
| #include "base/strings/string_util.h"
|
| #include "base/strings/utf_string_conversions.h"
|
| -#include "base/synchronization/lock.h"
|
| #include "base/time/time.h"
|
| #include "chrome/browser/chromeos/arc/arc_process.h"
|
| #include "chrome/browser/chromeos/arc/arc_process_service.h"
|
| @@ -32,6 +33,7 @@
|
| #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/common/process.mojom.h"
|
| #include "components/arc/metrics/oom_kills_histogram.h"
|
| @@ -80,6 +82,12 @@ bool IsArcMemoryManagementEnabled() {
|
| return base::FeatureList::IsEnabled(features::kArcMemoryManagement);
|
| }
|
|
|
| +void OnSetOomScoreAdj(bool success, const std::string& output) {
|
| + VLOG(2) << "OnSetOomScoreAdj " << success << " " << output;
|
| + if (!success || output != "")
|
| + LOG(WARNING) << "Set OOM score error: " << output;
|
| +}
|
| +
|
| } // namespace
|
|
|
| std::ostream& operator<<(std::ostream& os, const ProcessType& type) {
|
| @@ -159,73 +167,50 @@ ProcessType TabManagerDelegate::Candidate::GetProcessTypeInternal() const {
|
| // 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.
|
| -//
|
| -// This information might be set on UI thread and looked up on FILE thread. So a
|
| -// lock is needed to avoid racing.
|
| class TabManagerDelegate::FocusedProcess {
|
| public:
|
| static const int kInvalidArcAppNspid = 0;
|
| - struct Data {
|
| - union {
|
| - // If a chrome tqab.
|
| - base::ProcessHandle pid;
|
| - // If an ARC app.
|
| - int nspid;
|
| - };
|
| - bool is_arc_app;
|
| - };
|
| -
|
| - void SetTabPid(base::ProcessHandle pid) {
|
| - Data* data = new Data();
|
| - data->is_arc_app = false;
|
| - data->pid = pid;
|
| -
|
| - base::AutoLock lock(lock_);
|
| - data_.reset(data);
|
| - }
|
|
|
| - void SetArcAppNspid(int nspid) {
|
| - Data* data = new Data();
|
| - data->is_arc_app = true;
|
| - data->nspid = nspid;
|
| + FocusedProcess() { Reset(); }
|
|
|
| - base::AutoLock lock(lock_);
|
| - data_.reset(data);
|
| + void SetTabPid(const base::ProcessHandle pid) {
|
| + pid_ = pid;
|
| + nspid_ = kInvalidArcAppNspid;
|
| }
|
|
|
| - // Getter. Returns kNullProcessHandle if the process is not a tab.
|
| - base::ProcessHandle GetTabPid() {
|
| - base::AutoLock lock(lock_);
|
| - if (data_ && !data_->is_arc_app)
|
| - return data_->pid;
|
| - return base::kNullProcessHandle;
|
| + void SetArcAppNspid(const int nspid) {
|
| + pid_ = base::kNullProcessHandle;
|
| + nspid_ = nspid;
|
| }
|
|
|
| - // Getter. Returns kInvalidArcAppNspid if the process is not an arc app.
|
| - int GetArcAppNspid() {
|
| - base::AutoLock lock(lock_);
|
| - if (data_ && data_->is_arc_app)
|
| - return data_->nspid;
|
| - return kInvalidArcAppNspid;
|
| - }
|
| + base::ProcessHandle GetTabPid() const { return pid_; }
|
| +
|
| + int GetArcAppNspid() const { return nspid_; }
|
|
|
| - // An atomic operation which 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.
|
| + // 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() {
|
| - base::AutoLock lock(lock_);
|
| - if (data_ && data_->is_arc_app) {
|
| - data_.reset();
|
| + if (nspid_ != kInvalidArcAppNspid) {
|
| + Reset();
|
| return true;
|
| }
|
| return false;
|
| }
|
|
|
| private:
|
| - std::unique_ptr<Data> data_;
|
| - // Protects rw access to data_;
|
| - base::Lock lock_;
|
| + 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.
|
| @@ -465,7 +450,7 @@ void TabManagerDelegate::LowMemoryKill(
|
| }
|
|
|
| int TabManagerDelegate::GetCachedOomScore(ProcessHandle process_handle) {
|
| - base::AutoLock oom_score_autolock(oom_score_lock_);
|
| + DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| auto it = oom_score_map_.find(process_handle);
|
| if (it != oom_score_map_.end()) {
|
| return it->second;
|
| @@ -474,46 +459,40 @@ int TabManagerDelegate::GetCachedOomScore(ProcessHandle process_handle) {
|
| return -1001;
|
| }
|
|
|
| -void TabManagerDelegate::AdjustFocusedTabScoreOnFileThread() {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::FILE);
|
| +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. We can not avoid the race.
|
| + // just switched to an ARC app before the timeout. We can not avoid the race.
|
| if (pid == base::kNullProcessHandle)
|
| return;
|
| - {
|
| - base::AutoLock oom_score_autolock(oom_score_lock_);
|
| - oom_score_map_[pid] = chrome::kLowestRendererOomScore;
|
| - }
|
| +
|
| + // 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;
|
| - content::ZygoteHost::GetInstance()->AdjustRendererOOMScore(
|
| - pid, chrome::kLowestRendererOomScore);
|
| -}
|
| -
|
| -void TabManagerDelegate::OnFocusTabScoreAdjustmentTimeout() {
|
| - BrowserThread::PostTask(
|
| - BrowserThread::FILE, FROM_HERE,
|
| - base::Bind(&TabManagerDelegate::AdjustFocusedTabScoreOnFileThread,
|
| - base::Unretained(this)));
|
| + 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);
|
|
|
| - bool not_lowest_score = false;
|
| - {
|
| - base::AutoLock oom_score_autolock(oom_score_lock_);
|
| - // 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);
|
| - not_lowest_score = (it == oom_score_map_.end() ||
|
| - it->second != chrome::kLowestRendererOomScore);
|
| - }
|
| + // 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
|
| @@ -535,10 +514,7 @@ void TabManagerDelegate::Observe(int type,
|
| case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: {
|
| content::RenderProcessHost* host =
|
| content::Source<content::RenderProcessHost>(source).ptr();
|
| - {
|
| - base::AutoLock oom_score_autolock(oom_score_lock_);
|
| - oom_score_map_.erase(host->GetHandle());
|
| - }
|
| + 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
|
| @@ -611,9 +587,6 @@ std::vector<TabManagerDelegate::Candidate>
|
| TabManagerDelegate::GetSortedCandidates(
|
| const TabStatsList& tab_list,
|
| const std::vector<arc::ArcProcess>& arc_processes) {
|
| - static constexpr char kAppLauncherProcessName[] =
|
| - "org.chromium.arc.applauncher";
|
| -
|
| std::vector<Candidate> candidates;
|
| candidates.reserve(tab_list.size() + arc_processes.size());
|
|
|
| @@ -621,16 +594,19 @@ TabManagerDelegate::GetSortedCandidates(
|
| candidates.emplace_back(&tab);
|
| }
|
|
|
| + // A special process on Android side which serves as a dummy "focused" app
|
| + // when the focused window is a Chrome side window (i.e., all Android
|
| + // processes are running in the background). We don't want to kill it anyway.
|
| + static constexpr char kArcInBackgroundDummyprocess[] =
|
| + "org.chromium.arc.home";
|
| +
|
| for (const auto& app : arc_processes) {
|
| // Skip persistent android processes since they should never be killed here.
|
| // Neither do we set their OOM scores so their score remains minimum.
|
| - //
|
| - // AppLauncher is treated specially in ARC++. For example it is taken
|
| - // as the dummy foreground app from Android's point of view when the focused
|
| - // window is not an Android app. We prefer never kill it.
|
| if (app.process_state() <= arc::mojom::ProcessState::PERSISTENT_UI ||
|
| - app.process_name() == kAppLauncherProcessName)
|
| + app.process_name() == kArcInBackgroundDummyprocess) {
|
| continue;
|
| + }
|
| candidates.emplace_back(&app);
|
| }
|
|
|
| @@ -654,6 +630,11 @@ bool TabManagerDelegate::KillTab(int64_t 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) {
|
| @@ -688,6 +669,9 @@ void TabManagerDelegate::LowMemoryKillImpl(
|
| }
|
| } 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)) {
|
| @@ -742,47 +726,18 @@ void TabManagerDelegate::AdjustOomPrioritiesImpl(
|
| // Lower priority part.
|
| DistributeOomScoreInRange(lower_priority_part, candidates.end(), range_middle,
|
| chrome::kHighestRendererOomScore, &new_map);
|
| - base::AutoLock oom_score_autolock(oom_score_lock_);
|
| oom_score_map_.swap(new_map);
|
| }
|
|
|
| -void TabManagerDelegate::SetOomScoreAdjForApp(int nspid, int score) {
|
| - if (!arc_process_instance_)
|
| - return;
|
| - if (arc_process_instance_version_ < 2) {
|
| - VLOG(1) << "ProcessInstance version < 2 does not "
|
| - "support SetOomScoreAdj() yet.";
|
| - return;
|
| - }
|
| - arc_process_instance_->SetOomScoreAdj(nspid, score);
|
| -}
|
| -
|
| -void TabManagerDelegate::SetOomScoreAdjForTabs(
|
| - const std::vector<std::pair<base::ProcessHandle, int>>& entries) {
|
| - BrowserThread::PostTask(
|
| - BrowserThread::FILE, FROM_HERE,
|
| - base::Bind(&TabManagerDelegate::SetOomScoreAdjForTabsOnFileThread,
|
| - base::Unretained(this), entries));
|
| -}
|
| -
|
| -void TabManagerDelegate::SetOomScoreAdjForTabsOnFileThread(
|
| - const std::vector<std::pair<base::ProcessHandle, int>>& entries) {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::FILE);
|
| - for (const auto& entry : entries) {
|
| - content::ZygoteHost::GetInstance()->AdjustRendererOOMScore(entry.first,
|
| - entry.second);
|
| - }
|
| -}
|
| -
|
| 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) {
|
| - // OOM score setting for tabs involves file system operation so should be
|
| - // done on file thread.
|
| - std::vector<std::pair<base::ProcessHandle, int>> oom_score_for_tabs;
|
| + // 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
|
| @@ -794,46 +749,41 @@ void TabManagerDelegate::DistributeOomScoreInRange(
|
| float priority = range_begin;
|
| for (auto cur = begin; cur != end; ++cur) {
|
| int score = static_cast<int>(priority + 0.5f);
|
| +
|
| + base::ProcessHandle pid = base::kNullProcessHandle;
|
| if (cur->app()) {
|
| - // Use pid as map keys so it's globally unique.
|
| - (*new_map)[cur->app()->pid()] = score;
|
| - int cur_app_pid_score = 0;
|
| - {
|
| - base::AutoLock oom_score_autolock(oom_score_lock_);
|
| - cur_app_pid_score = oom_score_map_[cur->app()->pid()];
|
| - }
|
| - if (cur_app_pid_score != score) {
|
| - VLOG(3) << "Set OOM score " << score << " for " << *cur;
|
| - SetOomScoreAdjForApp(cur->app()->nspid(), score);
|
| - }
|
| + pid = cur->app()->pid();
|
| } else {
|
| - base::ProcessHandle process_handle = cur->tab()->renderer_handle;
|
| + 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 (process_handle != 0 &&
|
| - new_map->find(process_handle) == new_map->end()) {
|
| - (*new_map)[process_handle] = score;
|
| - int process_handle_score = 0;
|
| - {
|
| - base::AutoLock oom_score_autolock(oom_score_lock_);
|
| - process_handle_score = oom_score_map_[process_handle];
|
| - }
|
| - if (process_handle_score != score) {
|
| - oom_score_for_tabs.push_back(std::make_pair(process_handle, score));
|
| - VLOG(3) << "Set OOM score " << score << " for " << *cur;
|
| - }
|
| - } else {
|
| - continue; // Skip priority increment.
|
| - }
|
| + 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_score_for_tabs.size())
|
| - SetOomScoreAdjForTabs(oom_score_for_tabs);
|
| + if (oom_scores_to_change.size())
|
| + GetDebugDaemonClient()->SetOomScoreAdj(
|
| + oom_scores_to_change, base::Bind(&OnSetOomScoreAdj));
|
| }
|
|
|
| } // namespace memory
|
|
|