1116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch# Copyright 2014 The Chromium Authors. All rights reserved. 2116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch# Use of this source code is governed by a BSD-style license that can be 3116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch# found in the LICENSE file. 4116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 5116680a4aac90f2aa7413d9095a592090648e557Ben Murdochimport collections 6116680a4aac90f2aa7413d9095a592090648e557Ben Murdochimport re 7116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 8116680a4aac90f2aa7413d9095a592090648e557Ben Murdochfrom telemetry import decorators 95f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from telemetry.core.platform.power_monitor import sysfs_power_monitor 10116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 11116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)class CrosPowerMonitor(sysfs_power_monitor.SysfsPowerMonitor): 13116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch """PowerMonitor that relies on 'power_supply_info' to monitor power 14116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch consumption of a single ChromeOS application. 15116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch """ 161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def __init__(self, platform_backend): 17116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch """Constructor. 18116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 19116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch Args: 201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci platform_backend: A LinuxBasedPlatformBackend object. 21116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 22116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch Attributes: 235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) _initial_power: The result of 'power_supply_info' before the test. 2403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) _start_time: The epoch time at which the test starts executing. 25116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch """ 261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci super(CrosPowerMonitor, self).__init__(platform_backend) 275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._initial_power = None 286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) self._start_time = None 29116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 30116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch @decorators.Cache 31116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch def CanMonitorPower(self): 3203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) return super(CrosPowerMonitor, self).CanMonitorPower() 33116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 34116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch def StartMonitoringPower(self, browser): 355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) super(CrosPowerMonitor, self).StartMonitoringPower(browser) 3603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) if self._IsOnBatteryPower(): 371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci sample = self._platform.RunCommand(['power_supply_info;', 'date', '+%s']) 3803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) self._initial_power, self._start_time = CrosPowerMonitor.SplitSample( 3903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) sample) 40116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 41116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch def StopMonitoringPower(self): 425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) cpu_stats = super(CrosPowerMonitor, self).StopMonitoringPower() 4303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) power_stats = {} 4403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) if self._IsOnBatteryPower(): 451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci sample = self._platform.RunCommand(['power_supply_info;', 'date', '+%s']) 4603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) final_power, end_time = CrosPowerMonitor.SplitSample(sample) 4703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) # The length of the test is used to measure energy consumption. 4803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) length_h = (end_time - self._start_time) / 3600.0 4903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) power_stats = CrosPowerMonitor.ParsePower(self._initial_power, 5003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) final_power, length_h) 515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return CrosPowerMonitor.CombineResults(cpu_stats, power_stats) 52116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 53116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch @staticmethod 5403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) def SplitSample(sample): 5503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) """Splits a power and time sample into the two separate values. 5603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 5703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) Args: 5803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) sample: The result of calling 'power_supply_info; date +%s' on the 5903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) device. 6003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 6103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) Returns: 6203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) A tuple of power sample and epoch time of the sample. 6303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) """ 6403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) sample = sample.strip() 6503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) index = sample.rfind('\n') 6603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) power = sample[:index] 6703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) time = sample[index + 1:] 6803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) return power, int(time) 6903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 7003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) @staticmethod 71116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch def IsOnBatteryPower(status, board): 72116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch """Determines if the devices is being charged. 73116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 74116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch Args: 75116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch status: The parsed result of 'power_supply_info' 76116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch board: The name of the board running the test. 77116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 78116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch Returns: 79116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch True if the device is on battery power; False otherwise. 80116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch """ 81116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch on_battery = status['Line Power']['online'] == 'no' 82116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch # Butterfly can incorrectly report AC online for some time after unplug. 835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # Check battery discharge state to confirm. 84116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if board == 'butterfly': 85116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch on_battery |= status['Battery']['state'] == 'Discharging' 86116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return on_battery 87116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 88116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch def _IsOnBatteryPower(self): 89116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch """Determines if the device is being charged. 90116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 91116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch Returns: 92116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch True if the device is on battery power; False otherwise. 93116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch """ 94116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch status = CrosPowerMonitor.ParsePowerSupplyInfo( 951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci self._platform.RunCommand(['power_supply_info'])) 961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci board_data = self._platform.RunCommand(['cat', '/etc/lsb-release']) 97116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch board = re.search('BOARD=(.*)', board_data).group(1) 98116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return CrosPowerMonitor.IsOnBatteryPower(status, board) 99116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 100116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch @staticmethod 101116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch def ParsePowerSupplyInfo(sample): 102116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch """Parses 'power_supply_info' command output. 103116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 104116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch Args: 105116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch sample: The output of 'power_supply_info' 106116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 107116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch Returns: 108116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch Dictionary containing all fields from 'power_supply_info' 109116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch """ 110116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch rv = collections.defaultdict(dict) 111116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch dev = None 112116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch for ln in sample.splitlines(): 113116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch result = re.findall(r'^Device:\s+(.*)', ln) 114116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if result: 115116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch dev = result[0] 116116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch continue 117116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch result = re.findall(r'\s+(.+):\s+(.+)', ln) 118116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if result and dev: 119116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch kname = re.findall(r'(.*)\s+\(\w+\)', result[0][0]) 120116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if kname: 121116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch rv[dev][kname[0]] = result[0][1] 122116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch else: 123116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch rv[dev][result[0][0]] = result[0][1] 1245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return dict(rv) 125116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 126116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch @staticmethod 1275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def ParsePower(initial_stats, final_stats, length_h): 128116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch """Parse output of 'power_supply_info' 129116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 130116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch Args: 131116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch initial_stats: The output of 'power_supply_info' before the test. 132116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch final_stats: The output of 'power_supply_info' after the test. 133116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch length_h: The length of the test in hours. 134116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 135116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch Returns: 136116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch Dictionary in the format returned by StopMonitoringPower(). 137116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch """ 1385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) out_dict = {'identifier': 'power_supply_info'} 1395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) component_utilization = {} 140116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch initial = CrosPowerMonitor.ParsePowerSupplyInfo(initial_stats) 141116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch final = CrosPowerMonitor.ParsePowerSupplyInfo(final_stats) 142116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch # The charge value reported by 'power_supply_info' is not precise enough to 143116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch # give meaningful results across shorter tests, so average energy rate and 144116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch # the length of the test are used. 1455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) initial_power_mw = float(initial['Battery']['energy rate']) * 10 ** 3 1465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) final_power_mw = float(final['Battery']['energy rate']) * 10 ** 3 1475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) average_power_mw = (initial_power_mw + final_power_mw) / 2.0 1485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) out_dict['power_samples_mw'] = [initial_power_mw, final_power_mw] 149116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch out_dict['energy_consumption_mwh'] = average_power_mw * length_h 150116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch # Duplicating CrOS battery fields where applicable. 1515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) battery = {} 1525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) battery['charge_full'] = float(final['Battery']['full charge']) 1535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) battery['charge_full_design'] = ( 154116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch float(final['Battery']['full charge design'])) 1555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) battery['charge_now'] = float(final['Battery']['charge']) 1565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) battery['current_now'] = float(final['Battery']['current']) 1575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) battery['energy'] = float(final['Battery']['energy']) 1585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) battery['energy_rate'] = float(final['Battery']['energy rate']) 1595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) battery['voltage_now'] = float(final['Battery']['voltage']) 1605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) component_utilization['battery'] = battery 1615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) out_dict['component_utilization'] = component_utilization 162116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return out_dict 163