| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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/chromeos/memory/oom_priority_manager.h" | 5 #include "chrome/browser/chromeos/memory/oom_priority_manager.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <set> | 8 #include <set> |
| 9 #include <vector> | 9 #include <vector> |
| 10 | 10 |
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 164 is_selected(false), | 164 is_selected(false), |
| 165 is_discarded(false), | 165 is_discarded(false), |
| 166 renderer_handle(0), | 166 renderer_handle(0), |
| 167 tab_contents_id(0) { | 167 tab_contents_id(0) { |
| 168 } | 168 } |
| 169 | 169 |
| 170 OomPriorityManager::TabStats::~TabStats() { | 170 OomPriorityManager::TabStats::~TabStats() { |
| 171 } | 171 } |
| 172 | 172 |
| 173 OomPriorityManager::OomPriorityManager() | 173 OomPriorityManager::OomPriorityManager() |
| 174 : focused_tab_pid_(0), | 174 : focused_tab_process_info_(std::make_pair(0, 0)), |
| 175 low_memory_observer_(new LowMemoryObserver), | 175 low_memory_observer_(new LowMemoryObserver), |
| 176 discard_count_(0), | 176 discard_count_(0), |
| 177 recent_tab_discard_(false) { | 177 recent_tab_discard_(false) { |
| 178 registrar_.Add(this, | 178 registrar_.Add(this, |
| 179 content::NOTIFICATION_RENDERER_PROCESS_CLOSED, | 179 content::NOTIFICATION_RENDERER_PROCESS_CLOSED, |
| 180 content::NotificationService::AllBrowserContextsAndSources()); | 180 content::NotificationService::AllBrowserContextsAndSources()); |
| 181 registrar_.Add(this, | 181 registrar_.Add(this, |
| 182 content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, | 182 content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, |
| 183 content::NotificationService::AllBrowserContextsAndSources()); | 183 content::NotificationService::AllBrowserContextsAndSources()); |
| 184 registrar_.Add(this, | 184 registrar_.Add(this, |
| (...skipping 26 matching lines...) Expand all Loading... |
| 211 | 211 |
| 212 void OomPriorityManager::Stop() { | 212 void OomPriorityManager::Stop() { |
| 213 timer_.Stop(); | 213 timer_.Stop(); |
| 214 recent_tab_discard_timer_.Stop(); | 214 recent_tab_discard_timer_.Stop(); |
| 215 if (low_memory_observer_.get()) | 215 if (low_memory_observer_.get()) |
| 216 low_memory_observer_->Stop(); | 216 low_memory_observer_->Stop(); |
| 217 } | 217 } |
| 218 | 218 |
| 219 std::vector<base::string16> OomPriorityManager::GetTabTitles() { | 219 std::vector<base::string16> OomPriorityManager::GetTabTitles() { |
| 220 TabStatsList stats = GetTabStatsOnUIThread(); | 220 TabStatsList stats = GetTabStatsOnUIThread(); |
| 221 base::AutoLock pid_to_oom_score_autolock(pid_to_oom_score_lock_); | 221 base::AutoLock oom_score_autolock(oom_score_lock_); |
| 222 std::vector<base::string16> titles; | 222 std::vector<base::string16> titles; |
| 223 titles.reserve(stats.size()); | 223 titles.reserve(stats.size()); |
| 224 TabStatsList::iterator it = stats.begin(); | 224 TabStatsList::iterator it = stats.begin(); |
| 225 for ( ; it != stats.end(); ++it) { | 225 for ( ; it != stats.end(); ++it) { |
| 226 base::string16 str; | 226 base::string16 str; |
| 227 str.reserve(4096); | 227 str.reserve(4096); |
| 228 int score = pid_to_oom_score_[it->renderer_handle]; | 228 int score = oom_score_map_[it->child_process_host_id]; |
| 229 str += base::IntToString16(score); | 229 str += base::IntToString16(score); |
| 230 str += base::ASCIIToUTF16(" - "); | 230 str += base::ASCIIToUTF16(" - "); |
| 231 str += it->title; | 231 str += it->title; |
| 232 str += base::ASCIIToUTF16(it->is_app ? " app" : ""); | 232 str += base::ASCIIToUTF16(it->is_app ? " app" : ""); |
| 233 str += base::ASCIIToUTF16(it->is_reloadable_ui ? " reloadable_ui" : ""); | 233 str += base::ASCIIToUTF16(it->is_reloadable_ui ? " reloadable_ui" : ""); |
| 234 str += base::ASCIIToUTF16(it->is_playing_audio ? " playing_audio" : ""); | 234 str += base::ASCIIToUTF16(it->is_playing_audio ? " playing_audio" : ""); |
| 235 str += base::ASCIIToUTF16(it->is_pinned ? " pinned" : ""); | 235 str += base::ASCIIToUTF16(it->is_pinned ? " pinned" : ""); |
| 236 str += base::ASCIIToUTF16(it->is_discarded ? " discarded" : ""); | 236 str += base::ASCIIToUTF16(it->is_discarded ? " discarded" : ""); |
| 237 titles.push_back(str); | 237 titles.push_back(str); |
| 238 } | 238 } |
| (...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 449 // with ad networks and statistics scripts. Ideally we would like to check | 449 // with ad networks and statistics scripts. Ideally we would like to check |
| 450 // for beforeUnload handlers, which are likely to present a dialog asking | 450 // for beforeUnload handlers, which are likely to present a dialog asking |
| 451 // if the user wants to discard state. crbug.com/123049 | 451 // if the user wants to discard state. crbug.com/123049 |
| 452 | 452 |
| 453 // Being more recently active is more important. | 453 // Being more recently active is more important. |
| 454 return first.last_active > second.last_active; | 454 return first.last_active > second.last_active; |
| 455 } | 455 } |
| 456 | 456 |
| 457 void OomPriorityManager::AdjustFocusedTabScoreOnFileThread() { | 457 void OomPriorityManager::AdjustFocusedTabScoreOnFileThread() { |
| 458 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 458 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 459 base::AutoLock pid_to_oom_score_autolock(pid_to_oom_score_lock_); | 459 base::AutoLock oom_score_autolock(oom_score_lock_); |
| 460 base::ProcessHandle pid = focused_tab_process_info_.second; |
| 460 content::ZygoteHost::GetInstance()->AdjustRendererOOMScore( | 461 content::ZygoteHost::GetInstance()->AdjustRendererOOMScore( |
| 461 focused_tab_pid_, chrome::kLowestRendererOomScore); | 462 pid, chrome::kLowestRendererOomScore); |
| 462 pid_to_oom_score_[focused_tab_pid_] = chrome::kLowestRendererOomScore; | 463 oom_score_map_[focused_tab_process_info_.first] = |
| 464 chrome::kLowestRendererOomScore; |
| 463 } | 465 } |
| 464 | 466 |
| 465 void OomPriorityManager::OnFocusTabScoreAdjustmentTimeout() { | 467 void OomPriorityManager::OnFocusTabScoreAdjustmentTimeout() { |
| 466 BrowserThread::PostTask( | 468 BrowserThread::PostTask( |
| 467 BrowserThread::FILE, FROM_HERE, | 469 BrowserThread::FILE, FROM_HERE, |
| 468 base::Bind(&OomPriorityManager::AdjustFocusedTabScoreOnFileThread, | 470 base::Bind(&OomPriorityManager::AdjustFocusedTabScoreOnFileThread, |
| 469 base::Unretained(this))); | 471 base::Unretained(this))); |
| 470 } | 472 } |
| 471 | 473 |
| 472 void OomPriorityManager::Observe(int type, | 474 void OomPriorityManager::Observe(int type, |
| 473 const content::NotificationSource& source, | 475 const content::NotificationSource& source, |
| 474 const content::NotificationDetails& details) { | 476 const content::NotificationDetails& details) { |
| 475 base::ProcessHandle handle = 0; | 477 base::AutoLock oom_score_autolock(oom_score_lock_); |
| 476 base::AutoLock pid_to_oom_score_autolock(pid_to_oom_score_lock_); | |
| 477 switch (type) { | 478 switch (type) { |
| 478 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: { | 479 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: |
| 479 handle = | |
| 480 content::Details<content::RenderProcessHost::RendererClosedDetails>( | |
| 481 details)->handle; | |
| 482 pid_to_oom_score_.erase(handle); | |
| 483 break; | |
| 484 } | |
| 485 case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: { | 480 case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: { |
| 486 handle = content::Source<content::RenderProcessHost>(source)-> | 481 content::RenderProcessHost* host = |
| 487 GetHandle(); | 482 content::Source<content::RenderProcessHost>(source).ptr(); |
| 488 pid_to_oom_score_.erase(handle); | 483 oom_score_map_.erase(host->GetID()); |
| 489 break; | 484 break; |
| 490 } | 485 } |
| 491 case content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED: { | 486 case content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED: { |
| 492 bool visible = *content::Details<bool>(details).ptr(); | 487 bool visible = *content::Details<bool>(details).ptr(); |
| 493 if (visible) { | 488 if (visible) { |
| 494 focused_tab_pid_ = | 489 content::RenderProcessHost* render_host = |
| 495 content::Source<content::RenderWidgetHost>(source).ptr()-> | 490 content::Source<content::RenderWidgetHost>(source).ptr()-> |
| 496 GetProcess()->GetHandle(); | 491 GetProcess(); |
| 492 focused_tab_process_info_ = std::make_pair(render_host->GetID(), |
| 493 render_host->GetHandle()); |
| 497 | 494 |
| 498 // If the currently focused tab already has a lower score, do not | 495 // If the currently focused tab already has a lower score, do not |
| 499 // set it. This can happen in case the newly focused tab is script | 496 // set it. This can happen in case the newly focused tab is script |
| 500 // connected to the previous tab. | 497 // connected to the previous tab. |
| 501 ProcessScoreMap::iterator it; | 498 ProcessScoreMap::iterator it; |
| 502 it = pid_to_oom_score_.find(focused_tab_pid_); | 499 it = oom_score_map_.find(focused_tab_process_info_.first); |
| 503 if (it == pid_to_oom_score_.end() | 500 if (it == oom_score_map_.end() |
| 504 || it->second != chrome::kLowestRendererOomScore) { | 501 || it->second != chrome::kLowestRendererOomScore) { |
| 505 // By starting a timer we guarantee that the tab is focused for | 502 // By starting a timer we guarantee that the tab is focused for |
| 506 // certain amount of time. Secondly, it also does not add overhead | 503 // certain amount of time. Secondly, it also does not add overhead |
| 507 // to the tab switching time. | 504 // to the tab switching time. |
| 508 if (focus_tab_score_adjust_timer_.IsRunning()) | 505 if (focus_tab_score_adjust_timer_.IsRunning()) |
| 509 focus_tab_score_adjust_timer_.Reset(); | 506 focus_tab_score_adjust_timer_.Reset(); |
| 510 else | 507 else |
| 511 focus_tab_score_adjust_timer_.Start(FROM_HERE, | 508 focus_tab_score_adjust_timer_.Start(FROM_HERE, |
| 512 TimeDelta::FromMilliseconds(kFocusedTabScoreAdjustIntervalMs), | 509 TimeDelta::FromMilliseconds(kFocusedTabScoreAdjustIntervalMs), |
| 513 this, &OomPriorityManager::OnFocusTabScoreAdjustmentTimeout); | 510 this, &OomPriorityManager::OnFocusTabScoreAdjustmentTimeout); |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 574 TabStats stats; | 571 TabStats stats; |
| 575 stats.is_app = is_browser_for_app; | 572 stats.is_app = is_browser_for_app; |
| 576 stats.is_reloadable_ui = | 573 stats.is_reloadable_ui = |
| 577 IsReloadableUI(contents->GetLastCommittedURL()); | 574 IsReloadableUI(contents->GetLastCommittedURL()); |
| 578 stats.is_playing_audio = chrome::IsPlayingAudio(contents); | 575 stats.is_playing_audio = chrome::IsPlayingAudio(contents); |
| 579 stats.is_pinned = model->IsTabPinned(i); | 576 stats.is_pinned = model->IsTabPinned(i); |
| 580 stats.is_selected = browser_active && model->IsTabSelected(i); | 577 stats.is_selected = browser_active && model->IsTabSelected(i); |
| 581 stats.is_discarded = model->IsTabDiscarded(i); | 578 stats.is_discarded = model->IsTabDiscarded(i); |
| 582 stats.last_active = contents->GetLastActiveTime(); | 579 stats.last_active = contents->GetLastActiveTime(); |
| 583 stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle(); | 580 stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle(); |
| 581 stats.child_process_host_id = contents->GetRenderProcessHost()->GetID(); |
| 584 stats.title = contents->GetTitle(); | 582 stats.title = contents->GetTitle(); |
| 585 stats.tab_contents_id = IdFromWebContents(contents); | 583 stats.tab_contents_id = IdFromWebContents(contents); |
| 586 stats_list.push_back(stats); | 584 stats_list.push_back(stats); |
| 587 } | 585 } |
| 588 } | 586 } |
| 589 // We process the active browser window in the first iteration. | 587 // We process the active browser window in the first iteration. |
| 590 browser_active = false; | 588 browser_active = false; |
| 591 } | 589 } |
| 592 // Sort the data we collected so that least desirable to be | 590 // Sort the data we collected so that least desirable to be |
| 593 // killed is first, most desirable is last. | 591 // killed is first, most desirable is last. |
| 594 std::sort(stats_list.begin(), stats_list.end(), CompareTabStats); | 592 std::sort(stats_list.begin(), stats_list.end(), CompareTabStats); |
| 595 return stats_list; | 593 return stats_list; |
| 596 } | 594 } |
| 597 | 595 |
| 598 // static | 596 // static |
| 599 std::vector<base::ProcessHandle> OomPriorityManager::GetProcessHandles( | 597 std::vector<OomPriorityManager::ProcessInfo> |
| 598 OomPriorityManager::GetChildProcessInfos( |
| 600 const TabStatsList& stats_list) { | 599 const TabStatsList& stats_list) { |
| 601 std::vector<base::ProcessHandle> process_handles; | 600 std::vector<ProcessInfo> process_infos; |
| 602 std::set<base::ProcessHandle> already_seen; | 601 std::set<base::ProcessHandle> already_seen; |
| 603 for (TabStatsList::const_iterator iterator = stats_list.begin(); | 602 for (TabStatsList::const_iterator iterator = stats_list.begin(); |
| 604 iterator != stats_list.end(); ++iterator) { | 603 iterator != stats_list.end(); ++iterator) { |
| 605 // stats_list contains entries for already-discarded tabs. If the PID | 604 // stats_list contains entries for already-discarded tabs. If the PID |
| 606 // (renderer_handle) is zero, we don't need to adjust the oom_score. | 605 // (renderer_handle) is zero, we don't need to adjust the oom_score. |
| 607 if (iterator->renderer_handle == 0) | 606 if (iterator->renderer_handle == 0) |
| 608 continue; | 607 continue; |
| 609 | 608 |
| 610 bool inserted = already_seen.insert(iterator->renderer_handle).second; | 609 bool inserted = already_seen.insert(iterator->renderer_handle).second; |
| 611 if (!inserted) { | 610 if (!inserted) { |
| 612 // We've already seen this process handle. | 611 // We've already seen this process handle. |
| 613 continue; | 612 continue; |
| 614 } | 613 } |
| 615 | 614 |
| 616 process_handles.push_back(iterator->renderer_handle); | 615 process_infos.push_back(std::make_pair( |
| 616 iterator->child_process_host_id, iterator->renderer_handle)); |
| 617 } | 617 } |
| 618 return process_handles; | 618 return process_infos; |
| 619 } | 619 } |
| 620 | 620 |
| 621 void OomPriorityManager::AdjustOomPrioritiesOnFileThread( | 621 void OomPriorityManager::AdjustOomPrioritiesOnFileThread( |
| 622 TabStatsList stats_list) { | 622 TabStatsList stats_list) { |
| 623 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 623 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 624 base::AutoLock pid_to_oom_score_autolock(pid_to_oom_score_lock_); | 624 base::AutoLock oom_score_autolock(oom_score_lock_); |
| 625 | 625 |
| 626 // Remove any duplicate PIDs. Order of the list is maintained, so each | 626 // Remove any duplicate PIDs. Order of the list is maintained, so each |
| 627 // renderer process will take on the oom_score_adj of the most important | 627 // renderer process will take on the oom_score_adj of the most important |
| 628 // (least likely to be killed) tab. | 628 // (least likely to be killed) tab. |
| 629 std::vector<base::ProcessHandle> process_handles = | 629 std::vector<ProcessInfo> process_infos = GetChildProcessInfos(stats_list); |
| 630 GetProcessHandles(stats_list); | |
| 631 | 630 |
| 632 // Now we assign priorities based on the sorted list. We're | 631 // Now we assign priorities based on the sorted list. We're |
| 633 // assigning priorities in the range of kLowestRendererOomScore to | 632 // assigning priorities in the range of kLowestRendererOomScore to |
| 634 // kHighestRendererOomScore (defined in chrome_constants.h). | 633 // kHighestRendererOomScore (defined in chrome_constants.h). |
| 635 // oom_score_adj takes values from -1000 to 1000. Negative values | 634 // oom_score_adj takes values from -1000 to 1000. Negative values |
| 636 // are reserved for system processes, and we want to give some room | 635 // are reserved for system processes, and we want to give some room |
| 637 // below the range we're using to allow for things that want to be | 636 // below the range we're using to allow for things that want to be |
| 638 // above the renderers in priority, so the defined range gives us | 637 // above the renderers in priority, so the defined range gives us |
| 639 // some variation in priority without taking up the whole range. In | 638 // some variation in priority without taking up the whole range. In |
| 640 // the end, however, it's a pretty arbitrary range to use. Higher | 639 // the end, however, it's a pretty arbitrary range to use. Higher |
| 641 // values are more likely to be killed by the OOM killer. | 640 // values are more likely to be killed by the OOM killer. |
| 642 float priority = chrome::kLowestRendererOomScore; | 641 float priority = chrome::kLowestRendererOomScore; |
| 643 const int kPriorityRange = chrome::kHighestRendererOomScore - | 642 const int kPriorityRange = chrome::kHighestRendererOomScore - |
| 644 chrome::kLowestRendererOomScore; | 643 chrome::kLowestRendererOomScore; |
| 645 float priority_increment = | 644 float priority_increment = |
| 646 static_cast<float>(kPriorityRange) / process_handles.size(); | 645 static_cast<float>(kPriorityRange) / process_infos.size(); |
| 647 for (std::vector<base::ProcessHandle>::iterator iterator = | 646 for (const auto& process_info : process_infos) { |
| 648 process_handles.begin(); | |
| 649 iterator != process_handles.end(); ++iterator) { | |
| 650 int score = static_cast<int>(priority + 0.5f); | 647 int score = static_cast<int>(priority + 0.5f); |
| 651 ProcessScoreMap::iterator it = pid_to_oom_score_.find(*iterator); | 648 ProcessScoreMap::iterator it = |
| 649 oom_score_map_.find(process_info.first); |
| 652 // If a process has the same score as the newly calculated value, | 650 // If a process has the same score as the newly calculated value, |
| 653 // do not set it. | 651 // do not set it. |
| 654 if (it == pid_to_oom_score_.end() || it->second != score) { | 652 if (it == oom_score_map_.end() || it->second != score) { |
| 655 content::ZygoteHost::GetInstance()->AdjustRendererOOMScore(*iterator, | 653 content::ZygoteHost::GetInstance()->AdjustRendererOOMScore( |
| 656 score); | 654 process_info.second, score); |
| 657 pid_to_oom_score_[*iterator] = score; | 655 oom_score_map_[process_info.first] = score; |
| 658 } | 656 } |
| 659 priority += priority_increment; | 657 priority += priority_increment; |
| 660 } | 658 } |
| 661 } | 659 } |
| 662 | 660 |
| 663 } // namespace chromeos | 661 } // namespace chromeos |
| OLD | NEW |