xref: /spdk/test/common/autotest_common.sh (revision 61617956cb960d4e775c15137677771a1f626ef4)
1#!/usr/bin/env bash
2#  SPDX-License-Identifier: BSD-3-Clause
3#  Copyright (C) 2015 Intel Corporation
4#  All rights reserved.
5#  Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
6#
7rpc_py=rpc_cmd
8
9function xtrace_disable() {
10	set +x
11	X_STACK+=("${FUNCNAME[*]}") # push
12}
13
14function xtrace_restore() {
15	# unset'ing foo[-1] under older Bash (4.2 -> Centos7) won't work, hence the dance
16	unset -v "X_STACK[${#X_STACK[@]} - 1 < 0 ? 0 : ${#X_STACK[@]} - 1]" # pop
17	if ((${#X_STACK[@]} == 0)); then
18		set -x
19	fi
20}
21
22function xtrace_disable_per_cmd() { eval "$* ${BASH_XTRACEFD}> /dev/null"; }
23
24function xtrace_fd() {
25	if [[ -n ${BASH_XTRACEFD:-} && -e /proc/self/fd/$BASH_XTRACEFD ]]; then
26		# Close it first to make sure it's sane
27		exec {BASH_XTRACEFD}>&-
28	fi
29	exec {BASH_XTRACEFD}>&2
30
31	xtrace_restore
32}
33
34set -e
35shopt -s nullglob
36shopt -s extglob
37shopt -s inherit_errexit
38
39if [ -z "${output_dir:-}" ]; then
40	mkdir -p "$rootdir/../output"
41	export output_dir="$rootdir/../output"
42fi
43
44if [[ -e $rootdir/test/common/build_config.sh ]]; then
45	source "$rootdir/test/common/build_config.sh"
46elif [[ -e $rootdir/mk/config.mk ]]; then
47	build_config=$(< "$rootdir/mk/config.mk")
48	source <(echo "${build_config//\?=/=}")
49else
50	source "$rootdir/CONFIG"
51fi
52
53# Source scripts after the config so that the definitions are available.
54source "$rootdir/test/common/applications.sh"
55source "$rootdir/scripts/common.sh"
56source "$rootdir/scripts/perf/pm/common"
57
58: ${RUN_NIGHTLY:=0}
59export RUN_NIGHTLY
60
61# Set defaults for missing test config options
62: ${SPDK_AUTOTEST_DEBUG_APPS:=0}
63export SPDK_AUTOTEST_DEBUG_APPS
64: ${SPDK_RUN_VALGRIND=0}
65export SPDK_RUN_VALGRIND
66: ${SPDK_RUN_FUNCTIONAL_TEST=0}
67export SPDK_RUN_FUNCTIONAL_TEST
68: ${SPDK_TEST_UNITTEST=0}
69export SPDK_TEST_UNITTEST
70: ${SPDK_TEST_AUTOBUILD=""}
71export SPDK_TEST_AUTOBUILD
72: ${SPDK_TEST_RELEASE_BUILD=0}
73export SPDK_TEST_RELEASE_BUILD
74: ${SPDK_TEST_ISAL=0}
75export SPDK_TEST_ISAL
76: ${SPDK_TEST_ISCSI=0}
77export SPDK_TEST_ISCSI
78: ${SPDK_TEST_ISCSI_INITIATOR=0}
79export SPDK_TEST_ISCSI_INITIATOR
80: ${SPDK_TEST_NVME=0}
81export SPDK_TEST_NVME
82: ${SPDK_TEST_NVME_PMR=0}
83export SPDK_TEST_NVME_PMR
84: ${SPDK_TEST_NVME_BP=0}
85export SPDK_TEST_NVME_BP
86: ${SPDK_TEST_NVME_CLI=0}
87export SPDK_TEST_NVME_CLI
88: ${SPDK_TEST_NVME_CUSE=0}
89export SPDK_TEST_NVME_CUSE
90: ${SPDK_TEST_NVME_FDP=0}
91export SPDK_TEST_NVME_FDP
92: ${SPDK_TEST_NVMF=0}
93export SPDK_TEST_NVMF
94: ${SPDK_TEST_VFIOUSER=0}
95export SPDK_TEST_VFIOUSER
96: ${SPDK_TEST_VFIOUSER_QEMU=0}
97export SPDK_TEST_VFIOUSER_QEMU
98: ${SPDK_TEST_FUZZER=0}
99export SPDK_TEST_FUZZER
100: ${SPDK_TEST_FUZZER_SHORT=0}
101export SPDK_TEST_FUZZER_SHORT
102: ${SPDK_TEST_NVMF_TRANSPORT="rdma"}
103export SPDK_TEST_NVMF_TRANSPORT
104: ${SPDK_TEST_RBD=0}
105export SPDK_TEST_RBD
106: ${SPDK_TEST_VHOST=0}
107export SPDK_TEST_VHOST
108: ${SPDK_TEST_BLOCKDEV=0}
109export SPDK_TEST_BLOCKDEV
110: ${SPDK_TEST_RAID=0}
111export SPDK_TEST_RAID
112: ${SPDK_TEST_IOAT=0}
113export SPDK_TEST_IOAT
114: ${SPDK_TEST_BLOBFS=0}
115export SPDK_TEST_BLOBFS
116: ${SPDK_TEST_VHOST_INIT=0}
117export SPDK_TEST_VHOST_INIT
118: ${SPDK_TEST_LVOL=0}
119export SPDK_TEST_LVOL
120: ${SPDK_TEST_VBDEV_COMPRESS=0}
121export SPDK_TEST_VBDEV_COMPRESS
122: ${SPDK_RUN_ASAN=0}
123export SPDK_RUN_ASAN
124: ${SPDK_RUN_UBSAN=0}
125export SPDK_RUN_UBSAN
126: ${SPDK_RUN_EXTERNAL_DPDK=""}
127export SPDK_RUN_EXTERNAL_DPDK
128: ${SPDK_RUN_NON_ROOT=0}
129export SPDK_RUN_NON_ROOT
130: ${SPDK_TEST_CRYPTO=0}
131export SPDK_TEST_CRYPTO
132: ${SPDK_TEST_FTL=0}
133export SPDK_TEST_FTL
134: ${SPDK_TEST_OCF=0}
135export SPDK_TEST_OCF
136: ${SPDK_TEST_VMD=0}
137export SPDK_TEST_VMD
138: ${SPDK_TEST_OPAL=0}
139export SPDK_TEST_OPAL
140: ${SPDK_TEST_NATIVE_DPDK}
141export SPDK_TEST_NATIVE_DPDK
142: ${SPDK_AUTOTEST_X=true}
143export SPDK_AUTOTEST_X
144: ${SPDK_TEST_URING=0}
145export SPDK_TEST_URING
146: ${SPDK_TEST_USDT=0}
147export SPDK_TEST_USDT
148: ${SPDK_TEST_USE_IGB_UIO:=0}
149export SPDK_TEST_USE_IGB_UIO
150: ${SPDK_TEST_SCHEDULER:=0}
151export SPDK_TEST_SCHEDULER
152: ${SPDK_TEST_SCANBUILD:=0}
153export SPDK_TEST_SCANBUILD
154: ${SPDK_TEST_NVMF_NICS:=}
155export SPDK_TEST_NVMF_NICS
156: ${SPDK_TEST_SMA=0}
157export SPDK_TEST_SMA
158: ${SPDK_TEST_DAOS=0}
159export SPDK_TEST_DAOS
160: ${SPDK_TEST_XNVME:=0}
161export SPDK_TEST_XNVME
162: ${SPDK_TEST_ACCEL:=0}
163export SPDK_TEST_ACCEL
164: ${SPDK_TEST_ACCEL_DSA=0}
165export SPDK_TEST_ACCEL_DSA
166: ${SPDK_TEST_ACCEL_IAA=0}
167export SPDK_TEST_ACCEL_IAA
168# Comma-separated list of fuzzer targets matching test/fuzz/llvm/$target
169: ${SPDK_TEST_FUZZER_TARGET:=}
170export SPDK_TEST_FUZZER_TARGET
171: ${SPDK_TEST_NVMF_MDNS=0}
172export SPDK_TEST_NVMF_MDNS
173: ${SPDK_JSONRPC_GO_CLIENT=0}
174export SPDK_JSONRPC_GO_CLIENT
175: ${SPDK_TEST_SETUP=0}
176export SPDK_TEST_SETUP
177: ${SPDK_TEST_NVME_INTERRUPT=0}
178export SPDK_TEST_NVME_INTERRUPT
179
180# always test with SPDK shared objects.
181export SPDK_LIB_DIR="$rootdir/build/lib"
182export DPDK_LIB_DIR="${SPDK_RUN_EXTERNAL_DPDK:-$rootdir/dpdk/build}/lib"
183export VFIO_LIB_DIR="$rootdir/build/libvfio-user/usr/local/lib"
184export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$SPDK_LIB_DIR:$DPDK_LIB_DIR:$VFIO_LIB_DIR
185
186# Tell setup.sh to wait for block devices upon each reset
187export PCI_BLOCK_SYNC_ON_RESET=yes
188
189# Export PYTHONPATH with addition of RPC framework. New scripts can be created
190# specific use cases for tests.
191export PYTHONPATH=$PYTHONPATH:$rootdir/python
192
193# Don't create Python .pyc files. When running with sudo these will be
194# created with root ownership and can cause problems when cleaning the repository.
195export PYTHONDONTWRITEBYTECODE=1
196
197# Export new_delete_type_mismatch to skip the known bug that exists in librados
198# https://tracker.ceph.com/issues/24078
199export ASAN_OPTIONS=new_delete_type_mismatch=0:disable_coredump=0:abort_on_error=1:use_sigaltstack=0
200export UBSAN_OPTIONS='halt_on_error=1:print_stacktrace=1:abort_on_error=1:disable_coredump=0:exitcode=134'
201
202# Export LeakSanitizer option to use suppression file in order to prevent false positives
203# and known leaks in external executables or libraries from showing up.
204asan_suppression_file="/var/tmp/asan_suppression_file"
205rm -rf "$asan_suppression_file" 2> /dev/null || sudo rm -rf "$asan_suppression_file"
206cat << EOL >> "$asan_suppression_file"
207# ASAN has some bugs around thread_local variables.  We have a destructor in place
208# to free the thread contexts, but ASAN complains about the leak before those
209# destructors have a chance to run.  So suppress this one specific leak using
210# LSAN_OPTIONS.
211leak:spdk_fs_alloc_thread_ctx
212
213# Suppress known leaks in fio project
214leak:$CONFIG_FIO_SOURCE_DIR/parse.c
215leak:$CONFIG_FIO_SOURCE_DIR/iolog.c
216leak:$CONFIG_FIO_SOURCE_DIR/init.c
217leak:$CONFIG_FIO_SOURCE_DIR/filesetup.c
218leak:fio_memalign
219leak:spdk_fio_io_u_init
220# Suppress leaks in gperftools-libs from fio
221leak:libtcmalloc_minimal.so
222
223# Suppress leaks in libiscsi
224leak:libiscsi.so
225
226# Suppress leaks in libcrypto
227# Below is caused by openssl 3.0.8 leaks
228leak:libcrypto.so
229
230# Suppress leaks in accel-config
231# Versions with unresolved leaks:
232# v3.4.6.4 [Fedora 37]
233leak:add_wq
234leak:add_group
235# v3.5.2 [Fedora 38]
236leak:accfg_get_param_str
237# v4.0 [Fedora 39]
238leak:__scandir64_tail
239EOL
240
241# Suppress leaks in libfuse3
242echo "leak:libfuse3.so" >> "$asan_suppression_file"
243
244export LSAN_OPTIONS=suppressions="$asan_suppression_file"
245
246export DEFAULT_RPC_ADDR="/var/tmp/spdk.sock"
247
248if [ -z "${DEPENDENCY_DIR:-}" ]; then
249	export DEPENDENCY_DIR=/var/spdk/dependencies
250else
251	export DEPENDENCY_DIR
252fi
253
254# Export location of where all the SPDK binaries are
255export SPDK_BIN_DIR="$rootdir/build/bin"
256export SPDK_EXAMPLE_DIR="$rootdir/build/examples"
257
258# for vhost, vfio-user tests
259export QEMU_BIN=${QEMU_BIN:-}
260export VFIO_QEMU_BIN=${VFIO_QEMU_BIN:-}
261
262export AR_TOOL=$rootdir/scripts/ar-xnvme-fixer
263
264# For testing nvmes which are attached to some sort of a fanout switch in the CI pool
265export UNBIND_ENTIRE_IOMMU_GROUP=${UNBIND_ENTIRE_IOMMU_GROUP:-no}
266
267_LCOV_MAIN=0
268_LCOV_LLVM=1
269_LCOV=$LCOV_MAIN
270[[ $CC == *clang* || $SPDK_TEST_FUZZER -eq 1 ]] && _LCOV=$_LCOV_LLVM
271
272_lcov_opt[_LCOV_LLVM]="--gcov-tool $rootdir/test/fuzz/llvm/llvm-gcov.sh"
273_lcov_opt[_LCOV_MAIN]=""
274
275lcov_opt=${_lcov_opt[_LCOV]}
276
277# pass our valgrind desire on to unittest.sh
278if [ $SPDK_RUN_VALGRIND -eq 0 ]; then
279	export valgrind=''
280else
281	# unset all DEBUGINFOD_* vars that may affect our valgrind instance
282	unset -v "${!DEBUGINFOD_@}"
283fi
284
285if [ "$(uname -s)" = "Linux" ]; then
286	HUGEMEM=${HUGEMEM:-4096}
287	export CLEAR_HUGE=yes
288
289	MAKE="make"
290	MAKEFLAGS=${MAKEFLAGS:--j$(nproc)}
291elif [ "$(uname -s)" = "FreeBSD" ]; then
292	MAKE="gmake"
293	MAKEFLAGS=${MAKEFLAGS:--j$(sysctl -a | grep -E -i 'hw.ncpu' | awk '{print $2}')}
294	# FreeBSD runs a much more limited set of tests, so keep the default 2GB.
295	HUGEMEM=${HUGEMEM:-2048}
296elif [ "$(uname -s)" = "Windows" ]; then
297	MAKE="make"
298	MAKEFLAGS=${MAKEFLAGS:--j$(nproc)}
299	# Keep the default 2GB for Windows.
300	HUGEMEM=${HUGEMEM:-2048}
301else
302	echo "Unknown OS \"$(uname -s)\""
303	exit 1
304fi
305
306export HUGEMEM=$HUGEMEM
307
308NO_HUGE=()
309TEST_MODE=
310for i in "$@"; do
311	case "$i" in
312		--iso)
313			TEST_MODE=iso
314			;;
315		--transport=*)
316			TEST_TRANSPORT="${i#*=}"
317			;;
318		--sock=*)
319			TEST_SOCK="${i#*=}"
320			;;
321		--no-hugepages)
322			NO_HUGE=(--no-huge -s 1024)
323			;;
324		--interrupt-mode)
325			TEST_INTERRUPT_MODE=1
326			;;
327	esac
328done
329
330# start rpc.py coprocess if it's not started yet
331if [[ -z ${RPC_PIPE_PID:-} ]] || ! kill -0 "$RPC_PIPE_PID" &> /dev/null; then
332	# Include list to all known plugins we use in the tests
333	PYTHONPATH+=":$rootdir/test/rpc_plugins"
334	coproc RPC_PIPE { PYTHONPATH="$PYTHONPATH" "$rootdir/scripts/rpc.py" --server; }
335	exec {RPC_PIPE_OUTPUT}<&${RPC_PIPE[0]} {RPC_PIPE_INPUT}>&${RPC_PIPE[1]}
336	# all descriptors will automatically close together with this bash
337	# process, this will make rpc.py stop reading and exit gracefully
338fi
339
340function set_test_storage() {
341	[[ -v testdir ]] || return 0
342
343	local requested_size=$1 # bytes
344	local mount target_dir
345
346	local -A mounts fss sizes avails uses
347	local source fs size avail mount use
348
349	local storage_fallback storage_candidates
350
351	storage_fallback=$(mktemp -udt spdk.XXXXXX)
352	storage_candidates=(
353		"$testdir"
354		"$storage_fallback/tests/${testdir##*/}"
355		"$storage_fallback"
356	)
357
358	if [[ -n ${ADD_TEST_STORAGE:-} ]]; then
359		# List of dirs|mounts separated by whitespaces
360		storage_candidates+=($ADD_TEST_STORAGE)
361	fi
362
363	if [[ -n ${DEDICATED_TEST_STORAGE:-} ]]; then
364		# Single, dedicated dir|mount
365		storage_candidates=("$DEDICATED_TEST_STORAGE")
366	fi
367
368	mkdir -p "${storage_candidates[@]}"
369
370	# add some headroom - 64M
371	requested_size=$((requested_size + (64 << 20)))
372
373	while read -r source fs size use avail _ mount; do
374		mounts["$mount"]=$source fss["$mount"]=$fs
375		avails["$mount"]=$((avail * 1024)) sizes["$mount"]=$((size * 1024))
376		uses["$mount"]=$((use * 1024))
377	done < <(df -T | grep -v Filesystem)
378
379	printf '* Looking for test storage...\n' >&2
380
381	local target_space new_size
382	for target_dir in "${storage_candidates[@]}"; do
383		# FreeBSD's df is lacking the --output arg
384		# mount=$(df --output=target "$target_dir" | grep -v "Mounted on")
385		mount=$(df "$target_dir" | awk '$1 !~ /Filesystem/{print $6}')
386
387		target_space=${avails["$mount"]}
388		if ((target_space == 0 || target_space < requested_size)); then
389			continue
390		fi
391		if ((target_space >= requested_size)); then
392			# For in-memory fs, and / make sure our requested size won't fill most of the space.
393			if [[ ${fss["$mount"]} == tmpfs ]] || [[ ${fss["$mount"]} == ramfs ]] || [[ $mount == / ]]; then
394				new_size=$((uses["$mount"] + requested_size))
395				if ((new_size * 100 / sizes["$mount"] > 95)); then
396					continue
397				fi
398			fi
399		fi
400		export SPDK_TEST_STORAGE=$target_dir
401		printf '* Found test storage at %s\n' "$SPDK_TEST_STORAGE" >&2
402		return 0
403	done
404	printf '* Test storage is not available\n'
405	return 1
406}
407
408function get_config_params() {
409	xtrace_disable
410	config_params='--enable-debug --enable-werror'
411
412	# for options with dependencies but no test flag, set them here
413	if [ -f /usr/include/infiniband/verbs.h ]; then
414		config_params+=' --with-rdma'
415	fi
416
417	if [ $SPDK_TEST_USDT -eq 1 ]; then
418		config_params+=" --with-usdt"
419	fi
420
421	case "$(uname -s)" in
422		FreeBSD) [[ $(sysctl -n hw.model) == Intel* ]] ;;
423		Linux) [[ $(< /proc/cpuinfo) == *GenuineIntel* ]] ;;
424		*) false ;;
425	esac && config_params+=" --with-idxd" || config_params+=" --without-idxd"
426
427	if [[ -d $CONFIG_FIO_SOURCE_DIR ]]; then
428		config_params+=" --with-fio=$CONFIG_FIO_SOURCE_DIR"
429	fi
430
431	if [ -d ${DEPENDENCY_DIR}/vtune_codes ]; then
432		config_params+=' --with-vtune='${DEPENDENCY_DIR}'/vtune_codes'
433	fi
434
435	if [ -d /usr/include/iscsi ]; then
436		[[ $(< /usr/include/iscsi/iscsi.h) =~ "define LIBISCSI_API_VERSION ("([0-9]+)")" ]] \
437			&& libiscsi_version=${BASH_REMATCH[1]}
438		if ((libiscsi_version >= 20150621)); then
439			config_params+=' --with-iscsi-initiator'
440		fi
441	fi
442
443	if [[ $SPDK_TEST_UNITTEST -eq 0 &&
444		$SPDK_TEST_SCANBUILD -eq 0 && -z ${SPDK_TEST_AUTOBUILD:-} ]]; then
445		config_params+=' --disable-unit-tests'
446	fi
447
448	if [ -f /usr/include/libpmem.h ] && [ $SPDK_TEST_VBDEV_COMPRESS -eq 1 ]; then
449		if ge "$(nasm --version | awk '{print $3}')" 2.14 && [[ $SPDK_TEST_ISAL -eq 1 ]]; then
450			config_params+=' --with-vbdev-compress --with-dpdk-compressdev'
451		fi
452	fi
453
454	if [ -d /usr/include/rbd ] && [ -d /usr/include/rados ] && [ $SPDK_TEST_RBD -eq 1 ]; then
455		config_params+=' --with-rbd'
456	fi
457
458	# for options with no required dependencies, just test flags, set them here
459	if [ $SPDK_TEST_CRYPTO -eq 1 ]; then
460		config_params+=' --with-crypto'
461	fi
462
463	if [ $SPDK_TEST_OCF -eq 1 ]; then
464		config_params+=" --with-ocf"
465	fi
466
467	if [ $SPDK_RUN_UBSAN -eq 1 ]; then
468		config_params+=' --enable-ubsan'
469	fi
470
471	if [ $SPDK_RUN_ASAN -eq 1 ]; then
472		config_params+=' --enable-asan'
473	fi
474
475	config_params+=' --enable-coverage'
476
477	if [ $SPDK_TEST_BLOBFS -eq 1 ]; then
478		if [[ -d /usr/include/fuse3 ]] || [[ -d /usr/local/include/fuse3 ]]; then
479			config_params+=' --with-fuse'
480		fi
481	fi
482
483	if [[ -f /usr/include/liburing/io_uring.h && -f /usr/include/linux/ublk_cmd.h ]]; then
484		config_params+=' --with-ublk'
485	fi
486
487	if [ $SPDK_TEST_RAID -eq 1 ]; then
488		config_params+=' --with-raid5f'
489	fi
490
491	if [ $SPDK_TEST_VFIOUSER -eq 1 ] || [ $SPDK_TEST_VFIOUSER_QEMU -eq 1 ] || [ $SPDK_TEST_SMA -eq 1 ]; then
492		config_params+=' --with-vfio-user'
493	fi
494
495	# Check whether liburing library header exists
496	if [ -f /usr/include/liburing/io_uring.h ] && [ $SPDK_TEST_URING -eq 1 ]; then
497		config_params+=' --with-uring'
498	fi
499
500	if [ -n "${SPDK_RUN_EXTERNAL_DPDK:-}" ]; then
501		config_params+=" --with-dpdk=$SPDK_RUN_EXTERNAL_DPDK"
502	fi
503
504	if [[ $SPDK_TEST_SMA -eq 1 ]]; then
505		config_params+=' --with-sma'
506		config_params+=' --with-crypto'
507	fi
508
509	if [ -f /usr/include/daos.h ] && [ $SPDK_TEST_DAOS -eq 1 ]; then
510		config_params+=' --with-daos'
511	fi
512
513	# Make the xnvme module available for the tests
514	if [[ $SPDK_TEST_XNVME -eq 1 ]]; then
515		config_params+=' --with-xnvme'
516	fi
517
518	if [[ $SPDK_TEST_FUZZER -eq 1 ]]; then
519		config_params+=" $(get_fuzzer_target_config)"
520	fi
521
522	if [[ $SPDK_TEST_NVMF_MDNS -eq 1 ]]; then
523		config_params+=' --with-avahi'
524	fi
525
526	if [[ $SPDK_JSONRPC_GO_CLIENT -eq 1 ]]; then
527		config_params+=' --with-golang'
528	fi
529
530	echo "$config_params"
531	xtrace_restore
532}
533
534function get_fuzzer_target_config() {
535	local -A fuzzer_targets_to_config=()
536	local config target
537
538	fuzzer_targets_to_config["vfio"]="--with-vfio-user"
539	for target in $(get_fuzzer_targets); do
540		[[ -n ${fuzzer_targets_to_config["$target"]:-} ]] || continue
541		config+=("${fuzzer_targets_to_config["$target"]}")
542	done
543
544	if ((${#config[@]} > 0)); then
545		echo "${config[*]}"
546	fi
547}
548
549function get_fuzzer_targets() {
550	local fuzzers=()
551
552	if [[ -n ${SPDK_TEST_FUZZER_TARGET:-} ]]; then
553		IFS="," read -ra fuzzers <<< "$SPDK_TEST_FUZZER_TARGET"
554	else
555		fuzzers=("$rootdir/test/fuzz/llvm/"*)
556		fuzzers=("${fuzzers[@]##*/}")
557	fi
558
559	echo "${fuzzers[*]}"
560}
561
562function rpc_cmd() {
563	xtrace_disable
564	local rsp rc=1
565	local stdin cmd cmds_number=0 status_number=0 status
566
567	if (($#)); then
568		cmds_number=1
569		echo "$@" >&$RPC_PIPE_INPUT
570	elif [[ ! -t 0 ]]; then
571		mapfile -t stdin <&0
572		cmds_number=${#stdin[@]}
573		printf '%s\n' "${stdin[@]}" >&$RPC_PIPE_INPUT
574	else
575		return 0
576	fi
577
578	while read -t "${RPC_PIPE_TIMEOUT:-15}" -ru $RPC_PIPE_OUTPUT rsp; do
579		if [[ $rsp == "**STATUS="* ]]; then
580			status[${rsp#*=}]=$rsp
581			if ((++status_number == cmds_number)); then
582				break
583			fi
584			continue
585		fi
586		echo "$rsp"
587	done
588
589	rc=${!status[*]}
590	xtrace_restore
591	[[ $rc == 0 ]]
592}
593
594function rpc_cmd_simple_data_json() {
595
596	local elems="$1[@]" elem
597	local -gA jq_out=()
598	local jq val
599
600	local lvs=(
601		"uuid"
602		"name"
603		"base_bdev"
604		"total_data_clusters"
605		"free_clusters"
606		"block_size"
607		"cluster_size"
608	)
609
610	local bdev=(
611		"name"
612		"aliases[0]"
613		"block_size"
614		"num_blocks"
615		"uuid"
616		"product_name"
617		"supported_io_types.read"
618		"supported_io_types.write"
619		"driver_specific.lvol.clone"
620		"driver_specific.lvol.base_snapshot"
621		"driver_specific.lvol.esnap_clone"
622		"driver_specific.lvol.external_snapshot_name"
623	)
624
625	[[ -v $elems ]] || return 1
626
627	for elem in "${!elems}"; do
628		jq="${jq:+$jq,\"\\n\",}\"$elem\",\" \",.[0].$elem"
629	done
630	jq+=',"\n"'
631
632	shift
633	while read -r elem val; do
634		jq_out["$elem"]=$val
635	done < <(rpc_cmd "$@" | jq -jr "$jq")
636	((${#jq_out[@]} > 0)) || return 1
637}
638
639function valid_exec_arg() {
640	local arg=$1
641	# First argument must be the executable so do some basic sanity checks first. For bash, this
642	# covers two basic cases where es == 126 || es == 127 so catch them early on and fail hard
643	# if needed.
644	case "$(type -t "$arg")" in
645		builtin | function) ;;
646		file) arg=$(type -P "$arg") && [[ -x $arg ]] ;;
647		*) return 1 ;;
648	esac
649}
650
651function NOT() {
652	local es=0
653
654	valid_exec_arg "$@" || return 1
655	"$@" || es=$?
656
657	# Logic looks like so:
658	#  - return false if command exit successfully
659	#  - return false if command exit after receiving a core signal (FIXME: or any signal?)
660	#  - return true if command exit with an error
661
662	# This naively assumes that the process doesn't exit with > 128 on its own.
663	if ((es > 128)); then
664		es=$((es & ~128))
665		case "$es" in
666			3) ;&       # SIGQUIT
667			4) ;&       # SIGILL
668			6) ;&       # SIGABRT
669			8) ;&       # SIGFPE
670			9) ;&       # SIGKILL
671			11) es=0 ;; # SIGSEGV
672			*) es=1 ;;
673		esac
674	elif [[ -n ${EXIT_STATUS:-} ]] && ((es != EXIT_STATUS)); then
675		es=0
676	fi
677
678	# invert error code of any command and also trigger ERR on 0 (unlike bash ! prefix)
679	((!es == 0))
680}
681
682function timing() {
683	direction="$1"
684	testname="$2"
685
686	now=$(date +%s)
687
688	if [ "$direction" = "enter" ]; then
689		export timing_stack="${timing_stack:-};${now}"
690		export test_stack="${test_stack:-};${testname}"
691	else
692		touch "$output_dir/timing.txt"
693		child_time=$(grep "^${test_stack:1};" $output_dir/timing.txt | awk '{s+=$2} END {print s}')
694
695		start_time=$(echo "$timing_stack" | sed -e 's@^.*;@@')
696		timing_stack=$(echo "$timing_stack" | sed -e 's@;[^;]*$@@')
697
698		elapsed=$((now - start_time - child_time))
699		echo "${test_stack:1} $elapsed" >> $output_dir/timing.txt
700
701		test_stack=$(echo "$test_stack" | sed -e 's@;[^;]*$@@')
702	fi
703}
704
705function timing_cmd() (
706	# The use-case here is this: ts=$(timing_cmd echo bar). Since stdout is always redirected
707	# to a pipe handling the $(), lookup the stdin's device and determine if it's sane to send
708	# cmd's output to it. If not, just null it.
709	local cmd_es=$?
710
711	[[ -t 0 ]] && exec {cmd_out}>&0 || exec {cmd_out}> /dev/null
712
713	local time=0 TIMEFORMAT=%2R # seconds
714
715	# We redirect cmd's std{out,err} to a separate fd dup'ed to stdin's device (or /dev/null) to
716	# catch only output from the time builtin - output from the actual cmd would be still visible,
717	# but $() will return just the time's data, hence making it possible to just do:
718	#  time_of_super_verbose_cmd=$(timing_cmd super_verbose_cmd)
719	time=$({ time "$@" >&"$cmd_out" 2>&1; } 2>&1) || cmd_es=$?
720	echo "$time"
721
722	return "$cmd_es"
723)
724
725function timing_enter() {
726	xtrace_disable
727	timing "enter" "$1"
728	xtrace_restore
729}
730
731function timing_exit() {
732	xtrace_disable
733	timing "exit" "$1"
734	xtrace_restore
735}
736
737function timing_finish() {
738	[[ -e $output_dir/timing.txt ]] || return 0
739
740	flamegraph='/usr/local/FlameGraph/flamegraph.pl'
741	[[ -x "$flamegraph" ]] || return 1
742
743	"$flamegraph" \
744		--title 'Build Timing' \
745		--nametype 'Step:' \
746		--countname seconds \
747		"$output_dir/timing.txt" \
748		> "$output_dir/timing.svg"
749}
750
751function create_test_list() {
752	xtrace_disable
753	# First search all scripts in main SPDK directory.
754	completion=$(grep -shI -d skip --include="*.sh" -e "run_test " $rootdir/*)
755	# Follow up with search in test directory recursively.
756	completion+=$'\n'$(grep -rshI --include="*.sh" --exclude="*autotest_common.sh" -e "run_test " $rootdir/test)
757	printf "%s" "$completion" | grep -v "#" \
758		| sed 's/^.*run_test/run_test/' | awk '{print $2}' \
759		| sed 's/\"//g' | sort > $output_dir/all_tests.txt || true
760	xtrace_restore
761}
762
763function gdb_attach() {
764	gdb -q --batch \
765		-ex 'handle SIGHUP nostop pass' \
766		-ex 'handle SIGQUIT nostop pass' \
767		-ex 'handle SIGPIPE nostop pass' \
768		-ex 'handle SIGALRM nostop pass' \
769		-ex 'handle SIGTERM nostop pass' \
770		-ex 'handle SIGUSR1 nostop pass' \
771		-ex 'handle SIGUSR2 nostop pass' \
772		-ex 'handle SIGCHLD nostop pass' \
773		-ex 'set print thread-events off' \
774		-ex 'cont' \
775		-ex 'thread apply all bt' \
776		-ex 'quit' \
777		--tty=/dev/stdout \
778		-p $1
779}
780
781function process_core() {
782	# Note that this always was racy as we can't really sync with the kernel
783	# to see if there's any core queued up for writing. We could check if
784	# collector is running and wait for it explicitly, but it doesn't seem
785	# to be worth the effort. So assume that if we are being called via
786	# trap, as in, when some error has occurred, wait up to 10s for any
787	# potential cores. If we are called just for cleanup at the very end,
788	# don't wait since all the tests ended successfully, hence having any
789	# critical cores lying around is unlikely.
790	((autotest_es != 0)) && sleep 10
791
792	local coredumps core
793
794	coredumps=("$output_dir/coredumps/"*.bt.txt)
795
796	((${#coredumps[@]} > 0)) || return 0
797	chmod -R a+r "$output_dir/coredumps"
798
799	for core in "${coredumps[@]}"; do
800		cat <<- BT
801			##### CORE BT ${core##*/} #####
802
803			$(< "$core")
804
805			--
806		BT
807	done
808	return 1
809}
810
811function process_shm() {
812	type=$1
813	id=$2
814	if [ "$type" = "--pid" ]; then
815		id="pid${id}"
816	fi
817
818	shm_files=$(find /dev/shm -name "*.${id}" -printf "%f\n")
819
820	if [[ -z ${shm_files:-} ]]; then
821		echo "SHM File for specified PID or shared memory id: ${id} not found!"
822		return 1
823	fi
824	for n in $shm_files; do
825		tar -C /dev/shm/ -cvzf $output_dir/${n}_shm.tar.gz ${n}
826	done
827	return 0
828}
829
830# Parameters:
831# $1 - process pid
832# $2 - rpc address (optional)
833# $3 - max retries (optional)
834function waitforlisten() {
835	if [ -z "${1:-}" ]; then
836		exit 1
837	fi
838
839	local rpc_addr="${2:-$DEFAULT_RPC_ADDR}"
840	local max_retries=${3:-100}
841
842	echo "Waiting for process to start up and listen on UNIX domain socket $rpc_addr..."
843	# turn off trace for this loop
844	xtrace_disable
845	local ret=0
846	local i
847	for ((i = max_retries; i != 0; i--)); do
848		# if the process is no longer running, then exit the script
849		#  since it means the application crashed
850		if ! kill -s 0 $1; then
851			echo "ERROR: process (pid: $1) is no longer running"
852			ret=1
853			break
854		fi
855
856		if $rootdir/scripts/rpc.py -t 1 -s "$rpc_addr" rpc_get_methods &> /dev/null; then
857			break
858		fi
859
860		sleep 0.5
861	done
862
863	xtrace_restore
864	if ((i == 0)); then
865		echo "ERROR: timeout while waiting for process (pid: $1) to start listening on '$rpc_addr'"
866		ret=1
867	fi
868	return $ret
869}
870
871function waitfornbd() {
872	local nbd_name=$1
873	local i
874
875	for ((i = 1; i <= 20; i++)); do
876		if grep -q -w $nbd_name /proc/partitions; then
877			break
878		else
879			sleep 0.1
880		fi
881	done
882
883	# The nbd device is now recognized as a block device, but there can be
884	#  a small delay before we can start I/O to that block device.  So loop
885	#  here trying to read the first block of the nbd block device to a temp
886	#  file.  Note that dd returns success when reading an empty file, so we
887	#  need to check the size of the output file instead.
888	for ((i = 1; i <= 20; i++)); do
889		dd if=/dev/$nbd_name of="$SPDK_TEST_STORAGE/nbdtest" bs=4096 count=1 iflag=direct
890		size=$(stat -c %s "$SPDK_TEST_STORAGE/nbdtest")
891		rm -f "$SPDK_TEST_STORAGE/nbdtest"
892		if [ "$size" != "0" ]; then
893			return 0
894		else
895			sleep 0.1
896		fi
897	done
898
899	return 1
900}
901
902function waitforbdev() {
903	local bdev_name=$1
904	local bdev_timeout=$2
905	local i
906	[[ -z ${bdev_timeout:-} ]] && bdev_timeout=2000 # ms
907
908	$rpc_py bdev_wait_for_examine
909
910	if $rpc_py bdev_get_bdevs -b $bdev_name -t $bdev_timeout; then
911		return 0
912	fi
913
914	return 1
915}
916
917function waitforcondition() {
918	local cond=$1
919	local max=${2:-10}
920	while ((max--)); do
921		if eval $cond; then
922			return 0
923		fi
924		sleep 1
925	done
926	return 1
927}
928
929function make_filesystem() {
930	local fstype=$1
931	local dev_name=$2
932	local i=0
933	local force
934
935	if [ $fstype = ext4 ]; then
936		force=-F
937	else
938		force=-f
939	fi
940
941	while ! mkfs.${fstype} $force ${dev_name}; do
942		if [ $i -ge 15 ]; then
943			return 1
944		fi
945		i=$((i + 1))
946		sleep 1
947	done
948
949	return 0
950}
951
952function killprocess() {
953	# $1 = process pid
954	if [ -z "${1:-}" ]; then
955		return 1
956	fi
957
958	if kill -0 $1; then
959		if [ $(uname) = Linux ]; then
960			process_name=$(ps --no-headers -o comm= $1)
961		else
962			process_name=$(ps -c -o command $1 | tail -1)
963		fi
964		if [ "$process_name" = "sudo" ]; then
965			# kill the child process, which is the actual app
966			# (assume $1 has just one child)
967			local child
968			child="$(pgrep -P $1)"
969			echo "killing process with pid $child"
970			kill $child
971		else
972			echo "killing process with pid $1"
973			kill $1
974		fi
975
976		# wait for the process regardless if its the dummy sudo one
977		# or the actual app - it should terminate anyway
978		wait $1
979	else
980		# the process is not there anymore
981		echo "Process with pid $1 is not found"
982	fi
983}
984
985function iscsicleanup() {
986	echo "Cleaning up iSCSI connection"
987	iscsiadm -m node --logout || true
988	iscsiadm -m node -o delete || true
989	rm -rf /var/lib/iscsi/nodes/*
990}
991
992function stop_iscsi_service() {
993	if cat /etc/*-release | grep Ubuntu; then
994		service open-iscsi stop
995	else
996		service iscsid stop
997	fi
998}
999
1000function start_iscsi_service() {
1001	if cat /etc/*-release | grep Ubuntu; then
1002		service open-iscsi start
1003	else
1004		service iscsid start
1005	fi
1006}
1007
1008function rbd_setup() {
1009	# $1 = monitor ip address
1010	# $2 = name of the namespace
1011	if [ -z "${1:-}" ]; then
1012		echo "No monitor IP address provided for ceph"
1013		exit 1
1014	fi
1015	if [ -n "${2:-}" ]; then
1016		if ip netns list | grep "$2"; then
1017			NS_CMD="ip netns exec $2"
1018		else
1019			echo "No namespace $2 exists"
1020			exit 1
1021		fi
1022	fi
1023
1024	if hash ceph; then
1025		export PG_NUM=128
1026		export RBD_POOL=rbd
1027		export RBD_NAME=foo
1028		$NS_CMD $rootdir/scripts/ceph/stop.sh || true
1029		$NS_CMD $rootdir/scripts/ceph/start.sh $1
1030
1031		$NS_CMD ceph osd pool create $RBD_POOL $PG_NUM || true
1032		$NS_CMD rbd create $RBD_NAME --size 1000
1033	fi
1034}
1035
1036function rbd_cleanup() {
1037	if hash ceph; then
1038		$rootdir/scripts/ceph/stop.sh || true
1039		rm -f /var/tmp/ceph_raw.img
1040	fi
1041}
1042
1043function daos_setup() {
1044	# $1 = pool name
1045	# $2 = cont name
1046	if [ -z "${1:-}" ]; then
1047		echo "No pool name provided"
1048		exit 1
1049	fi
1050	if [ -z "${2:-}" ]; then
1051		echo "No cont name provided"
1052		exit 1
1053	fi
1054
1055	dmg pool create --size=10G $1 || true
1056	daos container create --type=POSIX --label=$2 $1 || true
1057}
1058
1059function daos_cleanup() {
1060	local pool=${1:-testpool}
1061	local cont=${2:-testcont}
1062
1063	daos container destroy -f $pool $cont || true
1064	sudo dmg pool destroy -f $pool || true
1065}
1066
1067function _start_stub() {
1068	# Disable ASLR for multi-process testing.  SPDK does support using DPDK multi-process,
1069	# but ASLR can still be unreliable in some cases.
1070	# We will re-enable it again after multi-process testing is complete in kill_stub().
1071	# Save current setting so it can be restored upon calling kill_stub().
1072	_randomize_va_space=$(< /proc/sys/kernel/randomize_va_space)
1073	echo 0 > /proc/sys/kernel/randomize_va_space
1074	$rootdir/test/app/stub/stub $1 &
1075	stubpid=$!
1076	echo Waiting for stub to ready for secondary processes...
1077	while ! [ -e /var/run/spdk_stub0 ]; do
1078		# If stub dies while we wait, bail
1079		[[ -e /proc/$stubpid ]] || return 1
1080		sleep 1s
1081	done
1082	echo done.
1083}
1084
1085function start_stub() {
1086	if ! _start_stub "$@"; then
1087		echo "stub failed" >&2
1088		return 1
1089	fi
1090}
1091
1092function kill_stub() {
1093	if [[ -e /proc/$stubpid ]]; then
1094		kill $1 $stubpid
1095		wait $stubpid
1096	fi 2> /dev/null || :
1097	rm -f /var/run/spdk_stub0
1098	# Re-enable ASLR now that we are done with multi-process testing
1099	# Note: "1" enables ASLR w/o randomizing data segments, "2" adds data segment
1100	#  randomizing and is the default on all recent Linux kernels
1101	echo "${_randomize_va_space:-2}" > /proc/sys/kernel/randomize_va_space
1102}
1103
1104function run_test() {
1105	if [ $# -le 1 ]; then
1106		echo "Not enough parameters"
1107		echo "usage: run_test test_name test_script [script_params]"
1108		exit 1
1109	fi
1110
1111	xtrace_disable
1112	local test_name="$1" pid
1113	shift
1114
1115	if [ -n "${test_domain:-}" ]; then
1116		export test_domain="${test_domain}.${test_name}"
1117	else
1118		export test_domain="$test_name"
1119	fi
1120
1121	# Signal our daemons to update the test tag
1122	update_tag_monitor_resources "$test_domain"
1123
1124	timing_enter $test_name
1125	echo "************************************"
1126	echo "START TEST $test_name"
1127	echo "************************************"
1128	xtrace_restore
1129	time "$@"
1130	xtrace_disable
1131	echo "************************************"
1132	echo "END TEST $test_name"
1133	echo "************************************"
1134	timing_exit $test_name
1135
1136	export test_domain=${test_domain%"$test_name"}
1137	if [ -n "$test_domain" ]; then
1138		export test_domain=${test_domain%?}
1139	fi
1140
1141	if [ -z "${test_domain:-}" ]; then
1142		echo "top_level $test_name" >> $output_dir/test_completions.txt
1143	else
1144		echo "$test_domain $test_name" >> $output_dir/test_completions.txt
1145	fi
1146	xtrace_restore
1147}
1148
1149function skip_run_test_with_warning() {
1150	echo "WARNING: $1"
1151	echo "Test run may fail if run with autorun.sh"
1152	echo "Please check your $rootdir/test/common/skipped_tests.txt"
1153}
1154
1155function print_backtrace() {
1156	# if errexit is not enabled, don't print a backtrace
1157	[[ "$-" =~ e ]] || return 0
1158
1159	local args=("${BASH_ARGV[@]}")
1160
1161	xtrace_disable
1162	# Reset IFS in case we were called from an environment where it was modified
1163	IFS=" "$'\t'$'\n'
1164	echo "========== Backtrace start: =========="
1165	echo ""
1166	for ((i = 1; i < ${#FUNCNAME[@]}; i++)); do
1167		local func="${FUNCNAME[$i]}"
1168		local line_nr="${BASH_LINENO[$((i - 1))]}"
1169		local src="${BASH_SOURCE[$i]}"
1170		local bt="" cmdline=()
1171
1172		if [[ -f $src ]]; then
1173			bt=$(nl -w 4 -ba -nln $src | grep -B 5 -A 5 "^${line_nr}[^0-9]" \
1174				| sed "s/^/   /g" | sed "s/^   $line_nr /=> $line_nr /g")
1175		fi
1176
1177		# If extdebug set the BASH_ARGC[i], try to fetch all the args
1178		if ((BASH_ARGC[i] > 0)); then
1179			# Use argc as index to reverse the stack
1180			local argc=${BASH_ARGC[i]} arg
1181			for arg in "${args[@]::BASH_ARGC[i]}"; do
1182				cmdline[argc--]="[\"$arg\"]"
1183			done
1184			args=("${args[@]:BASH_ARGC[i]}")
1185		fi
1186
1187		echo "in $src:$line_nr -> $func($(
1188			IFS=","
1189			printf '%s\n' "${cmdline[*]:-[]}"
1190		))"
1191		echo "     ..."
1192		echo "${bt:-backtrace unavailable}"
1193		echo "     ..."
1194	done
1195	echo ""
1196	echo "========== Backtrace end =========="
1197	xtrace_restore
1198	return 0
1199}
1200
1201function waitforserial() {
1202	local i=0
1203	local nvme_device_counter=1 nvme_devices=0
1204	if [[ -n "${2:-}" ]]; then
1205		nvme_device_counter=$2
1206	fi
1207
1208	# Wait initially for min 2s to make sure all devices are ready for use.
1209	sleep 2
1210	while ((i++ <= 15)); do
1211		nvme_devices=$(lsblk -l -o NAME,SERIAL | grep -c "$1")
1212		((nvme_devices == nvme_device_counter)) && return 0
1213		if ((nvme_devices > nvme_device_counter)); then
1214			echo "$nvme_device_counter device(s) expected, found $nvme_devices" >&2
1215		fi
1216		echo "Waiting for devices"
1217		sleep 1
1218	done
1219	return 1
1220}
1221
1222function waitforserial_disconnect() {
1223	local i=0
1224	while lsblk -o NAME,SERIAL | grep -q -w $1; do
1225		[ $i -lt 15 ] || break
1226		i=$((i + 1))
1227		echo "Waiting for disconnect devices"
1228		sleep 1
1229	done
1230
1231	if lsblk -l -o NAME,SERIAL | grep -q -w $1; then
1232		return 1
1233	fi
1234
1235	return 0
1236}
1237
1238function waitforblk() {
1239	local i=0
1240	while ! lsblk -l -o NAME | grep -q -w $1; do
1241		[ $i -lt 15 ] || break
1242		i=$((i + 1))
1243		sleep 1
1244	done
1245
1246	if ! lsblk -l -o NAME | grep -q -w $1; then
1247		return 1
1248	fi
1249
1250	return 0
1251}
1252
1253function waitforblk_disconnect() {
1254	local i=0
1255	while lsblk -l -o NAME | grep -q -w $1; do
1256		[ $i -lt 15 ] || break
1257		i=$((i + 1))
1258		sleep 1
1259	done
1260
1261	if lsblk -l -o NAME | grep -q -w $1; then
1262		return 1
1263	fi
1264
1265	return 0
1266}
1267
1268function waitforfile() {
1269	local i=0
1270	while [ ! -e $1 ]; do
1271		[ $i -lt 200 ] || break
1272		i=$((i + 1))
1273		sleep 0.1
1274	done
1275
1276	if [ ! -e $1 ]; then
1277		return 1
1278	fi
1279
1280	return 0
1281}
1282
1283function fio_config_gen() {
1284	local config_file=$1
1285	local workload=$2
1286	local bdev_type=$3
1287	local env_context=$4
1288	local fio_dir=$CONFIG_FIO_SOURCE_DIR
1289
1290	if [ -e "$config_file" ]; then
1291		echo "Configuration File Already Exists!: $config_file"
1292		return 1
1293	fi
1294
1295	if [ -z "${workload:-}" ]; then
1296		workload=randrw
1297	fi
1298
1299	if [ -n "$env_context" ]; then
1300		env_context="env_context=$env_context"
1301	fi
1302
1303	touch $1
1304
1305	cat > $1 << EOL
1306[global]
1307thread=1
1308$env_context
1309group_reporting=1
1310direct=1
1311norandommap=1
1312percentile_list=50:99:99.9:99.99:99.999
1313time_based=1
1314ramp_time=0
1315EOL
1316
1317	if [ "$workload" == "verify" ]; then
1318		cat <<- EOL >> $config_file
1319			verify=sha1
1320			verify_backlog=1024
1321			rw=randwrite
1322		EOL
1323
1324		# To avoid potential data race issue due to the AIO device
1325		# flush mechanism, add the flag to serialize the writes.
1326		# This is to fix the intermittent IO failure issue of #935
1327		if [ "$bdev_type" == "AIO" ]; then
1328			if [[ $($fio_dir/fio --version) == *"fio-3"* ]]; then
1329				echo "serialize_overlap=1" >> $config_file
1330			fi
1331		fi
1332	elif [ "$workload" == "trim" ]; then
1333		echo "rw=trimwrite" >> $config_file
1334	else
1335		echo "rw=$workload" >> $config_file
1336	fi
1337}
1338
1339function fio_plugin() {
1340	# Setup fio binary cmd line
1341	local fio_dir=$CONFIG_FIO_SOURCE_DIR
1342	# gcc and clang uses different sanitizer libraries
1343	local sanitizers=(libasan libclang_rt.asan)
1344	local plugin=$1
1345	shift
1346
1347	local asan_lib=
1348	for sanitizer in "${sanitizers[@]}"; do
1349		asan_lib=$(ldd $plugin | grep $sanitizer | awk '{print $3}')
1350		if [[ -n "${asan_lib:-}" ]]; then
1351			break
1352		fi
1353	done
1354
1355	# Preload the sanitizer library to fio if fio_plugin was compiled with it
1356	LD_PRELOAD="$asan_lib $plugin" "$fio_dir"/fio "$@"
1357}
1358
1359function fio_bdev() {
1360	fio_plugin "$rootdir/build/fio/spdk_bdev" "$@"
1361}
1362
1363function fio_nvme() {
1364	fio_plugin "$rootdir/build/fio/spdk_nvme" "$@"
1365}
1366
1367function get_lvs_free_mb() {
1368	local lvs_uuid=$1
1369	local lvs_info
1370	local fc
1371	local cs
1372	lvs_info=$($rpc_py bdev_lvol_get_lvstores)
1373	fc=$(jq ".[] | select(.uuid==\"$lvs_uuid\") .free_clusters" <<< "$lvs_info")
1374	cs=$(jq ".[] | select(.uuid==\"$lvs_uuid\") .cluster_size" <<< "$lvs_info")
1375
1376	# Change to MB's
1377	free_mb=$((fc * cs / 1024 / 1024))
1378	echo "$free_mb"
1379}
1380
1381function get_bdev_size() {
1382	local bdev_name=$1
1383	local bdev_info
1384	local bs
1385	local nb
1386	bdev_info=$($rpc_py bdev_get_bdevs -b $bdev_name)
1387	bs=$(jq ".[] .block_size" <<< "$bdev_info")
1388	nb=$(jq ".[] .num_blocks" <<< "$bdev_info")
1389
1390	# Change to MB's
1391	bdev_size=$((bs * nb / 1024 / 1024))
1392	echo "$bdev_size"
1393}
1394
1395function autotest_cleanup() {
1396	local autotest_es=$?
1397	xtrace_disable
1398
1399	# Slurp at_app_exit() so we can kill all lingering vhost and qemu processes
1400	# in one swing. We do this in a subshell as vhost/common.sh is too eager to
1401	# do some extra work which we don't care about in this context.
1402	# shellcheck source=/dev/null
1403	vhost_reap() (source "$rootdir/test/vhost/common.sh" &> /dev/null || return 0 && at_app_exit)
1404
1405	# catch any stray core files and kill all remaining SPDK processes. Update
1406	# autotest_es in case autotest reported success but cores and/or processes
1407	# were left behind regardless.
1408
1409	process_core || autotest_es=1
1410	reap_spdk_processes || autotest_es=1
1411	vhost_reap || autotest_es=1
1412
1413	$rootdir/scripts/setup.sh reset
1414	$rootdir/scripts/setup.sh cleanup
1415	if [ $(uname -s) = "Linux" ]; then
1416		modprobe -r uio_pci_generic
1417	fi
1418	rm -rf "$asan_suppression_file"
1419	if [[ -n ${old_core_pattern:-} ]]; then
1420		echo "$old_core_pattern" > /proc/sys/kernel/core_pattern
1421	fi
1422	if [[ -e /proc/$udevadm_pid/status ]]; then
1423		kill "$udevadm_pid" || :
1424	fi
1425
1426	local storage_fallback_purge=("${TMPDIR:-/tmp}/spdk."??????)
1427
1428	if ((${#storage_fallback_purge[@]} > 0)); then
1429		rm -rf "${storage_fallback_purge[@]}"
1430	fi
1431
1432	if ((autotest_es)); then
1433		if [[ $(uname) == FreeBSD ]]; then
1434			ps aux
1435		elif [[ $(uname) == Linux ]]; then
1436			# Get more detailed view
1437			grep . /proc/[0-9]*/status
1438			# Dump some extra info into kernel log
1439			echo "######## Autotest Cleanup Dump ########" > /dev/kmsg
1440			# Show cpus backtraces
1441			echo l > /proc/sysrq-trigger
1442			# Show mem usage
1443			echo m > /proc/sysrq-trigger
1444			# show blocked tasks
1445			echo w > /proc/sysrq-trigger
1446
1447		fi > "$output_dir/proc_list.txt" 2>&1 || :
1448	fi
1449
1450	stop_monitor_resources
1451
1452	xtrace_restore
1453	return $autotest_es
1454}
1455
1456function freebsd_update_contigmem_mod() {
1457	if [ $(uname) = FreeBSD ]; then
1458		kldunload contigmem.ko || true
1459		if [ -n "${SPDK_RUN_EXTERNAL_DPDK:-}" ]; then
1460			cp -f "$SPDK_RUN_EXTERNAL_DPDK/kmod/contigmem.ko" /boot/modules/
1461			cp -f "$SPDK_RUN_EXTERNAL_DPDK/kmod/contigmem.ko" /boot/kernel/
1462			cp -f "$SPDK_RUN_EXTERNAL_DPDK/kmod/nic_uio.ko" /boot/modules/
1463			cp -f "$SPDK_RUN_EXTERNAL_DPDK/kmod/nic_uio.ko" /boot/kernel/
1464		else
1465			cp -f "$rootdir/dpdk/build/kmod/contigmem.ko" /boot/modules/
1466			cp -f "$rootdir/dpdk/build/kmod/contigmem.ko" /boot/kernel/
1467			cp -f "$rootdir/dpdk/build/kmod/nic_uio.ko" /boot/modules/
1468			cp -f "$rootdir/dpdk/build/kmod/nic_uio.ko" /boot/kernel/
1469		fi
1470	fi
1471}
1472
1473function freebsd_set_maxsock_buf() {
1474	# FreeBSD needs 4MB maxsockbuf size to pass socket unit tests.
1475	# Otherwise tests fail due to ENOBUFS when trying to do setsockopt(SO_RCVBUF|SO_SNDBUF).
1476	# See https://github.com/spdk/spdk/issues/2943
1477	if [[ $(uname) = FreeBSD ]] && (($(sysctl -n kern.ipc.maxsockbuf) < 4194304)); then
1478		sysctl kern.ipc.maxsockbuf=4194304
1479	fi
1480}
1481
1482function get_nvme_name_from_bdf() {
1483	get_block_dev_from_nvme "$@"
1484}
1485
1486function get_nvme_ctrlr_from_bdf() {
1487	bdf_sysfs_path=$(readlink -f /sys/class/nvme/nvme* | grep "$1/nvme/nvme")
1488	if [[ -z "${bdf_sysfs_path:-}" ]]; then
1489		return
1490	fi
1491
1492	printf '%s\n' "$(basename $bdf_sysfs_path)"
1493}
1494
1495# Get BDF addresses of all NVMe drives currently attached to
1496# uio-pci-generic or vfio-pci
1497function get_nvme_bdfs() {
1498	local bdfs=()
1499	bdfs=($("$rootdir/scripts/gen_nvme.sh" | jq -r '.config[].params.traddr'))
1500	if ((${#bdfs[@]} == 0)); then
1501		echo "No bdevs found" >&2
1502		return 1
1503	fi
1504	printf '%s\n' "${bdfs[@]}"
1505}
1506
1507# Same as function above, but just get the first disks BDF address
1508function get_first_nvme_bdf() {
1509	local bdfs=()
1510	bdfs=($(get_nvme_bdfs))
1511
1512	echo "${bdfs[0]}"
1513}
1514
1515function nvme_namespace_revert() {
1516	$rootdir/scripts/setup.sh
1517	sleep 1
1518	local bdfs=()
1519	# If there are no nvme bdfs, just return immediately
1520	bdfs=($(get_nvme_bdfs)) || return 0
1521
1522	$rootdir/scripts/setup.sh reset
1523
1524	for bdf in "${bdfs[@]}"; do
1525		nvme_ctrlr=/dev/$(get_nvme_ctrlr_from_bdf ${bdf})
1526		if [[ -z "${nvme_ctrlr:-}" ]]; then
1527			continue
1528		fi
1529
1530		# Check Optional Admin Command Support for Namespace Management
1531		oacs=$(nvme id-ctrl ${nvme_ctrlr} | grep oacs | cut -d: -f2)
1532		oacs_ns_manage=$((oacs & 0x8))
1533
1534		if [[ "$oacs_ns_manage" -ne 0 ]]; then
1535			# This assumes every NVMe controller contains single namespace,
1536			# encompassing Total NVM Capacity and formatted as 512 block size.
1537			# 512 block size is needed for test/vhost/vhost_boot.sh to
1538			# successfully run.
1539
1540			unvmcap=$(nvme id-ctrl ${nvme_ctrlr} | grep unvmcap | cut -d: -f2)
1541			if [[ "$unvmcap" -eq 0 ]]; then
1542				# All available space already used
1543				continue
1544			fi
1545			tnvmcap=$(nvme id-ctrl ${nvme_ctrlr} | grep tnvmcap | cut -d: -f2)
1546			cntlid=$(nvme id-ctrl ${nvme_ctrlr} | grep cntlid | cut -d: -f2)
1547			blksize=512
1548
1549			size=$((tnvmcap / blksize))
1550
1551			nvme detach-ns ${nvme_ctrlr} -n 0xffffffff -c $cntlid || true
1552			nvme delete-ns ${nvme_ctrlr} -n 0xffffffff || true
1553			nvme create-ns ${nvme_ctrlr} -s ${size} -c ${size} -b ${blksize}
1554			nvme attach-ns ${nvme_ctrlr} -n 1 -c $cntlid
1555			nvme reset ${nvme_ctrlr}
1556			waitforfile "${nvme_ctrlr}n1"
1557		fi
1558	done
1559}
1560
1561# Get BDFs based on device ID, such as 0x0a54
1562function get_nvme_bdfs_by_id() {
1563	local bdfs=() _bdfs=()
1564	_bdfs=($(get_nvme_bdfs)) || return 0
1565	for bdf in "${_bdfs[@]}"; do
1566		device=$(cat /sys/bus/pci/devices/$bdf/device) || true
1567		if [[ "$device" == "$1" ]]; then
1568			bdfs+=($bdf)
1569		fi
1570	done
1571
1572	((${#bdfs[@]} > 0)) || return 0
1573	printf '%s\n' "${bdfs[@]}"
1574}
1575
1576function opal_revert_cleanup() {
1577	# The OPAL CI tests is only used for P4510 devices.
1578	mapfile -t bdfs < <(get_nvme_bdfs_by_id 0x0a54)
1579	if [[ -z ${bdfs[0]:-} ]]; then
1580		return 0
1581	fi
1582
1583	$SPDK_BIN_DIR/spdk_tgt &
1584	spdk_tgt_pid=$!
1585	waitforlisten $spdk_tgt_pid
1586
1587	bdf_id=0
1588	for bdf in "${bdfs[@]}"; do
1589		$rootdir/scripts/rpc.py bdev_nvme_attach_controller -b "nvme"${bdf_id} -t "pcie" -a ${bdf}
1590		# Ignore if this fails.
1591		$rootdir/scripts/rpc.py bdev_nvme_opal_revert -b "nvme"${bdf_id} -p test || true
1592		((++bdf_id))
1593	done
1594
1595	killprocess $spdk_tgt_pid
1596}
1597
1598function pap() {
1599	while read -r file; do
1600		cat <<- FILE
1601			--- $file ---
1602			$(< "$file")
1603			--- $file ---
1604		FILE
1605		rm -f "$file"
1606	done < <(find "$@" -type f | sort -u)
1607}
1608
1609function get_proc_paths() {
1610	case "$(uname -s)" in
1611		Linux) # ps -e -opid,exe <- not supported under {centos7,rocky8}'s procps-ng
1612			local pid exe
1613			for pid in /proc/[0-9]*; do
1614				exe=$(readlink "$pid/exe") || continue
1615				exe=${exe/ (deleted)/}
1616				echo "${pid##*/} $exe"
1617			done
1618			;;
1619		FreeeBSD) procstat -ab | awk '{print $1, $4}' ;;
1620	esac
1621}
1622
1623exec_files() { file "$@" | awk -F: '/ELF.+executable/{print $1}'; }
1624
1625function reap_spdk_processes() {
1626	local bins test_bins procs
1627	local spdk_procs spdk_pids
1628
1629	mapfile -t test_bins < <(find "$rootdir"/test/{app,env,event,nvme} -type f)
1630	mapfile -t bins < <(
1631		exec_files "${test_bins[@]}"
1632		readlink -f "$SPDK_BIN_DIR/"* "$SPDK_EXAMPLE_DIR/"*
1633	)
1634	((${#bins[@]} > 0)) || return 0
1635
1636	mapfile -t spdk_procs < <(get_proc_paths | grep -E "$(
1637		IFS="|"
1638		echo "${bins[*]#$rootdir/}"
1639	)" || true)
1640	((${#spdk_procs[@]} > 0)) || return 0
1641
1642	printf '%s is still up, killing\n' "${spdk_procs[@]}" >&2
1643	mapfile -t spdk_pids < <(printf '%s\n' "${spdk_procs[@]}" | awk '{print $1}')
1644
1645	kill -SIGKILL "${spdk_pids[@]}" 2> /dev/null || :
1646	return 1
1647}
1648
1649function is_block_zoned() {
1650	local device=$1
1651
1652	[[ -e /sys/block/$device/queue/zoned ]] || return 1
1653	[[ $(< "/sys/block/$device/queue/zoned") != none ]]
1654}
1655
1656function get_zoned_devs() {
1657	local -gA zoned_devs=()
1658	local -A zoned_ctrls=()
1659	local nvme bdf ns
1660
1661	# When given ctrl has > 1 namespaces attached, we need to make
1662	# sure we pick up ALL of them, even if only one of them is zoned.
1663	# This is because the zoned_devs[] is mainly used for PCI_BLOCKED
1664	# which passed to setup.sh will skip entire ctrl, not a single
1665	# ns. FIXME: this should not be necessary. We need to find a way
1666	# to handle zoned devices more gracefully instead of hiding them
1667	# like that from all the other non-zns test suites.
1668	for nvme in /sys/class/nvme/nvme*; do
1669		bdf=$(< "$nvme/address")
1670		for ns in "$nvme/"nvme*n*; do
1671			if is_block_zoned "${ns##*/}"; then
1672				zoned_ctrls["$nvme"]=$bdf
1673				continue 2
1674			fi
1675		done
1676	done
1677
1678	for nvme in "${!zoned_ctrls[@]}"; do
1679		for ns in "$nvme/"nvme*n*; do
1680			zoned_devs["${ns##*/}"]=${zoned_ctrls["$nvme"]}
1681		done
1682	done
1683}
1684
1685function is_pid_child() {
1686	local pid=$1 _pid
1687
1688	while read -r _pid; do
1689		((pid == _pid)) && return 0
1690	done < <(jobs -pr)
1691
1692	return 1
1693}
1694
1695# Define temp storage for all the tests. Look for 2GB at minimum
1696set_test_storage "${TEST_MIN_STORAGE_SIZE:-$((1 << 31))}"
1697
1698set -o errtrace
1699shopt -s extdebug
1700trap "trap - ERR; print_backtrace >&2" ERR
1701
1702PS4=' \t ${test_domain:-} -- ${BASH_SOURCE#${BASH_SOURCE%/*/*}/}@${LINENO} -- \$ '
1703if $SPDK_AUTOTEST_X; then
1704	# explicitly enable xtraces, overriding any tracking information.
1705	xtrace_fd
1706else
1707	xtrace_disable
1708fi
1709
1710if [[ $CONFIG_COVERAGE == y ]]; then
1711	if lt "$(lcov --version | awk '{print $NF}')" 2; then
1712		lcov_rc_opt="--rc lcov_branch_coverage=1 --rc lcov_function_coverage=1"
1713	else
1714		lcov_rc_opt="--rc branch_coverage=1 --rc function_coverage=1"
1715	fi
1716	export LCOV_OPTS="
1717		$lcov_rc_opt
1718		--rc genhtml_branch_coverage=1
1719		--rc genhtml_function_coverage=1
1720		--rc genhtml_legend=1
1721		--rc geninfo_all_blocks=1
1722		--rc geninfo_unexecuted_blocks=1
1723		$lcov_opt
1724		"
1725	export LCOV="lcov $LCOV_OPTS"
1726fi
1727