204 lines
6.6 KiB
Python
204 lines
6.6 KiB
Python
# 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()
|