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