blob: ec11737db9138a121c18c9b04d14fda61d8340b8 [file] [log] [blame]
Leonardo Sandoval9dfdd1b2020-08-06 17:08:11 -05001#!/usr/bin/env bash
Fathi Boudra422bf772019-12-02 11:10:16 +02002#
Zelalem219df412020-05-17 19:21:20 -05003# Copyright (c) 2019-2020, Arm Limited. All rights reserved.
Fathi Boudra422bf772019-12-02 11:10:16 +02004#
5# SPDX-License-Identifier: BSD-3-Clause
6#
7
8set -e
9
10# Enable job control to have background processes run in their own process
11# group. That way, we can kill a background process group in one go.
12set -m
13
14ci_root="$(readlink -f "$(dirname "$0")/..")"
15source "$ci_root/utils.sh"
16
17artefacts="${artefacts-$workspace/artefacts}"
18
19run_root="$workspace/run"
20pid_dir="$workspace/pids"
21
22mkdir -p "$pid_dir"
23mkdir -p "$run_root"
24
25kill_and_reap() {
26 local gid
Fathi Boudra422bf772019-12-02 11:10:16 +020027 # Kill an active process. Ignore errors
28 [ "$1" ] || return 0
29 kill -0 "$1" &>/dev/null || return 0
30
Zelalem219df412020-05-17 19:21:20 -050031 # Kill the children
32 kill -- "-$1" &>/dev/null || true
Fathi Boudra422bf772019-12-02 11:10:16 +020033 # Kill the group
Zelalem219df412020-05-17 19:21:20 -050034 { gid="$(awk '{print $5}' < /proc/$1/stat)";} 2>/dev/null || return
35 # For Code Coverage plugin it is needed to propagate
36 # the kill signal to the plugin in order to save
37 # the trace statistics.
38 if [ "${COVERAGE_ON}" == "1" ] || [ -n "$cc_enable" ]; then
39 kill -SIGTERM -- "-$gid" &>/dev/null || true
40 else
41 kill -SIGKILL -- "-$gid" &>/dev/null || true
42 fi
Fathi Boudra422bf772019-12-02 11:10:16 +020043 wait "$gid" &>/dev/null || true
44}
45
46# Perform clean up and ignore errors
47cleanup() {
48 local pid
49
50 # Test success. Kill all background processes so far and wait for them
51 pushd "$pid_dir"
52 set +e
53 while read pid; do
54 pid="$(cat $pid)"
Zelalem219df412020-05-17 19:21:20 -050055 # Forcefully killing model process does not show statistical
56 # data (Host CPU time spent running in User and System). Safely
57 # kill the model by using SIGINT(^C) that helps in printing
58 # statistical data.
Zelalem1af7a7b2020-08-04 17:34:32 -050059 if [ "$pid" == "$model_pid" ] && [ "${COVERAGE_ON}" != "1" ]; then
Zelalem219df412020-05-17 19:21:20 -050060 model_cid=$(pgrep -P "$model_pid" | xargs)
61 # ignore errors
62 kill -SIGINT "$model_cid" &>/dev/null || true
63 # Allow some time to print data
64 sleep 2
65 else
66 kill_and_reap "$pid"
67 fi
Fathi Boudra422bf772019-12-02 11:10:16 +020068 done < <(find -name '*.pid')
69 popd
70}
71
72# Launch a program. Have its PID saved in a file with given name with .pid
73# suffix. When the program exits, create a file with .success suffix, or one
74# with .fail if it fails. This function blocks, so the caller must '&' this if
75# they want to continue. Call must wait for $pid_dir/$name.pid to be created
76# should it want to read it.
77launch() {
78 local pid
79
80 "$@" &
81 pid="$!"
82 echo "$pid" > "$pid_dir/${name:?}.pid"
83 if wait "$pid"; then
84 touch "$pid_dir/$name.success"
85 else
86 touch "$pid_dir/$name.fail"
87 fi
88}
89
90# Cleanup actions
91trap cleanup SIGINT SIGHUP SIGTERM EXIT
92
93# Prevent xterm windows from untracked terminals from popping up, especially
94# when running locally
95not_upon "$test_run" && export DISPLAY=
96
97# Source variables required for run
98source "$artefacts/env"
99
100echo
101echo "RUNNING: $TEST_CONFIG"
102echo
103
104# Accept BIN_MODE from environment, or default to release. If bin_mode is set
105# and non-empty (intended to be set from command line), that takes precedence.
106pkg_bin_mode="${BIN_MODE:-release}"
107bin_mode="${bin_mode:-$pkg_bin_mode}"
108
109# Assume 0 is the primary UART to track
110primary_uart=0
111
112# Assume 4 UARTs by default
113num_uarts="${num_uarts:-4}"
114
115# Whether to display primary UART progress live on the console
116primary_live="${primary_live-$PRIMARY_LIVE}"
117
118# Change directory so that all binaries can be accessed realtive to where they
119# lie
120run_cwd="$artefacts/$bin_mode"
121cd "$run_cwd"
122
123# Source environment for run
124if [ -f "run/env" ]; then
125 source "run/env"
126fi
127
Zelalem1af7a7b2020-08-04 17:34:32 -0500128# Source model environment for run
129if [ -f "run/model_env" ]; then
130 source "run/model_env"
131fi
Fathi Boudra422bf772019-12-02 11:10:16 +0200132# Fail if there was no model path set
133if [ -z "$model_path" ]; then
134 die "No model path set by package!"
135fi
136
137# Launch model with parameters
138model_out="$run_root/model_log.txt"
139run_sh="$run_root/run.sh"
140
Zelalem219df412020-05-17 19:21:20 -0500141
Fathi Boudra422bf772019-12-02 11:10:16 +0200142# Generate run.sh
143echo "$model_path \\" > "$run_sh"
144sed '/^\s*$/d' < model_params | sort | sed 's/^/\t/;s/$/ \\/' >> "$run_sh"
Zelalem219df412020-05-17 19:21:20 -0500145
146if [ "${COVERAGE_ON}" == "1" ]; then
147 # Adding code coverage plugin
148 echo -e "\t-C TRACE.CoverageTrace.trace-file-prefix=$trace_file_prefix \\" >> "$run_sh"
149 echo -e "\t--plugin $coverage_trace_plugin \\" >> "$run_sh"
150fi
Fathi Boudra422bf772019-12-02 11:10:16 +0200151echo -e "\t\"\$@\"" >> "$run_sh"
152
Zelalem219df412020-05-17 19:21:20 -0500153# Running Reboot/Shutdown tests requires storing the state in non-volatile
154# memory(NVM) across reboot. On FVP, NVM is not persistent across reboot, hence
155# NVM was saved to a file($NVM_file) when running the model using the run.sh
156# shell script.
157# If TFTF Reboot/Shutdown tests are enabled, run the fvp model 10 times by
158# feeding the file containing NVM state generated from the previous run. Note
159# that this file also includes FIP image.
160
161if upon "$run_tftf_reboot_tests" = "1"; then
162 tftf_reboot_tests="$run_root/tftf_reboot_tests.sh"
163
164 # Generate tftf_reboot_tests command. It is similar to run_sh.
165 # The model would run the reboot and shutdown tests 10 times
166 # The uart log file generated by FVP model gets overwritten
167 # across reboots. Copy its contents at the end of the test
168 echo "cat $uart0_file >> UART0.log" >>"$tftf_reboot_tests"
169 echo "cat $uart1_file >> UART1.log" >>"$tftf_reboot_tests"
170 cat <<EOF >>"$tftf_reboot_tests"
171
172for i in {1..10}
173do
174EOF
175 cat "$run_sh" >> "$tftf_reboot_tests"
176 echo "cat $uart0_file >> UART0.log" >>"$tftf_reboot_tests"
177 echo "cat $uart1_file >> UART1.log" >>"$tftf_reboot_tests"
178 cat <<EOF >>"$tftf_reboot_tests"
179done
180EOF
181 #Replace fip.bin with file $NVM_file
182 sed -i 's/fip.bin/'"$NVM_file"'/' "$tftf_reboot_tests"
183
184 echo "TFTF Reboot/Shutdown Tests Enabled"
185 cat "$tftf_reboot_tests" >> "$run_sh"
186 rm "$tftf_reboot_tests"
187fi
188
Fathi Boudra422bf772019-12-02 11:10:16 +0200189echo "Model command line:"
190echo
191cat "$run_sh"
192chmod +x "$run_sh"
193echo
194
195# If it's a test run, skip all the hoops and launch model directly.
196if upon "$test_run"; then
197 "$run_sh" "$@"
198 exit 0
199fi
200
201# For an automated run, export a known variable so that we can identify stale
202# processes spawned by Trusted Firmware CI by inspecting its environment.
203export TRUSTED_FIRMWARE_CI="1"
204
205# Change directory to workspace, as all artifacts paths are relative to
206# that, and launch the model. Have model use no buffering on stdout
207: >"$model_out"
208name="model" launch stdbuf -o0 -e0 "$run_sh" &>"$model_out" &
209wait_count=0
210while :; do
211 if [ -f "$pid_dir/model.pid" ]; then
212 break
213 fi
214 sleep 0.1
215
216 let "wait_count += 1"
217 if [ "$wait_count" -gt 100 ]; then
218 die "Failed to launch model!"
219 fi
220done
Fathi Boudra422bf772019-12-02 11:10:16 +0200221
Zelalem219df412020-05-17 19:21:20 -0500222model_pid="$(cat $pid_dir/model.pid)"
Fathi Boudra422bf772019-12-02 11:10:16 +0200223ports_output="$(mktempfile)"
224if not_upon "$ports_script"; then
225 # Default AWK script to parse model ports
226 ports_script="$(mktempfile)"
227 cat <<'EOF' >"$ports_script"
228/terminal_0/ { ports[0] = $NF }
229/terminal_1/ { ports[1] = $NF }
230/terminal_2/ { ports[2] = $NF }
231/terminal_3/ { ports[3] = $NF }
232END {
233 for (i = 0; i < num_uarts; i++) {
234 if (ports[i] != "")
235 print "ports[" i "]=" ports[i]
236 }
237}
238EOF
239fi
240
241# Start a watchdog to kill ourselves if we wait too long for the model
242# response. Note that this is not the timeout for the whole test, but only for
243# the Model to output port numbers.
244(
245if upon "$jenkins_run"; then
246 # Increase this timeout for a cluster run, as it could take longer if
247 # the load on the Jenkins server is high.
248 model_wait_timeout=120
249else
250 model_wait_timeout=30
251fi
252sleep $model_wait_timeout
253echo "Model wait timeout!"
254kill "$$"
255) &
256watchdog="$!"
257
258# Parse UARTs ports from early model output. Send a SIGSTOP to the model
259# as soon as it outputs all UART ports. This is to prevent the model
260# executing before the expect scripts get a chance to connect to the
261# UART thereby losing messages.
262model_fail=1
263while :; do
264 awk -v "num_uarts=$num_uarts" -f "$ports_script" "$model_out" \
265 > "$ports_output"
266 if [ $(wc -l < "$ports_output") -eq "$num_uarts" ]; then
267 kill -SIGSTOP "$model_pid"
268 model_fail=0
269 break
270 fi
271
272 # Bail out if model exited meanwhile
273 if ! kill -0 "$model_pid" &>/dev/null; then
274 echo "Model terminated unexpectedly!"
275 break
276 fi
277done
278
279# Kill the watch dog
280kill_and_reap "$watchdog" || true
281
282# Check the model had failed meanwhile, for some reason
283if [ "$model_fail" -ne 0 ]; then
284 exit 1
285fi
286
Zelalem219df412020-05-17 19:21:20 -0500287if ! [ -x "$(command -v expect)" ]; then
288 echo "Error: Expect is not installed."
289 exit 1
290fi
291
Fathi Boudra422bf772019-12-02 11:10:16 +0200292# The wait loop above exited after model port numbers have been parsed. The
293# script's output is ready to be sourced now.
294declare -a ports
295source "$ports_output"
296rm -f "$ports_output"
297if [ "${#ports[@]}" -ne "$num_uarts" ]; then
298 echo "Failed to get UART port numbers"
299 kill_and_reap "$model_pid"
300 unset model_pid
301fi
302
303# Launch expect scripts for all UARTs
304uarts=0
Zelalem219df412020-05-17 19:21:20 -0500305for u in $(seq 0 $(( $num_uarts - 1 )) | tac); do
Fathi Boudra422bf772019-12-02 11:10:16 +0200306 script="run/uart$u/expect"
307 if [ -f "$script" ]; then
308 script="$(cat "$script")"
309 else
310 script=
311 fi
312
313 # Primary UART must have a script
314 if [ -z "$script" ]; then
315 if [ "$u" = "$primary_uart" ]; then
316 die "No primary UART script!"
317 else
Zelalem219df412020-05-17 19:21:20 -0500318 echo "Ignoring UART$u (no expect script provided)."
Fathi Boudra422bf772019-12-02 11:10:16 +0200319 continue
320 fi
321 fi
322
323 timeout="run/uart$u/timeout"
324 if [ -f "$timeout" ]; then
325 timeout="$(cat "$timeout")"
326 else
327 timeout=
328 fi
329 timeout="${timeout-600}"
330
331 full_log="$run_root/uart${u}_full.txt"
332
333 if [ "$u" = "$primary_uart" ]; then
334 star="*"
335 uart_name="primary_uart"
336 else
337 star=" "
338 uart_name="uart$u"
339 fi
340
341 # Launch expect after exporting required variables
342 (
343 if [ -f "run/uart$u/env" ]; then
344 set -a
345 source "run/uart$u/env"
346 set +a
347 fi
348
349 if [ "$u" = "$primary_uart" ] && upon "$primary_live"; then
350 uart_port="${ports[$u]}" timeout="$timeout" \
351 name="$uart_name" launch expect -f "$ci_root/expect/$script" | \
352 tee "$full_log"
353 echo
354 else
355 uart_port="${ports[$u]}" timeout="$timeout" \
356 name="$uart_name" launch expect -f "$ci_root/expect/$script" \
357 &>"$full_log"
358 fi
359
360 ) &
361
362 let "uarts += 1"
363 echo "Tracking UART$u$star with $script; timeout $timeout."
364done
Fathi Boudra422bf772019-12-02 11:10:16 +0200365# Wait here long 'enough' for expect scripts to connect to ports; then
366# let the model proceed
367sleep 2
368kill -SIGCONT "$model_pid"
369
370# Wait for all children. Note that the wait below is *not* a timed wait.
371result=0
372
373set +e
374pushd "$pid_dir"
375while :; do
376 wait -n
377
378 # Exit failure if we've any failures
379 if [ "$(wc -l < <(find -name '*.fail'))" -ne 0 ]; then
380 result=1
381 break
382 fi
383
384 # We're done if the primary UART exits success
385 if [ -f "$pid_dir/primary_uart.success" ]; then
386 break
387 fi
388done
389popd
390
391cleanup
392
393if [ "$result" -eq 0 ]; then
394 echo "Test success!"
395else
396 echo "Test failed!"
397fi
398
399if upon "$jenkins_run"; then
400 echo
401 echo "Artefacts location: $BUILD_URL."
402 echo
403fi
404
405if upon "$jenkins_run" && upon "$artefacts_receiver" && [ -d "$workspace/run" ]; then
Zelalem219df412020-05-17 19:21:20 -0500406 source "$CI_ROOT/script/send_artefacts.sh" "run"
Fathi Boudra422bf772019-12-02 11:10:16 +0200407fi
408
Fathi Boudra422bf772019-12-02 11:10:16 +0200409
Zelalem219df412020-05-17 19:21:20 -0500410exit "$result"
Fathi Boudra422bf772019-12-02 11:10:16 +0200411# vim: set tw=80 sw=8 noet: