| OLD | NEW |
| (Empty) | |
| 1 # Copyright (c) 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 subprocess |
| 6 import threading |
| 7 |
| 8 from systrace.tracing_agents import TracingAgent, TraceResults, usb_hubs |
| 9 |
| 10 from devil.utils import find_usb_devices |
| 11 from devil.android import battery_utils, device_utils |
| 12 from py_trace_event import trace_time |
| 13 |
| 14 |
| 15 def try_create_agent(options, categories, disable=False): |
| 16 if disable: |
| 17 return False |
| 18 if options.from_file is not None: |
| 19 return False |
| 20 return BattorTraceAgent(options, categories) |
| 21 |
| 22 class BattorError(Exception): |
| 23 pass |
| 24 |
| 25 class BattorTraceAgent(TracingAgent): |
| 26 # Class representing tracing agent that gets data from a BattOr. |
| 27 def __init__(self, options, categories): |
| 28 """Initialize a BattOr tracing agent. |
| 29 |
| 30 Args: |
| 31 options: The command-line options. |
| 32 categories: The trace categories to capture. |
| 33 """ |
| 34 super(BattorTraceAgent, self).__init__() |
| 35 self._trace_result = '' |
| 36 self._devstring = '' |
| 37 self._options = options |
| 38 self._categories = categories |
| 39 self._collection_process = None |
| 40 self._battor_process = None |
| 41 self._recording_error = None |
| 42 self._device_utils = device_utils.DeviceUtils(self._options.device_serial) |
| 43 self._battery_utils = battery_utils.BatteryUtils(self._device_utils) |
| 44 self._devstring = self._GetDeviceString() |
| 45 |
| 46 def _GetDeviceString(self): |
| 47 """Gets the device string to communicate with device. |
| 48 |
| 49 Returns: |
| 50 Device string used to communicate with device. |
| 51 |
| 52 Raises: |
| 53 ValueError: If serial number is not given. |
| 54 BattorError: If BattOr not found or unexpected USB topology. |
| 55 """ |
| 56 # If there's only one BattOr connected to the system, just use that one. |
| 57 # This allows for use on, e.g., a developer's workstation with no hubs. |
| 58 serial = self._options.device_serial |
| 59 devtree = find_usb_devices.GetBusNumberToDeviceTreeMap(fast=True) |
| 60 all_battors = find_usb_devices.GetBattorList(devtree) |
| 61 hub_types = [usb_hubs.GetHubType(x) for x in self._options.hub_types] |
| 62 if len(all_battors) == 1: |
| 63 return '/dev/' + all_battors[0] |
| 64 if serial: |
| 65 p2serial = find_usb_devices.GetAllPhysicalPortToSerialMaps( |
| 66 hub_types, device_tree_map=devtree) |
| 67 p2tty = find_usb_devices.GetAllPhysicalPortToTTYMaps( |
| 68 hub_types, device_tree_map=devtree) |
| 69 |
| 70 # get the port number of this device |
| 71 port_num = -1 |
| 72 for hub in p2serial: |
| 73 for (port, s) in hub.iteritems(): |
| 74 if serial == s: |
| 75 if port_num != -1: |
| 76 raise BattorError('Two devices with same serial number.') |
| 77 else: |
| 78 port_num = port |
| 79 if port_num == -1: |
| 80 raise BattorError('Device with given serial number not found.') |
| 81 |
| 82 # get the tty for this port number |
| 83 tty_string = None |
| 84 for hub in p2tty: |
| 85 if port_num in hub: |
| 86 if tty_string: |
| 87 raise BattorError('Two TTY devices with matching port number.') |
| 88 else: |
| 89 tty_string = hub[port_num] |
| 90 if not tty_string: |
| 91 raise BattorError('No BattOr detected on corresponding port.') |
| 92 if find_usb_devices.IsBattor(tty_string, devtree): |
| 93 return '/dev/' + tty_string |
| 94 else: |
| 95 raise BattorError('Device connected to matching port is not BattOr.') |
| 96 else: |
| 97 raise BattorError('Two or more BattOrs connected, no serial provided') |
| 98 |
| 99 def StartAgentTracing(self): |
| 100 """Start tracing. |
| 101 |
| 102 Raises: |
| 103 RuntimeError: If trace already in progress. |
| 104 """ |
| 105 if self._battor_process is not None: |
| 106 raise RuntimeError('Trace already in progress') |
| 107 self._battery_utils.SetCharging(False) |
| 108 try: |
| 109 self._battor_process = subprocess.Popen( |
| 110 ['battor_agent', |
| 111 '--battor-path='+self._devstring], |
| 112 stdin=subprocess.PIPE, |
| 113 stdout=subprocess.PIPE, |
| 114 shell=False) |
| 115 except OSError: |
| 116 raise RuntimeError('Error executing battor_agent command.' |
| 117 'Make sure that battor_agent is on PATH.') |
| 118 self._battor_process.stdin.write('StartTracing\n') |
| 119 status = self._battor_process.stdout.readline() |
| 120 if 'Done.' not in status: |
| 121 raise RuntimeError('battor_agent failed to start (%s)' % status) |
| 122 return True |
| 123 |
| 124 def _record_trace_result(self): |
| 125 result = self._battor_process.communicate('StopTracing\n')[0] |
| 126 self._battor_process = None |
| 127 self._battery_utils.SetCharging(True) |
| 128 if not result: |
| 129 self._recording_error = 'battor_agent failed to stop - no output' |
| 130 elif result[-6:-1] != 'Done.': |
| 131 self._recording_error = 'battor_agent failed to stop' |
| 132 else: |
| 133 self._recording_error = None |
| 134 self._trace_result = result |
| 135 |
| 136 def StopAgentTracing(self): |
| 137 """Collect the result of tracing. |
| 138 """ |
| 139 if self._battor_process is not None: |
| 140 self._collection_process = threading.Thread( |
| 141 target = self._record_trace_result) |
| 142 self._collection_process.start() |
| 143 return True |
| 144 else: |
| 145 return False |
| 146 |
| 147 def SupportsExplicitClockSync(self): |
| 148 """Returns whether this function supports explicit clock sync.""" |
| 149 self._battor_process.stdin.write('SupportsExplicitClockSync\n') |
| 150 result = self._battor_process.stdout.readline() |
| 151 return int(result) != 0 |
| 152 |
| 153 def RecordClockSyncMarker(self, sync_id, did_record_sync_marker_callback): |
| 154 """Records a clock sync marker. |
| 155 |
| 156 Args: |
| 157 sync_id: ID string for clock sync marker. |
| 158 """ |
| 159 t1 = trace_time.Now() |
| 160 self._battor_process.stdin.write('RecordClockSyncMarker %s\n' % sync_id) |
| 161 status = self._battor_process.stdout.readline() |
| 162 did_record_sync_marker_callback(t1, sync_id) |
| 163 if 'Done.' not in status: |
| 164 raise RuntimeError('battor_agent failed to record sync marker (%s)' |
| 165 % status) |
| 166 |
| 167 def GetResults(self): |
| 168 """Get the trace data. |
| 169 |
| 170 Returns: |
| 171 The trace data. |
| 172 """ |
| 173 if self._collection_process is None: |
| 174 raise RuntimeError('Must call StopAgentTracing before GetResults') |
| 175 self._collection_process.join() |
| 176 self._collection_process = None |
| 177 if self._recording_error: |
| 178 raise RuntimeError(self._recording_error) |
| 179 else: |
| 180 return TraceResults('powerTraceAsString', self._trace_result, True) |
| OLD | NEW |