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
5import logging
6
7from battor import battor_error
8from battor import battor_wrapper
9from py_utils import cloud_storage
10from devil.android import battery_utils
11from py_trace_event import trace_time
12from telemetry.internal.platform import tracing_agent
13from telemetry.internal.util import atexit_with_log
14from tracing.trace_data import trace_data
15
16
17def _ReenableChargingIfNeeded(battery):
18  if not battery.GetCharging():
19    battery.SetCharging(True)
20  logging.info('Charging status checked at exit.')
21
22
23class BattOrTracingAgent(tracing_agent.TracingAgent):
24  """A tracing agent for getting power data from a BattOr device.
25
26  BattOrTracingAgent allows Telemetry to issue high-level tracing commands
27  (StartTracing, StopTracing, RecordClockSyncMarker) to BattOrs, which are
28  high-frequency power monitors used for battery testing.
29  """
30
31  def __init__(self, platform_backend):
32    super(BattOrTracingAgent, self).__init__(platform_backend)
33    self._platform_backend = platform_backend
34    android_device = (
35        platform_backend.device if platform_backend.GetOSName() == 'android'
36        else None)
37    self._battery = (
38        battery_utils.BatteryUtils(platform_backend.device)
39        if platform_backend.GetOSName() == 'android' else None)
40    self._battor = battor_wrapper.BattOrWrapper(
41        platform_backend.GetOSName(), android_device=android_device,
42        serial_log_bucket=cloud_storage.TELEMETRY_OUTPUT)
43
44  @classmethod
45  def IsSupported(cls, platform_backend):
46    """Returns True if BattOr tracing is available."""
47    if platform_backend.GetOSName() == 'android':
48      # TODO(rnephew): When we pass BattOr device map into Telemetry, change
49      # this to reflect that.
50      return battor_wrapper.IsBattOrConnected(
51          'android', android_device=platform_backend.device)
52    return battor_wrapper.IsBattOrConnected(platform_backend.GetOSName())
53
54  def StartAgentTracing(self, config, timeout):
55    """Start tracing on the BattOr.
56
57    Args:
58      config: A TracingConfig instance.
59      timeout: number of seconds that this tracing agent should try to start
60        tracing until timing out.
61
62    Returns:
63      True if the tracing agent started successfully.
64    """
65    if not config.enable_battor_trace:
66      return False
67    try:
68      if self._battery:
69        self._battery.SetCharging(False)
70        atexit_with_log.Register(_ReenableChargingIfNeeded, self._battery)
71
72      self._battor.StartShell()
73      self._battor.StartTracing()
74      return True
75    except battor_error.BattOrError:
76      if self._battery:
77        self._battery.SetCharging(True)
78      raise
79
80  def StopAgentTracing(self):
81    """Stops tracing on the BattOr."""
82    try:
83      self._battor.StopTracing()
84    finally:
85      if self._battery:
86        self._battery.SetCharging(True)
87
88  def SupportsExplicitClockSync(self):
89    return self._battor.SupportsExplicitClockSync()
90
91  def RecordClockSyncMarker(self, sync_id,
92                            record_controller_clock_sync_marker_callback):
93    """Records a clock sync marker in the BattOr trace.
94
95    Args:
96      sync_id: Unique id for sync event.
97      record_controller_clock_sync_marker_callback: Function that takes a sync
98        ID and a timestamp as arguments. This function typically will record the
99        tracing controller clock sync marker.
100    """
101    timestamp = trace_time.Now()
102    try:
103      self._battor.RecordClockSyncMarker(sync_id)
104    except battor_error.BattOrError:
105      logging.critical(
106          'Error while clock syncing with BattOr. Killing BattOr shell.')
107      self._battor.KillBattOrShell()
108      raise
109    record_controller_clock_sync_marker_callback(sync_id, timestamp)
110
111  def CollectAgentTraceData(self, trace_data_builder, timeout=None):
112    data = self._battor.CollectTraceData(timeout=timeout)
113    trace_data_builder.AddTraceFor(trace_data.BATTOR_TRACE_PART, data)
114