1 # Copyright 2016 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 atexit 6 import logging 7 import optparse 8 import py_utils 9 10 from battor import battor_wrapper 11 from devil.android import battery_utils 12 from devil.android import device_utils 13 from devil.utils import battor_device_mapping 14 from devil.utils import find_usb_devices 15 from py_trace_event import trace_time 16 from systrace import trace_result 17 from systrace import tracing_agents 18 19 20 def try_create_agent(config): 21 if config.from_file is not None: 22 return None 23 if config.battor: 24 return BattOrTraceAgent() 25 return None 26 27 28 class BattOrConfig(tracing_agents.TracingConfig): 29 def __init__(self, battor_categories, serial_map, battor_path, 30 battor, target, from_file, device_serial_number): 31 tracing_agents.TracingConfig.__init__(self) 32 self.battor_categories = battor_categories 33 self.serial_map = serial_map 34 self.battor_path = battor_path 35 self.battor = battor 36 self.target = target 37 self.from_file = from_file 38 self.device_serial_number = device_serial_number 39 40 41 def add_options(parser): 42 options = optparse.OptionGroup(parser, 'BattOr trace options') 43 options.add_option('--battor-categories', dest='battor_categories', 44 help='Select battor categories with a comma-delimited ' 45 'list, e.g. --battor-categories=cat1,cat2,cat3') 46 options.add_option('--serial-map', dest='serial_map', 47 default='serial_map.json', 48 help='File containing pregenerated map of phone serial ' 49 'numbers to BattOr serial numbers.') 50 options.add_option('--battor-path', dest='battor_path', default=None, 51 type='string', help='specify a BattOr path to use') 52 options.add_option('--battor', dest='battor', default=False, 53 action='store_true', help='Use the BattOr tracing agent.') 54 return options 55 56 def get_config(options): 57 return BattOrConfig( 58 options.battor_categories, options.serial_map, options.battor_path, 59 options.battor, options.target, options.from_file, 60 options.device_serial_number) 61 62 def _reenable_charging_if_needed(battery): 63 if not battery.GetCharging(): 64 battery.SetCharging(True) 65 logging.info('Charging status checked at exit.') 66 67 68 class BattOrTraceAgent(tracing_agents.TracingAgent): 69 # Class representing tracing agent that gets data from a BattOr. 70 # BattOrs are high-frequency power monitors used for battery testing. 71 def __init__(self): 72 super(BattOrTraceAgent, self).__init__() 73 self._collection_process = None 74 self._recording_error = None 75 self._battor_wrapper = None 76 self._battery_utils = None 77 78 @staticmethod 79 def _FindBattOrPath(config): 80 device_tree = find_usb_devices.GetBusNumberToDeviceTreeMap() 81 battors = battor_device_mapping.GetBattOrList(device_tree) 82 battor_path = config.battor_path 83 if not config.battor_path and not config.serial_map: 84 assert len(battors) == 1, ('Must specify BattOr path if there is not ' 85 'exactly one') 86 battor_path = battors[0] 87 return battor_path 88 89 @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT) 90 def StartAgentTracing(self, config, timeout=None): 91 """Starts tracing. 92 93 Args: 94 config: Tracing config. 95 96 Raises: 97 RuntimeError: If trace already in progress. 98 AssertionError: If There is no BattOr path given and more 99 than one BattOr is attached. 100 """ 101 battor_path = self._FindBattOrPath(config) 102 self._battor_wrapper = battor_wrapper.BattOrWrapper( 103 target_platform=config.target, 104 android_device=config.device_serial_number, 105 battor_path=battor_path, 106 battor_map_file=config.serial_map) 107 108 dev_utils = device_utils.DeviceUtils(config.device_serial_number) 109 self._battery_utils = battery_utils.BatteryUtils(dev_utils) 110 self._battery_utils.SetCharging(False) 111 atexit.register(_reenable_charging_if_needed, self._battery_utils) 112 self._battor_wrapper.StartShell() 113 self._battor_wrapper.StartTracing() 114 return True 115 116 @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT) 117 def StopAgentTracing(self, timeout=None): 118 """Stops tracing and collects the results asynchronously. 119 120 Creates a new process that stops the tracing and collects the results. 121 Returns immediately after the process is created (does not wait for 122 trace results to be collected). 123 """ 124 self._battor_wrapper.StopTracing() 125 self._battery_utils.SetCharging(True) 126 return True 127 128 def SupportsExplicitClockSync(self): 129 """Returns whether this function supports explicit clock sync.""" 130 return self._battor_wrapper.SupportsExplicitClockSync() 131 132 def RecordClockSyncMarker(self, sync_id, did_record_sync_marker_callback): 133 """Records a clock sync marker. 134 135 Args: 136 sync_id: ID string for clock sync marker. 137 did_record_sync_marker_callback: Callback function to call after 138 the clock sync marker is recorded. 139 """ 140 ts = trace_time.Now() 141 self._battor_wrapper.RecordClockSyncMarker(sync_id) 142 did_record_sync_marker_callback(ts, sync_id) 143 144 @py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT) 145 def GetResults(self, timeout=None): 146 """Waits until data collection is completed and get the trace data. 147 148 The trace data is the data that comes out of the BattOr, and is in the 149 format with the following lines: 150 151 time current voltage <sync_id> 152 153 where the sync_id is only there if a clock sync marker was recorded 154 during that sample. 155 156 time = time since start of trace (ms) 157 current = current through battery (mA) - this can be negative if the 158 battery is charging 159 voltage = voltage of battery (mV) 160 161 Returns: 162 The trace data. 163 """ 164 return trace_result.TraceResult( 165 'powerTraceAsString', self._battor_wrapper.CollectTraceData()) 166