Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / tools / perf / metrics / power.py
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 import time
6
7 from metrics import Metric
8 from telemetry.value import scalar
9
10
11 class PowerMetric(Metric):
12   """A metric for measuring power usage."""
13
14   # System power draw while idle.
15   _quiescent_power_draw_mwh = 0
16
17   def __init__(self, browser, quiescent_measurement_time_s=0):
18     """PowerMetric Constructor.
19
20     Args:
21         browser: browser object to use.
22         quiescent_measurement_time_s: time to measure quiescent power,
23             in seconds. 0 means don't measure quiescent power."""
24     super(PowerMetric, self).__init__()
25     self._browser = browser
26     self._running = False
27     self._starting_cpu_stats = None
28     self._results = None
29     self._MeasureQuiescentPower(quiescent_measurement_time_s)
30
31   def __del__(self):
32     # TODO(jeremy): Remove once crbug.com/350841 is fixed.
33     # Don't leave power monitoring processes running on the system.
34     self._StopInternal()
35     parent = super(PowerMetric, self)
36     if hasattr(parent, '__del__'):
37       parent.__del__()
38
39   def _StopInternal(self):
40     """Stop monitoring power if measurement is running. This function is
41     idempotent."""
42     if not self._running:
43       return
44     self._running = False
45     self._results = self._browser.platform.StopMonitoringPower()
46     if self._results: # StopMonitoringPower() can return None.
47       self._results['cpu_stats'] = (
48           _SubtractCpuStats(self._browser.cpu_stats, self._starting_cpu_stats))
49
50   def _MeasureQuiescentPower(self, measurement_time_s):
51     """Measure quiescent power draw for the system."""
52     platform = self._browser.platform
53     if not platform.CanMonitorPower() or \
54         platform.CanMeasurePerApplicationPower() or \
55         not measurement_time_s:
56       return
57
58     # Only perform quiescent measurement once per run.
59     if PowerMetric._quiescent_power_draw_mwh:
60       return
61
62     platform.StartMonitoringPower(self._browser)
63     time.sleep(measurement_time_s)
64     power_results = platform.StopMonitoringPower()
65     PowerMetric._quiescent_power_draw_mwh = (
66         power_results.get('energy_consumption_mwh', 0))
67
68   def Start(self, _, tab):
69     if not tab.browser.platform.CanMonitorPower():
70       return
71
72     self._results = None
73     self._StopInternal()
74
75     # This line invokes top a few times, call before starting power measurement.
76     self._starting_cpu_stats = self._browser.cpu_stats
77     self._browser.platform.StartMonitoringPower(self._browser)
78     self._running = True
79
80   def Stop(self, _, tab):
81     if not tab.browser.platform.CanMonitorPower():
82       return
83
84     self._StopInternal()
85
86   def AddResults(self, _, results):
87     """Add the collected power data into the results object.
88
89     This function needs to be robust in the face of differing power data on
90     various platforms. Therefore data existence needs to be checked when
91     building up the results. Additionally 0 is a valid value for many of the
92     metrics here which is why there are plenty of checks for 'is not None'
93     below.
94     """
95     if not self._results:
96       return
97
98     application_energy_consumption_mwh = (
99         self._results.get('application_energy_consumption_mwh'))
100     total_energy_consumption_mwh = self._results.get('energy_consumption_mwh')
101
102     if not application_energy_consumption_mwh and total_energy_consumption_mwh:
103       application_energy_consumption_mwh = max(
104           total_energy_consumption_mwh - PowerMetric._quiescent_power_draw_mwh,
105           0)
106
107     if total_energy_consumption_mwh is not None:
108       results.AddValue(scalar.ScalarValue(
109           results.current_page, 'energy_consumption_mwh', 'mWh',
110           total_energy_consumption_mwh))
111
112     if application_energy_consumption_mwh is not None:
113       results.AddValue(scalar.ScalarValue(
114           results.current_page, 'application_energy_consumption_mwh', 'mWh',
115           application_energy_consumption_mwh))
116
117     component_utilization = self._results.get('component_utilization', {})
118     # GPU Frequency.
119     gpu_power = component_utilization.get('gpu', {})
120     gpu_freq_hz = gpu_power.get('average_frequency_hz')
121     if gpu_freq_hz is not None:
122       results.AddValue(scalar.ScalarValue(
123           results.current_page, 'gpu_average_frequency_hz', 'hz', gpu_freq_hz,
124           important=False))
125
126     # Add idle wakeup numbers for all processes.
127     for (process_type, stats) in self._results.get('cpu_stats', {}).items():
128       trace_name_for_process = 'idle_wakeups_%s' % (process_type.lower())
129       results.AddValue(scalar.ScalarValue(
130           results.current_page, trace_name_for_process, 'count', stats,
131           important=False))
132
133     # Add temperature measurements.
134     whole_package_utilization = component_utilization.get('whole_package', {})
135     board_temperature_c = whole_package_utilization.get('average_temperature_c')
136     if board_temperature_c is not None:
137       results.AddValue(scalar.ScalarValue(
138           results.current_page, 'board_temperature', 'celsius',
139           board_temperature_c, important=False))
140
141     self._results = None
142
143 def _SubtractCpuStats(cpu_stats, start_cpu_stats):
144   """Computes number of idle wakeups that occurred over measurement period.
145
146   Each of the two cpu_stats arguments is a dict as returned by the
147   Browser.cpu_stats call.
148
149   Returns:
150     A dict of process type names (Browser, Renderer, etc.) to idle wakeup count
151     over the period recorded by the input.
152   """
153   cpu_delta = {}
154   for process_type in cpu_stats:
155     assert process_type in start_cpu_stats, 'Mismatching process types'
156     # Skip any process_types that are empty.
157     if (not cpu_stats[process_type]) or (not start_cpu_stats[process_type]):
158       continue
159     # Skip if IdleWakeupCount is not present.
160     if (('IdleWakeupCount' not in cpu_stats[process_type]) or
161         ('IdleWakeupCount' not in start_cpu_stats[process_type])):
162       continue
163     idle_wakeup_delta = (cpu_stats[process_type]['IdleWakeupCount'] -
164                         start_cpu_stats[process_type]['IdleWakeupCount'])
165     cpu_delta[process_type] = idle_wakeup_delta
166   return cpu_delta