| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/memory/tab_manager_delegate_chromeos.h" | 5 #include "chrome/browser/memory/tab_manager_delegate_chromeos.h" |
| 6 | 6 |
| 7 #include <stdint.h> |
| 8 |
| 7 #include <algorithm> | 9 #include <algorithm> |
| 8 #include <set> | 10 #include <map> |
| 9 #include <string> | 11 #include <string> |
| 10 #include <vector> | 12 #include <vector> |
| 11 | 13 |
| 12 #include "ash/shell.h" | 14 #include "ash/shell.h" |
| 13 #include "base/bind.h" | 15 #include "base/bind.h" |
| 14 #include "base/command_line.h" | 16 #include "base/command_line.h" |
| 15 #include "base/files/file_path.h" | 17 #include "base/files/file_path.h" |
| 16 #include "base/files/file_util.h" | 18 #include "base/files/file_util.h" |
| 17 #include "base/memory/memory_pressure_monitor_chromeos.h" | 19 #include "base/memory/memory_pressure_monitor_chromeos.h" |
| 18 #include "base/metrics/histogram_macros.h" | 20 #include "base/metrics/histogram_macros.h" |
| 19 #include "base/process/process_handle.h" // kNullProcessHandle. | 21 #include "base/process/process_handle.h" // kNullProcessHandle. |
| 20 #include "base/process/process_metrics.h" | 22 #include "base/process/process_metrics.h" |
| 21 #include "base/strings/string16.h" | 23 #include "base/strings/string16.h" |
| 22 #include "base/strings/string_number_conversions.h" | 24 #include "base/strings/string_number_conversions.h" |
| 23 #include "base/strings/string_util.h" | 25 #include "base/strings/string_util.h" |
| 24 #include "base/strings/utf_string_conversions.h" | 26 #include "base/strings/utf_string_conversions.h" |
| 25 #include "base/synchronization/lock.h" | |
| 26 #include "base/time/time.h" | 27 #include "base/time/time.h" |
| 27 #include "chrome/browser/chromeos/arc/arc_process.h" | 28 #include "chrome/browser/chromeos/arc/arc_process.h" |
| 28 #include "chrome/browser/chromeos/arc/arc_process_service.h" | 29 #include "chrome/browser/chromeos/arc/arc_process_service.h" |
| 29 #include "chrome/browser/memory/tab_stats.h" | 30 #include "chrome/browser/memory/tab_stats.h" |
| 30 #include "chrome/browser/ui/browser.h" | 31 #include "chrome/browser/ui/browser.h" |
| 31 #include "chrome/browser/ui/browser_list.h" | 32 #include "chrome/browser/ui/browser_list.h" |
| 32 #include "chrome/browser/ui/tabs/tab_strip_model.h" | 33 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| 33 #include "chrome/common/chrome_constants.h" | 34 #include "chrome/common/chrome_constants.h" |
| 34 #include "chrome/common/chrome_features.h" | 35 #include "chrome/common/chrome_features.h" |
| 36 #include "chromeos/dbus/dbus_thread_manager.h" |
| 35 #include "components/arc/arc_bridge_service.h" | 37 #include "components/arc/arc_bridge_service.h" |
| 36 #include "components/arc/common/process.mojom.h" | 38 #include "components/arc/common/process.mojom.h" |
| 37 #include "components/arc/metrics/oom_kills_histogram.h" | 39 #include "components/arc/metrics/oom_kills_histogram.h" |
| 38 #include "components/exo/shell_surface.h" | 40 #include "components/exo/shell_surface.h" |
| 39 #include "content/public/browser/browser_thread.h" | 41 #include "content/public/browser/browser_thread.h" |
| 40 #include "content/public/browser/notification_service.h" | 42 #include "content/public/browser/notification_service.h" |
| 41 #include "content/public/browser/notification_types.h" | 43 #include "content/public/browser/notification_types.h" |
| 42 #include "content/public/browser/render_process_host.h" | 44 #include "content/public/browser/render_process_host.h" |
| 43 #include "content/public/browser/render_widget_host.h" | 45 #include "content/public/browser/render_widget_host.h" |
| 44 #include "content/public/browser/zygote_host_linux.h" | 46 #include "content/public/browser/zygote_host_linux.h" |
| (...skipping 28 matching lines...) Expand all Loading... |
| 73 return false; | 75 return false; |
| 74 std::string application_id = exo::ShellSurface::GetApplicationId(window); | 76 std::string application_id = exo::ShellSurface::GetApplicationId(window); |
| 75 return base::StartsWith(application_id, kArcProcessNamePrefix, | 77 return base::StartsWith(application_id, kArcProcessNamePrefix, |
| 76 base::CompareCase::SENSITIVE); | 78 base::CompareCase::SENSITIVE); |
| 77 } | 79 } |
| 78 | 80 |
| 79 bool IsArcMemoryManagementEnabled() { | 81 bool IsArcMemoryManagementEnabled() { |
| 80 return base::FeatureList::IsEnabled(features::kArcMemoryManagement); | 82 return base::FeatureList::IsEnabled(features::kArcMemoryManagement); |
| 81 } | 83 } |
| 82 | 84 |
| 85 void OnSetOomScoreAdj(bool success, const std::string& output) { |
| 86 VLOG(2) << "OnSetOomScoreAdj " << success << " " << output; |
| 87 if (!success || output != "") |
| 88 LOG(WARNING) << "Set OOM score error: " << output; |
| 89 } |
| 90 |
| 83 } // namespace | 91 } // namespace |
| 84 | 92 |
| 85 std::ostream& operator<<(std::ostream& os, const ProcessType& type) { | 93 std::ostream& operator<<(std::ostream& os, const ProcessType& type) { |
| 86 switch (type) { | 94 switch (type) { |
| 87 case ProcessType::FOCUSED_APP: | 95 case ProcessType::FOCUSED_APP: |
| 88 return os << "FOCUSED_APP/FOCUSED_TAB"; | 96 return os << "FOCUSED_APP/FOCUSED_TAB"; |
| 89 case ProcessType::VISIBLE_APP: | 97 case ProcessType::VISIBLE_APP: |
| 90 return os << "VISIBLE_APP"; | 98 return os << "VISIBLE_APP"; |
| 91 case ProcessType::BACKGROUND_APP: | 99 case ProcessType::BACKGROUND_APP: |
| 92 return os << "BACKGROUND_APP"; | 100 return os << "BACKGROUND_APP"; |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 152 return ProcessType::BACKGROUND_TAB; | 160 return ProcessType::BACKGROUND_TAB; |
| 153 } | 161 } |
| 154 NOTREACHED() << "Unexpected process type"; | 162 NOTREACHED() << "Unexpected process type"; |
| 155 return ProcessType::UNKNOWN_TYPE; | 163 return ProcessType::UNKNOWN_TYPE; |
| 156 } | 164 } |
| 157 | 165 |
| 158 // Holds the info of a newly focused tab or app window. The focused process is | 166 // Holds the info of a newly focused tab or app window. The focused process is |
| 159 // set to highest priority (lowest OOM score), but not immediately. To avoid | 167 // set to highest priority (lowest OOM score), but not immediately. To avoid |
| 160 // redundant settings the OOM score adjusting only happens after a timeout. If | 168 // redundant settings the OOM score adjusting only happens after a timeout. If |
| 161 // the process loses focus before the timeout, the adjustment is canceled. | 169 // the process loses focus before the timeout, the adjustment is canceled. |
| 162 // | |
| 163 // This information might be set on UI thread and looked up on FILE thread. So a | |
| 164 // lock is needed to avoid racing. | |
| 165 class TabManagerDelegate::FocusedProcess { | 170 class TabManagerDelegate::FocusedProcess { |
| 166 public: | 171 public: |
| 167 static const int kInvalidArcAppNspid = 0; | 172 static const int kInvalidArcAppNspid = 0; |
| 168 struct Data { | |
| 169 union { | |
| 170 // If a chrome tqab. | |
| 171 base::ProcessHandle pid; | |
| 172 // If an ARC app. | |
| 173 int nspid; | |
| 174 }; | |
| 175 bool is_arc_app; | |
| 176 }; | |
| 177 | 173 |
| 178 void SetTabPid(base::ProcessHandle pid) { | 174 FocusedProcess() { Reset(); } |
| 179 Data* data = new Data(); | |
| 180 data->is_arc_app = false; | |
| 181 data->pid = pid; | |
| 182 | 175 |
| 183 base::AutoLock lock(lock_); | 176 void SetTabPid(const base::ProcessHandle pid) { |
| 184 data_.reset(data); | 177 pid_ = pid; |
| 178 nspid_ = kInvalidArcAppNspid; |
| 185 } | 179 } |
| 186 | 180 |
| 187 void SetArcAppNspid(int nspid) { | 181 void SetArcAppNspid(const int nspid) { |
| 188 Data* data = new Data(); | 182 pid_ = base::kNullProcessHandle; |
| 189 data->is_arc_app = true; | 183 nspid_ = nspid; |
| 190 data->nspid = nspid; | |
| 191 | |
| 192 base::AutoLock lock(lock_); | |
| 193 data_.reset(data); | |
| 194 } | 184 } |
| 195 | 185 |
| 196 // Getter. Returns kNullProcessHandle if the process is not a tab. | 186 base::ProcessHandle GetTabPid() const { return pid_; } |
| 197 base::ProcessHandle GetTabPid() { | |
| 198 base::AutoLock lock(lock_); | |
| 199 if (data_ && !data_->is_arc_app) | |
| 200 return data_->pid; | |
| 201 return base::kNullProcessHandle; | |
| 202 } | |
| 203 | 187 |
| 204 // Getter. Returns kInvalidArcAppNspid if the process is not an arc app. | 188 int GetArcAppNspid() const { return nspid_; } |
| 205 int GetArcAppNspid() { | |
| 206 base::AutoLock lock(lock_); | |
| 207 if (data_ && data_->is_arc_app) | |
| 208 return data_->nspid; | |
| 209 return kInvalidArcAppNspid; | |
| 210 } | |
| 211 | 189 |
| 212 // An atomic operation which checks whether the containing instance is an ARC | 190 // Checks whether the containing instance is an ARC app. If so it resets the |
| 213 // app. If so it resets the data and returns true. Useful when canceling an | 191 // data and returns true. Useful when canceling an ongoing OOM score setting |
| 214 // ongoing OOM score setting for a focused ARC app because the focus has been | 192 // for a focused ARC app because the focus has been shifted away shortly. |
| 215 // shifted away shortly. | |
| 216 bool ResetIfIsArcApp() { | 193 bool ResetIfIsArcApp() { |
| 217 base::AutoLock lock(lock_); | 194 if (nspid_ != kInvalidArcAppNspid) { |
| 218 if (data_ && data_->is_arc_app) { | 195 Reset(); |
| 219 data_.reset(); | |
| 220 return true; | 196 return true; |
| 221 } | 197 } |
| 222 return false; | 198 return false; |
| 223 } | 199 } |
| 224 | 200 |
| 225 private: | 201 private: |
| 226 std::unique_ptr<Data> data_; | 202 void Reset() { |
| 227 // Protects rw access to data_; | 203 pid_ = base::kNullProcessHandle; |
| 228 base::Lock lock_; | 204 nspid_ = kInvalidArcAppNspid; |
| 205 } |
| 206 |
| 207 // The focused app could be a Chrome tab or an Android app, but not both. |
| 208 // At most one of them contains a valid value at any time. |
| 209 |
| 210 // If a chrome tab. |
| 211 base::ProcessHandle pid_; |
| 212 // If an Android app. |
| 213 int nspid_; |
| 229 }; | 214 }; |
| 230 | 215 |
| 231 // TabManagerDelegate::MemoryStat implementation. | 216 // TabManagerDelegate::MemoryStat implementation. |
| 232 | 217 |
| 233 // static | 218 // static |
| 234 int TabManagerDelegate::MemoryStat::ReadIntFromFile( | 219 int TabManagerDelegate::MemoryStat::ReadIntFromFile( |
| 235 const char* file_name, const int default_val) { | 220 const char* file_name, const int default_val) { |
| 236 std::string file_string; | 221 std::string file_string; |
| 237 if (!base::ReadFileToString(base::FilePath(file_name), &file_string)) { | 222 if (!base::ReadFileToString(base::FilePath(file_name), &file_string)) { |
| 238 LOG(WARNING) << "Unable to read file" << file_name; | 223 LOG(WARNING) << "Unable to read file" << file_name; |
| (...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 458 // LowMemoryKillImpl will be called asynchronously so nothing left to do. | 443 // LowMemoryKillImpl will be called asynchronously so nothing left to do. |
| 459 return; | 444 return; |
| 460 } | 445 } |
| 461 // If the list of ARC processes is not available, call LowMemoryKillImpl | 446 // If the list of ARC processes is not available, call LowMemoryKillImpl |
| 462 // synchronously with an empty list of apps. | 447 // synchronously with an empty list of apps. |
| 463 std::vector<arc::ArcProcess> dummy_apps; | 448 std::vector<arc::ArcProcess> dummy_apps; |
| 464 LowMemoryKillImpl(tab_list, dummy_apps); | 449 LowMemoryKillImpl(tab_list, dummy_apps); |
| 465 } | 450 } |
| 466 | 451 |
| 467 int TabManagerDelegate::GetCachedOomScore(ProcessHandle process_handle) { | 452 int TabManagerDelegate::GetCachedOomScore(ProcessHandle process_handle) { |
| 468 base::AutoLock oom_score_autolock(oom_score_lock_); | 453 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 469 auto it = oom_score_map_.find(process_handle); | 454 auto it = oom_score_map_.find(process_handle); |
| 470 if (it != oom_score_map_.end()) { | 455 if (it != oom_score_map_.end()) { |
| 471 return it->second; | 456 return it->second; |
| 472 } | 457 } |
| 473 // An impossible value for oom_score_adj. | 458 // An impossible value for oom_score_adj. |
| 474 return -1001; | 459 return -1001; |
| 475 } | 460 } |
| 476 | 461 |
| 477 void TabManagerDelegate::AdjustFocusedTabScoreOnFileThread() { | 462 void TabManagerDelegate::OnFocusTabScoreAdjustmentTimeout() { |
| 478 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 463 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 479 base::ProcessHandle pid = focused_process_->GetTabPid(); | 464 base::ProcessHandle pid = focused_process_->GetTabPid(); |
| 480 // The focused process doesn't render a tab. Could happen when the focus | 465 // The focused process doesn't render a tab. Could happen when the focus |
| 481 // just switched to an ARC app. We can not avoid the race. | 466 // just switched to an ARC app before the timeout. We can not avoid the race. |
| 482 if (pid == base::kNullProcessHandle) | 467 if (pid == base::kNullProcessHandle) |
| 483 return; | 468 return; |
| 484 { | 469 |
| 485 base::AutoLock oom_score_autolock(oom_score_lock_); | 470 // Update the OOM score cache. |
| 486 oom_score_map_[pid] = chrome::kLowestRendererOomScore; | 471 oom_score_map_[pid] = chrome::kLowestRendererOomScore; |
| 487 } | 472 |
| 473 // Sets OOM score. |
| 488 VLOG(3) << "Set OOM score " << chrome::kLowestRendererOomScore | 474 VLOG(3) << "Set OOM score " << chrome::kLowestRendererOomScore |
| 489 << " for focused tab " << pid; | 475 << " for focused tab " << pid; |
| 490 content::ZygoteHost::GetInstance()->AdjustRendererOOMScore( | 476 std::map<int, int> dict; |
| 491 pid, chrome::kLowestRendererOomScore); | 477 dict[pid] = chrome::kLowestRendererOomScore; |
| 492 } | 478 GetDebugDaemonClient()->SetOomScoreAdj(dict, base::Bind(&OnSetOomScoreAdj)); |
| 493 | |
| 494 void TabManagerDelegate::OnFocusTabScoreAdjustmentTimeout() { | |
| 495 BrowserThread::PostTask( | |
| 496 BrowserThread::FILE, FROM_HERE, | |
| 497 base::Bind(&TabManagerDelegate::AdjustFocusedTabScoreOnFileThread, | |
| 498 base::Unretained(this))); | |
| 499 } | 479 } |
| 500 | 480 |
| 501 void TabManagerDelegate::AdjustFocusedTabScore(base::ProcessHandle pid) { | 481 void TabManagerDelegate::AdjustFocusedTabScore(base::ProcessHandle pid) { |
| 482 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 483 |
| 502 // Clear running timer if one was set for a previous focused tab/app. | 484 // Clear running timer if one was set for a previous focused tab/app. |
| 503 if (focus_process_score_adjust_timer_.IsRunning()) | 485 if (focus_process_score_adjust_timer_.IsRunning()) |
| 504 focus_process_score_adjust_timer_.Stop(); | 486 focus_process_score_adjust_timer_.Stop(); |
| 505 focused_process_->SetTabPid(pid); | 487 focused_process_->SetTabPid(pid); |
| 506 | 488 |
| 507 bool not_lowest_score = false; | 489 // If the currently focused tab already has a lower score, do not |
| 508 { | 490 // set it. This can happen in case the newly focused tab is script |
| 509 base::AutoLock oom_score_autolock(oom_score_lock_); | 491 // connected to the previous tab. |
| 510 // If the currently focused tab already has a lower score, do not | 492 ProcessScoreMap::iterator it = oom_score_map_.find(pid); |
| 511 // set it. This can happen in case the newly focused tab is script | 493 const bool not_lowest_score = (it == oom_score_map_.end() || |
| 512 // connected to the previous tab. | 494 it->second != chrome::kLowestRendererOomScore); |
| 513 ProcessScoreMap::iterator it = oom_score_map_.find(pid); | 495 |
| 514 not_lowest_score = (it == oom_score_map_.end() || | |
| 515 it->second != chrome::kLowestRendererOomScore); | |
| 516 } | |
| 517 if (not_lowest_score) { | 496 if (not_lowest_score) { |
| 518 // By starting a timer we guarantee that the tab is focused for | 497 // By starting a timer we guarantee that the tab is focused for |
| 519 // certain amount of time. Secondly, it also does not add overhead | 498 // certain amount of time. Secondly, it also does not add overhead |
| 520 // to the tab switching time. | 499 // to the tab switching time. |
| 521 // If there's an existing running timer (could be for ARC app), it | 500 // If there's an existing running timer (could be for ARC app), it |
| 522 // would be replaced by a new task. | 501 // would be replaced by a new task. |
| 523 focus_process_score_adjust_timer_.Start( | 502 focus_process_score_adjust_timer_.Start( |
| 524 FROM_HERE, | 503 FROM_HERE, |
| 525 TimeDelta::FromMilliseconds(kFocusedProcessScoreAdjustIntervalMs), | 504 TimeDelta::FromMilliseconds(kFocusedProcessScoreAdjustIntervalMs), |
| 526 this, &TabManagerDelegate::OnFocusTabScoreAdjustmentTimeout); | 505 this, &TabManagerDelegate::OnFocusTabScoreAdjustmentTimeout); |
| 527 } | 506 } |
| 528 } | 507 } |
| 529 | 508 |
| 530 void TabManagerDelegate::Observe(int type, | 509 void TabManagerDelegate::Observe(int type, |
| 531 const content::NotificationSource& source, | 510 const content::NotificationSource& source, |
| 532 const content::NotificationDetails& details) { | 511 const content::NotificationDetails& details) { |
| 533 switch (type) { | 512 switch (type) { |
| 534 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: | 513 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: |
| 535 case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: { | 514 case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: { |
| 536 content::RenderProcessHost* host = | 515 content::RenderProcessHost* host = |
| 537 content::Source<content::RenderProcessHost>(source).ptr(); | 516 content::Source<content::RenderProcessHost>(source).ptr(); |
| 538 { | 517 oom_score_map_.erase(host->GetHandle()); |
| 539 base::AutoLock oom_score_autolock(oom_score_lock_); | |
| 540 oom_score_map_.erase(host->GetHandle()); | |
| 541 } | |
| 542 // Coming here we know that a renderer was just killed and memory should | 518 // Coming here we know that a renderer was just killed and memory should |
| 543 // come back into the pool. However - the memory pressure observer did | 519 // come back into the pool. However - the memory pressure observer did |
| 544 // not yet update its status and therefore we ask it to redo the | 520 // not yet update its status and therefore we ask it to redo the |
| 545 // measurement, calling us again if we have to release more. | 521 // measurement, calling us again if we have to release more. |
| 546 // Note: We do not only accelerate the discarding speed by doing another | 522 // Note: We do not only accelerate the discarding speed by doing another |
| 547 // check in short succession - we also accelerate it because the timer | 523 // check in short succession - we also accelerate it because the timer |
| 548 // driven MemoryPressureMonitor will continue to produce timed events | 524 // driven MemoryPressureMonitor will continue to produce timed events |
| 549 // on top. So the longer the cleanup phase takes, the more tabs will | 525 // on top. So the longer the cleanup phase takes, the more tabs will |
| 550 // get discarded in parallel. | 526 // get discarded in parallel. |
| 551 base::chromeos::MemoryPressureMonitor* monitor = | 527 base::chromeos::MemoryPressureMonitor* monitor = |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 604 } | 580 } |
| 605 | 581 |
| 606 // Excludes persistent ARC apps, but still preserves active chrome tabs and | 582 // Excludes persistent ARC apps, but still preserves active chrome tabs and |
| 607 // focused ARC apps. The latter ones should not be killed by TabManager here, | 583 // focused ARC apps. The latter ones should not be killed by TabManager here, |
| 608 // but we want to adjust their oom_score_adj. | 584 // but we want to adjust their oom_score_adj. |
| 609 // static | 585 // static |
| 610 std::vector<TabManagerDelegate::Candidate> | 586 std::vector<TabManagerDelegate::Candidate> |
| 611 TabManagerDelegate::GetSortedCandidates( | 587 TabManagerDelegate::GetSortedCandidates( |
| 612 const TabStatsList& tab_list, | 588 const TabStatsList& tab_list, |
| 613 const std::vector<arc::ArcProcess>& arc_processes) { | 589 const std::vector<arc::ArcProcess>& arc_processes) { |
| 614 static constexpr char kAppLauncherProcessName[] = | |
| 615 "org.chromium.arc.applauncher"; | |
| 616 | |
| 617 std::vector<Candidate> candidates; | 590 std::vector<Candidate> candidates; |
| 618 candidates.reserve(tab_list.size() + arc_processes.size()); | 591 candidates.reserve(tab_list.size() + arc_processes.size()); |
| 619 | 592 |
| 620 for (const auto& tab : tab_list) { | 593 for (const auto& tab : tab_list) { |
| 621 candidates.emplace_back(&tab); | 594 candidates.emplace_back(&tab); |
| 622 } | 595 } |
| 623 | 596 |
| 597 // A special process on Android side which serves as a dummy "focused" app |
| 598 // when the focused window is a Chrome side window (i.e., all Android |
| 599 // processes are running in the background). We don't want to kill it anyway. |
| 600 static constexpr char kArcInBackgroundDummyprocess[] = |
| 601 "org.chromium.arc.home"; |
| 602 |
| 624 for (const auto& app : arc_processes) { | 603 for (const auto& app : arc_processes) { |
| 625 // Skip persistent android processes since they should never be killed here. | 604 // Skip persistent android processes since they should never be killed here. |
| 626 // Neither do we set their OOM scores so their score remains minimum. | 605 // Neither do we set their OOM scores so their score remains minimum. |
| 627 // | |
| 628 // AppLauncher is treated specially in ARC++. For example it is taken | |
| 629 // as the dummy foreground app from Android's point of view when the focused | |
| 630 // window is not an Android app. We prefer never kill it. | |
| 631 if (app.process_state() <= arc::mojom::ProcessState::PERSISTENT_UI || | 606 if (app.process_state() <= arc::mojom::ProcessState::PERSISTENT_UI || |
| 632 app.process_name() == kAppLauncherProcessName) | 607 app.process_name() == kArcInBackgroundDummyprocess) { |
| 633 continue; | 608 continue; |
| 609 } |
| 634 candidates.emplace_back(&app); | 610 candidates.emplace_back(&app); |
| 635 } | 611 } |
| 636 | 612 |
| 637 // Sort candidates according to priority. | 613 // Sort candidates according to priority. |
| 638 std::sort(candidates.begin(), candidates.end()); | 614 std::sort(candidates.begin(), candidates.end()); |
| 639 | 615 |
| 640 return candidates; | 616 return candidates; |
| 641 } | 617 } |
| 642 | 618 |
| 643 bool TabManagerDelegate::KillArcProcess(const int nspid) { | 619 bool TabManagerDelegate::KillArcProcess(const int nspid) { |
| 644 if (!arc_process_instance_) | 620 if (!arc_process_instance_) |
| 645 return false; | 621 return false; |
| 646 arc_process_instance_->KillProcess(nspid, "LowMemoryKill"); | 622 arc_process_instance_->KillProcess(nspid, "LowMemoryKill"); |
| 647 return true; | 623 return true; |
| 648 } | 624 } |
| 649 | 625 |
| 650 bool TabManagerDelegate::KillTab(int64_t tab_id) { | 626 bool TabManagerDelegate::KillTab(int64_t tab_id) { |
| 651 // Check |tab_manager_| is alive before taking tabs into consideration. | 627 // Check |tab_manager_| is alive before taking tabs into consideration. |
| 652 return tab_manager_ && | 628 return tab_manager_ && |
| 653 tab_manager_->CanDiscardTab(tab_id) && | 629 tab_manager_->CanDiscardTab(tab_id) && |
| 654 tab_manager_->DiscardTabById(tab_id); | 630 tab_manager_->DiscardTabById(tab_id); |
| 655 } | 631 } |
| 656 | 632 |
| 633 |
| 634 chromeos::DebugDaemonClient* TabManagerDelegate::GetDebugDaemonClient() { |
| 635 return chromeos::DBusThreadManager::Get()->GetDebugDaemonClient(); |
| 636 } |
| 637 |
| 657 void TabManagerDelegate::LowMemoryKillImpl( | 638 void TabManagerDelegate::LowMemoryKillImpl( |
| 658 const TabStatsList& tab_list, | 639 const TabStatsList& tab_list, |
| 659 const std::vector<arc::ArcProcess>& arc_processes) { | 640 const std::vector<arc::ArcProcess>& arc_processes) { |
| 660 | 641 |
| 661 VLOG(2) << "LowMemoryKilleImpl"; | 642 VLOG(2) << "LowMemoryKilleImpl"; |
| 662 const std::vector<TabManagerDelegate::Candidate> candidates = | 643 const std::vector<TabManagerDelegate::Candidate> candidates = |
| 663 GetSortedCandidates(tab_list, arc_processes); | 644 GetSortedCandidates(tab_list, arc_processes); |
| 664 | 645 |
| 665 int target_memory_to_free_kb = mem_stat_->TargetMemoryToFreeKB(); | 646 int target_memory_to_free_kb = mem_stat_->TargetMemoryToFreeKB(); |
| 666 // Kill processes until the estimated amount of freed memory is sufficient to | 647 // Kill processes until the estimated amount of freed memory is sufficient to |
| (...skipping 14 matching lines...) Expand all Loading... |
| 681 if (it->app()) { | 662 if (it->app()) { |
| 682 int estimated_memory_freed_kb = | 663 int estimated_memory_freed_kb = |
| 683 mem_stat_->EstimatedMemoryFreedKB(it->app()->pid()); | 664 mem_stat_->EstimatedMemoryFreedKB(it->app()->pid()); |
| 684 if (KillArcProcess(it->app()->nspid())) { | 665 if (KillArcProcess(it->app()->nspid())) { |
| 685 target_memory_to_free_kb -= estimated_memory_freed_kb; | 666 target_memory_to_free_kb -= estimated_memory_freed_kb; |
| 686 uma_->ReportKill(estimated_memory_freed_kb); | 667 uma_->ReportKill(estimated_memory_freed_kb); |
| 687 VLOG(2) << "Killed " << *it; | 668 VLOG(2) << "Killed " << *it; |
| 688 } | 669 } |
| 689 } else { | 670 } else { |
| 690 int64_t tab_id = it->tab()->tab_contents_id; | 671 int64_t tab_id = it->tab()->tab_contents_id; |
| 672 // The estimation is problematic since multiple tabs may share the same |
| 673 // process, while the calculation counts memory used by the whole process. |
| 674 // So |estimated_memory_freed_kb| is an over-estimation. |
| 691 int estimated_memory_freed_kb = | 675 int estimated_memory_freed_kb = |
| 692 mem_stat_->EstimatedMemoryFreedKB(it->tab()->renderer_handle); | 676 mem_stat_->EstimatedMemoryFreedKB(it->tab()->renderer_handle); |
| 693 if (KillTab(tab_id)) { | 677 if (KillTab(tab_id)) { |
| 694 target_memory_to_free_kb -= estimated_memory_freed_kb; | 678 target_memory_to_free_kb -= estimated_memory_freed_kb; |
| 695 uma_->ReportKill(estimated_memory_freed_kb); | 679 uma_->ReportKill(estimated_memory_freed_kb); |
| 696 VLOG(2) << "Killed " << *it; | 680 VLOG(2) << "Killed " << *it; |
| 697 } | 681 } |
| 698 } | 682 } |
| 699 if (target_memory_to_free_kb < 0) | 683 if (target_memory_to_free_kb < 0) |
| 700 break; | 684 break; |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 735 | 719 |
| 736 ProcessScoreMap new_map; | 720 ProcessScoreMap new_map; |
| 737 | 721 |
| 738 // Higher priority part. | 722 // Higher priority part. |
| 739 DistributeOomScoreInRange(candidates.begin(), lower_priority_part, | 723 DistributeOomScoreInRange(candidates.begin(), lower_priority_part, |
| 740 chrome::kLowestRendererOomScore, range_middle, | 724 chrome::kLowestRendererOomScore, range_middle, |
| 741 &new_map); | 725 &new_map); |
| 742 // Lower priority part. | 726 // Lower priority part. |
| 743 DistributeOomScoreInRange(lower_priority_part, candidates.end(), range_middle, | 727 DistributeOomScoreInRange(lower_priority_part, candidates.end(), range_middle, |
| 744 chrome::kHighestRendererOomScore, &new_map); | 728 chrome::kHighestRendererOomScore, &new_map); |
| 745 base::AutoLock oom_score_autolock(oom_score_lock_); | |
| 746 oom_score_map_.swap(new_map); | 729 oom_score_map_.swap(new_map); |
| 747 } | 730 } |
| 748 | 731 |
| 749 void TabManagerDelegate::SetOomScoreAdjForApp(int nspid, int score) { | |
| 750 if (!arc_process_instance_) | |
| 751 return; | |
| 752 if (arc_process_instance_version_ < 2) { | |
| 753 VLOG(1) << "ProcessInstance version < 2 does not " | |
| 754 "support SetOomScoreAdj() yet."; | |
| 755 return; | |
| 756 } | |
| 757 arc_process_instance_->SetOomScoreAdj(nspid, score); | |
| 758 } | |
| 759 | |
| 760 void TabManagerDelegate::SetOomScoreAdjForTabs( | |
| 761 const std::vector<std::pair<base::ProcessHandle, int>>& entries) { | |
| 762 BrowserThread::PostTask( | |
| 763 BrowserThread::FILE, FROM_HERE, | |
| 764 base::Bind(&TabManagerDelegate::SetOomScoreAdjForTabsOnFileThread, | |
| 765 base::Unretained(this), entries)); | |
| 766 } | |
| 767 | |
| 768 void TabManagerDelegate::SetOomScoreAdjForTabsOnFileThread( | |
| 769 const std::vector<std::pair<base::ProcessHandle, int>>& entries) { | |
| 770 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | |
| 771 for (const auto& entry : entries) { | |
| 772 content::ZygoteHost::GetInstance()->AdjustRendererOOMScore(entry.first, | |
| 773 entry.second); | |
| 774 } | |
| 775 } | |
| 776 | |
| 777 void TabManagerDelegate::DistributeOomScoreInRange( | 732 void TabManagerDelegate::DistributeOomScoreInRange( |
| 778 std::vector<TabManagerDelegate::Candidate>::const_iterator begin, | 733 std::vector<TabManagerDelegate::Candidate>::const_iterator begin, |
| 779 std::vector<TabManagerDelegate::Candidate>::const_iterator end, | 734 std::vector<TabManagerDelegate::Candidate>::const_iterator end, |
| 780 int range_begin, | 735 int range_begin, |
| 781 int range_end, | 736 int range_end, |
| 782 ProcessScoreMap* new_map) { | 737 ProcessScoreMap* new_map) { |
| 783 // OOM score setting for tabs involves file system operation so should be | 738 // Processes whose OOM scores should be updated. Ignore duplicated pids but |
| 784 // done on file thread. | 739 // the last occurrence. |
| 785 std::vector<std::pair<base::ProcessHandle, int>> oom_score_for_tabs; | 740 std::map<base::ProcessHandle, int32_t> oom_scores_to_change; |
| 786 | 741 |
| 787 // Though there might be duplicate process handles, it doesn't matter to | 742 // Though there might be duplicate process handles, it doesn't matter to |
| 788 // overestimate the number of processes here since the we don't need to | 743 // overestimate the number of processes here since the we don't need to |
| 789 // use up the full range. | 744 // use up the full range. |
| 790 int num = (end - begin); | 745 int num = (end - begin); |
| 791 const float priority_increment = | 746 const float priority_increment = |
| 792 static_cast<float>(range_end - range_begin) / num; | 747 static_cast<float>(range_end - range_begin) / num; |
| 793 | 748 |
| 794 float priority = range_begin; | 749 float priority = range_begin; |
| 795 for (auto cur = begin; cur != end; ++cur) { | 750 for (auto cur = begin; cur != end; ++cur) { |
| 796 int score = static_cast<int>(priority + 0.5f); | 751 int score = static_cast<int>(priority + 0.5f); |
| 752 |
| 753 base::ProcessHandle pid = base::kNullProcessHandle; |
| 797 if (cur->app()) { | 754 if (cur->app()) { |
| 798 // Use pid as map keys so it's globally unique. | 755 pid = cur->app()->pid(); |
| 799 (*new_map)[cur->app()->pid()] = score; | |
| 800 int cur_app_pid_score = 0; | |
| 801 { | |
| 802 base::AutoLock oom_score_autolock(oom_score_lock_); | |
| 803 cur_app_pid_score = oom_score_map_[cur->app()->pid()]; | |
| 804 } | |
| 805 if (cur_app_pid_score != score) { | |
| 806 VLOG(3) << "Set OOM score " << score << " for " << *cur; | |
| 807 SetOomScoreAdjForApp(cur->app()->nspid(), score); | |
| 808 } | |
| 809 } else { | 756 } else { |
| 810 base::ProcessHandle process_handle = cur->tab()->renderer_handle; | 757 pid = cur->tab()->renderer_handle; |
| 811 // 1. tab_list contains entries for already-discarded tabs. If the PID | 758 // 1. tab_list contains entries for already-discarded tabs. If the PID |
| 812 // (renderer_handle) is zero, we don't need to adjust the oom_score. | 759 // (renderer_handle) is zero, we don't need to adjust the oom_score. |
| 813 // 2. Only add unseen process handle so if there's multiple tab maps to | 760 // 2. Only add unseen process handle so if there's multiple tab maps to |
| 814 // the same process, the process is set to an OOM score based on its "most | 761 // the same process, the process is set to an OOM score based on its "most |
| 815 // important" tab. | 762 // important" tab. |
| 816 if (process_handle != 0 && | 763 if (pid == base::kNullProcessHandle || |
| 817 new_map->find(process_handle) == new_map->end()) { | 764 new_map->find(pid) != new_map->end()) |
| 818 (*new_map)[process_handle] = score; | 765 continue; |
| 819 int process_handle_score = 0; | 766 } |
| 820 { | 767 |
| 821 base::AutoLock oom_score_autolock(oom_score_lock_); | 768 if (pid == base::kNullProcessHandle) |
| 822 process_handle_score = oom_score_map_[process_handle]; | 769 continue; |
| 823 } | 770 |
| 824 if (process_handle_score != score) { | 771 // Update the to-be-cached OOM score map. Use pid as map keys so it's |
| 825 oom_score_for_tabs.push_back(std::make_pair(process_handle, score)); | 772 // globally unique. |
| 826 VLOG(3) << "Set OOM score " << score << " for " << *cur; | 773 (*new_map)[pid] = score; |
| 827 } | 774 |
| 828 } else { | 775 // Need to update OOM score if the calculated score is different from |
| 829 continue; // Skip priority increment. | 776 // current cached score. |
| 830 } | 777 if (oom_score_map_[pid] != score) { |
| 778 VLOG(3) << "Update OOM score " << score << " for " << *cur; |
| 779 oom_scores_to_change[pid] = static_cast<int32_t>(score); |
| 831 } | 780 } |
| 832 priority += priority_increment; | 781 priority += priority_increment; |
| 833 } | 782 } |
| 834 | 783 |
| 835 if (oom_score_for_tabs.size()) | 784 if (oom_scores_to_change.size()) |
| 836 SetOomScoreAdjForTabs(oom_score_for_tabs); | 785 GetDebugDaemonClient()->SetOomScoreAdj( |
| 786 oom_scores_to_change, base::Bind(&OnSetOomScoreAdj)); |
| 837 } | 787 } |
| 838 | 788 |
| 839 } // namespace memory | 789 } // namespace memory |
| OLD | NEW |