blob: 382c336367745c31805bbcf4ce280946ae446884 [file] [log] [blame]
Fathi Boudra422bf772019-12-02 11:10:16 +02001#!/bin/bash
2#
3# Copyright (c) 2019, Arm Limited. All rights reserved.
4#
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
27
28 # Kill an active process. Ignore errors
29 [ "$1" ] || return 0
30 kill -0 "$1" &>/dev/null || return 0
31
32 # Kill the group
33 gid="$(awk '{print $5}' < /proc/$1/stat)"
34 kill -SIGKILL -- "-$gid" &>/dev/null || true
35 wait "$gid" &>/dev/null || true
36}
37
38# Perform clean up and ignore errors
39cleanup() {
40 local pid
41
42 # Test success. Kill all background processes so far and wait for them
43 pushd "$pid_dir"
44 set +e
45 while read pid; do
46 pid="$(cat $pid)"
47 kill_and_reap "$pid"
48 done < <(find -name '*.pid')
49 popd
50}
51
52# Launch a program. Have its PID saved in a file with given name with .pid
53# suffix. When the program exits, create a file with .success suffix, or one
54# with .fail if it fails. This function blocks, so the caller must '&' this if
55# they want to continue. Call must wait for $pid_dir/$name.pid to be created
56# should it want to read it.
57launch() {
58 local pid
59
60 "$@" &
61 pid="$!"
62 echo "$pid" > "$pid_dir/${name:?}.pid"
63 if wait "$pid"; then
64 touch "$pid_dir/$name.success"
65 else
66 touch "$pid_dir/$name.fail"
67 fi
68}
69
70# Cleanup actions
71trap cleanup SIGINT SIGHUP SIGTERM EXIT
72
73# Prevent xterm windows from untracked terminals from popping up, especially
74# when running locally
75not_upon "$test_run" && export DISPLAY=
76
77# Source variables required for run
78source "$artefacts/env"
79
80echo
81echo "RUNNING: $TEST_CONFIG"
82echo
83
84# Accept BIN_MODE from environment, or default to release. If bin_mode is set
85# and non-empty (intended to be set from command line), that takes precedence.
86pkg_bin_mode="${BIN_MODE:-release}"
87bin_mode="${bin_mode:-$pkg_bin_mode}"
88
89# Assume 0 is the primary UART to track
90primary_uart=0
91
92# Assume 4 UARTs by default
93num_uarts="${num_uarts:-4}"
94
95# Whether to display primary UART progress live on the console
96primary_live="${primary_live-$PRIMARY_LIVE}"
97
98# Change directory so that all binaries can be accessed realtive to where they
99# lie
100run_cwd="$artefacts/$bin_mode"
101cd "$run_cwd"
102
103# Source environment for run
104if [ -f "run/env" ]; then
105 source "run/env"
106fi
107
108# Fail if there was no model path set
109if [ -z "$model_path" ]; then
110 die "No model path set by package!"
111fi
112
113# Launch model with parameters
114model_out="$run_root/model_log.txt"
115run_sh="$run_root/run.sh"
116
117# Generate run.sh
118echo "$model_path \\" > "$run_sh"
119sed '/^\s*$/d' < model_params | sort | sed 's/^/\t/;s/$/ \\/' >> "$run_sh"
120echo -e "\t\"\$@\"" >> "$run_sh"
121
122echo "Model command line:"
123echo
124cat "$run_sh"
125chmod +x "$run_sh"
126echo
127
128# If it's a test run, skip all the hoops and launch model directly.
129if upon "$test_run"; then
130 "$run_sh" "$@"
131 exit 0
132fi
133
134# For an automated run, export a known variable so that we can identify stale
135# processes spawned by Trusted Firmware CI by inspecting its environment.
136export TRUSTED_FIRMWARE_CI="1"
137
138# Change directory to workspace, as all artifacts paths are relative to
139# that, and launch the model. Have model use no buffering on stdout
140: >"$model_out"
141name="model" launch stdbuf -o0 -e0 "$run_sh" &>"$model_out" &
142wait_count=0
143while :; do
144 if [ -f "$pid_dir/model.pid" ]; then
145 break
146 fi
147 sleep 0.1
148
149 let "wait_count += 1"
150 if [ "$wait_count" -gt 100 ]; then
151 die "Failed to launch model!"
152 fi
153done
154model_pid="$(cat "$pid_dir/model.pid")"
155
156ports_output="$(mktempfile)"
157if not_upon "$ports_script"; then
158 # Default AWK script to parse model ports
159 ports_script="$(mktempfile)"
160 cat <<'EOF' >"$ports_script"
161/terminal_0/ { ports[0] = $NF }
162/terminal_1/ { ports[1] = $NF }
163/terminal_2/ { ports[2] = $NF }
164/terminal_3/ { ports[3] = $NF }
165END {
166 for (i = 0; i < num_uarts; i++) {
167 if (ports[i] != "")
168 print "ports[" i "]=" ports[i]
169 }
170}
171EOF
172fi
173
174# Start a watchdog to kill ourselves if we wait too long for the model
175# response. Note that this is not the timeout for the whole test, but only for
176# the Model to output port numbers.
177(
178if upon "$jenkins_run"; then
179 # Increase this timeout for a cluster run, as it could take longer if
180 # the load on the Jenkins server is high.
181 model_wait_timeout=120
182else
183 model_wait_timeout=30
184fi
185sleep $model_wait_timeout
186echo "Model wait timeout!"
187kill "$$"
188) &
189watchdog="$!"
190
191# Parse UARTs ports from early model output. Send a SIGSTOP to the model
192# as soon as it outputs all UART ports. This is to prevent the model
193# executing before the expect scripts get a chance to connect to the
194# UART thereby losing messages.
195model_fail=1
196while :; do
197 awk -v "num_uarts=$num_uarts" -f "$ports_script" "$model_out" \
198 > "$ports_output"
199 if [ $(wc -l < "$ports_output") -eq "$num_uarts" ]; then
200 kill -SIGSTOP "$model_pid"
201 model_fail=0
202 break
203 fi
204
205 # Bail out if model exited meanwhile
206 if ! kill -0 "$model_pid" &>/dev/null; then
207 echo "Model terminated unexpectedly!"
208 break
209 fi
210done
211
212# Kill the watch dog
213kill_and_reap "$watchdog" || true
214
215# Check the model had failed meanwhile, for some reason
216if [ "$model_fail" -ne 0 ]; then
217 exit 1
218fi
219
220# The wait loop above exited after model port numbers have been parsed. The
221# script's output is ready to be sourced now.
222declare -a ports
223source "$ports_output"
224rm -f "$ports_output"
225if [ "${#ports[@]}" -ne "$num_uarts" ]; then
226 echo "Failed to get UART port numbers"
227 kill_and_reap "$model_pid"
228 unset model_pid
229fi
230
231# Launch expect scripts for all UARTs
232uarts=0
233for u in $(seq 0 $num_uarts | tac); do
234 script="run/uart$u/expect"
235 if [ -f "$script" ]; then
236 script="$(cat "$script")"
237 else
238 script=
239 fi
240
241 # Primary UART must have a script
242 if [ -z "$script" ]; then
243 if [ "$u" = "$primary_uart" ]; then
244 die "No primary UART script!"
245 else
246 continue
247 fi
248 fi
249
250 timeout="run/uart$u/timeout"
251 if [ -f "$timeout" ]; then
252 timeout="$(cat "$timeout")"
253 else
254 timeout=
255 fi
256 timeout="${timeout-600}"
257
258 full_log="$run_root/uart${u}_full.txt"
259
260 if [ "$u" = "$primary_uart" ]; then
261 star="*"
262 uart_name="primary_uart"
263 else
264 star=" "
265 uart_name="uart$u"
266 fi
267
268 # Launch expect after exporting required variables
269 (
270 if [ -f "run/uart$u/env" ]; then
271 set -a
272 source "run/uart$u/env"
273 set +a
274 fi
275
276 if [ "$u" = "$primary_uart" ] && upon "$primary_live"; then
277 uart_port="${ports[$u]}" timeout="$timeout" \
278 name="$uart_name" launch expect -f "$ci_root/expect/$script" | \
279 tee "$full_log"
280 echo
281 else
282 uart_port="${ports[$u]}" timeout="$timeout" \
283 name="$uart_name" launch expect -f "$ci_root/expect/$script" \
284 &>"$full_log"
285 fi
286
287 ) &
288
289 let "uarts += 1"
290 echo "Tracking UART$u$star with $script; timeout $timeout."
291done
292
293# Wait here long 'enough' for expect scripts to connect to ports; then
294# let the model proceed
295sleep 2
296kill -SIGCONT "$model_pid"
297
298# Wait for all children. Note that the wait below is *not* a timed wait.
299result=0
300
301set +e
302pushd "$pid_dir"
303while :; do
304 wait -n
305
306 # Exit failure if we've any failures
307 if [ "$(wc -l < <(find -name '*.fail'))" -ne 0 ]; then
308 result=1
309 break
310 fi
311
312 # We're done if the primary UART exits success
313 if [ -f "$pid_dir/primary_uart.success" ]; then
314 break
315 fi
316done
317popd
318
319cleanup
320
321if [ "$result" -eq 0 ]; then
322 echo "Test success!"
323else
324 echo "Test failed!"
325fi
326
327if upon "$jenkins_run"; then
328 echo
329 echo "Artefacts location: $BUILD_URL."
330 echo
331fi
332
333if upon "$jenkins_run" && upon "$artefacts_receiver" && [ -d "$workspace/run" ]; then
334 pushd "$workspace"
335 run_archive="run.tar.xz"
336 tar -cJf "$run_archive" "run"
337 where="$artefacts_receiver/${TEST_GROUP:?}/${TEST_CONFIG:?}/$run_archive"
338 where+="?j=$JOB_NAME&b=$BUILD_NUMBER"
339 if wget -q --method=PUT --body-file="$run_archive" "$where"; then
340 echo "Run logs submitted to $where."
341 else
342 echo "Error submitting run logs to $where."
343 fi
344 popd
345fi
346
347exit "$result"
348
349# vim: set tw=80 sw=8 noet: