summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/app_window.h4
-rw-r--r--chrome/browser/DEPS1
-rw-r--r--chrome/browser/chrome_browser_main.cc10
-rw-r--r--chrome/browser/chrome_browser_main.h6
-rw-r--r--chrome/browser/power/OWNERS3
-rw-r--r--chrome/browser/power/process_power_collector.cc201
-rw-r--r--chrome/browser/power/process_power_collector.h146
-rw-r--r--chrome/browser/power/process_power_collector_unittest.cc314
-rw-r--r--chrome/chrome_browser.gypi3
-rw-r--r--chrome/chrome_tests_unit.gypi2
-rw-r--r--content/public/test/mock_render_process_host.cc2
-rw-r--r--content/public/test/mock_render_process_host.h5
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);
};