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 <math.h> | |
7 #include <stdint.h> | 8 #include <stdint.h> |
8 | 9 |
9 #include <algorithm> | 10 #include <algorithm> |
10 #include <map> | 11 #include <map> |
11 #include <vector> | 12 #include <vector> |
12 | 13 |
13 #include "ash/shell.h" | 14 #include "ash/shell.h" |
14 #include "base/bind.h" | 15 #include "base/bind.h" |
15 #include "base/command_line.h" | 16 #include "base/command_line.h" |
16 #include "base/files/file_path.h" | 17 #include "base/files/file_path.h" |
(...skipping 12 matching lines...) Expand all Loading... | |
29 #include "chrome/browser/memory/memory_kills_monitor.h" | 30 #include "chrome/browser/memory/memory_kills_monitor.h" |
30 #include "chrome/browser/memory/tab_stats.h" | 31 #include "chrome/browser/memory/tab_stats.h" |
31 #include "chrome/browser/ui/browser.h" | 32 #include "chrome/browser/ui/browser.h" |
32 #include "chrome/browser/ui/browser_list.h" | 33 #include "chrome/browser/ui/browser_list.h" |
33 #include "chrome/browser/ui/tabs/tab_strip_model.h" | 34 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
34 #include "chrome/common/chrome_constants.h" | 35 #include "chrome/common/chrome_constants.h" |
35 #include "chrome/common/chrome_features.h" | 36 #include "chrome/common/chrome_features.h" |
36 #include "chromeos/dbus/dbus_thread_manager.h" | 37 #include "chromeos/dbus/dbus_thread_manager.h" |
37 #include "components/arc/arc_bridge_service.h" | 38 #include "components/arc/arc_bridge_service.h" |
38 #include "components/arc/arc_service_manager.h" | 39 #include "components/arc/arc_service_manager.h" |
39 #include "components/arc/common/process.mojom.h" | |
40 #include "components/device_event_log/device_event_log.h" | 40 #include "components/device_event_log/device_event_log.h" |
41 #include "components/exo/shell_surface.h" | 41 #include "components/exo/shell_surface.h" |
42 #include "content/public/browser/browser_thread.h" | 42 #include "content/public/browser/browser_thread.h" |
43 #include "content/public/browser/notification_service.h" | 43 #include "content/public/browser/notification_service.h" |
44 #include "content/public/browser/notification_types.h" | 44 #include "content/public/browser/notification_types.h" |
45 #include "content/public/browser/render_process_host.h" | 45 #include "content/public/browser/render_process_host.h" |
46 #include "content/public/browser/render_widget_host.h" | 46 #include "content/public/browser/render_widget_host.h" |
47 #include "content/public/browser/zygote_host_linux.h" | 47 #include "content/public/browser/zygote_host_linux.h" |
48 #include "ui/aura/window.h" | 48 #include "ui/aura/window.h" |
49 #include "ui/wm/public/activation_client.h" | 49 #include "ui/wm/public/activation_client.h" |
(...skipping 29 matching lines...) Expand all Loading... | |
79 return base::StartsWith(application_id, kArcProcessNamePrefix, | 79 return base::StartsWith(application_id, kArcProcessNamePrefix, |
80 base::CompareCase::SENSITIVE); | 80 base::CompareCase::SENSITIVE); |
81 } | 81 } |
82 | 82 |
83 bool IsArcMemoryManagementEnabled() { | 83 bool IsArcMemoryManagementEnabled() { |
84 return base::FeatureList::IsEnabled(features::kArcMemoryManagement); | 84 return base::FeatureList::IsEnabled(features::kArcMemoryManagement); |
85 } | 85 } |
86 | 86 |
87 void OnSetOomScoreAdj(bool success, const std::string& output) { | 87 void OnSetOomScoreAdj(bool success, const std::string& output) { |
88 VLOG(2) << "OnSetOomScoreAdj " << success << " " << output; | 88 VLOG(2) << "OnSetOomScoreAdj " << success << " " << output; |
89 if (!success || output != "") | 89 if (!success) |
90 LOG(WARNING) << "Set OOM score error: " << output; | 90 LOG(ERROR) << "Set OOM score error: " << output; |
91 else if (!output.empty()) | |
92 LOG(WARNING) << "Set OOM score: " << output; | |
91 } | 93 } |
92 | 94 |
93 } // namespace | 95 } // namespace |
94 | 96 |
97 // static | |
98 const int TabManagerDelegate::kLowestOomScore = -1000; | |
99 | |
95 std::ostream& operator<<(std::ostream& os, const ProcessType& type) { | 100 std::ostream& operator<<(std::ostream& os, const ProcessType& type) { |
96 switch (type) { | 101 switch (type) { |
97 case ProcessType::FOCUSED_TAB: | 102 case ProcessType::FOCUSED_TAB: |
98 return os << "FOCUSED_TAB"; | 103 return os << "FOCUSED_TAB"; |
99 case ProcessType::FOCUSED_APP: | 104 case ProcessType::FOCUSED_APP: |
100 return os << "FOCUSED_APP"; | 105 return os << "FOCUSED_APP"; |
101 case ProcessType::VISIBLE_APP: | 106 case ProcessType::IMPORTANT_APP: |
102 return os << "VISIBLE_APP"; | 107 return os << "IMPORTANT_APP"; |
103 case ProcessType::BACKGROUND_TAB: | 108 case ProcessType::BACKGROUND_TAB: |
104 return os << "BACKGROUND_TAB"; | 109 return os << "BACKGROUND_TAB"; |
105 case ProcessType::BACKGROUND_APP: | 110 case ProcessType::BACKGROUND_APP: |
106 return os << "BACKGROUND_APP"; | 111 return os << "BACKGROUND_APP"; |
107 case ProcessType::UNKNOWN_TYPE: | 112 case ProcessType::UNKNOWN_TYPE: |
108 return os << "UNKNOWN_TYPE"; | 113 return os << "UNKNOWN_TYPE"; |
109 default: | 114 default: |
110 return os << "NOT_IMPLEMENTED_ERROR"; | 115 return os << "NOT_IMPLEMENTED_ERROR"; |
111 } | 116 } |
112 return os; | 117 return os; |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
148 // apps. | 153 // apps. |
149 NOTREACHED() << "Undefined comparison between apps and tabs: process_type=" | 154 NOTREACHED() << "Undefined comparison between apps and tabs: process_type=" |
150 << process_type(); | 155 << process_type(); |
151 return app(); | 156 return app(); |
152 } | 157 } |
153 | 158 |
154 ProcessType TabManagerDelegate::Candidate::GetProcessTypeInternal() const { | 159 ProcessType TabManagerDelegate::Candidate::GetProcessTypeInternal() const { |
155 if (app()) { | 160 if (app()) { |
156 if (app()->is_focused()) | 161 if (app()->is_focused()) |
157 return ProcessType::FOCUSED_APP; | 162 return ProcessType::FOCUSED_APP; |
158 if (app()->process_state() <= | 163 if (app()->IsImportant()) |
159 arc::mojom::ProcessState::IMPORTANT_FOREGROUND) { | 164 return ProcessType::IMPORTANT_APP; |
160 return ProcessType::VISIBLE_APP; | |
161 } | |
162 return ProcessType::BACKGROUND_APP; | 165 return ProcessType::BACKGROUND_APP; |
163 } | 166 } |
164 if (tab()) { | 167 if (tab()) { |
165 if (tab()->is_selected) | 168 if (tab()->is_selected) |
166 return ProcessType::FOCUSED_TAB; | 169 return ProcessType::FOCUSED_TAB; |
167 return ProcessType::BACKGROUND_TAB; | 170 return ProcessType::BACKGROUND_TAB; |
168 } | 171 } |
169 NOTREACHED() << "Unexpected process type"; | 172 NOTREACHED() << "Unexpected process type"; |
170 return ProcessType::UNKNOWN_TYPE; | 173 return ProcessType::UNKNOWN_TYPE; |
171 } | 174 } |
(...skipping 352 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
524 TabManagerDelegate::GetSortedCandidates( | 527 TabManagerDelegate::GetSortedCandidates( |
525 const TabStatsList& tab_list, | 528 const TabStatsList& tab_list, |
526 const std::vector<arc::ArcProcess>& arc_processes) { | 529 const std::vector<arc::ArcProcess>& arc_processes) { |
527 std::vector<Candidate> candidates; | 530 std::vector<Candidate> candidates; |
528 candidates.reserve(tab_list.size() + arc_processes.size()); | 531 candidates.reserve(tab_list.size() + arc_processes.size()); |
529 | 532 |
530 for (const auto& tab : tab_list) { | 533 for (const auto& tab : tab_list) { |
531 candidates.emplace_back(&tab); | 534 candidates.emplace_back(&tab); |
532 } | 535 } |
533 | 536 |
534 // A special process on Android side which serves as a dummy "focused" app | |
535 // when the focused window is a Chrome side window (i.e., all Android | |
536 // processes are running in the background). We don't want to kill it anyway. | |
537 static constexpr char kArcInBackgroundDummyprocess[] = | |
538 "org.chromium.arc.home"; | |
539 | |
540 for (const auto& app : arc_processes) { | 537 for (const auto& app : arc_processes) { |
541 // Skip persistent android processes since they should never be killed here. | |
542 // Neither do we set their OOM scores so their score remains minimum. | |
543 if (app.process_state() <= arc::mojom::ProcessState::PERSISTENT_UI || | |
544 app.process_name() == kArcInBackgroundDummyprocess) { | |
545 continue; | |
546 } | |
547 candidates.emplace_back(&app); | 538 candidates.emplace_back(&app); |
548 } | 539 } |
549 | 540 |
550 // Sort candidates according to priority. | 541 // Sort candidates according to priority. |
551 std::sort(candidates.begin(), candidates.end()); | 542 std::sort(candidates.begin(), candidates.end()); |
552 | 543 |
553 return candidates; | 544 return candidates; |
554 } | 545 } |
555 | 546 |
556 bool TabManagerDelegate::IsRecentlyKilledArcProcess( | 547 bool TabManagerDelegate::IsRecentlyKilledArcProcess( |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
602 | 593 |
603 // Kill processes until the estimated amount of freed memory is sufficient to | 594 // Kill processes until the estimated amount of freed memory is sufficient to |
604 // bring the system memory back to a normal level. | 595 // bring the system memory back to a normal level. |
605 // The list is sorted by descending importance, so we go through the list | 596 // The list is sorted by descending importance, so we go through the list |
606 // backwards. | 597 // backwards. |
607 for (auto it = candidates.rbegin(); it != candidates.rend(); ++it) { | 598 for (auto it = candidates.rbegin(); it != candidates.rend(); ++it) { |
608 MEMORY_LOG(ERROR) << "Target memory to free: " << target_memory_to_free_kb | 599 MEMORY_LOG(ERROR) << "Target memory to free: " << target_memory_to_free_kb |
609 << " KB"; | 600 << " KB"; |
610 if (target_memory_to_free_kb <= 0) | 601 if (target_memory_to_free_kb <= 0) |
611 break; | 602 break; |
612 // Never kill selected tab or Android foreground app, regardless whether | 603 // Never kill selected tab, foreground app, and important apps regardless of |
613 // they're in the active window. Since the user experience would be bad. | 604 // whether they're in the active window. Since the user experience would be |
605 // bad. | |
614 ProcessType process_type = it->process_type(); | 606 ProcessType process_type = it->process_type(); |
615 if (process_type == ProcessType::VISIBLE_APP || | 607 if (process_type <= ProcessType::IMPORTANT_APP) { |
616 process_type == ProcessType::FOCUSED_APP || | |
617 process_type == ProcessType::FOCUSED_TAB) { | |
618 MEMORY_LOG(ERROR) << "Skipped killing " << *it; | 608 MEMORY_LOG(ERROR) << "Skipped killing " << *it; |
619 continue; | 609 continue; |
620 } | 610 } |
621 if (it->app()) { | 611 if (it->app()) { |
622 if (IsRecentlyKilledArcProcess(it->app()->process_name(), now)) { | 612 if (IsRecentlyKilledArcProcess(it->app()->process_name(), now)) { |
623 MEMORY_LOG(ERROR) << "Avoided killing " << *it << " too often"; | 613 MEMORY_LOG(ERROR) << "Avoided killing " << *it << " too often"; |
624 continue; | 614 continue; |
625 } | 615 } |
626 int estimated_memory_freed_kb = | 616 int estimated_memory_freed_kb = |
627 mem_stat_->EstimatedMemoryFreedKB(it->app()->pid()); | 617 mem_stat_->EstimatedMemoryFreedKB(it->app()->pid()); |
(...skipping 25 matching lines...) Expand all Loading... | |
653 } | 643 } |
654 if (target_memory_to_free_kb > 0) { | 644 if (target_memory_to_free_kb > 0) { |
655 MEMORY_LOG(ERROR) | 645 MEMORY_LOG(ERROR) |
656 << "Unable to kill enough candidates to meet target_memory_to_free_kb "; | 646 << "Unable to kill enough candidates to meet target_memory_to_free_kb "; |
657 } | 647 } |
658 } | 648 } |
659 | 649 |
660 void TabManagerDelegate::AdjustOomPrioritiesImpl( | 650 void TabManagerDelegate::AdjustOomPrioritiesImpl( |
661 const TabStatsList& tab_list, | 651 const TabStatsList& tab_list, |
662 const std::vector<arc::ArcProcess>& arc_processes) { | 652 const std::vector<arc::ArcProcess>& arc_processes) { |
653 std::vector<TabManagerDelegate::Candidate> candidates; | |
654 std::vector<TabManagerDelegate::Candidate> apps_non_killable; | |
655 | |
663 // Least important first. | 656 // Least important first. |
664 const auto candidates = GetSortedCandidates(tab_list, arc_processes); | 657 auto all_candidates = GetSortedCandidates(tab_list, arc_processes); |
658 for (auto& candidate : all_candidates) { | |
659 // TODO(cylee|yusukes): Consider using IsImportant() instead of | |
660 // IsKernelKillable() for simplicity. | |
661 // TODO(cylee): Also consider protecting FOCUSED_TAB from the kernel OOM | |
Yusuke Sato
2017/05/11 18:50:16
Added this to track.
cylee1
2017/05/11 19:29:14
Done.
| |
662 // killer so that Chrome and the kernel do the same regarding OOM handling. | |
663 if (!candidate.app() || candidate.app()->IsKernelKillable()) { | |
664 // Add tabs and killable apps to |candidates|. | |
665 candidates.emplace_back(std::move(candidate)); | |
666 } else { | |
667 // Add non-killable apps to |apps_non_killable|. | |
668 apps_non_killable.emplace_back(std::move(candidate)); | |
669 } | |
670 } | |
665 | 671 |
666 // Now we assign priorities based on the sorted list. We're assigning | 672 // Now we assign priorities based on the sorted list. We're assigning |
667 // priorities in the range of kLowestRendererOomScore to | 673 // priorities in the range of kLowestRendererOomScore to |
668 // kHighestRendererOomScore (defined in chrome_constants.h). oom_score_adj | 674 // kHighestRendererOomScore (defined in chrome_constants.h). oom_score_adj |
669 // takes values from -1000 to 1000. Negative values are reserved for system | 675 // takes values from -1000 to 1000. Negative values are reserved for system |
670 // processes, and we want to give some room below the range we're using to | 676 // processes, and we want to give some room below the range we're using to |
671 // allow for things that want to be above the renderers in priority, so the | 677 // allow for things that want to be above the renderers in priority, so the |
672 // defined range gives us some variation in priority without taking up the | 678 // defined range gives us some variation in priority without taking up the |
673 // whole range. In the end, however, it's a pretty arbitrary range to use. | 679 // whole range. In the end, however, it's a pretty arbitrary range to use. |
674 // Higher values are more likely to be killed by the OOM killer. | 680 // Higher values are more likely to be killed by the OOM killer. |
675 | 681 |
676 // Break the processes into 2 parts. This is to help lower the chance of | 682 // Break the processes into 2 parts. This is to help lower the chance of |
677 // altering OOM score for many processes on any small change. | 683 // altering OOM score for many processes on any small change. |
678 int range_middle = | 684 int range_middle = |
679 (chrome::kLowestRendererOomScore + chrome::kHighestRendererOomScore) / 2; | 685 (chrome::kLowestRendererOomScore + chrome::kHighestRendererOomScore) / 2; |
680 | 686 |
681 // Find some pivot point. For now processes with priority >= CHROME_INTERNAL | 687 // Find some pivot point. For now processes with priority >= CHROME_INTERNAL |
682 // are prone to be affected by LRU change. Taking them as "high priority" | 688 // are prone to be affected by LRU change. Taking them as "high priority" |
683 // processes. | 689 // processes. |
684 auto lower_priority_part = candidates.end(); | 690 auto lower_priority_part = candidates.end(); |
685 for (auto it = candidates.begin(); it != candidates.end(); ++it) { | 691 for (auto it = candidates.begin(); it != candidates.end(); ++it) { |
686 if (it->process_type() >= ProcessType::BACKGROUND_APP) { | 692 if (it->process_type() >= ProcessType::BACKGROUND_APP) { |
687 lower_priority_part = it; | 693 lower_priority_part = it; |
688 break; | 694 break; |
689 } | 695 } |
690 } | 696 } |
691 | 697 |
692 ProcessScoreMap new_map; | 698 ProcessScoreMap new_map; |
693 | 699 |
700 // Make the apps non-killable. | |
701 if (!apps_non_killable.empty()) | |
702 SetOomScore(apps_non_killable, kLowestOomScore, &new_map); | |
703 | |
694 // Higher priority part. | 704 // Higher priority part. |
695 DistributeOomScoreInRange(candidates.begin(), lower_priority_part, | 705 DistributeOomScoreInRange(candidates.begin(), lower_priority_part, |
696 chrome::kLowestRendererOomScore, range_middle, | 706 chrome::kLowestRendererOomScore, range_middle, |
697 &new_map); | 707 &new_map); |
698 // Lower priority part. | 708 // Lower priority part. |
699 DistributeOomScoreInRange(lower_priority_part, candidates.end(), range_middle, | 709 DistributeOomScoreInRange(lower_priority_part, candidates.end(), range_middle, |
700 chrome::kHighestRendererOomScore, &new_map); | 710 chrome::kHighestRendererOomScore, &new_map); |
711 | |
701 oom_score_map_.swap(new_map); | 712 oom_score_map_.swap(new_map); |
702 } | 713 } |
703 | 714 |
715 void TabManagerDelegate::SetOomScore( | |
716 const std::vector<TabManagerDelegate::Candidate>& candidates, | |
717 int score, | |
718 ProcessScoreMap* new_map) { | |
719 DistributeOomScoreInRange(candidates.begin(), candidates.end(), score, score, | |
720 new_map); | |
721 } | |
722 | |
704 void TabManagerDelegate::DistributeOomScoreInRange( | 723 void TabManagerDelegate::DistributeOomScoreInRange( |
705 std::vector<TabManagerDelegate::Candidate>::const_iterator begin, | 724 std::vector<TabManagerDelegate::Candidate>::const_iterator begin, |
706 std::vector<TabManagerDelegate::Candidate>::const_iterator end, | 725 std::vector<TabManagerDelegate::Candidate>::const_iterator end, |
707 int range_begin, | 726 int range_begin, |
708 int range_end, | 727 int range_end, |
709 ProcessScoreMap* new_map) { | 728 ProcessScoreMap* new_map) { |
710 // Processes whose OOM scores should be updated. Ignore duplicated pids but | 729 // Processes whose OOM scores should be updated. Ignore duplicated pids but |
711 // the last occurrence. | 730 // the last occurrence. |
712 std::map<base::ProcessHandle, int32_t> oom_scores_to_change; | 731 std::map<base::ProcessHandle, int32_t> oom_scores_to_change; |
713 | 732 |
714 // Though there might be duplicate process handles, it doesn't matter to | 733 // Though there might be duplicate process handles, it doesn't matter to |
715 // overestimate the number of processes here since the we don't need to | 734 // overestimate the number of processes here since the we don't need to |
716 // use up the full range. | 735 // use up the full range. |
717 int num = (end - begin); | 736 int num = (end - begin); |
718 const float priority_increment = | 737 const float priority_increment = |
719 static_cast<float>(range_end - range_begin) / num; | 738 static_cast<float>(range_end - range_begin) / num; |
720 | 739 |
721 float priority = range_begin; | 740 float priority = range_begin; |
722 for (auto cur = begin; cur != end; ++cur) { | 741 for (auto cur = begin; cur != end; ++cur) { |
723 int score = static_cast<int>(priority + 0.5f); | 742 const int score = round(priority); |
724 | 743 |
725 base::ProcessHandle pid = base::kNullProcessHandle; | 744 base::ProcessHandle pid = base::kNullProcessHandle; |
726 if (cur->app()) { | 745 if (cur->app()) { |
727 pid = cur->app()->pid(); | 746 pid = cur->app()->pid(); |
728 } else { | 747 } else { |
729 pid = cur->tab()->renderer_handle; | 748 pid = cur->tab()->renderer_handle; |
730 // 1. tab_list contains entries for already-discarded tabs. If the PID | 749 // 1. tab_list contains entries for already-discarded tabs. If the PID |
731 // (renderer_handle) is zero, we don't need to adjust the oom_score. | 750 // (renderer_handle) is zero, we don't need to adjust the oom_score. |
732 // 2. Only add unseen process handle so if there's multiple tab maps to | 751 // 2. Only add unseen process handle so if there's multiple tab maps to |
733 // the same process, the process is set to an OOM score based on its "most | 752 // the same process, the process is set to an OOM score based on its "most |
(...skipping 12 matching lines...) Expand all Loading... | |
746 | 765 |
747 // Need to update OOM score if the calculated score is different from | 766 // Need to update OOM score if the calculated score is different from |
748 // current cached score. | 767 // current cached score. |
749 if (oom_score_map_[pid] != score) { | 768 if (oom_score_map_[pid] != score) { |
750 VLOG(3) << "Update OOM score " << score << " for " << *cur; | 769 VLOG(3) << "Update OOM score " << score << " for " << *cur; |
751 oom_scores_to_change[pid] = static_cast<int32_t>(score); | 770 oom_scores_to_change[pid] = static_cast<int32_t>(score); |
752 } | 771 } |
753 priority += priority_increment; | 772 priority += priority_increment; |
754 } | 773 } |
755 | 774 |
756 if (oom_scores_to_change.size()) | 775 if (oom_scores_to_change.size()) { |
Yusuke Sato
2017/05/11 18:50:16
I believe multi-line if-body requires {}.
cylee1
2017/05/11 19:29:14
Acknowledged.
| |
757 GetDebugDaemonClient()->SetOomScoreAdj( | 776 GetDebugDaemonClient()->SetOomScoreAdj( |
758 oom_scores_to_change, base::Bind(&OnSetOomScoreAdj)); | 777 oom_scores_to_change, base::Bind(&OnSetOomScoreAdj)); |
778 } | |
759 } | 779 } |
760 | 780 |
761 } // namespace memory | 781 } // namespace memory |
OLD | NEW |