// 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 "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 "extensions/browser/app_window/app_window.h" #include "extensions/browser/app_window/app_window_registry.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 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 open_profiles = pm->GetLoadedProfiles(); for (std::vector::const_iterator it = open_profiles.begin(); it != open_profiles.end(); ++it) { const extensions::AppWindowRegistry::AppWindowList& app_windows = extensions::AppWindowRegistry::Get(*it)->app_windows(); for (extensions::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); } // Iterate over all profiles to let them know we've finished updating. ProfileManager* pm = g_browser_process->profile_manager(); std::vector open_profiles = pm->GetLoadedProfiles(); for (std::vector::const_iterator it = open_profiles.begin(); it != open_profiles.end(); ++it) { power::OriginPowerMap* origin_power_map = power::OriginPowerMapFactory::GetForBrowserContext(*it); origin_power_map->OnAllOriginsUpdated(); } } 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(new PerProcessData( #if defined(OS_MACOSX) scoped_ptr( base::ProcessMetrics::CreateProcessMetrics(handle, NULL)), #else scoped_ptr( base::ProcessMetrics::CreateProcessMetrics(handle)), #endif origin, Profile::FromBrowserContext(rph->GetBrowserContext()))); } linked_ptr& 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); }