blob: 278ab6ab8473ce7145f6d7d35ff463fc6a898844 [file] [log] [blame]
#!/bin/bash
# Copyright 2019 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
function usage() {
echo "Usage: $0"
echo
echo " Starts up femu.sh, runs a script, captures the result, and terminates the emulator"
echo
echo " [--exec <script>]"
echo " Executes the specified script or command that can access the emulator"
echo " [--femu-log <file>]"
echo " Specify log file instead of mktemp, can also be /dev/null or /dev/stdout"
echo " [--headless]"
echo " Do not use any graphics support, do not open any new windows"
echo " [--interactive]"
echo " Opens up xterm window with femu serial console"
echo " [--image <name>]"
echo " System image to use with femu.sh, defaults to ${IMAGE_NAME}"
echo
echo " All other arguments are passed on, see femu.sh --help for more info"
}
set -eu # Error checking
err_print() {
echo "Error at $1"
}
trap 'err_print $0:$LINENO' ERR
SCRIPT_SRC_DIR="$(cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd)"
# shellcheck disable=SC1090
source "${SCRIPT_SRC_DIR}/fuchsia-common.sh" || exit $?
function get_child_pids {
children=()
if is-mac; then
while IFS='' read -r v; do children+=("$v"); done < <(pgrep -P "$1")
else
while IFS='' read -r v; do children+=("$v"); done < <(ps -o pid= --ppid "$1")
fi
if (( ${#children[@]} )); then
for pid in "${children[@]}"; do
while IFS='' read -r v; do children+=("$v"); done < <(get_child_pids "$pid")
done
echo "${children[@]}"
fi
}
# Called when there is an error and we need to dump the log
function dump_femu_log {
if [[ "${FEMU_LOG}" = /dev/* ]]; then
echo "Cannot dump log for device ${FEMU_LOG}"
else
echo "Dumping log file ${FEMU_LOG}"
cat -n "${FEMU_LOG}"
fi
}
# This is only used after femu.sh has been started
function cleanup {
declare -a CHILD_PIDS=()
IFS=" " read -r -a CHILD_PIDS <<< "$(get_child_pids "${FEMU_PID}")"
msg="Cleaning up femu pid ${FEMU_PID}"
if (( "${#CHILD_PIDS[@]}" )); then
echo "$msg qemu child processes ${CHILD_PIDS[*]} and package server for exit ..."
kill "${CHILD_PIDS[@]}" > /dev/null 2>&1 || true
elif [[ "${FEMU_PID}" != "" ]]; then
echo "${msg}"
kill "${FEMU_PID}" > /dev/null 2>&1 || true
fi
"${SCRIPT_SRC_DIR}/fserve.sh" --kill > /dev/null 2>&1 || true
}
HEADLESS=""
INTERACTIVE=""
EXEC_SCRIPT=""
IMAGE_NAME="qemu-x64"
FEMU_LOG=""
FEMU_LOG_TEMP=""
EMU_ARGS=()
# Check for some of the emu flags, but pass everything else on to femu.sh
while (( "$#" )); do
case $1 in
--help|-h)
usage
exit 0
;;
--headless)
HEADLESS="yes"
;;
--interactive)
INTERACTIVE="yes"
;;
--exec)
shift
EXEC_SCRIPT="${1}"
;;
--femu-log)
shift
FEMU_LOG="${1}"
;;
--image)
shift
IMAGE_NAME="${1}"
;;
*)
# Everything else is passed on to the emulator
EMU_ARGS+=( "${1}" )
;;
esac
shift
done
# This IPv6 address is always generated according to the hash of the qemu network interface in femu.sh
EMULATOR_ADDRESS="fe80::5054:ff:fe63:5e7a%qemu"
# Set up log file if not specified
if [[ "${INTERACTIVE}" == "yes" && "${FEMU_LOG}" != "" && "${FEMU_LOG}" != "/dev/null" ]]; then
fx-error "--interactive does not write to --femu-log"
exit 1
fi
if [[ "${FEMU_LOG}" == "" ]]; then
FEMU_LOG="$(mktemp)"
FEMU_LOG_TEMP="${FEMU_LOG_TEMP}"
fi
# Always start femu with network access.
femu_command_args=("${SCRIPT_SRC_DIR}/femu.sh" --image "${IMAGE_NAME}" -N)
echo "Starting emulator with logging to ${FEMU_LOG}"
if [[ "${INTERACTIVE}" == "yes" ]]; then
if (( ${#EMU_ARGS[@]} )); then
femu_command_args+=("${EMU_ARGS[@]}")
fi
# Start up the emulator in the background within a new window, useful for interactive debugging
xterm -T "femu" -e "${femu_command_args[@]}" &
FEMU_PID=$!
elif [[ "${HEADLESS}" == "yes" ]]; then
# When there is no graphics support, run femu in the background with no output visible, and use a software GPU
femu_command_args+=("--headless" "--software-gpu")
if (( ${#EMU_ARGS[@]} )); then
femu_command_args+=("${EMU_ARGS[@]}")
fi
"${femu_command_args[@]}" &> "${FEMU_LOG}" < /dev/null &
FEMU_PID=$!
else
# Allow femu to open up a window for the emulator and use hardware Vulkan support
if (( ${#EMU_ARGS[@]} )); then
femu_command_args+=("${EMU_ARGS[@]}")
fi
"${femu_command_args[@]}" &> "${FEMU_LOG}" < /dev/null &
FEMU_PID=$!
fi
trap cleanup EXIT
# Wait for the emulator to start, and check if emu is still running in the background or this will never complete
echo "Waiting for emulator to start"
COUNT=0
COUNT_TIMEOUT=120
while [[ $COUNT -lt $COUNT_TIMEOUT ]] ; do
kill -0 ${FEMU_PID} &> /dev/null || ( echo "ERROR: Emulator pid $FEMU_PID has exited, cannot connect"; dump_femu_log; exit 1; )
"${SCRIPT_SRC_DIR}/fssh.sh" --device-ip "${EMULATOR_ADDRESS}" "echo hello" &> /dev/null && break
echo "Waiting for emulator SSH server ${EMULATOR_ADDRESS} - attempt ${COUNT} ..."
COUNT=$((COUNT+1))
sleep 1s
done
if (( COUNT == COUNT_TIMEOUT )); then
echo "ERROR: Timeout of ${COUNT_TIMEOUT} seconds reached waiting for emulator pid $FEMU_PID to start, cannot connect"
dump_femu_log
exit 1
fi
echo "Emulator pid ${FEMU_PID} is running and accepting connections"
# Start the package server after the emulator is ready, so we know it is configured when we run commands
echo "Starting package server"
"${SCRIPT_SRC_DIR}/fserve.sh" --image "${IMAGE_NAME}"
# Execute the script specified on the command-line
EXEC_RESULT=0
if [[ "${EXEC_SCRIPT}" == "" ]]; then
fx-warn "No --exec script specified, will now clean up"
else
eval "${EXEC_SCRIPT}" "${EMULATOR_ADDRESS}" || EXEC_RESULT=$?
fi
if [[ "${EXEC_RESULT}" != "0" ]]; then
fx-error "ERROR: \"${EXEC_SCRIPT}\" returned ${EXEC_RESULT}"
dump_femu_log
else
# Clean up the temporary file on success
if [[ "${FEMU_LOG_TEMP}" != "" ]]; then
rm -f "${FEMU_LOG_TEMP}"
fi
fi
# Exit with the result of the test script, and also run the cleanup function
exit $EXEC_RESULT