| #!/bin/bash |
| # test-wrapper script for NaCl. |
| |
| # Copyright (C) 2015-2016 Free Software Foundation, Inc. |
| # This file is part of the GNU C Library. |
| |
| # The GNU C Library is free software; you can redistribute it and/or |
| # modify it under the terms of the GNU Lesser General Public |
| # License as published by the Free Software Foundation; either |
| # version 2.1 of the License, or (at your option) any later version. |
| |
| # The GNU C Library is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| # Lesser General Public License for more details. |
| |
| # You should have received a copy of the GNU Lesser General Public |
| # License along with the GNU C Library; if not, see |
| # <http://www.gnu.org/licenses/>. |
| |
| progname="$(basename "$0")" |
| |
| usage="usage: ${progname} --arch=ARCH [VAR=VAL...] COMMAND ..." |
| help=" |
| " |
| |
| use_bootstrap=true |
| arch= |
| env=() |
| envi=0 |
| while [ $# -gt 0 ]; do |
| case "$1" in |
| |
| --help) |
| echo "$usage" |
| echo "$help" |
| exit 0 |
| ;; |
| |
| --arch=*) |
| arch="${1#--arch=}" |
| shift |
| ;; |
| |
| *=*) |
| env[envi++]='-E' |
| env[envi++]="$1" |
| shift |
| ;; |
| |
| --) |
| shift |
| break |
| ;; |
| |
| *) |
| break |
| ;; |
| esac |
| done |
| |
| if [ $# -lt 1 -o -z "$arch" ]; then |
| echo "$usage" >&2 |
| echo "Type '${progname} --help' for more detailed help." >&2 |
| exit 1 |
| fi |
| |
| test_args=("$@") |
| |
| if [ -z "$NACL_SDK_ROOT" ]; then |
| echo >&2 "$0: NACL_SDK_ROOT must be set in the environment" |
| exit 77 |
| fi |
| |
| # We use a handful of things from the NaCl SDK, or at least |
| # from a directory matching the layout of the NaCl SDK. |
| sdk_tools="${NACL_SDK_ROOT}/tools" |
| |
| NACL_BOOTSTRAP="${sdk_tools}/nacl_helper_bootstrap_${arch}" |
| NACL_SEL_LDR="${sdk_tools}/sel_ldr_${arch}" |
| NACL_IRT="${sdk_tools}/irt_core_${arch}.nexe" |
| NACL_LOADER="${sdk_tools}/elf_loader_${arch}.nexe" |
| |
| if [ ! -x "$NACL_BOOTSTRAP" -o ! -x "$NACL_SEL_LDR" ]; then |
| echo >&2 "$0: sel_ldr_${arch} and/or nacl_helper_bootstrap_${arch} missing" |
| echo >&2 "$0: from directory $sdk_tools" |
| exit 77 |
| fi |
| |
| if [ ! -r "$NACL_IRT" -o ! -r "$NACL_LOADER" ]; then |
| echo >&2 "$0: irt_core_${arch}.nexe and/or loader_${arch}.nexe missing" |
| echo >&2 "$0: from directory $sdk_tools" |
| exit 77 |
| fi |
| |
| # Figure out if we are building for the native machine or not. |
| # If not, we'll run sel_ldr under qemu. |
| decide_use_emulator() |
| { |
| local arg |
| for arg; do |
| if [[ "$(uname -m)" = "$1" ]]; then |
| return |
| fi |
| done |
| use_emulator=true |
| } |
| |
| use_emulator=false |
| case "$arch" in |
| arm) |
| decide_use_emulator 'arm*' |
| emulator=(qemu-arm -cpu cortex-a15 -L "${sdk_tools}/arm_trusted") |
| ;; |
| x86_32) |
| decide_use_emulator 'i?86' 'x86_64*' |
| emulator=(qemu-i386) |
| ;; |
| x86_64) |
| decide_use_emulator 'x86_64*' |
| emulator=(qemu-x86_64) |
| ;; |
| esac |
| |
| if $use_emulator; then |
| ldr_args=('-Q') |
| emulator_factor=10 |
| else |
| emulator=() |
| ldr_args=() |
| emulator_factor=1 |
| fi |
| |
| if $use_bootstrap; then |
| ldr=( |
| "${NACL_BOOTSTRAP}" |
| "${NACL_SEL_LDR}" |
| '--r_debug=0xXXXXXXXXXXXXXXXX' |
| '--reserved_at_zero=0xXXXXXXXXXXXXXXXX' |
| ) |
| else |
| ldr=("${NACL_SEL_LDR}") |
| fi |
| |
| static=true |
| case "$1" in |
| */ld-nacl*) static=false ;; |
| esac |
| |
| if $static; then |
| loader=() |
| else |
| loader=(-f "${NACL_LOADER}") |
| fi |
| |
| run_test() |
| { |
| local test_fifo="$1" |
| local cmd=( |
| "${emulator[@]}" "${ldr[@]}" -q -S -a "${ldr_args[@]}" -B "${NACL_IRT}" |
| "${loader[@]}" "${env[@]}" -E TEST_DIRECT="$test_fifo" -- "${test_args[@]}" |
| ) |
| if [ "${NACLVERBOSITY:+set}" = set ]; then |
| "${cmd[@]}" |
| else |
| NACLLOG=/dev/null "${cmd[@]}" |
| fi |
| } |
| |
| temp_files=() |
| test_fifo= |
| do_cleanup() |
| { |
| rm -rf "$test_fifo" "${temp_files[@]}" |
| } |
| trap do_cleanup EXIT HUP INT TERM |
| |
| # Create a named pipe to receive the TEST_DIRECT information from the test |
| # program. |
| test_fifo=${TMPDIR:-/tmp}/libc-test-fifo.$$ |
| rm -f "$test_fifo" |
| mkfifo "$test_fifo" || { |
| echo "Cannot create test FIFO '$test_fifo'" |
| exit 1 |
| } |
| |
| # Run the test in the background, so we can implement a timeout. |
| # The no-op redirection defeats the default behavior of "< /dev/null" |
| # for a background command. |
| run_test "$test_fifo" <&0 & test_pid=$! |
| |
| # Set up a short timeout before we read from the FIFO, in case |
| # the program doesn't actually write to the FIFO at all (it is |
| # not a test-skeleton.c program, or it dies very early). |
| no_skeleton=false |
| script_pid=$$ |
| trap 'no_skeleton=true' USR1 |
| (sleep 2; kill -USR1 $script_pid) 2> /dev/null & |
| |
| # The test should first write into the FIFO to describe its expectations. |
| # Our open-for-reading of the FIFO will block until the test starts up and |
| # opens it for writing. Then our reads will block until the test finishes |
| # writing out info and closes the FIFO. At that point we will have |
| # collected (and evaluated) what it emitted. It sets these variables: |
| # timeout=%u |
| # timeoutfactor=%u |
| # exit=%u |
| # signal=%s |
| unset exit signal |
| . "$test_fifo" 2> /dev/null |
| |
| # If we got this far, either the 'no_skeleton=true' watchdog already |
| # fired, or else we don't want it to. |
| trap '' USR1 |
| |
| if $no_skeleton; then |
| # We hit the timeout, so we didn't get full information about test |
| # expectations. Reset any partial results we may have gotten. |
| unset exit signal |
| else |
| # Now we know the expected timeout, so we can start the timer running. |
| ((sleep_time = timeout * timeoutfactor * emulator_factor)) |
| |
| # Now start a background subshell to enforce the timeout. |
| (sleep "$sleep_time"; kill -ALRM $test_pid) 2> /dev/null & |
| fi |
| |
| # This corresponds to '#ifdef EXPECTED_STATUS' in test-skeleton.c. |
| expected_status() |
| { |
| test "${exit+yes}" = yes |
| } |
| # This corresponds to '#ifdef EXPECTED_SIGNAL' in test-skeleton.c. |
| expected_signal() |
| { |
| test "${signal+yes}" = yes |
| } |
| # This corresponds to 'if (WIFEXITED (status))' in test-skeleton.c. |
| wifexited() |
| { |
| test $test_rc -lt 128 |
| } |
| |
| # Now wait for the test process to finish. |
| wait $test_pid |
| test_rc=$? |
| |
| # This exactly duplicates the logic in test-skeleton.c. |
| if wifexited; then |
| if ! expected_status; then |
| if ! expected_signal; then |
| # Simply exit with the return value of the test. */ |
| exit $test_rc |
| else |
| echo "Expected signal '${signal}' from child, got none" |
| exit 1 |
| fi |
| else |
| if [ $test_rc -ne $exit ]; then |
| echo "Expected status $exit, got $test_rc" |
| exit 1 |
| fi |
| exit 0 |
| fi |
| else |
| # Process was killed by timer or other signal. |
| ((test_signal = test_rc > 192 ? 256 - test_rc : test_rc - 128 )) |
| test_signame=$(kill -l "$test_signal") |
| if ! expected_signal; then |
| echo "Didn't expect signal from child; got '${test_signame}'" |
| exit 1 |
| else |
| if [ "$test_signame" != "$signal" ]; then |
| echo "\ |
| Incorrect signal from child: got '${test_signame}', need '${signal}'" |
| exit 1 |
| fi |
| exit 0 |
| fi |
| fi |