OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/oom_priority_manager.h" |
| 6 |
| 7 #include <list> |
| 8 |
| 9 #include "base/process.h" |
| 10 #include "base/process_util.h" |
| 11 #include "base/thread.h" |
| 12 #include "build/build_config.h" |
| 13 #include "chrome/browser/browser_list.h" |
| 14 #include "chrome/browser/chrome_thread.h" |
| 15 #include "chrome/browser/renderer_host/render_process_host.h" |
| 16 #include "chrome/browser/tab_contents/tab_contents.h" |
| 17 #include "chrome/browser/tabs/tab_strip_model.h" |
| 18 #include "chrome/browser/zygote_host_linux.h" |
| 19 |
| 20 #if !defined(OS_CHROMEOS) |
| 21 #error This file only meant to be compiled on ChromeOS |
| 22 #endif |
| 23 |
| 24 using base::TimeDelta; |
| 25 using base::TimeTicks; |
| 26 using base::ProcessHandle; |
| 27 using base::ProcessMetrics; |
| 28 |
| 29 namespace browser { |
| 30 |
| 31 // The default interval in seconds after which to adjust the oom_adj |
| 32 // value. |
| 33 #define ADJUSTMENT_INTERVAL_SECONDS 10 |
| 34 |
| 35 // The default interval in minutes for considering activation times |
| 36 // "equal". |
| 37 #define BUCKET_INTERVAL_MINUTES 10 |
| 38 |
| 39 OomPriorityManager::OomPriorityManager() { |
| 40 StartTimer(); |
| 41 } |
| 42 |
| 43 OomPriorityManager::~OomPriorityManager() { |
| 44 StopTimer(); |
| 45 } |
| 46 |
| 47 void OomPriorityManager::StartTimer() { |
| 48 if (!timer_.IsRunning()) { |
| 49 timer_.Start(TimeDelta::FromSeconds(ADJUSTMENT_INTERVAL_SECONDS), |
| 50 this, |
| 51 &OomPriorityManager::AdjustOomPriorities); |
| 52 } |
| 53 } |
| 54 |
| 55 void OomPriorityManager::StopTimer() { |
| 56 timer_.Stop(); |
| 57 } |
| 58 |
| 59 |
| 60 namespace { |
| 61 struct RendererStats { |
| 62 bool is_pinned; |
| 63 TimeTicks last_selected; |
| 64 size_t memory_used; |
| 65 ProcessHandle renderer_handle; |
| 66 }; |
| 67 |
| 68 // Returns true if |first| is considered less desirable to be killed |
| 69 // than |second|. |
| 70 bool SortRendererStats(RendererStats first, RendererStats second) { |
| 71 // The size of the slop in comparing activation times. [This is |
| 72 // allocated here to avoid static initialization at startup time.] |
| 73 static const int64 kTimeBucketInterval = |
| 74 TimeDelta::FromMinutes(BUCKET_INTERVAL_MINUTES).ToInternalValue(); |
| 75 |
| 76 // Being pinned is most important. |
| 77 if (first.is_pinned != second.is_pinned) |
| 78 return first.is_pinned == true; |
| 79 |
| 80 // We want to be a little "fuzzy" when we compare these, because |
| 81 // it's not really possible for the times to be identical, but if |
| 82 // the user selected two tabs at about the same time, we still want |
| 83 // to take the one that uses more memory. |
| 84 if (abs((first.last_selected - second.last_selected).ToInternalValue()) < |
| 85 kTimeBucketInterval) |
| 86 return first.last_selected < second.last_selected; |
| 87 |
| 88 return first.memory_used < second.memory_used; |
| 89 } |
| 90 } // anonymous namespace |
| 91 |
| 92 // Post a new task for the file thread to actually adjust the priorities. |
| 93 void OomPriorityManager::AdjustOomPriorities() { |
| 94 ChromeThread::PostTask( |
| 95 ChromeThread::FILE, FROM_HERE, |
| 96 NewRunnableMethod(this, &OomPriorityManager::DoAdjustOomPriorities)); |
| 97 } |
| 98 |
| 99 // Here we collect all the information we need to sort the existing |
| 100 // renderers in priority order, and hand out oom_adj scores based on |
| 101 // that sort order. |
| 102 // |
| 103 // Things we need to collect: |
| 104 // 1) whether or not a tab is pinned |
| 105 // 2) last time a tab was selected |
| 106 // 3) size in memory of a tab |
| 107 void OomPriorityManager::DoAdjustOomPriorities() { |
| 108 std::list<RendererStats> renderer_stats; |
| 109 |
| 110 if (BrowserList::size() == 0) |
| 111 return; |
| 112 |
| 113 for (BrowserList::const_iterator browser_iterator = BrowserList::begin(); |
| 114 browser_iterator != BrowserList::end(); ++browser_iterator) { |
| 115 Browser* browser = *browser_iterator; |
| 116 const TabStripModel* model = browser->tabstrip_model(); |
| 117 for (int i = 0; i < model->count(); i++) { |
| 118 TabContents* contents = model->GetTabContentsAt(i); |
| 119 RendererStats stats; |
| 120 stats.last_selected = contents->last_selected_time(); |
| 121 stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle(); |
| 122 scoped_ptr<ProcessMetrics> metrics( |
| 123 ProcessMetrics::CreateProcessMetrics(stats.renderer_handle)); |
| 124 |
| 125 base::WorkingSetKBytes working_set_kbytes; |
| 126 if (metrics->GetWorkingSetKBytes(&working_set_kbytes)) { |
| 127 // We use the proportional set size (PSS) to calculate memory |
| 128 // usage "badness" on Linux. |
| 129 stats.memory_used = working_set_kbytes.shared * 1024; |
| 130 } else { |
| 131 // and if for some reason we can't get PSS, we revert to using |
| 132 // resident set size (RSS). |
| 133 stats.memory_used = metrics->GetWorkingSetSize(); |
| 134 } |
| 135 |
| 136 stats.is_pinned = model->IsTabPinned(i); |
| 137 renderer_stats.push_back(stats); |
| 138 } |
| 139 } |
| 140 |
| 141 // Now we sort the data we collected so that least desirable to be |
| 142 // killed is first, most desirable is last. |
| 143 renderer_stats.sort(SortRendererStats); |
| 144 |
| 145 // Now we assign priorities based on the sorted list. We're |
| 146 // assigning priorities in the range of 5 to 10. oom_adj takes |
| 147 // values from -17 to 15. Negative values are reserved for system |
| 148 // processes, and we want to give some room on either side of the |
| 149 // range we're using to allow for things that want to be above or |
| 150 // below the renderers in priority, so 5 to 10 gives us some |
| 151 // variation in priority without taking up the whole range. In the |
| 152 // end, however, it's a pretty arbitrary range to use. Higher |
| 153 // values are more likely to be killed by the OOM killer. |
| 154 const int kMinPriority = 5; |
| 155 const int kMaxPriority = 10; |
| 156 const int kPriorityRange = kMaxPriority - kMinPriority; |
| 157 float priority_increment = |
| 158 static_cast<float>(kPriorityRange) / renderer_stats.size(); |
| 159 float priority = kMinPriority; |
| 160 for (std::list<RendererStats>::iterator iterator = renderer_stats.begin(); |
| 161 iterator != renderer_stats.end(); ++iterator) { |
| 162 Singleton<ZygoteHost>()->AdjustRendererOOMScore( |
| 163 iterator->renderer_handle, |
| 164 static_cast<int>(priority + 0.5f)); |
| 165 priority += priority_increment; |
| 166 } |
| 167 } |
| 168 |
| 169 } // namespace browser |
OLD | NEW |