diff options
-rw-r--r-- | apps/app_window.h | 4 | ||||
-rw-r--r-- | chrome/browser/DEPS | 1 | ||||
-rw-r--r-- | chrome/browser/chrome_browser_main.cc | 10 | ||||
-rw-r--r-- | chrome/browser/chrome_browser_main.h | 6 | ||||
-rw-r--r-- | chrome/browser/power/OWNERS | 3 | ||||
-rw-r--r-- | chrome/browser/power/process_power_collector.cc | 201 | ||||
-rw-r--r-- | chrome/browser/power/process_power_collector.h | 146 | ||||
-rw-r--r-- | chrome/browser/power/process_power_collector_unittest.cc | 314 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 3 | ||||
-rw-r--r-- | chrome/chrome_tests_unit.gypi | 2 | ||||
-rw-r--r-- | content/public/test/mock_render_process_host.cc | 2 | ||||
-rw-r--r-- | content/public/test/mock_render_process_host.h | 5 |
12 files changed, 697 insertions, 0 deletions
diff --git a/apps/app_window.h b/apps/app_window.h index a7f2293..1379922 100644 --- a/apps/app_window.h +++ b/apps/app_window.h @@ -347,6 +347,10 @@ class AppWindow : public content::NotificationObserver, // Whether the app window wants to be alpha enabled. bool requested_alpha_enabled() const { return requested_alpha_enabled_; } + void SetAppWindowContentsForTesting(scoped_ptr<AppWindowContents> contents) { + app_window_contents_ = contents.Pass(); + } + protected: virtual ~AppWindow(); diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS index 1aa16d9..8433194 100644 --- a/chrome/browser/DEPS +++ b/chrome/browser/DEPS @@ -48,6 +48,7 @@ include_rules = [ "+components/os_crypt", "+components/password_manager", "+components/policy", + "+components/power", "+components/precache", "+components/pref_registry", "+components/query_parser", diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc index 1c2ca9a..42206e6 100644 --- a/chrome/browser/chrome_browser_main.cc +++ b/chrome/browser/chrome_browser_main.cc @@ -69,6 +69,7 @@ #include "chrome/browser/performance_monitor/performance_monitor.h" #include "chrome/browser/performance_monitor/startup_timer.h" #include "chrome/browser/plugins/plugin_prefs.h" +#include "chrome/browser/power/process_power_collector.h" #include "chrome/browser/pref_service_flags_storage.h" #include "chrome/browser/prefs/chrome_pref_service_factory.h" #include "chrome/browser/prefs/command_line_pref_store.h" @@ -1551,6 +1552,11 @@ int ChromeBrowserMainParts::PreMainMessageLoopRunImpl() { performance_monitor::PerformanceMonitor::GetInstance()->Initialize(); +#if !defined(OS_ANDROID) + process_power_collector_.reset(new ProcessPowerCollector); + process_power_collector_->Initialize(); +#endif + PostBrowserStart(); if (parameters().ui_task) { @@ -1625,6 +1631,10 @@ void ChromeBrowserMainParts::PostMainMessageLoopRun() { // Disarm the startup hang detector time bomb if it is still Arm'ed. startup_watcher_->Disarm(); + // Remove observers attached to D-Bus clients before DbusThreadManager is + // shut down. + process_power_collector_.reset(); + for (size_t i = 0; i < chrome_extra_parts_.size(); ++i) chrome_extra_parts_[i]->PostMainMessageLoopRun(); diff --git a/chrome/browser/chrome_browser_main.h b/chrome/browser/chrome_browser_main.h index a856a2b7..21138a0 100644 --- a/chrome/browser/chrome_browser_main.h +++ b/chrome/browser/chrome_browser_main.h @@ -23,6 +23,7 @@ class ChromeBrowserMainExtraParts; class FieldTrialSynchronizer; class MetricsService; class PrefService; +class ProcessPowerCollector; class Profile; class StartupBrowserCreator; class StartupTimeBomb; @@ -150,6 +151,11 @@ class ChromeBrowserMainParts : public content::BrowserMainParts { ChromeBrowserFieldTrials browser_field_trials_; +#if !defined(OS_ANDROID) && !defined(OS_IOS) + // A monitor for attributing power consumption to origins. + scoped_ptr<ProcessPowerCollector> process_power_collector_; +#endif + // Vector of additional ChromeBrowserMainExtraParts. // Parts are deleted in the inverse order they are added. std::vector<ChromeBrowserMainExtraParts*> chrome_extra_parts_; diff --git a/chrome/browser/power/OWNERS b/chrome/browser/power/OWNERS new file mode 100644 index 0000000..76a50a4 --- /dev/null +++ b/chrome/browser/power/OWNERS @@ -0,0 +1,3 @@ +derat@chromium.org +dhnishi@chromium.org +sivachandra@chromium.org diff --git a/chrome/browser/power/process_power_collector.cc b/chrome/browser/power/process_power_collector.cc new file mode 100644 index 0000000..71a16f8 --- /dev/null +++ b/chrome/browser/power/process_power_collector.cc @@ -0,0 +1,201 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/power/process_power_collector.h" + +#include "apps/app_window.h" +#include "apps/app_window_registry.h" +#include "base/process/process_handle.h" +#include "base/process/process_metrics.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" +#include "components/power/origin_power_map.h" +#include "components/power/origin_power_map_factory.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/web_contents.h" +#include "url/gurl.h" + +#if defined(OS_CHROMEOS) +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/power_manager/power_supply_properties.pb.h" +#endif + +namespace { +const int kSecondsPerSample = 30; +} + +ProcessPowerCollector::PerProcessData::PerProcessData( + scoped_ptr<base::ProcessMetrics> metrics, + const GURL& origin, + Profile* profile) + : metrics_(metrics.Pass()), + profile_(profile), + last_origin_(origin), + last_cpu_percent_(0), + seen_this_cycle_(true) { +} + +ProcessPowerCollector::PerProcessData::PerProcessData() + : profile_(NULL), + last_cpu_percent_(0.0), + seen_this_cycle_(false) { +} + +ProcessPowerCollector::PerProcessData::~PerProcessData() { +} + +ProcessPowerCollector::ProcessPowerCollector() + : scale_factor_(1.0) { +#if defined(OS_CHROMEOS) + chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver( + this); +#endif +} + +ProcessPowerCollector::~ProcessPowerCollector() { +#if defined(OS_CHROMEOS) + chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver( + this); +#endif +} + +#if defined(OS_CHROMEOS) +void ProcessPowerCollector::PowerChanged( + const power_manager::PowerSupplyProperties& prop) { + if (prop.battery_state() == + power_manager::PowerSupplyProperties::DISCHARGING) { + if (!timer_.IsRunning()) + StartTimer(); + scale_factor_ = prop.battery_discharge_rate(); + } else { + timer_.Stop(); + } +} +#endif + +void ProcessPowerCollector::Initialize() { + StartTimer(); +} + +double ProcessPowerCollector::UpdatePowerConsumptionForTesting() { + return UpdatePowerConsumption(); +} + +void ProcessPowerCollector::StartTimer() { + DCHECK(!timer_.IsRunning()); + timer_.Start(FROM_HERE, + base::TimeDelta::FromSeconds(kSecondsPerSample), + this, + &ProcessPowerCollector::HandleUpdateTimeout); +} + +double ProcessPowerCollector::UpdatePowerConsumption() { + double total_cpu_percent = SynchronizeProcesses(); + + for (ProcessMetricsMap::iterator it = metrics_map_.begin(); + it != metrics_map_.end(); + ++it) { + // Invalidate the process for the next cycle. + it->second->set_seen_this_cycle(false); + } + + RecordCpuUsageByOrigin(total_cpu_percent); + return total_cpu_percent; +} + +void ProcessPowerCollector::HandleUpdateTimeout() { + UpdatePowerConsumption(); +} + +double ProcessPowerCollector::SynchronizeProcesses() { + // Update all tabs. + for (TabContentsIterator it; !it.done(); it.Next()) { + content::RenderProcessHost* render_process = it->GetRenderProcessHost(); + // Skip incognito web contents. + if (render_process->GetBrowserContext()->IsOffTheRecord()) + continue; + UpdateProcessInMap(render_process, it->GetLastCommittedURL().GetOrigin()); + } + + // Iterate over all profiles to find all app windows to attribute all apps. + ProfileManager* pm = g_browser_process->profile_manager(); + std::vector<Profile*> open_profiles = pm->GetLoadedProfiles(); + for (std::vector<Profile*>::const_iterator it = open_profiles.begin(); + it != open_profiles.end(); + ++it) { + const apps::AppWindowRegistry::AppWindowList& app_windows = + apps::AppWindowRegistry::Get(*it)->app_windows(); + for (apps::AppWindowRegistry::AppWindowList::const_iterator itr = + app_windows.begin(); + itr != app_windows.end(); + ++itr) { + content::WebContents* web_contents = (*itr)->web_contents(); + + UpdateProcessInMap(web_contents->GetRenderProcessHost(), + web_contents->GetLastCommittedURL().GetOrigin()); + } + } + + // Remove invalid processes and sum up the cpu cycle. + double total_cpu_percent = 0.0; + ProcessMetricsMap::iterator it = metrics_map_.begin(); + while (it != metrics_map_.end()) { + if (!it->second->seen_this_cycle()) { + metrics_map_.erase(it++); + continue; + } + + total_cpu_percent += it->second->last_cpu_percent(); + ++it; + } + + return total_cpu_percent; +} + +void ProcessPowerCollector::RecordCpuUsageByOrigin(double total_cpu_percent) { + DCHECK_GE(total_cpu_percent, 0); + if (total_cpu_percent == 0) + return; + + for (ProcessMetricsMap::iterator it = metrics_map_.begin(); + it != metrics_map_.end(); + ++it) { + double last_process_power_usage = it->second->last_cpu_percent(); + last_process_power_usage *= scale_factor_ / total_cpu_percent; + + GURL origin = it->second->last_origin(); + power::OriginPowerMap* origin_power_map = + power::OriginPowerMapFactory::GetForBrowserContext( + it->second->profile()); + DCHECK(origin_power_map); + origin_power_map->AddPowerForOrigin(origin, last_process_power_usage); + } +} + +void ProcessPowerCollector::UpdateProcessInMap( + const content::RenderProcessHost* rph, + const GURL& origin) { + base::ProcessHandle handle = rph->GetHandle(); + if (metrics_map_.find(handle) == metrics_map_.end()) { + metrics_map_[handle] = linked_ptr<PerProcessData>(new PerProcessData( +#if defined(OS_MACOSX) + scoped_ptr<base::ProcessMetrics>( + base::ProcessMetrics::CreateProcessMetrics(handle, NULL)), +#else + scoped_ptr<base::ProcessMetrics>( + base::ProcessMetrics::CreateProcessMetrics(handle)), +#endif + origin, + Profile::FromBrowserContext(rph->GetBrowserContext()))); + } + + linked_ptr<PerProcessData>& process_data = metrics_map_[handle]; + process_data->set_last_cpu_percent(std::max(0.0, + cpu_usage_callback_.is_null() ? process_data->metrics()->GetCPUUsage() + : cpu_usage_callback_.Run(handle))); + process_data->set_seen_this_cycle(true); +} diff --git a/chrome/browser/power/process_power_collector.h b/chrome/browser/power/process_power_collector.h new file mode 100644 index 0000000..b172a8f --- /dev/null +++ b/chrome/browser/power/process_power_collector.h @@ -0,0 +1,146 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_POWER_PROCESS_POWER_COLLECTOR_H_ +#define CHROME_BROWSER_POWER_PROCESS_POWER_COLLECTOR_H_ + +#include <map> + +#include "base/memory/linked_ptr.h" +#include "base/process/process_handle.h" +#include "base/process/process_metrics.h" +#include "base/timer/timer.h" +#include "components/power/origin_power_map_factory.h" +#include "url/gurl.h" + +#if defined(OS_CHROMEOS) +#include "chromeos/dbus/power_manager_client.h" +#endif + +class Profile; + +namespace content { +class RenderProcessHost; +} + +#if defined(OS_CHROMEOS) +namespace power_manager { +class PowerSupplyProperties; +} +#endif + +// Manages regular updates of the profile power consumption. +class ProcessPowerCollector +#if defined(OS_CHROMEOS) + : public chromeos::PowerManagerClient::Observer +#endif + { + public: + class PerProcessData { + public: + PerProcessData(scoped_ptr<base::ProcessMetrics> metrics, + const GURL& origin, + Profile* profile); + PerProcessData(); + ~PerProcessData(); + + base::ProcessMetrics* metrics() const { return metrics_.get(); } + Profile* profile() const { return profile_; } + GURL last_origin() const { return last_origin_; } + int last_cpu_percent() const { return last_cpu_percent_; } + bool seen_this_cycle() const { return seen_this_cycle_; } + void set_last_cpu_percent(double new_cpu) { last_cpu_percent_ = new_cpu; } + void set_seen_this_cycle(bool seen) { seen_this_cycle_ = seen; } + + private: + // |metrics_| holds the ProcessMetrics information for the given process. + scoped_ptr<base::ProcessMetrics> metrics_; + + // |profile| is the profile that is visiting the |last_origin_|. + // It is not owned by PerProcessData. + Profile* profile_; + + // |last_origin_| is the last origin visited by the process. + GURL last_origin_; + + // |last_cpu_percent_| is the proportion of the CPU used since the last + // query. + double last_cpu_percent_; + + // |seen_this_cycle| represents if the process still exists in this cycle. + // If it doesn't, we erase the PerProcessData. + bool seen_this_cycle_; + + DISALLOW_COPY_AND_ASSIGN(PerProcessData); + }; + + // A map from all process handles to a metric. + typedef std::map<base::ProcessHandle, linked_ptr<PerProcessData> > + ProcessMetricsMap; + // A callback used to define mock CPU usage for testing. + typedef base::Callback<double(base::ProcessHandle)> CpuUsageCallback; + + // On Chrome OS, can only be initialized after the DBusThreadManager has been + // initialized. + ProcessPowerCollector(); + // On Chrome OS, can only be destroyed before DBusThreadManager is. + virtual ~ProcessPowerCollector(); + + void set_cpu_usage_callback_for_testing(const CpuUsageCallback& callback) { + cpu_usage_callback_ = callback; + } + + ProcessMetricsMap* metrics_map_for_testing() { return &metrics_map_; } + +#if defined(OS_CHROMEOS) + // PowerManagerClient::Observer implementation: + virtual void PowerChanged( + const power_manager::PowerSupplyProperties& prop) OVERRIDE; +#endif + + // Begin periodically updating the power consumption numbers by profile. + void Initialize(); + + // Calls UpdatePowerConsumption() and returns the total CPU percent. + double UpdatePowerConsumptionForTesting(); + + private: + // Starts the timer for updating the power consumption. + void StartTimer(); + + // Calls SynchronizerProcesses() and RecordCpuUsageByOrigin() to update the + // |metrics_map_| and attribute power consumption. Invoked by |timer_| and as + // a helper method for UpdatePowerConsumptionForTesting(). + double UpdatePowerConsumption(); + + // Calls UpdatePowerConsumption(). Invoked by |timer_|. + void HandleUpdateTimeout(); + + // Synchronizes the currently active processes to the |metrics_map_| and + // returns the total amount of cpu usage in the cycle. + double SynchronizeProcesses(); + + // Attributes the power usage to the profiles and origins using the + // information from SynchronizeProcesses() given a total amount + // of CPU used in this cycle, |total_cpu_percent|. + void RecordCpuUsageByOrigin(double total_cpu_percent); + + // Adds the information from a given RenderProcessHost to the |metrics_map_| + // for a given origin. Called by SynchronizeProcesses(). + void UpdateProcessInMap(const content::RenderProcessHost* render_process, + const GURL& origin); + + ProcessMetricsMap metrics_map_; + base::RepeatingTimer<ProcessPowerCollector> timer_; + + // Callback to use to get CPU usage if set. + CpuUsageCallback cpu_usage_callback_; + + // The factor to scale the CPU usage by. + double scale_factor_; + + DISALLOW_COPY_AND_ASSIGN(ProcessPowerCollector); +}; + +#endif // CHROME_BROWSER_POWER_PROCESS_POWER_COLLECTOR_H_ diff --git a/chrome/browser/power/process_power_collector_unittest.cc b/chrome/browser/power/process_power_collector_unittest.cc new file mode 100644 index 0000000..0a21e34 --- /dev/null +++ b/chrome/browser/power/process_power_collector_unittest.cc @@ -0,0 +1,314 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/power/process_power_collector.h" + +#include "apps/app_window_contents.h" +#include "apps/app_window_registry.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/ui/apps/chrome_app_delegate.h" +#include "chrome/browser/ui/browser_commands.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/test/base/browser_with_test_window_test.h" +#include "chrome/test/base/testing_browser_process.h" +#include "chrome/test/base/testing_profile_manager.h" +#include "components/power/origin_power_map.h" +#include "components/power/origin_power_map_factory.h" +#include "content/public/browser/site_instance.h" +#include "content/public/test/browser_test_utils.h" +#include "content/public/test/mock_render_process_host.h" +#include "extensions/browser/app_window/native_app_window.h" +#include "extensions/common/extension.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +#if defined(OS_CHROMEOS) +#include "chromeos/dbus/power_manager/power_supply_properties.pb.h" +#endif + +using power::OriginPowerMap; +using power::OriginPowerMapFactory; + +class BrowserProcessPowerTest : public BrowserWithTestWindowTest { + public: + BrowserProcessPowerTest() {} + virtual ~BrowserProcessPowerTest() {} + + virtual void SetUp() OVERRIDE { + BrowserWithTestWindowTest::SetUp(); + collector.reset(new ProcessPowerCollector); + +#if defined(OS_CHROMEOS) + power_manager::PowerSupplyProperties prop; + prop.set_external_power(power_manager::PowerSupplyProperties::AC); + prop.set_battery_state(power_manager::PowerSupplyProperties::DISCHARGING); + prop.set_battery_percent(20.00); + prop.set_battery_discharge_rate(1); + collector->PowerChanged(prop); +#endif + + profile_manager_.reset( + new TestingProfileManager(TestingBrowserProcess::GetGlobal())); + ASSERT_TRUE(profile_manager_->SetUp()); + } + + virtual void TearDown() OVERRIDE { + collector.reset(); + BrowserWithTestWindowTest::TearDown(); + } + + // Mocks out CPU usage for all processes as |value| percent. + double ReturnCpuAsConstant(double value, base::ProcessHandle handle) { + return value; + } + + protected: + content::MockRenderProcessHost* GetProcess(Browser* browser) { + return static_cast<content::MockRenderProcessHost*>( + browser->tab_strip_model() + ->GetActiveWebContents() + ->GetRenderViewHost() + ->GetProcess()); + } + + scoped_ptr<base::ProcessHandle> MakeProcessHandle(int process_id) { + scoped_ptr<base::ProcessHandle> proc_handle(new base::ProcessHandle( +#if defined(OS_WIN) + reinterpret_cast<HANDLE>(process_id)) +#else + process_id) +#endif + ); + return proc_handle.Pass(); + } + + scoped_ptr<ProcessPowerCollector> collector; + scoped_ptr<TestingProfileManager> profile_manager_; +}; + +class TestAppWindowContents : public apps::AppWindowContents { + public: + explicit TestAppWindowContents(content::WebContents* web_contents) + : web_contents_(web_contents) {} + + // apps:AppWindowContents + virtual void Initialize(content::BrowserContext* context, + const GURL& url) OVERRIDE {} + virtual void LoadContents(int32 creator_process_id) OVERRIDE {} + virtual void NativeWindowChanged( + extensions::NativeAppWindow* native_app_window) OVERRIDE {} + virtual void NativeWindowClosed() OVERRIDE {} + virtual void DispatchWindowShownForTests() const OVERRIDE {} + virtual content::WebContents* GetWebContents() const OVERRIDE { + return web_contents_.get(); + } + + private: + scoped_ptr<content::WebContents> web_contents_; +}; + +TEST_F(BrowserProcessPowerTest, NoSite) { + collector->UpdatePowerConsumptionForTesting(); + EXPECT_EQ(0u, collector->metrics_map_for_testing()->size()); +} + +TEST_F(BrowserProcessPowerTest, OneSite) { + GURL url("http://www.google.com"); + AddTab(browser(), url); + collector->UpdatePowerConsumptionForTesting(); + ProcessPowerCollector::ProcessMetricsMap* metrics_map = + collector->metrics_map_for_testing(); + EXPECT_EQ(1u, metrics_map->size()); + + // Create fake process numbers. + GetProcess(browser())->SetProcessHandle(MakeProcessHandle(1).Pass()); + + OriginPowerMap* origin_power_map = + OriginPowerMapFactory::GetForBrowserContext(profile()); + EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(url)); + + collector->set_cpu_usage_callback_for_testing( + base::Bind(&BrowserProcessPowerTest::ReturnCpuAsConstant, + base::Unretained(this), + 5)); + EXPECT_DOUBLE_EQ(5, collector->UpdatePowerConsumptionForTesting()); + EXPECT_EQ(100, origin_power_map->GetPowerForOrigin(url)); +} + +TEST_F(BrowserProcessPowerTest, MultipleSites) { + Browser::CreateParams native_params(profile(), + chrome::HOST_DESKTOP_TYPE_NATIVE); + GURL url1("http://www.google.com"); + GURL url2("http://www.example.com"); + GURL url3("https://www.google.com"); + scoped_ptr<Browser> browser2( + chrome::CreateBrowserWithTestWindowForParams(&native_params)); + scoped_ptr<Browser> browser3( + chrome::CreateBrowserWithTestWindowForParams(&native_params)); + AddTab(browser(), url1); + AddTab(browser2.get(), url2); + AddTab(browser3.get(), url3); + + // Create fake process numbers. + GetProcess(browser())->SetProcessHandle(MakeProcessHandle(1).Pass()); + GetProcess(browser2.get())->SetProcessHandle(MakeProcessHandle(2).Pass()); + GetProcess(browser3.get())->SetProcessHandle(MakeProcessHandle(3).Pass()); + + collector->UpdatePowerConsumptionForTesting(); + ProcessPowerCollector::ProcessMetricsMap* metrics_map = + collector->metrics_map_for_testing(); + EXPECT_EQ(3u, metrics_map->size()); + + // Since all handlers are uninitialized, this should be 0. + EXPECT_DOUBLE_EQ(0, collector->UpdatePowerConsumptionForTesting()); + OriginPowerMap* origin_power_map = + OriginPowerMapFactory::GetForBrowserContext(profile()); + EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(url1)); + EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(url2)); + EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(url3)); + + collector->set_cpu_usage_callback_for_testing( + base::Bind(&BrowserProcessPowerTest::ReturnCpuAsConstant, + base::Unretained(this), + 5)); + EXPECT_DOUBLE_EQ(15, collector->UpdatePowerConsumptionForTesting()); + EXPECT_EQ(33, origin_power_map->GetPowerForOrigin(url1)); + EXPECT_EQ(33, origin_power_map->GetPowerForOrigin(url2)); + EXPECT_EQ(33, origin_power_map->GetPowerForOrigin(url3)); + + // Close some tabs and verify that they are removed from the metrics map. + chrome::CloseTab(browser2.get()); + chrome::CloseTab(browser3.get()); + + collector->UpdatePowerConsumptionForTesting(); + EXPECT_EQ(1u, metrics_map->size()); +} + +TEST_F(BrowserProcessPowerTest, IncognitoDoesntRecordPowerUsage) { + Browser::CreateParams native_params(profile()->GetOffTheRecordProfile(), + chrome::HOST_DESKTOP_TYPE_NATIVE); + scoped_ptr<Browser> incognito_browser( + chrome::CreateBrowserWithTestWindowForParams(&native_params)); + GURL url("http://www.google.com"); + AddTab(browser(), url); + + GURL hidden_url("http://foo.com"); + AddTab(incognito_browser.get(), hidden_url); + + // Create fake process numbers. + GetProcess(browser())->SetProcessHandle(MakeProcessHandle(1).Pass()); + GetProcess(incognito_browser.get()) + ->SetProcessHandle(MakeProcessHandle(2).Pass()); + + EXPECT_DOUBLE_EQ(0, collector->UpdatePowerConsumptionForTesting()); + ProcessPowerCollector::ProcessMetricsMap* metrics_map = + collector->metrics_map_for_testing(); + EXPECT_EQ(1u, metrics_map->size()); + + OriginPowerMap* origin_power_map = + OriginPowerMapFactory::GetForBrowserContext(profile()); + EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(url)); + + collector->set_cpu_usage_callback_for_testing( + base::Bind(&BrowserProcessPowerTest::ReturnCpuAsConstant, + base::Unretained(this), + 5)); + EXPECT_DOUBLE_EQ(5, collector->UpdatePowerConsumptionForTesting()); + + // Verify that the incognito data was not stored. + EXPECT_EQ(100, origin_power_map->GetPowerForOrigin(url)); + EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(hidden_url)); + + chrome::CloseTab(incognito_browser.get()); +} + +TEST_F(BrowserProcessPowerTest, MultipleProfilesRecordSeparately) { + scoped_ptr<Profile> other_profile(CreateProfile()); + Browser::CreateParams native_params(other_profile.get(), + chrome::HOST_DESKTOP_TYPE_NATIVE); + scoped_ptr<Browser> other_user( + chrome::CreateBrowserWithTestWindowForParams(&native_params)); + + GURL url("http://www.google.com"); + AddTab(browser(), url); + + GURL hidden_url("http://foo.com"); + AddTab(other_user.get(), hidden_url); + + // Create fake process numbers. + GetProcess(browser())->SetProcessHandle(MakeProcessHandle(1).Pass()); + GetProcess(other_user.get())->SetProcessHandle(MakeProcessHandle(2).Pass()); + + EXPECT_DOUBLE_EQ(0, collector->UpdatePowerConsumptionForTesting()); + EXPECT_EQ(2u, collector->metrics_map_for_testing()->size()); + + collector->set_cpu_usage_callback_for_testing( + base::Bind(&BrowserProcessPowerTest::ReturnCpuAsConstant, + base::Unretained(this), + 5)); + EXPECT_DOUBLE_EQ(10, collector->UpdatePowerConsumptionForTesting()); + + // profile() should have an entry for |url| but not |hidden_url|. + OriginPowerMap* origin_power_map_first = + OriginPowerMapFactory::GetForBrowserContext(profile()); + EXPECT_EQ(100, origin_power_map_first->GetPowerForOrigin(url)); + EXPECT_EQ(0, origin_power_map_first->GetPowerForOrigin(hidden_url)); + + // |other_profile| should have an entry for |hidden_url| but not |url|. + OriginPowerMap* origin_power_map_second = + OriginPowerMapFactory::GetForBrowserContext(other_profile.get()); + EXPECT_EQ(0, origin_power_map_second->GetPowerForOrigin(url)); + EXPECT_EQ(100, origin_power_map_second->GetPowerForOrigin(hidden_url)); + + // Clean up + chrome::CloseTab(other_user.get()); +} + +TEST_F(BrowserProcessPowerTest, AppsRecordPowerUsage) { +// Install an app (an extension*). +#if defined(OS_WIN) + base::FilePath extension_path(FILE_PATH_LITERAL("c:\\foo")); +#elif defined(OS_POSIX) + base::FilePath extension_path(FILE_PATH_LITERAL("/foo")); +#endif + base::DictionaryValue manifest; + manifest.SetString("name", "Fake Name"); + manifest.SetString("version", "1"); + std::string error; + char kTestAppId[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + scoped_refptr<extensions::Extension> extension( + extensions::Extension::Create(extension_path, + extensions::Manifest::INTERNAL, + manifest, + extensions::Extension::NO_FLAGS, + kTestAppId, + &error)); + EXPECT_TRUE(extension.get()) << error; + + Profile* current_profile = + profile_manager_->CreateTestingProfile("Test user"); + GURL url("chrome-extension://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + apps::AppWindow* window = + new apps::AppWindow(current_profile, new ChromeAppDelegate(), extension); + scoped_ptr<content::WebContents> web_contents( + content::WebContents::Create(content::WebContents::CreateParams( + current_profile, + content::SiteInstance::CreateForURL(current_profile, url)))); + window->SetAppWindowContentsForTesting(scoped_ptr<apps::AppWindowContents>( + new TestAppWindowContents(web_contents.get()))); + apps::AppWindowRegistry* app_registry = + apps::AppWindowRegistry::Get(current_profile); + app_registry->AddAppWindow(window); + + collector->set_cpu_usage_callback_for_testing( + base::Bind(&BrowserProcessPowerTest::ReturnCpuAsConstant, + base::Unretained(this), + 5)); + collector->UpdatePowerConsumptionForTesting(); + EXPECT_EQ(1u, collector->metrics_map_for_testing()->size()); + + app_registry->RemoveAppWindow(window); + collector->UpdatePowerConsumptionForTesting(); + EXPECT_EQ(0u, collector->metrics_map_for_testing()->size()); +} diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 1350d68..ddb5b58 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -870,6 +870,8 @@ 'browser/platform_util_chromeos.cc', 'browser/platform_util_mac.mm', 'browser/platform_util_win.cc', + 'browser/power/process_power_collector.cc', + 'browser/power/process_power_collector.h', 'browser/precache/most_visited_urls_provider.cc', 'browser/precache/most_visited_urls_provider.h', 'browser/predictors/autocomplete_action_predictor.cc', @@ -2919,6 +2921,7 @@ '../components/components.gyp:keyed_service_content', '../components/components.gyp:navigation_interception', '../components/components.gyp:password_manager_content_browser', + '../components/components.gyp:power', '../components/components.gyp:precache_content', '../components/components.gyp:sessions', '../components/components.gyp:storage_monitor', diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index 2af14f9..f8923bf 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -1137,6 +1137,7 @@ 'browser/policy/policy_path_parser_unittest.cc', 'browser/policy/profile_policy_connector_unittest.cc', 'browser/policy/url_blacklist_manager_unittest.cc', + 'browser/power/process_power_collector_unittest.cc', 'browser/predictors/autocomplete_action_predictor_table_unittest.cc', 'browser/predictors/autocomplete_action_predictor_unittest.cc', 'browser/prefs/chrome_pref_service_unittest.cc', @@ -2565,6 +2566,7 @@ 'browser/process_singleton_posix_unittest.cc', 'browser/profiles/off_the_record_profile_impl_unittest.cc', 'browser/profiles/profile_list_desktop_unittest.cc', + 'browser/power/process_power_collector_unittest.cc', 'browser/renderer_context_menu/render_view_context_menu_unittest.cc', 'browser/search/instant_service_unittest.cc', 'browser/search/search_unittest.cc', diff --git a/content/public/test/mock_render_process_host.cc b/content/public/test/mock_render_process_host.cc index 1f5de5f..a59ce5f 100644 --- a/content/public/test/mock_render_process_host.cc +++ b/content/public/test/mock_render_process_host.cc @@ -122,6 +122,8 @@ void MockRenderProcessHost::DumpHandles() { base::ProcessHandle MockRenderProcessHost::GetHandle() const { // Return the current-process handle for the IPC::GetFileHandleForProcess // function. + if (process_handle) + return *process_handle; return base::Process::Current().handle(); } diff --git a/content/public/test/mock_render_process_host.h b/content/public/test/mock_render_process_host.h index 6340170..f769c5f 100644 --- a/content/public/test/mock_render_process_host.h +++ b/content/public/test/mock_render_process_host.h @@ -103,6 +103,10 @@ class MockRenderProcessHost : public RenderProcessHost { is_isolated_guest_ = is_isolated_guest; } + void SetProcessHandle(scoped_ptr<base::ProcessHandle> new_handle) { + process_handle = new_handle.Pass(); + } + private: // Stores IPC messages that would have been sent to the renderer. IPC::TestSink sink_; @@ -118,6 +122,7 @@ class MockRenderProcessHost : public RenderProcessHost { bool fast_shutdown_started_; bool deletion_callback_called_; bool is_isolated_guest_; + scoped_ptr<base::ProcessHandle> process_handle; DISALLOW_COPY_AND_ASSIGN(MockRenderProcessHost); }; |