| OLD | NEW |
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 # pylint: disable=W0212 | 5 # pylint: disable=W0212 |
| 6 | 6 |
| 7 import fcntl | 7 import fcntl |
| 8 import logging | 8 import logging |
| 9 import os | 9 import os |
| 10 import psutil | 10 import psutil |
| 11 | 11 |
| 12 from pylib import cmd_helper | 12 from pylib import cmd_helper |
| 13 from pylib import constants | 13 from pylib import constants |
| 14 from pylib import valgrind_tools | 14 from pylib import valgrind_tools |
| 15 | 15 |
| 16 # TODO(jbudorick) Remove once telemetry gets switched over. |
| 17 import pylib.android_commands |
| 18 import pylib.device.device_utils |
| 19 |
| 16 | 20 |
| 17 def _GetProcessStartTime(pid): | 21 def _GetProcessStartTime(pid): |
| 18 return psutil.Process(pid).create_time | 22 return psutil.Process(pid).create_time |
| 19 | 23 |
| 20 | 24 |
| 21 class _FileLock(object): | 25 class _FileLock(object): |
| 22 """With statement-aware implementation of a file lock. | 26 """With statement-aware implementation of a file lock. |
| 23 | 27 |
| 24 File locks are needed for cross-process synchronization when the | 28 File locks are needed for cross-process synchronization when the |
| 25 multiprocessing Python module is used. | 29 multiprocessing Python module is used. |
| (...skipping 27 matching lines...) Expand all Loading... |
| 53 _HOST_FORWARDER_LOG = '/tmp/host_forwarder_log' | 57 _HOST_FORWARDER_LOG = '/tmp/host_forwarder_log' |
| 54 | 58 |
| 55 _instance = None | 59 _instance = None |
| 56 | 60 |
| 57 @staticmethod | 61 @staticmethod |
| 58 def UseMultiprocessing(): | 62 def UseMultiprocessing(): |
| 59 """Tells the forwarder that multiprocessing is used.""" | 63 """Tells the forwarder that multiprocessing is used.""" |
| 60 os.environ[Forwarder._MULTIPROCESSING_ENV_VAR] = '1' | 64 os.environ[Forwarder._MULTIPROCESSING_ENV_VAR] = '1' |
| 61 | 65 |
| 62 @staticmethod | 66 @staticmethod |
| 63 def Map(port_pairs, adb, tool=None): | 67 def Map(port_pairs, device, tool=None): |
| 64 """Runs the forwarder. | 68 """Runs the forwarder. |
| 65 | 69 |
| 66 Args: | 70 Args: |
| 67 port_pairs: A list of tuples (device_port, host_port) to forward. Note | 71 port_pairs: A list of tuples (device_port, host_port) to forward. Note |
| 68 that you can specify 0 as a device_port, in which case a | 72 that you can specify 0 as a device_port, in which case a |
| 69 port will by dynamically assigned on the device. You can | 73 port will by dynamically assigned on the device. You can |
| 70 get the number of the assigned port using the | 74 get the number of the assigned port using the |
| 71 DevicePortForHostPort method. | 75 DevicePortForHostPort method. |
| 72 adb: An AndroidCommands instance. | 76 device: A DeviceUtils instance. |
| 73 tool: Tool class to use to get wrapper, if necessary, for executing the | 77 tool: Tool class to use to get wrapper, if necessary, for executing the |
| 74 forwarder (see valgrind_tools.py). | 78 forwarder (see valgrind_tools.py). |
| 75 | 79 |
| 76 Raises: | 80 Raises: |
| 77 Exception on failure to forward the port. | 81 Exception on failure to forward the port. |
| 78 """ | 82 """ |
| 83 # TODO(jbudorick) Remove once telemetry gets switched over. |
| 84 if isinstance(device, pylib.android_commands.AndroidCommands): |
| 85 device = pylib.device.device_utils.DeviceUtils(device) |
| 79 if not tool: | 86 if not tool: |
| 80 tool = valgrind_tools.CreateTool(None, adb) | 87 tool = valgrind_tools.CreateTool(None, device) |
| 81 with _FileLock(Forwarder._LOCK_PATH): | 88 with _FileLock(Forwarder._LOCK_PATH): |
| 82 instance = Forwarder._GetInstanceLocked(tool) | 89 instance = Forwarder._GetInstanceLocked(tool) |
| 83 instance._InitDeviceLocked(adb, tool) | 90 instance._InitDeviceLocked(device, tool) |
| 84 | 91 |
| 85 device_serial = adb.Adb().GetSerialNumber() | 92 device_serial = device.old_interface.Adb().GetSerialNumber() |
| 86 redirection_commands = [ | 93 redirection_commands = [ |
| 87 ['--serial-id=' + device_serial, '--map', str(device), | 94 ['--serial-id=' + device_serial, '--map', str(device), |
| 88 str(host)] for device, host in port_pairs] | 95 str(host)] for device, host in port_pairs] |
| 89 logging.info('Forwarding using commands: %s', redirection_commands) | 96 logging.info('Forwarding using commands: %s', redirection_commands) |
| 90 | 97 |
| 91 for redirection_command in redirection_commands: | 98 for redirection_command in redirection_commands: |
| 92 try: | 99 try: |
| 93 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( | 100 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( |
| 94 [instance._host_forwarder_path] + redirection_command) | 101 [instance._host_forwarder_path] + redirection_command) |
| 95 except OSError as e: | 102 except OSError as e: |
| (...skipping 10 matching lines...) Expand all Loading... |
| 106 'expected "device_port:host_port"') % output) | 113 'expected "device_port:host_port"') % output) |
| 107 device_port = int(tokens[0]) | 114 device_port = int(tokens[0]) |
| 108 host_port = int(tokens[1]) | 115 host_port = int(tokens[1]) |
| 109 serial_with_port = (device_serial, device_port) | 116 serial_with_port = (device_serial, device_port) |
| 110 instance._device_to_host_port_map[serial_with_port] = host_port | 117 instance._device_to_host_port_map[serial_with_port] = host_port |
| 111 instance._host_to_device_port_map[host_port] = serial_with_port | 118 instance._host_to_device_port_map[host_port] = serial_with_port |
| 112 logging.info('Forwarding device port: %d to host port: %d.', | 119 logging.info('Forwarding device port: %d to host port: %d.', |
| 113 device_port, host_port) | 120 device_port, host_port) |
| 114 | 121 |
| 115 @staticmethod | 122 @staticmethod |
| 116 def UnmapDevicePort(device_port, adb): | 123 def UnmapDevicePort(device_port, device): |
| 117 """Unmaps a previously forwarded device port. | 124 """Unmaps a previously forwarded device port. |
| 118 | 125 |
| 119 Args: | 126 Args: |
| 120 adb: An AndroidCommands instance. | 127 device: A DeviceUtils instance. |
| 121 device_port: A previously forwarded port (through Map()). | 128 device_port: A previously forwarded port (through Map()). |
| 122 """ | 129 """ |
| 130 # TODO(jbudorick) Remove once telemetry gets switched over. |
| 131 if isinstance(device, pylib.android_commands.AndroidCommands): |
| 132 device = pylib.device.device_utils.DeviceUtils(device) |
| 123 with _FileLock(Forwarder._LOCK_PATH): | 133 with _FileLock(Forwarder._LOCK_PATH): |
| 124 Forwarder._UnmapDevicePortLocked(device_port, adb) | 134 Forwarder._UnmapDevicePortLocked(device_port, device) |
| 125 | 135 |
| 126 @staticmethod | 136 @staticmethod |
| 127 def UnmapAllDevicePorts(adb): | 137 def UnmapAllDevicePorts(device): |
| 128 """Unmaps all the previously forwarded ports for the provided device. | 138 """Unmaps all the previously forwarded ports for the provided device. |
| 129 | 139 |
| 130 Args: | 140 Args: |
| 131 adb: An AndroidCommands instance. | 141 device: A DeviceUtils instance. |
| 132 port_pairs: A list of tuples (device_port, host_port) to unmap. | 142 port_pairs: A list of tuples (device_port, host_port) to unmap. |
| 133 """ | 143 """ |
| 134 with _FileLock(Forwarder._LOCK_PATH): | 144 with _FileLock(Forwarder._LOCK_PATH): |
| 135 if not Forwarder._instance: | 145 if not Forwarder._instance: |
| 136 return | 146 return |
| 137 adb_serial = adb.Adb().GetSerialNumber() | 147 adb_serial = device.old_interface.Adb().GetSerialNumber() |
| 138 if adb_serial not in Forwarder._instance._initialized_devices: | 148 if adb_serial not in Forwarder._instance._initialized_devices: |
| 139 return | 149 return |
| 140 port_map = Forwarder._GetInstanceLocked( | 150 port_map = Forwarder._GetInstanceLocked( |
| 141 None)._device_to_host_port_map | 151 None)._device_to_host_port_map |
| 142 for (device_serial, device_port) in port_map.keys(): | 152 for (device_serial, device_port) in port_map.keys(): |
| 143 if adb_serial == device_serial: | 153 if adb_serial == device_serial: |
| 144 Forwarder._UnmapDevicePortLocked(device_port, adb) | 154 Forwarder._UnmapDevicePortLocked(device_port, device) |
| 145 # There are no more ports mapped, kill the device_forwarder. | 155 # There are no more ports mapped, kill the device_forwarder. |
| 146 tool = valgrind_tools.CreateTool(None, adb) | 156 tool = valgrind_tools.CreateTool(None, device) |
| 147 Forwarder._KillDeviceLocked(adb, tool) | 157 Forwarder._KillDeviceLocked(device, tool) |
| 148 Forwarder._instance._initialized_devices.remove(adb_serial) | 158 Forwarder._instance._initialized_devices.remove(adb_serial) |
| 149 | 159 |
| 150 | 160 |
| 151 @staticmethod | 161 @staticmethod |
| 152 def DevicePortForHostPort(host_port): | 162 def DevicePortForHostPort(host_port): |
| 153 """Returns the device port that corresponds to a given host port.""" | 163 """Returns the device port that corresponds to a given host port.""" |
| 154 with _FileLock(Forwarder._LOCK_PATH): | 164 with _FileLock(Forwarder._LOCK_PATH): |
| 155 (_device_serial, device_port) = Forwarder._GetInstanceLocked( | 165 (_device_serial, device_port) = Forwarder._GetInstanceLocked( |
| 156 None)._host_to_device_port_map.get(host_port) | 166 None)._host_to_device_port_map.get(host_port) |
| 157 return device_port | 167 return device_port |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 198 self._device_to_host_port_map = dict() | 208 self._device_to_host_port_map = dict() |
| 199 self._host_to_device_port_map = dict() | 209 self._host_to_device_port_map = dict() |
| 200 self._host_forwarder_path = os.path.join( | 210 self._host_forwarder_path = os.path.join( |
| 201 constants.GetOutDirectory(), 'host_forwarder') | 211 constants.GetOutDirectory(), 'host_forwarder') |
| 202 assert os.path.exists(self._host_forwarder_path), 'Please build forwarder2' | 212 assert os.path.exists(self._host_forwarder_path), 'Please build forwarder2' |
| 203 self._device_forwarder_path_on_host = os.path.join( | 213 self._device_forwarder_path_on_host = os.path.join( |
| 204 constants.GetOutDirectory(), 'forwarder_dist') | 214 constants.GetOutDirectory(), 'forwarder_dist') |
| 205 self._InitHostLocked() | 215 self._InitHostLocked() |
| 206 | 216 |
| 207 @staticmethod | 217 @staticmethod |
| 208 def _UnmapDevicePortLocked(device_port, adb): | 218 def _UnmapDevicePortLocked(device_port, device): |
| 209 """Internal method used by UnmapDevicePort(). | 219 """Internal method used by UnmapDevicePort(). |
| 210 | 220 |
| 211 Note that the global lock must be acquired before calling this method. | 221 Note that the global lock must be acquired before calling this method. |
| 212 """ | 222 """ |
| 213 instance = Forwarder._GetInstanceLocked(None) | 223 instance = Forwarder._GetInstanceLocked(None) |
| 214 serial = adb.Adb().GetSerialNumber() | 224 serial = device.old_interface.Adb().GetSerialNumber() |
| 215 serial_with_port = (serial, device_port) | 225 serial_with_port = (serial, device_port) |
| 216 if not serial_with_port in instance._device_to_host_port_map: | 226 if not serial_with_port in instance._device_to_host_port_map: |
| 217 logging.error('Trying to unmap non-forwarded port %d' % device_port) | 227 logging.error('Trying to unmap non-forwarded port %d' % device_port) |
| 218 return | 228 return |
| 219 redirection_command = ['--serial-id=' + serial, '--unmap', str(device_port)] | 229 redirection_command = ['--serial-id=' + serial, '--unmap', str(device_port)] |
| 220 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( | 230 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( |
| 221 [instance._host_forwarder_path] + redirection_command) | 231 [instance._host_forwarder_path] + redirection_command) |
| 222 if exit_code != 0: | 232 if exit_code != 0: |
| 223 logging.error('%s exited with %d:\n%s' % ( | 233 logging.error('%s exited with %d:\n%s' % ( |
| 224 instance._host_forwarder_path, exit_code, '\n'.join(output))) | 234 instance._host_forwarder_path, exit_code, '\n'.join(output))) |
| (...skipping 28 matching lines...) Expand all Loading... |
| 253 if pid_with_start_time: | 263 if pid_with_start_time: |
| 254 (pid, process_start_time) = pid_with_start_time.split(':') | 264 (pid, process_start_time) = pid_with_start_time.split(':') |
| 255 if pid == str(pid_for_lock): | 265 if pid == str(pid_for_lock): |
| 256 if process_start_time == str(_GetProcessStartTime(pid_for_lock)): | 266 if process_start_time == str(_GetProcessStartTime(pid_for_lock)): |
| 257 return | 267 return |
| 258 self._KillHostLocked() | 268 self._KillHostLocked() |
| 259 pid_file.seek(0) | 269 pid_file.seek(0) |
| 260 pid_file.write( | 270 pid_file.write( |
| 261 '%s:%s' % (pid_for_lock, str(_GetProcessStartTime(pid_for_lock)))) | 271 '%s:%s' % (pid_for_lock, str(_GetProcessStartTime(pid_for_lock)))) |
| 262 | 272 |
| 263 def _InitDeviceLocked(self, adb, tool): | 273 def _InitDeviceLocked(self, device, tool): |
| 264 """Initializes the device_forwarder daemon for a specific device (once). | 274 """Initializes the device_forwarder daemon for a specific device (once). |
| 265 | 275 |
| 266 Note that the global lock must be acquired before calling this method. This | 276 Note that the global lock must be acquired before calling this method. This |
| 267 method kills any existing device_forwarder daemon on the device that could | 277 method kills any existing device_forwarder daemon on the device that could |
| 268 be stale, pushes the latest version of the daemon (to the device) and starts | 278 be stale, pushes the latest version of the daemon (to the device) and starts |
| 269 it. | 279 it. |
| 270 | 280 |
| 271 Args: | 281 Args: |
| 272 adb: An AndroidCommands instance. | 282 device: A DeviceUtils instance. |
| 273 tool: Tool class to use to get wrapper, if necessary, for executing the | 283 tool: Tool class to use to get wrapper, if necessary, for executing the |
| 274 forwarder (see valgrind_tools.py). | 284 forwarder (see valgrind_tools.py). |
| 275 """ | 285 """ |
| 276 device_serial = adb.Adb().GetSerialNumber() | 286 device_serial = device.old_interface.Adb().GetSerialNumber() |
| 277 if device_serial in self._initialized_devices: | 287 if device_serial in self._initialized_devices: |
| 278 return | 288 return |
| 279 Forwarder._KillDeviceLocked(adb, tool) | 289 Forwarder._KillDeviceLocked(device, tool) |
| 280 adb.PushIfNeeded( | 290 device.old_interface.PushIfNeeded( |
| 281 self._device_forwarder_path_on_host, | 291 self._device_forwarder_path_on_host, |
| 282 Forwarder._DEVICE_FORWARDER_FOLDER) | 292 Forwarder._DEVICE_FORWARDER_FOLDER) |
| 283 (exit_code, output) = adb.GetShellCommandStatusAndOutput( | 293 (exit_code, output) = device.old_interface.GetShellCommandStatusAndOutput( |
| 284 '%s %s %s' % (Forwarder._LD_LIBRARY_PATH, tool.GetUtilWrapper(), | 294 '%s %s %s' % (Forwarder._LD_LIBRARY_PATH, tool.GetUtilWrapper(), |
| 285 Forwarder._DEVICE_FORWARDER_PATH)) | 295 Forwarder._DEVICE_FORWARDER_PATH)) |
| 286 if exit_code != 0: | 296 if exit_code != 0: |
| 287 raise Exception( | 297 raise Exception( |
| 288 'Failed to start device forwarder:\n%s' % '\n'.join(output)) | 298 'Failed to start device forwarder:\n%s' % '\n'.join(output)) |
| 289 self._initialized_devices.add(device_serial) | 299 self._initialized_devices.add(device_serial) |
| 290 | 300 |
| 291 def _KillHostLocked(self): | 301 def _KillHostLocked(self): |
| 292 """Kills the forwarder process running on the host. | 302 """Kills the forwarder process running on the host. |
| 293 | 303 |
| 294 Note that the global lock must be acquired before calling this method. | 304 Note that the global lock must be acquired before calling this method. |
| 295 """ | 305 """ |
| 296 logging.info('Killing host_forwarder.') | 306 logging.info('Killing host_forwarder.') |
| 297 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( | 307 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( |
| 298 [self._host_forwarder_path, '--kill-server']) | 308 [self._host_forwarder_path, '--kill-server']) |
| 299 if exit_code != 0: | 309 if exit_code != 0: |
| 300 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( | 310 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( |
| 301 ['pkill', '-9', 'host_forwarder']) | 311 ['pkill', '-9', 'host_forwarder']) |
| 302 if exit_code != 0: | 312 if exit_code != 0: |
| 303 raise Exception('%s exited with %d:\n%s' % ( | 313 raise Exception('%s exited with %d:\n%s' % ( |
| 304 self._host_forwarder_path, exit_code, '\n'.join(output))) | 314 self._host_forwarder_path, exit_code, '\n'.join(output))) |
| 305 | 315 |
| 306 @staticmethod | 316 @staticmethod |
| 307 def _KillDeviceLocked(adb, tool): | 317 def _KillDeviceLocked(device, tool): |
| 308 """Kills the forwarder process running on the device. | 318 """Kills the forwarder process running on the device. |
| 309 | 319 |
| 310 Note that the global lock must be acquired before calling this method. | 320 Note that the global lock must be acquired before calling this method. |
| 311 | 321 |
| 312 Args: | 322 Args: |
| 313 adb: Instance of AndroidCommands for talking to the device. | 323 device: Instance of DeviceUtils for talking to the device. |
| 314 tool: Wrapper tool (e.g. valgrind) that can be used to execute the device | 324 tool: Wrapper tool (e.g. valgrind) that can be used to execute the device |
| 315 forwarder (see valgrind_tools.py). | 325 forwarder (see valgrind_tools.py). |
| 316 """ | 326 """ |
| 317 logging.info('Killing device_forwarder.') | 327 logging.info('Killing device_forwarder.') |
| 318 if not adb.FileExistsOnDevice(Forwarder._DEVICE_FORWARDER_PATH): | 328 if not device.old_interface.FileExistsOnDevice( |
| 329 Forwarder._DEVICE_FORWARDER_PATH): |
| 319 return | 330 return |
| 320 adb.GetShellCommandStatusAndOutput( | 331 device.old_interface.GetShellCommandStatusAndOutput( |
| 321 '%s %s --kill-server' % (tool.GetUtilWrapper(), | 332 '%s %s --kill-server' % (tool.GetUtilWrapper(), |
| 322 Forwarder._DEVICE_FORWARDER_PATH)) | 333 Forwarder._DEVICE_FORWARDER_PATH)) |
| 323 # TODO(pliard): Remove the following call to KillAllBlocking() when we are | 334 # TODO(pliard): Remove the following call to KillAllBlocking() when we are |
| 324 # sure that the old version of device_forwarder (not supporting | 335 # sure that the old version of device_forwarder (not supporting |
| 325 # 'kill-server') is not running on the bots anymore. | 336 # 'kill-server') is not running on the bots anymore. |
| 326 timeout_sec = 5 | 337 timeout_sec = 5 |
| 327 processes_killed = adb.KillAllBlocking('device_forwarder', timeout_sec) | 338 processes_killed = device.old_interface.KillAllBlocking( |
| 339 'device_forwarder', timeout_sec) |
| 328 if not processes_killed: | 340 if not processes_killed: |
| 329 pids = adb.ExtractPid('device_forwarder') | 341 pids = device.old_interface.ExtractPid('device_forwarder') |
| 330 if pids: | 342 if pids: |
| 331 raise Exception('Timed out while killing device_forwarder') | 343 raise Exception('Timed out while killing device_forwarder') |
| OLD | NEW |