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 5import json 6import logging 7import multiprocessing 8import tempfile 9import time 10 11from telemetry.core import exceptions 12from telemetry.internal.platform.power_monitor import android_power_monitor_base 13from telemetry.internal.platform.profiler import monsoon 14 15 16def _MonitorPower(device, is_collecting, output): 17 """Monitoring process 18 Args: 19 device: A profiler.monsoon object to collect samples from. 20 is_collecting: the event to synchronize on. 21 output: opened file to write the samples. 22 """ 23 with output: 24 samples = [] 25 start_time = None 26 end_time = None 27 try: 28 device.StartDataCollection() 29 is_collecting.set() 30 # First sample also calibrate the computation. 31 device.CollectData() 32 start_time = time.time() 33 while is_collecting.is_set(): 34 new_data = device.CollectData() 35 assert new_data, 'Unable to collect data from device' 36 samples += new_data 37 end_time = time.time() 38 finally: 39 device.StopDataCollection() 40 result = { 41 'duration_s': end_time - start_time, 42 'samples': samples 43 } 44 json.dump(result, output) 45 46 47class MonsoonPowerMonitor(android_power_monitor_base.AndroidPowerMonitorBase): 48 def __init__(self, _, platform_backend): 49 super(MonsoonPowerMonitor, self).__init__() 50 self._powermonitor_process = None 51 self._powermonitor_output_file = None 52 self._is_collecting = None 53 self._monsoon = None 54 self._platform = platform_backend 55 try: 56 self._monsoon = monsoon.Monsoon(wait=False) 57 # Nominal Li-ion voltage is 3.7V, but it puts out 4.2V at max capacity. 58 # Use 4.0V to simulate a "~80%" charged battery. Google "li-ion voltage 59 # curve". This is true only for a single cell. (Most smartphones, some 60 # tablets.) 61 self._monsoon.SetVoltage(4.0) 62 except EnvironmentError: 63 self._monsoon = None 64 65 def CanMonitorPower(self): 66 return self._monsoon is not None 67 68 def StartMonitoringPower(self, browser): 69 self._CheckStart() 70 self._powermonitor_output_file = tempfile.TemporaryFile() 71 self._is_collecting = multiprocessing.Event() 72 self._powermonitor_process = multiprocessing.Process( 73 target=_MonitorPower, 74 args=(self._monsoon, 75 self._is_collecting, 76 self._powermonitor_output_file)) 77 # Ensure child is not left behind: parent kills daemonic children on exit. 78 self._powermonitor_process.daemon = True 79 self._powermonitor_process.start() 80 if not self._is_collecting.wait(timeout=0.5): 81 self._powermonitor_process.terminate() 82 raise exceptions.ProfilingException('Failed to start data collection.') 83 84 def StopMonitoringPower(self): 85 self._CheckStop() 86 try: 87 # Tell powermonitor to take an immediate sample and join. 88 self._is_collecting.clear() 89 self._powermonitor_process.join() 90 with self._powermonitor_output_file: 91 self._powermonitor_output_file.seek(0) 92 powermonitor_output = self._powermonitor_output_file.read() 93 assert powermonitor_output, 'PowerMonitor produced no output' 94 return MonsoonPowerMonitor.ParseSamplingOutput(powermonitor_output) 95 finally: 96 self._powermonitor_output_file = None 97 self._powermonitor_process = None 98 self._is_collecting = None 99 100 @staticmethod 101 def ParseSamplingOutput(powermonitor_output): 102 """Parse the output of of the samples collector process. 103 104 Returns: 105 Dictionary in the format returned by StopMonitoringPower(). 106 """ 107 result = json.loads(powermonitor_output) 108 if result['samples']: 109 timedelta_h = (result['duration_s'] / len(result['samples'])) / 3600.0 110 power_samples = [current_a * voltage_v * 10**3 111 for (current_a, voltage_v) in result['samples']] 112 total_energy_consumption_mwh = sum(power_samples) * timedelta_h 113 else: 114 logging.warning('Sample information not available.') 115 power_samples = [] 116 total_energy_consumption_mwh = 0 117 118 return {'identifier':'monsoon', 119 'power_samples_mw':power_samples, 120 'monsoon_energy_consumption_mwh':total_energy_consumption_mwh} 121