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 27 namespace { 28 const int kSecondsPerSample = 30; 29 } 30 31 ProcessPowerCollector::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 42 ProcessPowerCollector::PerProcessData::PerProcessData() 43 : profile_(NULL), 44 last_cpu_percent_(0.0), 45 seen_this_cycle_(false) { 46 } 47 48 ProcessPowerCollector::PerProcessData::~PerProcessData() { 49 } 50 51 ProcessPowerCollector::ProcessPowerCollector() 52 : scale_factor_(1.0) { 53 #if defined(OS_CHROMEOS) 54 chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver( 55 this); 56 #endif 57 } 58 59 ProcessPowerCollector::~ProcessPowerCollector() { 60 #if defined(OS_CHROMEOS) 61 chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver( 62 this); 63 #endif 64 } 65 66 #if defined(OS_CHROMEOS) 67 void 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 80 void ProcessPowerCollector::Initialize() { 81 StartTimer(); 82 } 83 84 double ProcessPowerCollector::UpdatePowerConsumptionForTesting() { 85 return UpdatePowerConsumption(); 86 } 87 88 void ProcessPowerCollector::StartTimer() { 89 DCHECK(!timer_.IsRunning()); 90 timer_.Start(FROM_HERE, 91 base::TimeDelta::FromSeconds(kSecondsPerSample), 92 this, 93 &ProcessPowerCollector::HandleUpdateTimeout); 94 } 95 96 double 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 110 void ProcessPowerCollector::HandleUpdateTimeout() { 111 UpdatePowerConsumption(); 112 } 113 114 double 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 159 void 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 195 void 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