1// Copyright 2014 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/power/process_power_collector.h" 6 7#include "base/process/process_handle.h" 8#include "base/process/process_metrics.h" 9#include "chrome/browser/browser_process.h" 10#include "chrome/browser/profiles/profile.h" 11#include "chrome/browser/profiles/profile_manager.h" 12#include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" 13#include "components/power/origin_power_map.h" 14#include "components/power/origin_power_map_factory.h" 15#include "content/public/browser/browser_context.h" 16#include "content/public/browser/render_process_host.h" 17#include "content/public/browser/web_contents.h" 18#include "extensions/browser/app_window/app_window.h" 19#include "extensions/browser/app_window/app_window_registry.h" 20#include "url/gurl.h" 21 22#if defined(OS_CHROMEOS) 23#include "chromeos/dbus/dbus_thread_manager.h" 24#include "chromeos/dbus/power_manager/power_supply_properties.pb.h" 25#endif 26 27namespace { 28const int kSecondsPerSample = 30; 29} 30 31ProcessPowerCollector::PerProcessData::PerProcessData( 32 scoped_ptr<base::ProcessMetrics> metrics, 33 const GURL& origin, 34 Profile* profile) 35 : metrics_(metrics.Pass()), 36 profile_(profile), 37 last_origin_(origin), 38 last_cpu_percent_(0), 39 seen_this_cycle_(true) { 40} 41 42ProcessPowerCollector::PerProcessData::PerProcessData() 43 : profile_(NULL), 44 last_cpu_percent_(0.0), 45 seen_this_cycle_(false) { 46} 47 48ProcessPowerCollector::PerProcessData::~PerProcessData() { 49} 50 51ProcessPowerCollector::ProcessPowerCollector() 52 : scale_factor_(1.0) { 53#if defined(OS_CHROMEOS) 54 chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver( 55 this); 56#endif 57} 58 59ProcessPowerCollector::~ProcessPowerCollector() { 60#if defined(OS_CHROMEOS) 61 chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver( 62 this); 63#endif 64} 65 66#if defined(OS_CHROMEOS) 67void ProcessPowerCollector::PowerChanged( 68 const power_manager::PowerSupplyProperties& prop) { 69 if (prop.battery_state() == 70 power_manager::PowerSupplyProperties::DISCHARGING) { 71 if (!timer_.IsRunning()) 72 StartTimer(); 73 scale_factor_ = prop.battery_discharge_rate(); 74 } else { 75 timer_.Stop(); 76 } 77} 78#endif 79 80void ProcessPowerCollector::Initialize() { 81 StartTimer(); 82} 83 84double ProcessPowerCollector::UpdatePowerConsumptionForTesting() { 85 return UpdatePowerConsumption(); 86} 87 88void ProcessPowerCollector::StartTimer() { 89 DCHECK(!timer_.IsRunning()); 90 timer_.Start(FROM_HERE, 91 base::TimeDelta::FromSeconds(kSecondsPerSample), 92 this, 93 &ProcessPowerCollector::HandleUpdateTimeout); 94} 95 96double ProcessPowerCollector::UpdatePowerConsumption() { 97 double total_cpu_percent = SynchronizeProcesses(); 98 99 for (ProcessMetricsMap::iterator it = metrics_map_.begin(); 100 it != metrics_map_.end(); 101 ++it) { 102 // Invalidate the process for the next cycle. 103 it->second->set_seen_this_cycle(false); 104 } 105 106 RecordCpuUsageByOrigin(total_cpu_percent); 107 return total_cpu_percent; 108} 109 110void ProcessPowerCollector::HandleUpdateTimeout() { 111 UpdatePowerConsumption(); 112} 113 114double ProcessPowerCollector::SynchronizeProcesses() { 115 // Update all tabs. 116 for (TabContentsIterator it; !it.done(); it.Next()) { 117 content::RenderProcessHost* render_process = it->GetRenderProcessHost(); 118 // Skip incognito web contents. 119 if (render_process->GetBrowserContext()->IsOffTheRecord()) 120 continue; 121 UpdateProcessInMap(render_process, it->GetLastCommittedURL().GetOrigin()); 122 } 123 124 // Iterate over all profiles to find all app windows to attribute all apps. 125 ProfileManager* pm = g_browser_process->profile_manager(); 126 std::vector<Profile*> open_profiles = pm->GetLoadedProfiles(); 127 for (std::vector<Profile*>::const_iterator it = open_profiles.begin(); 128 it != open_profiles.end(); 129 ++it) { 130 const extensions::AppWindowRegistry::AppWindowList& app_windows = 131 extensions::AppWindowRegistry::Get(*it)->app_windows(); 132 for (extensions::AppWindowRegistry::AppWindowList::const_iterator itr = 133 app_windows.begin(); 134 itr != app_windows.end(); 135 ++itr) { 136 content::WebContents* web_contents = (*itr)->web_contents(); 137 138 UpdateProcessInMap(web_contents->GetRenderProcessHost(), 139 web_contents->GetLastCommittedURL().GetOrigin()); 140 } 141 } 142 143 // Remove invalid processes and sum up the cpu cycle. 144 double total_cpu_percent = 0.0; 145 ProcessMetricsMap::iterator it = metrics_map_.begin(); 146 while (it != metrics_map_.end()) { 147 if (!it->second->seen_this_cycle()) { 148 metrics_map_.erase(it++); 149 continue; 150 } 151 152 total_cpu_percent += it->second->last_cpu_percent(); 153 ++it; 154 } 155 156 return total_cpu_percent; 157} 158 159void ProcessPowerCollector::RecordCpuUsageByOrigin(double total_cpu_percent) { 160 DCHECK_GE(total_cpu_percent, 0); 161 if (total_cpu_percent == 0) 162 return; 163 164 for (ProcessMetricsMap::iterator it = metrics_map_.begin(); 165 it != metrics_map_.end(); 166 ++it) { 167 double last_process_power_usage = it->second->last_cpu_percent(); 168 last_process_power_usage *= scale_factor_ / total_cpu_percent; 169 170 GURL origin = it->second->last_origin(); 171 power::OriginPowerMap* origin_power_map = 172 power::OriginPowerMapFactory::GetForBrowserContext( 173 it->second->profile()); 174 // |origin_power_map| can be NULL, if the profile is a guest profile in 175 // Chrome OS. 176 if (!origin_power_map) 177 continue; 178 origin_power_map->AddPowerForOrigin(origin, last_process_power_usage); 179 } 180 181 // Iterate over all profiles to let them know we've finished updating. 182 ProfileManager* pm = g_browser_process->profile_manager(); 183 std::vector<Profile*> open_profiles = pm->GetLoadedProfiles(); 184 for (std::vector<Profile*>::const_iterator it = open_profiles.begin(); 185 it != open_profiles.end(); 186 ++it) { 187 power::OriginPowerMap* origin_power_map = 188 power::OriginPowerMapFactory::GetForBrowserContext(*it); 189 if (!origin_power_map) 190 continue; 191 origin_power_map->OnAllOriginsUpdated(); 192 } 193} 194 195void ProcessPowerCollector::UpdateProcessInMap( 196 const content::RenderProcessHost* rph, 197 const GURL& origin) { 198 base::ProcessHandle handle = rph->GetHandle(); 199 if (metrics_map_.find(handle) == metrics_map_.end()) { 200 metrics_map_[handle] = linked_ptr<PerProcessData>(new PerProcessData( 201#if defined(OS_MACOSX) 202 scoped_ptr<base::ProcessMetrics>( 203 base::ProcessMetrics::CreateProcessMetrics(handle, NULL)), 204#else 205 scoped_ptr<base::ProcessMetrics>( 206 base::ProcessMetrics::CreateProcessMetrics(handle)), 207#endif 208 origin, 209 Profile::FromBrowserContext(rph->GetBrowserContext()))); 210 } 211 212 linked_ptr<PerProcessData>& process_data = metrics_map_[handle]; 213 process_data->set_last_cpu_percent(std::max(0.0, 214 cpu_usage_callback_.is_null() ? process_data->metrics()->GetCPUUsage() 215 : cpu_usage_callback_.Run(handle))); 216 process_data->set_seen_this_cycle(true); 217} 218