blob: 58ca1b07f27f848e51421d1e0a0fe02aaa528e03 [file] [log] [blame]
# Copyright 2019 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.
"""A wrapper for common operations on a Fuchsia device"""
import logging
import os
import subprocess
from telemetry.core import util
FUCHSIA_BROWSERS = ['web-engine-shell']
_LLVM_SYMBOLIZER_PATH = os.path.join(
util.GetCatapultDir(), '..', 'llvm-build', 'Release+Asserts', 'bin',
'llvm-symbolizer')
class CommandRunner(object):
"""Helper class used to execute commands on Fuchsia devices on a remote host
over SSH."""
def __init__(self, config_path, host, port):
"""Creates a CommandRunner that connects to the specified |host| and |port|
using the ssh config at the specified |config_path|.
Args:
config_path: Full path to SSH configuration.
host: The hostname or IP address of the remote host.
port: The port to connect to."""
self._config_path = config_path
self._host = host
self._port = port
def _GetSshCommandLinePrefix(self):
prefix_cmd = ['ssh', '-F', self._config_path, self._host]
if self._port:
prefix_cmd += ['-p', str(self._port)]
return prefix_cmd
def RunCommandPiped(self, command=None, ssh_args=None, **kwargs):
"""Executes an SSH command on the remote host and returns a process object
with access to the command's stdio streams. Does not block.
Args:
command: A list of strings containing the command and its arguments.
ssh_args: Arguments that will be passed to SSH.
kwargs: A dictionary of parameters to be passed to subprocess.Popen().
The parameters can be used to override stdin and stdout, for example.
Returns:
A subprocess.Popen object for the command."""
if not command:
command = []
if not ssh_args:
ssh_args = []
# Having control master causes weird behavior in port_forwarding.
ssh_args.append('-oControlMaster=no')
ssh_command = self._GetSshCommandLinePrefix() + ssh_args + ['--'] + command
logging.debug(' '.join(ssh_command))
return subprocess.Popen(ssh_command, **kwargs)
def RunCommand(self, command=None, ssh_args=None, **kwargs):
"""Executes an SSH command on the remote host and returns stdout, stderr,
and return code of the command. Blocks."""
cmd_proc = self.RunCommandPiped(command, ssh_args, **kwargs)
stdout, stderr = cmd_proc.communicate()
return cmd_proc.returncode, stdout, stderr
def StartSymbolizerForProcessIfPossible(input_file, output_file, build_id_file):
"""Starts an llvm symbolizer process if possible.
Args:
input_file: Input file to be symbolized.
output_file: Output file for symbolizer stdout and stderr.
build_id_file: Path to the ids.txt file which maps build IDs to
unstripped binaries on the filesystem.
Returns:
A subprocess.Popen object for the started process, None if symbolizer
fails to start."""
if (os.path.isfile(_LLVM_SYMBOLIZER_PATH) and
os.path.isfile(build_id_file)):
sdk_root = os.path.join(util.GetCatapultDir(), '..', 'fuchsia-sdk', 'sdk')
symbolizer = os.path.join(sdk_root, 'tools', 'x64', 'symbolize')
symbolizer_cmd = [symbolizer,
'-ids-rel', '-llvm-symbolizer', _LLVM_SYMBOLIZER_PATH,
'-build-id-dir', os.path.join(sdk_root, '.build-id')]
symbolizer_cmd.extend(['-ids', build_id_file])
logging.debug('Running "%s".' % ' '.join(symbolizer_cmd))
return subprocess.Popen(symbolizer_cmd,
stdin=input_file,
stdout=output_file,
stderr=subprocess.STDOUT,
close_fds=True)
else:
logging.info('Symbolizer cannot be started.')
return None