Source code

Revision control

Copy as Markdown

Other Tools

# Copyright 2021 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.
"""Implements commands for running and interacting with Fuchsia on FVDL."""
import boot_data
import common
import emu_target
import logging
import os
import re
import subprocess
import tempfile
_SSH_KEY_DIR = os.path.expanduser('~/.ssh')
_DEFAULT_SSH_PORT = 22
_DEVICE_PROTO_TEMPLATE = """
device_spec: {{
horizontal_resolution: 1024
vertical_resolution: 600
vm_heap: 192
ram: {ramsize}
cache: 32
screen_density: 240
}}
"""
def GetTargetType():
return FvdlTarget
class EmulatorNetworkNotFoundError(Exception):
"""Raised when emulator's address cannot be found"""
pass
class FvdlTarget(emu_target.EmuTarget):
EMULATOR_NAME = 'aemu'
_FVDL_PATH = os.path.join(common.SDK_ROOT, 'tools', 'x64', 'fvdl')
def __init__(self, out_dir, target_cpu, system_log_file, require_kvm,
enable_graphics, hardware_gpu, with_network, ram_size_mb):
super(FvdlTarget, self).__init__(out_dir, target_cpu, system_log_file)
self._require_kvm = require_kvm
self._enable_graphics = enable_graphics
self._hardware_gpu = hardware_gpu
self._with_network = with_network
self._ram_size_mb = ram_size_mb
self._host = None
self._pid = None
# Use a temp file for vdl output.
self._vdl_output_file = tempfile.NamedTemporaryFile()
# Use a temp file for the device proto and write the ram size.
self._device_proto_file = tempfile.NamedTemporaryFile()
with open(self._device_proto_file.name, 'w') as file:
file.write(_DEVICE_PROTO_TEMPLATE.format(ramsize=self._ram_size_mb))
@staticmethod
def CreateFromArgs(args):
return FvdlTarget(args.out_dir, args.target_cpu, args.system_log_file,
args.require_kvm, args.enable_graphics, args.hardware_gpu,
args.with_network, args.ram_size_mb)
@staticmethod
def RegisterArgs(arg_parser):
fvdl_args = arg_parser.add_argument_group('fvdl', 'FVDL arguments')
fvdl_args.add_argument('--with-network',
action='store_true',
default=False,
help='Run emulator with emulated nic via tun/tap.')
def _BuildCommand(self):
boot_data.ProvisionSSH()
self._host_ssh_port = common.GetAvailableTcpPort()
kernel_image = common.EnsurePathExists(
boot_data.GetTargetFile('qemu-kernel.kernel', self._GetTargetSdkArch(),
boot_data.TARGET_TYPE_QEMU))
zbi_image = common.EnsurePathExists(
boot_data.GetTargetFile('zircon-a.zbi', self._GetTargetSdkArch(),
boot_data.TARGET_TYPE_QEMU))
fvm_image = common.EnsurePathExists(
boot_data.GetTargetFile('storage-full.blk', self._GetTargetSdkArch(),
boot_data.TARGET_TYPE_QEMU))
aemu_path = common.EnsurePathExists(
os.path.join(common.GetEmuRootForPlatform(self.EMULATOR_NAME),
'emulator'))
emu_command = [
self._FVDL_PATH,
'--sdk',
'start',
'--nopackageserver',
'--nointeractive',
# Host port mapping for user-networking mode.
'--port-map',
'hostfwd=tcp::{}-:22'.format(self._host_ssh_port),
# no-interactive requires a --vdl-output flag to shutdown the emulator.
'--vdl-output',
self._vdl_output_file.name,
# Use existing images instead of downloading new ones.
'--kernel-image',
kernel_image,
'--zbi-image',
zbi_image,
'--fvm-image',
fvm_image,
'--image-architecture',
self._target_cpu,
# Use an existing emulator checked out by Chromium.
'--aemu-path',
aemu_path,
# Use this flag and temp file to define ram size.
'--device-proto',
self._device_proto_file.name
]
if not self._require_kvm:
emu_command.append('--noacceleration')
if not self._enable_graphics:
emu_command.append('--headless')
if self._hardware_gpu:
emu_command.append('--host-gpu')
if self._with_network:
emu_command.append('-N')
logging.info('FVDL command: ' + ' '.join(emu_command))
return emu_command
def _WaitUntilReady(self):
# Indicates the FVDL command finished running.
self._emu_process.communicate()
super(FvdlTarget, self)._WaitUntilReady()
def _IsEmuStillRunning(self):
if not self._pid:
try:
with open(self._vdl_output_file.name) as vdl_file:
for line in vdl_file:
if 'pid' in line:
match = re.match(r'.*pid:\s*(\d*).*', line)
if match:
self._pid = match.group(1)
except IOError:
logging.error('vdl_output file no longer found. '
'Cannot get emulator pid.')
return False
if subprocess.check_output(['ps', '-p', self._pid, 'o', 'comm=']):
return True
logging.error('Emulator pid no longer found. Emulator must be down.')
return False
def _GetEndpoint(self):
if self._with_network:
return self._GetNetworkAddress()
return ('localhost', self._host_ssh_port)
def _GetNetworkAddress(self):
if self._host:
return (self._host, _DEFAULT_SSH_PORT)
try:
with open(self._vdl_output_file.name) as vdl_file:
for line in vdl_file:
if 'network_address' in line:
address = re.match(r'.*network_address:\s*"\[(.*)\]".*', line)
if address:
self._host = address.group(1)
return (self._host, _DEFAULT_SSH_PORT)
logging.error('Network address not found.')
raise EmulatorNetworkNotFoundError()
except IOError as e:
logging.error('vdl_output file not found. Cannot get network address.')
raise
def Shutdown(self):
if not self._emu_process:
logging.error('%s did not start' % (self.EMULATOR_NAME))
return
femu_command = [
self._FVDL_PATH, '--sdk', 'kill', '--launched-proto',
self._vdl_output_file.name
]
femu_process = subprocess.Popen(femu_command)
returncode = femu_process.wait()
if returncode == 0:
logging.info('FVDL shutdown successfully')
else:
logging.info('FVDL kill returned error status {}'.format(returncode))
emu_target.LogProcessStatistics('proc_stat_end_log')
emu_target.LogSystemStatistics('system_statistics_end_log')
self._vdl_output_file.close()
self._device_proto_file.close()
def _GetSshConfigPath(self):
return boot_data.GetSSHConfigPath()