- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / memory / oom_priority_manager.cc
1 // Copyright (c) 2012 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/chromeos/memory/oom_priority_manager.h"
6
7 #include <algorithm>
8 #include <set>
9 #include <vector>
10
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/command_line.h"
14 #include "base/metrics/field_trial.h"
15 #include "base/metrics/histogram.h"
16 #include "base/process/process.h"
17 #include "base/strings/string16.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/synchronization/lock.h"
22 #include "base/threading/thread.h"
23 #include "base/time/time.h"
24 #include "build/build_config.h"
25 #include "chrome/browser/browser_process.h"
26 #include "chrome/browser/browser_process_platform_part_chromeos.h"
27 #include "chrome/browser/memory_details.h"
28 #include "chrome/browser/ui/browser.h"
29 #include "chrome/browser/ui/browser_iterator.h"
30 #include "chrome/browser/ui/browser_list.h"
31 #include "chrome/browser/ui/host_desktop.h"
32 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
33 #include "chrome/browser/ui/tabs/tab_strip_model.h"
34 #include "chrome/browser/ui/tabs/tab_utils.h"
35 #include "chrome/common/chrome_constants.h"
36 #include "chrome/common/url_constants.h"
37 #include "chromeos/chromeos_switches.h"
38 #include "chromeos/memory/low_memory_listener.h"
39 #include "content/public/browser/browser_thread.h"
40 #include "content/public/browser/notification_service.h"
41 #include "content/public/browser/notification_types.h"
42 #include "content/public/browser/render_process_host.h"
43 #include "content/public/browser/render_widget_host.h"
44 #include "content/public/browser/web_contents.h"
45 #include "content/public/browser/zygote_host_linux.h"
46 #include "ui/base/text/bytes_formatting.h"
47
48 using base::TimeDelta;
49 using base::TimeTicks;
50 using content::BrowserThread;
51 using content::WebContents;
52
53 namespace chromeos {
54
55 namespace {
56
57 // Record a size in megabytes, over a potential interval up to 32 GB.
58 #define HISTOGRAM_MEGABYTES(name, sample)                     \
59     UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 32768, 50)
60
61 // The default interval in seconds after which to adjust the oom_score_adj
62 // value.
63 const int kAdjustmentIntervalSeconds = 10;
64
65 // For each period of this length we record a statistic to indicate whether
66 // or not the user experienced a low memory event. If you change this interval
67 // you must replace Tabs.Discard.DiscardInLastMinute with a new statistic.
68 const int kRecentTabDiscardIntervalSeconds = 60;
69
70 // If there has been no priority adjustment in this interval, we assume the
71 // machine was suspended and correct our timing statistics.
72 const int kSuspendThresholdSeconds = kAdjustmentIntervalSeconds * 4;
73
74 // When switching to a new tab the tab's renderer's OOM score needs to be
75 // updated to reflect its front-most status and protect it from discard.
76 // However, doing this immediately might slow down tab switch time, so wait
77 // a little while before doing the adjustment.
78 const int kFocusedTabScoreAdjustIntervalMs = 500;
79
80 // Returns a unique ID for a WebContents.  Do not cast back to a pointer, as
81 // the WebContents could be deleted if the user closed the tab.
82 int64 IdFromWebContents(WebContents* web_contents) {
83   return reinterpret_cast<int64>(web_contents);
84 }
85
86 // Records a statistics |sample| for UMA histogram |name| using a linear
87 // distribution of buckets.
88 void RecordLinearHistogram(const std::string& name,
89                            int sample,
90                            int maximum,
91                            size_t bucket_count) {
92   // Do not use the UMA_HISTOGRAM_... macros here.  They cache the Histogram
93   // instance and thus only work if |name| is constant.
94   base::HistogramBase* counter = base::LinearHistogram::FactoryGet(
95       name,
96       1,  // Minimum. The 0 bin for underflow is automatically added.
97       maximum + 1,  // Ensure bucket size of |maximum| / |bucket_count|.
98       bucket_count + 2,  // Account for the underflow and overflow bins.
99       base::Histogram::kUmaTargetedHistogramFlag);
100   counter->Add(sample);
101 }
102
103 }  // namespace
104
105 ////////////////////////////////////////////////////////////////////////////////
106 // OomMemoryDetails logs details about all Chrome processes during an out-of-
107 // memory event in an attempt to identify the culprit, then discards a tab and
108 // deletes itself.
109 class OomMemoryDetails : public MemoryDetails {
110  public:
111   OomMemoryDetails();
112
113   // MemoryDetails overrides:
114   virtual void OnDetailsAvailable() OVERRIDE;
115
116  private:
117   virtual ~OomMemoryDetails() {}
118
119   TimeTicks start_time_;
120
121   DISALLOW_COPY_AND_ASSIGN(OomMemoryDetails);
122 };
123
124 OomMemoryDetails::OomMemoryDetails() {
125   AddRef();  // Released in OnDetailsAvailable().
126   start_time_ = TimeTicks::Now();
127 }
128
129 void OomMemoryDetails::OnDetailsAvailable() {
130   TimeDelta delta = TimeTicks::Now() - start_time_;
131   // These logs are collected by user feedback reports.  We want them to help
132   // diagnose user-reported problems with frequently discarded tabs.
133   std::string log_string = ToLogString();
134   base::SystemMemoryInfoKB memory;
135   if (base::GetSystemMemoryInfo(&memory) && memory.gem_size != -1) {
136     log_string += "Graphics ";
137     log_string += UTF16ToASCII(ui::FormatBytes(memory.gem_size));
138   }
139   LOG(WARNING) << "OOM details (" << delta.InMilliseconds() << " ms):\n"
140       << log_string;
141   if (g_browser_process &&
142       g_browser_process->platform_part()->oom_priority_manager()) {
143     OomPriorityManager* manager =
144         g_browser_process->platform_part()->oom_priority_manager();
145     manager->PurgeBrowserMemory();
146     manager->DiscardTab();
147   }
148   // Delete ourselves so we don't have to worry about OomPriorityManager
149   // deleting us when we're still working.
150   Release();
151 }
152
153 ////////////////////////////////////////////////////////////////////////////////
154 // OomPriorityManager
155
156 OomPriorityManager::TabStats::TabStats()
157   : is_app(false),
158     is_reloadable_ui(false),
159     is_playing_audio(false),
160     is_pinned(false),
161     is_selected(false),
162     is_discarded(false),
163     renderer_handle(0),
164     tab_contents_id(0) {
165 }
166
167 OomPriorityManager::TabStats::~TabStats() {
168 }
169
170 OomPriorityManager::OomPriorityManager()
171     : focused_tab_pid_(0),
172       low_memory_listener_(new LowMemoryListener(this)),
173       discard_count_(0),
174       recent_tab_discard_(false) {
175   registrar_.Add(this,
176       content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
177       content::NotificationService::AllBrowserContextsAndSources());
178   registrar_.Add(this,
179       content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
180       content::NotificationService::AllBrowserContextsAndSources());
181   registrar_.Add(this,
182       content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
183       content::NotificationService::AllBrowserContextsAndSources());
184 }
185
186 OomPriorityManager::~OomPriorityManager() {
187   Stop();
188 }
189
190 void OomPriorityManager::Start() {
191   if (!timer_.IsRunning()) {
192     timer_.Start(FROM_HERE,
193                  TimeDelta::FromSeconds(kAdjustmentIntervalSeconds),
194                  this,
195                  &OomPriorityManager::AdjustOomPriorities);
196   }
197   if (!recent_tab_discard_timer_.IsRunning()) {
198     recent_tab_discard_timer_.Start(
199         FROM_HERE,
200         TimeDelta::FromSeconds(kRecentTabDiscardIntervalSeconds),
201         this,
202         &OomPriorityManager::RecordRecentTabDiscard);
203   }
204   if (low_memory_listener_.get())
205     low_memory_listener_->Start();
206   start_time_ = TimeTicks::Now();
207 }
208
209 void OomPriorityManager::Stop() {
210   timer_.Stop();
211   recent_tab_discard_timer_.Stop();
212   if (low_memory_listener_.get())
213     low_memory_listener_->Stop();
214 }
215
216 std::vector<string16> OomPriorityManager::GetTabTitles() {
217   TabStatsList stats = GetTabStatsOnUIThread();
218   base::AutoLock pid_to_oom_score_autolock(pid_to_oom_score_lock_);
219   std::vector<string16> titles;
220   titles.reserve(stats.size());
221   TabStatsList::iterator it = stats.begin();
222   for ( ; it != stats.end(); ++it) {
223     string16 str;
224     str.reserve(4096);
225     int score = pid_to_oom_score_[it->renderer_handle];
226     str += base::IntToString16(score);
227     str += ASCIIToUTF16(" - ");
228     str += it->title;
229     str += ASCIIToUTF16(it->is_app ? " app" : "");
230     str += ASCIIToUTF16(it->is_reloadable_ui ? " reloadable_ui" : "");
231     str += ASCIIToUTF16(it->is_playing_audio ? " playing_audio" : "");
232     str += ASCIIToUTF16(it->is_pinned ? " pinned" : "");
233     str += ASCIIToUTF16(it->is_discarded ? " discarded" : "");
234     titles.push_back(str);
235   }
236   return titles;
237 }
238
239 // TODO(jamescook): This should consider tabs with references to other tabs,
240 // such as tabs created with JavaScript window.open().  We might want to
241 // discard the entire set together, or use that in the priority computation.
242 bool OomPriorityManager::DiscardTab() {
243   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
244   TabStatsList stats = GetTabStatsOnUIThread();
245   if (stats.empty())
246     return false;
247   // Loop until we find a non-discarded tab to kill.
248   for (TabStatsList::const_reverse_iterator stats_rit = stats.rbegin();
249        stats_rit != stats.rend();
250        ++stats_rit) {
251     int64 least_important_tab_id = stats_rit->tab_contents_id;
252     if (DiscardTabById(least_important_tab_id))
253       return true;
254   }
255   return false;
256 }
257
258 void OomPriorityManager::LogMemoryAndDiscardTab() {
259   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
260   // Deletes itself upon completion.
261   OomMemoryDetails* details = new OomMemoryDetails();
262   details->StartFetch(MemoryDetails::SKIP_USER_METRICS);
263 }
264
265 ///////////////////////////////////////////////////////////////////////////////
266 // OomPriorityManager, private:
267
268 // static
269 bool OomPriorityManager::IsReloadableUI(const GURL& url) {
270   // There are many chrome:// UI URLs, but only look for the ones that users
271   // are likely to have open. Most of the benefit is the from NTP URL.
272   const char* kReloadableUrlPrefixes[] = {
273       chrome::kChromeUIDownloadsURL,
274       chrome::kChromeUIHistoryURL,
275       chrome::kChromeUINewTabURL,
276       chrome::kChromeUISettingsURL,
277   };
278   // Prefix-match against the table above. Use strncmp to avoid allocating
279   // memory to convert the URL prefix constants into std::strings.
280   for (size_t i = 0; i < arraysize(kReloadableUrlPrefixes); ++i) {
281     if (!strncmp(url.spec().c_str(),
282                  kReloadableUrlPrefixes[i],
283                  strlen(kReloadableUrlPrefixes[i])))
284       return true;
285   }
286   return false;
287 }
288
289 bool OomPriorityManager::DiscardTabById(int64 target_web_contents_id) {
290   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
291     Browser* browser = *it;
292     TabStripModel* model = browser->tab_strip_model();
293     for (int idx = 0; idx < model->count(); idx++) {
294       // Can't discard tabs that are already discarded or active.
295       if (model->IsTabDiscarded(idx) || (model->active_index() == idx))
296         continue;
297       WebContents* web_contents = model->GetWebContentsAt(idx);
298       int64 web_contents_id = IdFromWebContents(web_contents);
299       if (web_contents_id == target_web_contents_id) {
300         LOG(WARNING) << "Discarding tab " << idx
301                      << " id " << target_web_contents_id;
302         // Record statistics before discarding because we want to capture the
303         // memory state that lead to the discard.
304         RecordDiscardStatistics();
305         model->DiscardWebContentsAt(idx);
306         recent_tab_discard_ = true;
307         return true;
308       }
309     }
310   }
311   return false;
312 }
313
314 void OomPriorityManager::RecordDiscardStatistics() {
315   // Record a raw count so we can compare to discard reloads.
316   discard_count_++;
317   UMA_HISTOGRAM_CUSTOM_COUNTS(
318       "Tabs.Discard.DiscardCount", discard_count_, 1, 1000, 50);
319
320   // TODO(jamescook): Maybe incorporate extension count?
321   UMA_HISTOGRAM_CUSTOM_COUNTS(
322       "Tabs.Discard.TabCount", GetTabCount(), 1, 100, 50);
323
324   // TODO(jamescook): If the time stats prove too noisy, then divide up users
325   // based on how heavily they use Chrome using tab count as a proxy.
326   // Bin into <= 1, <= 2, <= 4, <= 8, etc.
327   if (last_discard_time_.is_null()) {
328     // This is the first discard this session.
329     TimeDelta interval = TimeTicks::Now() - start_time_;
330     int interval_seconds = static_cast<int>(interval.InSeconds());
331     // Record time in seconds over an interval of approximately 1 day.
332     UMA_HISTOGRAM_CUSTOM_COUNTS(
333         "Tabs.Discard.InitialTime2", interval_seconds, 1, 100000, 50);
334   } else {
335     // Not the first discard, so compute time since last discard.
336     TimeDelta interval = TimeTicks::Now() - last_discard_time_;
337     int interval_ms = static_cast<int>(interval.InMilliseconds());
338     // Record time in milliseconds over an interval of approximately 1 day.
339     // Start at 100 ms to get extra resolution in the target 750 ms range.
340     UMA_HISTOGRAM_CUSTOM_COUNTS(
341         "Tabs.Discard.IntervalTime2", interval_ms, 100, 100000 * 1000, 50);
342   }
343   // Record Chrome's concept of system memory usage at the time of the discard.
344   base::SystemMemoryInfoKB memory;
345   if (base::GetSystemMemoryInfo(&memory)) {
346     // TODO(jamescook): Remove this after R25 is deployed to stable. It does
347     // not have sufficient resolution in the 2-4 GB range and does not properly
348     // account for graphics memory on ARM. Replace with MemAllocatedMB below.
349     int mem_anonymous_mb = (memory.active_anon + memory.inactive_anon) / 1024;
350     HISTOGRAM_MEGABYTES("Tabs.Discard.MemAnonymousMB", mem_anonymous_mb);
351
352     // Record graphics GEM object size in a histogram with 50 MB buckets.
353     int mem_graphics_gem_mb = 0;
354     if (memory.gem_size != -1)
355       mem_graphics_gem_mb = memory.gem_size / 1024 / 1024;
356     RecordLinearHistogram(
357         "Tabs.Discard.MemGraphicsMB", mem_graphics_gem_mb, 2500, 50);
358
359     // Record shared memory (used by renderer/GPU buffers).
360     int mem_shmem_mb = memory.shmem / 1024;
361     RecordLinearHistogram("Tabs.Discard.MemShmemMB", mem_shmem_mb, 2500, 50);
362
363     // On Intel, graphics objects are in anonymous pages, but on ARM they are
364     // not. For a total "allocated count" add in graphics pages on ARM.
365     int mem_allocated_mb = mem_anonymous_mb;
366 #if defined(ARCH_CPU_ARM_FAMILY)
367     mem_allocated_mb += mem_graphics_gem_mb;
368 #endif
369     UMA_HISTOGRAM_CUSTOM_COUNTS(
370         "Tabs.Discard.MemAllocatedMB", mem_allocated_mb, 256, 32768, 50);
371
372     int mem_available_mb =
373         (memory.active_file + memory.inactive_file + memory.free) / 1024;
374     HISTOGRAM_MEGABYTES("Tabs.Discard.MemAvailableMB", mem_available_mb);
375   }
376   // Set up to record the next interval.
377   last_discard_time_ = TimeTicks::Now();
378 }
379
380 void OomPriorityManager::RecordRecentTabDiscard() {
381   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
382   // If we change the interval we need to change the histogram name.
383   UMA_HISTOGRAM_BOOLEAN("Tabs.Discard.DiscardInLastMinute",
384                         recent_tab_discard_);
385   // Reset for the next interval.
386   recent_tab_discard_ = false;
387 }
388
389 void OomPriorityManager::PurgeBrowserMemory() {
390   // Based on experimental evidence, attempts to free memory from renderers
391   // have been too slow to use in OOM situations (V8 garbage collection) or
392   // do not lead to persistent decreased usage (image/bitmap caches). This
393   // function therefore only targets large blocks of memory in the browser.
394   for (TabContentsIterator it; !it.done(); it.Next()) {
395     WebContents* web_contents = *it;
396     // Screenshots can consume ~5 MB per web contents for platforms that do
397     // touch back/forward.
398     web_contents->GetController().ClearAllScreenshots();
399   }
400   // TODO(jamescook): Are there other things we could flush? Drive metadata?
401 }
402
403 int OomPriorityManager::GetTabCount() const {
404   int tab_count = 0;
405   for (chrome::BrowserIterator it; !it.done(); it.Next())
406     tab_count += it->tab_strip_model()->count();
407   return tab_count;
408 }
409
410 // Returns true if |first| is considered less desirable to be killed
411 // than |second|.
412 bool OomPriorityManager::CompareTabStats(TabStats first,
413                                          TabStats second) {
414   // Being currently selected is most important to protect.
415   if (first.is_selected != second.is_selected)
416     return first.is_selected;
417
418   // Tab with internal web UI like NTP or Settings are good choices to discard,
419   // so protect non-Web UI and let the other conditionals finish the sort.
420   if (first.is_reloadable_ui != second.is_reloadable_ui)
421     return !first.is_reloadable_ui;
422
423   // Being pinned is important to protect.
424   if (first.is_pinned != second.is_pinned)
425     return first.is_pinned;
426
427   // Being an app is important too, as you're the only visible surface in the
428   // window and we don't want to discard that.
429   if (first.is_app != second.is_app)
430     return first.is_app;
431
432   // Protect streaming audio and video conferencing tabs.
433   if (first.is_playing_audio != second.is_playing_audio)
434     return first.is_playing_audio;
435
436   // TODO(jamescook): Incorporate sudden_termination_allowed into the sort
437   // order.  We don't do this now because pages with unload handlers set
438   // sudden_termination_allowed false, and that covers too many common pages
439   // with ad networks and statistics scripts.  Ideally we would like to check
440   // for beforeUnload handlers, which are likely to present a dialog asking
441   // if the user wants to discard state.  crbug.com/123049
442
443   // Being more recently selected is more important.
444   return first.last_selected > second.last_selected;
445 }
446
447 void OomPriorityManager::AdjustFocusedTabScoreOnFileThread() {
448   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
449   base::AutoLock pid_to_oom_score_autolock(pid_to_oom_score_lock_);
450   content::ZygoteHost::GetInstance()->AdjustRendererOOMScore(
451       focused_tab_pid_, chrome::kLowestRendererOomScore);
452   pid_to_oom_score_[focused_tab_pid_] = chrome::kLowestRendererOomScore;
453 }
454
455 void OomPriorityManager::OnFocusTabScoreAdjustmentTimeout() {
456   BrowserThread::PostTask(
457       BrowserThread::FILE, FROM_HERE,
458       base::Bind(&OomPriorityManager::AdjustFocusedTabScoreOnFileThread,
459                  base::Unretained(this)));
460 }
461
462 void OomPriorityManager::Observe(int type,
463                                  const content::NotificationSource& source,
464                                  const content::NotificationDetails& details) {
465   base::ProcessHandle handle = 0;
466   base::AutoLock pid_to_oom_score_autolock(pid_to_oom_score_lock_);
467   switch (type) {
468     case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: {
469       handle =
470           content::Details<content::RenderProcessHost::RendererClosedDetails>(
471               details)->handle;
472       pid_to_oom_score_.erase(handle);
473       break;
474     }
475     case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: {
476       handle = content::Source<content::RenderProcessHost>(source)->
477           GetHandle();
478       pid_to_oom_score_.erase(handle);
479       break;
480     }
481     case content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED: {
482       bool visible = *content::Details<bool>(details).ptr();
483       if (visible) {
484         focused_tab_pid_ =
485             content::Source<content::RenderWidgetHost>(source).ptr()->
486             GetProcess()->GetHandle();
487
488         // If the currently focused tab already has a lower score, do not
489         // set it. This can happen in case the newly focused tab is script
490         // connected to the previous tab.
491         ProcessScoreMap::iterator it;
492         it = pid_to_oom_score_.find(focused_tab_pid_);
493         if (it == pid_to_oom_score_.end()
494             || it->second != chrome::kLowestRendererOomScore) {
495           // By starting a timer we guarantee that the tab is focused for
496           // certain amount of time. Secondly, it also does not add overhead
497           // to the tab switching time.
498           if (focus_tab_score_adjust_timer_.IsRunning())
499             focus_tab_score_adjust_timer_.Reset();
500           else
501             focus_tab_score_adjust_timer_.Start(FROM_HERE,
502               TimeDelta::FromMilliseconds(kFocusedTabScoreAdjustIntervalMs),
503               this, &OomPriorityManager::OnFocusTabScoreAdjustmentTimeout);
504         }
505       }
506       break;
507     }
508     default:
509       NOTREACHED() << L"Received unexpected notification";
510       break;
511   }
512 }
513
514 // Here we collect most of the information we need to sort the
515 // existing renderers in priority order, and hand out oom_score_adj
516 // scores based on that sort order.
517 //
518 // Things we need to collect on the browser thread (because
519 // TabStripModel isn't thread safe):
520 // 1) whether or not a tab is pinned
521 // 2) last time a tab was selected
522 // 3) is the tab currently selected
523 void OomPriorityManager::AdjustOomPriorities() {
524   if (BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH)->empty())
525     return;
526
527   // Check for a discontinuity in time caused by the machine being suspended.
528   if (!last_adjust_time_.is_null()) {
529     TimeDelta suspend_time = TimeTicks::Now() - last_adjust_time_;
530     if (suspend_time.InSeconds() > kSuspendThresholdSeconds) {
531       // We were probably suspended, move our event timers forward in time so
532       // when we subtract them out later we are counting "uptime".
533       start_time_ += suspend_time;
534       if (!last_discard_time_.is_null())
535         last_discard_time_ += suspend_time;
536     }
537   }
538   last_adjust_time_ = TimeTicks::Now();
539
540   TabStatsList stats_list = GetTabStatsOnUIThread();
541   BrowserThread::PostTask(
542       BrowserThread::FILE, FROM_HERE,
543       base::Bind(&OomPriorityManager::AdjustOomPrioritiesOnFileThread,
544                  base::Unretained(this), stats_list));
545 }
546
547 OomPriorityManager::TabStatsList OomPriorityManager::GetTabStatsOnUIThread() {
548   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
549   TabStatsList stats_list;
550   stats_list.reserve(32);  // 99% of users have < 30 tabs open
551   bool browser_active = true;
552   const BrowserList* ash_browser_list =
553       BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
554   for (BrowserList::const_reverse_iterator browser_iterator =
555            ash_browser_list->begin_last_active();
556        browser_iterator != ash_browser_list->end_last_active();
557        ++browser_iterator) {
558     Browser* browser = *browser_iterator;
559     bool is_browser_for_app = browser->is_app();
560     const TabStripModel* model = browser->tab_strip_model();
561     for (int i = 0; i < model->count(); i++) {
562       WebContents* contents = model->GetWebContentsAt(i);
563       if (!contents->IsCrashed()) {
564         TabStats stats;
565         stats.is_app = is_browser_for_app;
566         stats.is_reloadable_ui =
567             IsReloadableUI(contents->GetLastCommittedURL());
568         stats.is_playing_audio = chrome::IsPlayingAudio(contents);
569         stats.is_pinned = model->IsTabPinned(i);
570         stats.is_selected = browser_active && model->IsTabSelected(i);
571         stats.is_discarded = model->IsTabDiscarded(i);
572         stats.last_selected = contents->GetLastSelectedTime();
573         stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle();
574         stats.title = contents->GetTitle();
575         stats.tab_contents_id = IdFromWebContents(contents);
576         stats_list.push_back(stats);
577       }
578     }
579     // We process the active browser window in the first iteration.
580     browser_active = false;
581   }
582   // Sort the data we collected so that least desirable to be
583   // killed is first, most desirable is last.
584   std::sort(stats_list.begin(), stats_list.end(), CompareTabStats);
585   return stats_list;
586 }
587
588 void OomPriorityManager::AdjustOomPrioritiesOnFileThread(
589     TabStatsList stats_list) {
590   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
591   base::AutoLock pid_to_oom_score_autolock(pid_to_oom_score_lock_);
592
593   // Now we assign priorities based on the sorted list.  We're
594   // assigning priorities in the range of kLowestRendererOomScore to
595   // kHighestRendererOomScore (defined in chrome_constants.h).
596   // oom_score_adj takes values from -1000 to 1000.  Negative values
597   // are reserved for system processes, and we want to give some room
598   // below the range we're using to allow for things that want to be
599   // above the renderers in priority, so the defined range gives us
600   // some variation in priority without taking up the whole range.  In
601   // the end, however, it's a pretty arbitrary range to use.  Higher
602   // values are more likely to be killed by the OOM killer.
603   //
604   // We also remove any duplicate PIDs, leaving the most important
605   // (least likely to be killed) of the duplicates, so that a
606   // particular renderer process takes on the oom_score_adj of the
607   // least likely tab to be killed.
608   const int kPriorityRange = chrome::kHighestRendererOomScore -
609                              chrome::kLowestRendererOomScore;
610   float priority_increment =
611       static_cast<float>(kPriorityRange) / stats_list.size();
612   float priority = chrome::kLowestRendererOomScore;
613   std::set<base::ProcessHandle> already_seen;
614   int score = 0;
615   ProcessScoreMap::iterator it;
616   for (TabStatsList::iterator iterator = stats_list.begin();
617        iterator != stats_list.end(); ++iterator) {
618     // stats_list also contains discarded tab stat. If renderer_handler is zero,
619     // we don't need to adjust oom_score.
620     if (iterator->renderer_handle == 0)
621       continue;
622     if (already_seen.find(iterator->renderer_handle) == already_seen.end()) {
623       already_seen.insert(iterator->renderer_handle);
624       // If a process has the same score as the newly calculated value,
625       // do not set it.
626       score = static_cast<int>(priority + 0.5f);
627       it = pid_to_oom_score_.find(iterator->renderer_handle);
628       if (it == pid_to_oom_score_.end() || it->second != score) {
629         content::ZygoteHost::GetInstance()->AdjustRendererOOMScore(
630             iterator->renderer_handle, score);
631         pid_to_oom_score_[iterator->renderer_handle] = score;
632       }
633       priority += priority_increment;
634     }
635   }
636 }
637
638 void OomPriorityManager::OnMemoryLow() {
639   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
640   LogMemoryAndDiscardTab();
641 }
642
643 }  // namespace chromeos