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 |