Upstream version 7.36.149.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 logging
6
7 from metrics import Metric
8 from telemetry.core.platform import factory
9
10
11 class PowerMetric(Metric):
12   """A metric for measuring power usage."""
13
14   enabled = True
15
16   def __init__(self):
17     super(PowerMetric, self).__init__()
18     self._browser = None
19     self._running = False
20     self._starting_cpu_stats = None
21     self._results = None
22
23   def __del__(self):
24     # TODO(jeremy): Remove once crbug.com/350841 is fixed.
25     # Don't leave power monitoring processes running on the system.
26     self._StopInternal()
27     parent = super(PowerMetric, self)
28     if hasattr(parent, '__del__'):
29       parent.__del__()
30
31   def _StopInternal(self):
32     """ Stop monitoring power if measurement is running. This function is
33     idempotent."""
34     if not self._running:
35       return
36     self._running = False
37     self._results = self._browser.platform.StopMonitoringPower()
38     if self._results: # StopMonitoringPower() can return None.
39       self._results['cpu_stats'] = (
40           _SubtractCpuStats(self._browser.cpu_stats, self._starting_cpu_stats))
41
42   @classmethod
43   def CustomizeBrowserOptions(cls, options):
44     PowerMetric.enabled = options.report_root_metrics
45
46     # Friendly informational messages if measurement won't run.
47     system_supports_power_monitoring = (
48         factory.GetPlatformBackendForCurrentOS().CanMonitorPower())
49     if system_supports_power_monitoring:
50       if not PowerMetric.enabled:
51         logging.warning(
52             "--report-root-metrics omitted, power measurement disabled.")
53     else:
54       logging.info("System doesn't support power monitoring, power measurement"
55           " disabled.")
56
57   def Start(self, _, tab):
58     if not PowerMetric.enabled:
59       return
60
61     if not tab.browser.platform.CanMonitorPower():
62       return
63
64     self._results = None
65     self._browser = tab.browser
66     self._StopInternal()
67
68     # This line invokes top a few times, call before starting power measurement.
69     self._starting_cpu_stats = self._browser.cpu_stats
70     self._browser.platform.StartMonitoringPower(self._browser)
71     self._running = True
72
73   def Stop(self, _, tab):
74     if not PowerMetric.enabled:
75       return
76
77     if not tab.browser.platform.CanMonitorPower():
78       return
79
80     self._StopInternal()
81
82   def AddResults(self, _, results):
83     """Add the collected power data into the results object.
84
85     This function needs to be robust in the face of differing power data on
86     various platforms. Therefore data existence needs to be checked when
87     building up the results. Additionally 0 is a valid value for many of the
88     metrics here which is why there are plenty of checks for 'is not None'
89     below.
90     """
91     if not self._results:
92       return
93
94     energy_consumption_mwh = self._results.get('energy_consumption_mwh')
95     if energy_consumption_mwh is not None:
96       results.Add('energy_consumption_mwh', 'mWh', energy_consumption_mwh)
97
98     component_utilization = self._results.get('component_utilization', {})
99     # GPU Frequency.
100     gpu_power = component_utilization.get('gpu', {})
101     gpu_freq_hz = gpu_power.get('average_frequency_hz')
102     if gpu_freq_hz is not None:
103       results.Add('gpu_average_frequency_hz', 'hz', gpu_freq_hz)
104
105     # Add idle wakeup numbers for all processes.
106     for (process_type, stats) in self._results.get('cpu_stats', {}).items():
107       trace_name_for_process = 'idle_wakeups_%s' % (process_type.lower())
108       results.Add(trace_name_for_process, 'count', stats)
109
110     # Add temperature measurements.
111     whole_package_utilization = component_utilization.get('whole_package', {})
112     board_temperature_c = whole_package_utilization.get('average_temperature_c')
113     if board_temperature_c is not None:
114       results.Add('board_temperature', 'celsius', board_temperature_c)
115
116     self._results = None
117
118 def _SubtractCpuStats(cpu_stats, start_cpu_stats):
119   """Computes number of idle wakeups that occurred over measurement period.
120
121   Each of the two cpu_stats arguments is a dict as returned by the
122   Browser.cpu_stats call.
123
124   Returns:
125     A dict of process type names (Browser, Renderer, etc.) to idle wakeup count
126     over the period recorded by the input.
127   """
128   cpu_delta = {}
129   for process_type in cpu_stats:
130     assert process_type in start_cpu_stats, 'Mismatching process types'
131     # Skip any process_types that are empty.
132     if (not cpu_stats[process_type]) or (not start_cpu_stats[process_type]):
133       continue
134     # Skip if IdleWakeupCount is not present.
135     if (('IdleWakeupCount' not in cpu_stats[process_type]) or
136         ('IdleWakeupCount' not in start_cpu_stats[process_type])):
137       continue
138     idle_wakeup_delta = (cpu_stats[process_type]['IdleWakeupCount'] -
139                         start_cpu_stats[process_type]['IdleWakeupCount'])
140     cpu_delta[process_type] = idle_wakeup_delta
141   return cpu_delta