blob: a48ea87ee4cc2a04da020c2258995984b9013e1e [file] [log] [blame]
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import atexit
import logging
import optparse
import py_utils
from battor import battor_wrapper
from devil.android import battery_utils
from devil.android import device_utils
from devil.utils import battor_device_mapping
from devil.utils import find_usb_devices
from py_trace_event import trace_time
from systrace import trace_result
from systrace import tracing_agents
def try_create_agent(config):
if config.from_file is not None:
return None
if config.battor:
return BattOrTraceAgent()
return None
class BattOrConfig(tracing_agents.TracingConfig):
def __init__(self, battor_categories, serial_map, battor_path,
battor, target, from_file, device_serial_number):
tracing_agents.TracingConfig.__init__(self)
self.battor_categories = battor_categories
self.serial_map = serial_map
self.battor_path = battor_path
self.battor = battor
self.target = target
self.from_file = from_file
self.device_serial_number = device_serial_number
def add_options(parser):
options = optparse.OptionGroup(parser, 'BattOr trace options')
options.add_option('--battor-categories', dest='battor_categories',
help='Select battor categories with a comma-delimited '
'list, e.g. --battor-categories=cat1,cat2,cat3')
options.add_option('--serial-map', dest='serial_map',
default='serial_map.json',
help='File containing pregenerated map of phone serial '
'numbers to BattOr serial numbers.')
options.add_option('--battor-path', dest='battor_path', default=None,
type='string', help='specify a BattOr path to use')
options.add_option('--battor', dest='battor', default=False,
action='store_true', help='Use the BattOr tracing agent.')
return options
def get_config(options):
return BattOrConfig(
options.battor_categories, options.serial_map, options.battor_path,
options.battor, options.target, options.from_file,
options.device_serial_number)
def _reenable_charging_if_needed(battery):
if not battery.GetCharging():
battery.SetCharging(True)
logging.info('Charging status checked at exit.')
class BattOrTraceAgent(tracing_agents.TracingAgent):
# Class representing tracing agent that gets data from a BattOr.
# BattOrs are high-frequency power monitors used for battery testing.
def __init__(self):
super(BattOrTraceAgent, self).__init__()
self._collection_process = None
self._recording_error = None
self._battor_wrapper = None
self._battery_utils = None
@staticmethod
def _FindBattOrPath(config):
device_tree = find_usb_devices.GetBusNumberToDeviceTreeMap()
battors = battor_device_mapping.GetBattOrList(device_tree)
battor_path = config.battor_path
if not config.battor_path and not config.serial_map:
assert len(battors) == 1, ('Must specify BattOr path if there is not '
'exactly one')
battor_path = battors[0]
return battor_path
@py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
def StartAgentTracing(self, config, timeout=None):
"""Starts tracing.
Args:
config: Tracing config.
Raises:
RuntimeError: If trace already in progress.
AssertionError: If There is no BattOr path given and more
than one BattOr is attached.
"""
battor_path = self._FindBattOrPath(config)
self._battor_wrapper = battor_wrapper.BattOrWrapper(
target_platform=config.target,
android_device=config.device_serial_number,
battor_path=battor_path,
battor_map_file=config.serial_map)
dev_utils = device_utils.DeviceUtils(config.device_serial_number)
self._battery_utils = battery_utils.BatteryUtils(dev_utils)
self._battery_utils.SetCharging(False)
atexit.register(_reenable_charging_if_needed, self._battery_utils)
self._battor_wrapper.StartShell()
self._battor_wrapper.StartTracing()
return True
@py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
def StopAgentTracing(self, timeout=None):
"""Stops tracing and collects the results asynchronously.
Creates a new process that stops the tracing and collects the results.
Returns immediately after the process is created (does not wait for
trace results to be collected).
"""
self._battor_wrapper.StopTracing()
self._battery_utils.SetCharging(True)
return True
def SupportsExplicitClockSync(self):
"""Returns whether this function supports explicit clock sync."""
return self._battor_wrapper.SupportsExplicitClockSync()
def RecordClockSyncMarker(self, sync_id, did_record_sync_marker_callback):
"""Records a clock sync marker.
Args:
sync_id: ID string for clock sync marker.
did_record_sync_marker_callback: Callback function to call after
the clock sync marker is recorded.
"""
ts = trace_time.Now()
self._battor_wrapper.RecordClockSyncMarker(sync_id)
did_record_sync_marker_callback(ts, sync_id)
@py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT)
def GetResults(self, timeout=None):
"""Waits until data collection is completed and get the trace data.
The trace data is the data that comes out of the BattOr, and is in the
format with the following lines:
time current voltage <sync_id>
where the sync_id is only there if a clock sync marker was recorded
during that sample.
time = time since start of trace (ms)
current = current through battery (mA) - this can be negative if the
battery is charging
voltage = voltage of battery (mV)
Returns:
The trace data.
"""
return trace_result.TraceResult(
'powerTraceAsString', self._battor_wrapper.CollectTraceData())