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.
5 #include "chrome/browser/power/process_power_collector.h"
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"
22 #if defined(OS_CHROMEOS)
23 #include "chromeos/dbus/dbus_thread_manager.h"
24 #include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
28 const int kSecondsPerSample
= 30;
31 ProcessPowerCollector::PerProcessData::PerProcessData(
32 scoped_ptr
<base::ProcessMetrics
> metrics
,
35 : metrics_(metrics
.Pass()),
39 seen_this_cycle_(true) {
42 ProcessPowerCollector::PerProcessData::PerProcessData()
44 last_cpu_percent_(0.0),
45 seen_this_cycle_(false) {
48 ProcessPowerCollector::PerProcessData::~PerProcessData() {
51 ProcessPowerCollector::ProcessPowerCollector()
52 : scale_factor_(1.0) {
53 #if defined(OS_CHROMEOS)
54 chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(
59 ProcessPowerCollector::~ProcessPowerCollector() {
60 #if defined(OS_CHROMEOS)
61 chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(
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())
73 scale_factor_
= prop
.battery_discharge_rate();
80 void ProcessPowerCollector::Initialize() {
84 double ProcessPowerCollector::UpdatePowerConsumptionForTesting() {
85 return UpdatePowerConsumption();
88 void ProcessPowerCollector::StartTimer() {
89 DCHECK(!timer_
.IsRunning());
90 timer_
.Start(FROM_HERE
,
91 base::TimeDelta::FromSeconds(kSecondsPerSample
),
93 &ProcessPowerCollector::HandleUpdateTimeout
);
96 double ProcessPowerCollector::UpdatePowerConsumption() {
97 double total_cpu_percent
= SynchronizeProcesses();
99 for (ProcessMetricsMap::iterator it
= metrics_map_
.begin();
100 it
!= metrics_map_
.end();
102 // Invalidate the process for the next cycle.
103 it
->second
->set_seen_this_cycle(false);
106 RecordCpuUsageByOrigin(total_cpu_percent
);
107 return total_cpu_percent
;
110 void ProcessPowerCollector::HandleUpdateTimeout() {
111 UpdatePowerConsumption();
114 double ProcessPowerCollector::SynchronizeProcesses() {
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())
121 UpdateProcessInMap(render_process
, it
->GetLastCommittedURL().GetOrigin());
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();
130 const extensions::AppWindowRegistry::AppWindowList
& app_windows
=
131 extensions::AppWindowRegistry::Get(*it
)->app_windows();
132 for (extensions::AppWindowRegistry::AppWindowList::const_iterator itr
=
134 itr
!= app_windows
.end();
136 content::WebContents
* web_contents
= (*itr
)->web_contents();
138 UpdateProcessInMap(web_contents
->GetRenderProcessHost(),
139 web_contents
->GetLastCommittedURL().GetOrigin());
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
++);
152 total_cpu_percent
+= it
->second
->last_cpu_percent();
156 return total_cpu_percent
;
159 void ProcessPowerCollector::RecordCpuUsageByOrigin(double total_cpu_percent
) {
160 DCHECK_GE(total_cpu_percent
, 0);
161 if (total_cpu_percent
== 0)
164 for (ProcessMetricsMap::iterator it
= metrics_map_
.begin();
165 it
!= metrics_map_
.end();
167 double last_process_power_usage
= it
->second
->last_cpu_percent();
168 last_process_power_usage
*= scale_factor_
/ total_cpu_percent
;
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
176 if (!origin_power_map
)
178 origin_power_map
->AddPowerForOrigin(origin
, last_process_power_usage
);
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();
187 power::OriginPowerMap
* origin_power_map
=
188 power::OriginPowerMapFactory::GetForBrowserContext(*it
);
189 if (!origin_power_map
)
191 origin_power_map
->OnAllOriginsUpdated();
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
)),
205 scoped_ptr
<base::ProcessMetrics
>(
206 base::ProcessMetrics::CreateProcessMetrics(handle
)),
209 Profile::FromBrowserContext(rph
->GetBrowserContext())));
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);