xref: /spdk/test/common/autotest_common.sh (revision b30d57cdad6d2bc75cc1e4e2ebbcebcb0d98dcfa)
1#!/usr/bin/env bash
2
3function xtrace_disable() {
4	if [ "$XTRACE_DISABLED" != "yes" ]; then
5		PREV_BASH_OPTS="$-"
6		if [[ "$PREV_BASH_OPTS" == *"x"* ]]; then
7			XTRACE_DISABLED="yes"
8		fi
9		set +x
10	elif [ -z $XTRACE_NESTING_LEVEL ]; then
11		XTRACE_NESTING_LEVEL=1
12	else
13		XTRACE_NESTING_LEVEL=$((++XTRACE_NESTING_LEVEL))
14	fi
15}
16
17xtrace_disable
18set -e
19shopt -s expand_aliases
20
21source "$rootdir/test/common/applications.sh"
22source "$rootdir/scripts/common.sh"
23if [[ -e $rootdir/test/common/build_config.sh ]]; then
24	source "$rootdir/test/common/build_config.sh"
25elif [[ -e $rootdir/mk/config.mk ]]; then
26	build_config=$(< "$rootdir/mk/config.mk")
27	source <(echo "${build_config//\?=/=}")
28else
29	source "$rootdir/CONFIG"
30fi
31
32# Dummy function to be called after restoring xtrace just so that it appears in the
33# xtrace log. This way we can consistently track when xtrace is enabled/disabled.
34function xtrace_enable() {
35	# We have to do something inside a function in bash, and calling any command
36	# (even `:`) will produce an xtrace entry, so we just define another function.
37	function xtrace_dummy() { :; }
38}
39
40# Keep it as alias to avoid xtrace_enable backtrace always pointing to xtrace_restore.
41# xtrace_enable will appear as called directly from the user script, from the same line
42# that "called" xtrace_restore.
43alias xtrace_restore='if [ -z $XTRACE_NESTING_LEVEL ]; then
44        if [[ "$PREV_BASH_OPTS" == *"x"* ]]; then
45		XTRACE_DISABLED="no"; PREV_BASH_OPTS=""; set -x; xtrace_enable;
46	fi
47else
48	XTRACE_NESTING_LEVEL=$((--XTRACE_NESTING_LEVEL));
49	if [ $XTRACE_NESTING_LEVEL -eq "0" ]; then
50		unset XTRACE_NESTING_LEVEL
51	fi
52fi'
53
54: ${RUN_NIGHTLY:=0}
55export RUN_NIGHTLY
56
57# Set defaults for missing test config options
58: ${SPDK_AUTOTEST_DEBUG_APPS:=0}
59export SPDK_AUTOTEST_DEBUG_APPS
60: ${SPDK_RUN_VALGRIND=0}
61export SPDK_RUN_VALGRIND
62: ${SPDK_RUN_FUNCTIONAL_TEST=0}
63export SPDK_RUN_FUNCTIONAL_TEST
64: ${SPDK_TEST_UNITTEST=0}
65export SPDK_TEST_UNITTEST
66: ${SPDK_TEST_AUTOBUILD=0}
67export SPDK_TEST_AUTOBUILD
68: ${SPDK_TEST_RELEASE_BUILD=0}
69export SPDK_TEST_RELEASE_BUILD
70: ${SPDK_TEST_ISAL=0}
71export SPDK_TEST_ISAL
72: ${SPDK_TEST_ISCSI=0}
73export SPDK_TEST_ISCSI
74: ${SPDK_TEST_ISCSI_INITIATOR=0}
75export SPDK_TEST_ISCSI_INITIATOR
76: ${SPDK_TEST_NVME=0}
77export SPDK_TEST_NVME
78: ${SPDK_TEST_NVME_CLI=0}
79export SPDK_TEST_NVME_CLI
80: ${SPDK_TEST_NVME_CUSE=0}
81export SPDK_TEST_NVME_CUSE
82: ${SPDK_TEST_NVMF=0}
83export SPDK_TEST_NVMF
84: ${SPDK_TEST_NVMF_TRANSPORT="rdma"}
85export SPDK_TEST_NVMF_TRANSPORT
86: ${SPDK_TEST_RBD=0}
87export SPDK_TEST_RBD
88: ${SPDK_TEST_VHOST=0}
89export SPDK_TEST_VHOST
90: ${SPDK_TEST_BLOCKDEV=0}
91export SPDK_TEST_BLOCKDEV
92: ${SPDK_TEST_IOAT=0}
93export SPDK_TEST_IOAT
94: ${SPDK_TEST_BLOBFS=0}
95export SPDK_TEST_BLOBFS
96: ${SPDK_TEST_VHOST_INIT=0}
97export SPDK_TEST_VHOST_INIT
98: ${SPDK_TEST_PMDK=0}
99export SPDK_TEST_PMDK
100: ${SPDK_TEST_LVOL=0}
101export SPDK_TEST_LVOL
102: ${SPDK_TEST_JSON=0}
103export SPDK_TEST_JSON
104: ${SPDK_TEST_REDUCE=0}
105export SPDK_TEST_REDUCE
106: ${SPDK_RUN_ASAN=0}
107export SPDK_RUN_ASAN
108: ${SPDK_RUN_UBSAN=0}
109export SPDK_RUN_UBSAN
110: ${SPDK_RUN_EXTERNAL_DPDK=""}
111export SPDK_RUN_EXTERNAL_DPDK
112: ${SPDK_RUN_NON_ROOT=0}
113export SPDK_RUN_NON_ROOT
114: ${SPDK_TEST_CRYPTO=0}
115export SPDK_TEST_CRYPTO
116: ${SPDK_TEST_FTL=0}
117export SPDK_TEST_FTL
118: ${SPDK_TEST_OCF=0}
119export SPDK_TEST_OCF
120: ${SPDK_TEST_FTL_EXTENDED=0}
121export SPDK_TEST_FTL_EXTENDED
122: ${SPDK_TEST_VMD=0}
123export SPDK_TEST_VMD
124: ${SPDK_TEST_OPAL=0}
125export SPDK_TEST_OPAL
126: ${SPDK_TEST_NATIVE_DPDK}
127export SPDK_TEST_NATIVE_DPDK
128: ${SPDK_AUTOTEST_X=true}
129export SPDK_AUTOTEST_X
130: ${SPDK_TEST_RAID5=0}
131export SPDK_TEST_RAID5
132: ${SPDK_TEST_URING=0}
133export SPDK_TEST_URING
134: ${SPDK_TEST_USE_IGB_UIO:=0}
135export SPDK_TEST_USE_IGB_UIO
136
137export DPDK_LIB_DIR="${SPDK_RUN_EXTERNAL_DPDK:-$rootdir/dpdk/build}/lib"
138export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$SPDK_LIB_DIR:$DPDK_LIB_DIR
139
140# Tell setup.sh to wait for block devices upon each reset
141export PCI_BLOCK_SYNC_ON_RESET=yes
142
143# Export PYTHONPATH with addition of RPC framework. New scripts can be created
144# specific use cases for tests.
145export PYTHONPATH=$PYTHONPATH:$rootdir/scripts
146
147# Don't create Python .pyc files. When running with sudo these will be
148# created with root ownership and can cause problems when cleaning the repository.
149export PYTHONDONTWRITEBYTECODE=1
150
151# Export flag to skip the known bug that exists in librados
152# Bug is reported on ceph bug tracker with number 24078
153export ASAN_OPTIONS=new_delete_type_mismatch=0
154export UBSAN_OPTIONS='halt_on_error=1:print_stacktrace=1:abort_on_error=1'
155
156# Export LeakSanitizer option to use suppression file in order to prevent false positives
157# and known leaks in external executables or libraries from showing up.
158asan_suppression_file="/var/tmp/asan_suppression_file"
159sudo rm -rf "$asan_suppression_file"
160cat << EOL >> "$asan_suppression_file"
161# ASAN has some bugs around thread_local variables.  We have a destructor in place
162# to free the thread contexts, but ASAN complains about the leak before those
163# destructors have a chance to run.  So suppress this one specific leak using
164# LSAN_OPTIONS.
165leak:spdk_fs_alloc_thread_ctx
166
167# Suppress known leaks in fio project
168leak:$CONFIG_FIO_SOURCE_DIR/parse.c
169leak:$CONFIG_FIO_SOURCE_DIR/iolog.c
170leak:$CONFIG_FIO_SOURCE_DIR/init.c
171leak:$CONFIG_FIO_SOURCE_DIR/filesetup.c
172leak:fio_memalign
173leak:spdk_fio_io_u_init
174
175# Suppress leaks in libiscsi
176leak:libiscsi.so
177EOL
178
179# Suppress leaks in libfuse3
180echo "leak:libfuse3.so" >> "$asan_suppression_file"
181
182export LSAN_OPTIONS=suppressions="$asan_suppression_file"
183
184export DEFAULT_RPC_ADDR="/var/tmp/spdk.sock"
185
186if [ -z "$DEPENDENCY_DIR" ]; then
187	export DEPENDENCY_DIR=/home/sys_sgsw
188else
189	export DEPENDENCY_DIR
190fi
191
192# Export location of where all the SPDK binaries are
193export SPDK_BIN_DIR="$rootdir/build/bin"
194export SPDK_EXAMPLE_DIR="$rootdir/build/examples"
195
196# pass our valgrind desire on to unittest.sh
197if [ $SPDK_RUN_VALGRIND -eq 0 ]; then
198	export valgrind=''
199fi
200
201if [ "$(uname -s)" = "Linux" ]; then
202	MAKE="make"
203	MAKEFLAGS=${MAKEFLAGS:--j$(nproc)}
204	# Override the default HUGEMEM in scripts/setup.sh to allocate 8GB in hugepages.
205	export HUGEMEM=8192
206	if [[ $SPDK_TEST_USE_IGB_UIO -eq 1 ]]; then
207		export DRIVER_OVERRIDE=$rootdir/dpdk/build-tmp/kernel/linux/igb_uio/igb_uio.ko
208		# Building kernel modules requires root privileges
209		MAKE="sudo $MAKE"
210	fi
211elif [ "$(uname -s)" = "FreeBSD" ]; then
212	MAKE="gmake"
213	MAKEFLAGS=${MAKEFLAGS:--j$(sysctl -a | grep -E -i 'hw.ncpu' | awk '{print $2}')}
214	# FreeBSD runs a much more limited set of tests, so keep the default 2GB.
215	export HUGEMEM=2048
216else
217	echo "Unknown OS \"$(uname -s)\""
218	exit 1
219fi
220
221if [ -z "$output_dir" ]; then
222	mkdir -p "$rootdir/../output"
223	export output_dir="$rootdir/../output"
224fi
225
226TEST_MODE=
227for i in "$@"; do
228	case "$i" in
229		--iso)
230			TEST_MODE=iso
231			;;
232		--transport=*)
233			TEST_TRANSPORT="${i#*=}"
234			;;
235		--sock=*)
236			TEST_SOCK="${i#*=}"
237			;;
238	esac
239done
240
241# start rpc.py coprocess if it's not started yet
242if [[ -z $RPC_PIPE_PID ]] || ! kill -0 "$RPC_PIPE_PID" &> /dev/null; then
243	coproc RPC_PIPE { "$rootdir/scripts/rpc.py" --server; }
244	exec {RPC_PIPE_OUTPUT}<&${RPC_PIPE[0]} {RPC_PIPE_INPUT}>&${RPC_PIPE[1]}
245	# all descriptors will automatically close together with this bash
246	# process, this will make rpc.py stop reading and exit gracefully
247fi
248
249function set_test_storage() {
250	[[ -v testdir ]] || return 0
251
252	local requested_size=$1 # bytes
253	local mount target_dir
254
255	local -A mounts fss sizes avails uses
256	local source fs size avail mount use
257
258	local storage_fallback storage_candidates
259	local storage_fallback_purge
260
261	shopt -s nullglob
262	storage_fallback_purge=("${TMPDIR:-/tmp}/spdk."??????)
263	shopt -u nullglob
264
265	if ((${#storage_fallback_purge[@]} > 0)); then
266		printf '* Purging old temporary test storage (%s)\n' \
267			"${storage_fallback_purge[*]}" >&2
268		sudo rm -rf "${storage_fallback_purge[@]}"
269	fi
270
271	storage_fallback=$(mktemp -udt spdk.XXXXXX)
272	storage_candidates=(
273		"$testdir"
274		"$storage_fallback/tests/${testdir##*/}"
275		"$storage_fallback"
276	)
277
278	if [[ -n $ADD_TEST_STORAGE ]]; then
279		# List of dirs|mounts separated by whitespaces
280		storage_candidates+=($ADD_TEST_STORAGE)
281	fi
282
283	if [[ -n $DEDICATED_TEST_STORAGE ]]; then
284		# Single, dedicated dir|mount
285		storage_candidates=("$DEDICATED_TEST_STORAGE")
286	fi
287
288	mkdir -p "${storage_candidates[@]}"
289
290	# add some headroom - 64M
291	requested_size=$((requested_size + (64 << 20)))
292
293	while read -r source fs size use avail _ mount; do
294		mounts["$mount"]=$source fss["$mount"]=$fs
295		avails["$mount"]=$((avail * 1024)) sizes["$mount"]=$((size * 1024))
296		uses["$mount"]=$((use * 1024))
297	done < <(df -T | grep -v Filesystem)
298
299	printf '* Looking for test storage...\n' >&2
300
301	local target_space new_size
302	for target_dir in "${storage_candidates[@]}"; do
303		# FreeBSD's df is lacking the --output arg
304		# mount=$(df --output=target "$target_dir" | grep -v "Mounted on")
305		mount=$(df "$target_dir" | awk '$1 !~ /Filesystem/{print $6}')
306
307		target_space=${avails["$mount"]}
308		if ((target_space == 0 || target_space < requested_size)); then
309			continue
310		fi
311		if ((target_space >= requested_size)); then
312			# For in-memory fs, and / make sure our requested size won't fill most of the space.
313			if [[ ${fss["$mount"]} == tmpfs ]] || [[ ${fss["$mount"]} == ramfs ]] || [[ $mount == / ]]; then
314				new_size=$((uses["$mount"] + requested_size))
315				if ((new_size * 100 / sizes["$mount"] > 95)); then
316					continue
317				fi
318			fi
319		fi
320		export SPDK_TEST_STORAGE=$target_dir
321		printf '* Found test storage at %s\n' "$SPDK_TEST_STORAGE" >&2
322		return 0
323	done
324	printf '* Test storage is not available\n'
325	return 1
326}
327
328function get_config_params() {
329	xtrace_disable
330	config_params='--enable-debug --enable-werror'
331
332	# for options with dependencies but no test flag, set them here
333	if [ -f /usr/include/infiniband/verbs.h ]; then
334		config_params+=' --with-rdma'
335	fi
336
337	if [ $(uname -s) == "FreeBSD" ]; then
338		intel="hw.model: Intel"
339		cpu_vendor=$(sysctl -a | grep hw.model | cut -c 1-15)
340	else
341		intel="GenuineIntel"
342		cpu_vendor=$(grep -i 'vendor' /proc/cpuinfo --max-count=1)
343	fi
344	if [[ "$cpu_vendor" != *"$intel"* ]]; then
345		config_params+=" --without-idxd"
346	else
347		config_params+=" --with-idxd"
348	fi
349
350	if [[ -d $CONFIG_FIO_SOURCE_DIR ]]; then
351		config_params+=" --with-fio=$CONFIG_FIO_SOURCE_DIR"
352	fi
353
354	if [ -d ${DEPENDENCY_DIR}/vtune_codes ]; then
355		config_params+=' --with-vtune='${DEPENDENCY_DIR}'/vtune_codes'
356	fi
357
358	if [ -d /usr/include/iscsi ]; then
359		libiscsi_version=$(grep LIBISCSI_API_VERSION /usr/include/iscsi/iscsi.h | head -1 | awk '{print $3}' | awk -F '(' '{print $2}' | awk -F ')' '{print $1}')
360		if [ $libiscsi_version -ge 20150621 ]; then
361			config_params+=' --with-iscsi-initiator'
362		fi
363	fi
364
365	if [ $SPDK_TEST_UNITTEST -eq 0 ]; then
366		config_params+=' --disable-unit-tests'
367	fi
368
369	if [ $SPDK_TEST_NVME_CUSE -eq 1 ]; then
370		config_params+=' --with-nvme-cuse'
371	fi
372
373	# for options with both dependencies and a test flag, set them here
374	if [ -f /usr/include/libpmemblk.h ] && [ $SPDK_TEST_PMDK -eq 1 ]; then
375		config_params+=' --with-pmdk'
376	fi
377
378	if [ -f /usr/include/libpmem.h ] && [ $SPDK_TEST_REDUCE -eq 1 ]; then
379		if ge "$(nasm --version | awk '{print $3}')" 2.14 && [[ $SPDK_TEST_ISAL -eq 1 ]]; then
380			config_params+=' --with-reduce'
381		fi
382	fi
383
384	if [ -d /usr/include/rbd ] && [ -d /usr/include/rados ] && [ $SPDK_TEST_RBD -eq 1 ]; then
385		config_params+=' --with-rbd'
386	fi
387
388	# for options with no required dependencies, just test flags, set them here
389	if [ $SPDK_TEST_CRYPTO -eq 1 ]; then
390		config_params+=' --with-crypto'
391	fi
392
393	if [ $SPDK_TEST_OCF -eq 1 ]; then
394		config_params+=" --with-ocf"
395	fi
396
397	if [ $SPDK_RUN_UBSAN -eq 1 ]; then
398		config_params+=' --enable-ubsan'
399	fi
400
401	if [ $SPDK_RUN_ASAN -eq 1 ]; then
402		config_params+=' --enable-asan'
403	fi
404
405	if [ "$(uname -s)" = "Linux" ]; then
406		config_params+=' --enable-coverage'
407	fi
408
409	if [ $SPDK_TEST_ISAL -eq 0 ]; then
410		config_params+=' --without-isal'
411	fi
412
413	if [ $SPDK_TEST_BLOBFS -eq 1 ]; then
414		if [[ -d /usr/include/fuse3 ]] || [[ -d /usr/local/include/fuse3 ]]; then
415			config_params+=' --with-fuse'
416		fi
417	fi
418
419	if [ $SPDK_TEST_RAID5 -eq 1 ]; then
420		config_params+=' --with-raid5'
421	fi
422
423	# Check whether liburing library header exists
424	if [ -f /usr/include/liburing/io_uring.h ] && [ $SPDK_TEST_URING -eq 1 ]; then
425		config_params+=' --with-uring'
426	fi
427
428	if [ -n "$SPDK_RUN_EXTERNAL_DPDK" ]; then
429		config_params+=" --with-dpdk=$SPDK_RUN_EXTERNAL_DPDK"
430	fi
431
432	if [[ $SPDK_TEST_USE_IGB_UIO -eq 1 ]]; then
433		config_params+=" --with-igb-uio-driver"
434	fi
435	echo "$config_params"
436	xtrace_restore
437}
438
439function rpc_cmd() {
440	xtrace_disable
441	local rsp rc
442
443	echo "$@" >&$RPC_PIPE_INPUT
444	while read -t 5 -ru $RPC_PIPE_OUTPUT rsp; do
445		if [[ $rsp == "**STATUS="* ]]; then
446			break
447		fi
448		echo "$rsp"
449	done
450
451	rc=${rsp#*=}
452	xtrace_restore
453	[[ $rc == 0 ]]
454}
455
456function rpc_cmd_simple_data_json() {
457
458	local elems="$1[@]" elem
459	local -gA jq_out=()
460	local jq val
461
462	local lvs=(
463		"uuid"
464		"name"
465		"base_bdev"
466		"total_data_clusters"
467		"free_clusters"
468		"block_size"
469		"cluster_size"
470	)
471
472	local bdev=(
473		"name"
474		"aliases[0]"
475		"block_size"
476		"num_blocks"
477		"uuid"
478		"product_name"
479	)
480
481	[[ -v $elems ]] || return 1
482
483	for elem in "${!elems}"; do
484		jq="${jq:+$jq,\"\\n\",}\"$elem\",\" \",.[0].$elem"
485	done
486	jq+=',"\n"'
487
488	shift
489	while read -r elem val; do
490		jq_out["$elem"]=$val
491	done < <(rpc_cmd "$@" | jq -jr "$jq")
492	((${#jq_out[@]} > 0)) || return 1
493}
494
495function NOT() {
496	local es=0
497
498	"$@" || es=$?
499
500	# Logic looks like so:
501	#  - return false if command exit successfully
502	#  - return false if command exit after receiving a core signal (FIXME: or any signal?)
503	#  - return true if command exit with an error
504
505	# This naively assumes that the process doesn't exit with > 128 on its own.
506	if ((es > 128)); then
507		es=$((es & ~128))
508		case "$es" in
509			3) ;&       # SIGQUIT
510			4) ;&       # SIGILL
511			6) ;&       # SIGABRT
512			8) ;&       # SIGFPE
513			9) ;&       # SIGKILL
514			11) es=0 ;; # SIGSEGV
515			*) es=1 ;;
516		esac
517	elif [[ -n $EXIT_STATUS ]] && ((es != EXIT_STATUS)); then
518		es=0
519	fi
520
521	# invert error code of any command and also trigger ERR on 0 (unlike bash ! prefix)
522	((!es == 0))
523}
524
525function timing() {
526	direction="$1"
527	testname="$2"
528
529	now=$(date +%s)
530
531	if [ "$direction" = "enter" ]; then
532		export timing_stack="${timing_stack};${now}"
533		export test_stack="${test_stack};${testname}"
534	else
535		touch "$output_dir/timing.txt"
536		child_time=$(grep "^${test_stack:1};" $output_dir/timing.txt | awk '{s+=$2} END {print s}')
537
538		start_time=$(echo "$timing_stack" | sed -e 's@^.*;@@')
539		timing_stack=$(echo "$timing_stack" | sed -e 's@;[^;]*$@@')
540
541		elapsed=$((now - start_time - child_time))
542		echo "${test_stack:1} $elapsed" >> $output_dir/timing.txt
543
544		test_stack=$(echo "$test_stack" | sed -e 's@;[^;]*$@@')
545	fi
546}
547
548function timing_enter() {
549	xtrace_disable
550	timing "enter" "$1"
551	xtrace_restore
552}
553
554function timing_exit() {
555	xtrace_disable
556	timing "exit" "$1"
557	xtrace_restore
558}
559
560function timing_finish() {
561	flamegraph='/usr/local/FlameGraph/flamegraph.pl'
562	if [ -x "$flamegraph" ]; then
563		"$flamegraph" \
564			--title 'Build Timing' \
565			--nametype 'Step:' \
566			--countname seconds \
567			$output_dir/timing.txt \
568			> $output_dir/timing.svg
569	fi
570}
571
572function create_test_list() {
573	xtrace_disable
574	# First search all scripts in main SPDK directory.
575	completion=$(grep -shI -d skip --include="*.sh" -e "run_test " $rootdir/*)
576	# Follow up with search in test directory recursively.
577	completion+=$(grep -rshI --include="*.sh" --exclude="autotest_common.sh" -e "run_test " $rootdir/test)
578	printf "%s" "$completion" | grep -v "#" \
579		| sed 's/^.*run_test/run_test/' | awk '{print $2}' \
580		| sed 's/\"//g' | sort > $output_dir/all_tests.txt || true
581	xtrace_restore
582}
583
584function gdb_attach() {
585	gdb -q --batch \
586		-ex 'handle SIGHUP nostop pass' \
587		-ex 'handle SIGQUIT nostop pass' \
588		-ex 'handle SIGPIPE nostop pass' \
589		-ex 'handle SIGALRM nostop pass' \
590		-ex 'handle SIGTERM nostop pass' \
591		-ex 'handle SIGUSR1 nostop pass' \
592		-ex 'handle SIGUSR2 nostop pass' \
593		-ex 'handle SIGCHLD nostop pass' \
594		-ex 'set print thread-events off' \
595		-ex 'cont' \
596		-ex 'thread apply all bt' \
597		-ex 'quit' \
598		--tty=/dev/stdout \
599		-p $1
600}
601
602function process_core() {
603	# Note that this always was racy as we can't really sync with the kernel
604	# to see if there's any core queued up for writing. We could check if
605	# collector is running and wait for it explicitly, but it doesn't seem
606	# to be worth the effort. So assume that if we are being called via
607	# trap, as in, when some error has occurred, wait up to 5s for any
608	# potential cores. If we are called just for cleanup at the very end,
609	# don't wait since all the tests ended successfully, hence having any
610	# critical cores lying around is unlikely.
611	local es=$?
612	((es != 0)) && sleep 5s
613
614	local coredumps core
615
616	shopt -s nullglob
617	coredumps=("$output_dir/coredumps/"*.bt.txt)
618	shopt -u nullglob
619
620	((${#coredumps[@]} > 0)) || return 0
621	chmod -R a+r "$output_dir/coredumps"
622
623	for core in "${coredumps[@]}"; do
624		cat <<- BT
625			##### CORE BT ${core##*/} #####
626
627			$(<"$core")
628
629			--
630		BT
631	done
632	return 1
633}
634
635function process_shm() {
636	type=$1
637	id=$2
638	if [ "$type" = "--pid" ]; then
639		id="pid${id}"
640	elif [ "$type" = "--id" ]; then
641		id="${id}"
642	else
643		echo "Please specify to search for pid or shared memory id."
644		return 1
645	fi
646
647	shm_files=$(find /dev/shm -name "*.${id}" -printf "%f\n")
648
649	if [[ -z $shm_files ]]; then
650		echo "SHM File for specified PID or shared memory id: ${id} not found!"
651		return 1
652	fi
653	for n in $shm_files; do
654		tar -C /dev/shm/ -cvzf $output_dir/${n}_shm.tar.gz ${n}
655	done
656	return 0
657}
658
659function waitforlisten() {
660	# $1 = process pid
661	if [ -z "$1" ]; then
662		exit 1
663	fi
664
665	local rpc_addr="${2:-$DEFAULT_RPC_ADDR}"
666
667	echo "Waiting for process to start up and listen on UNIX domain socket $rpc_addr..."
668	# turn off trace for this loop
669	xtrace_disable
670	local ret=0
671	local i
672	for ((i = 40; i != 0; i--)); do
673		# if the process is no longer running, then exit the script
674		#  since it means the application crashed
675		if ! kill -s 0 $1; then
676			echo "ERROR: process (pid: $1) is no longer running"
677			ret=1
678			break
679		fi
680
681		if $rootdir/scripts/rpc.py -t 1 -s "$rpc_addr" rpc_get_methods &> /dev/null; then
682			break
683		fi
684
685		sleep 0.5
686	done
687
688	xtrace_restore
689	if ((i == 0)); then
690		echo "ERROR: timeout while waiting for process (pid: $1) to start listening on '$rpc_addr'"
691		ret=1
692	fi
693	return $ret
694}
695
696function waitfornbd() {
697	local nbd_name=$1
698	local i
699
700	for ((i = 1; i <= 20; i++)); do
701		if grep -q -w $nbd_name /proc/partitions; then
702			break
703		else
704			sleep 0.1
705		fi
706	done
707
708	# The nbd device is now recognized as a block device, but there can be
709	#  a small delay before we can start I/O to that block device.  So loop
710	#  here trying to read the first block of the nbd block device to a temp
711	#  file.  Note that dd returns success when reading an empty file, so we
712	#  need to check the size of the output file instead.
713	for ((i = 1; i <= 20; i++)); do
714		dd if=/dev/$nbd_name of="$SPDK_TEST_STORAGE/nbdtest" bs=4096 count=1 iflag=direct
715		size=$(stat -c %s "$SPDK_TEST_STORAGE/nbdtest")
716		rm -f "$SPDK_TEST_STORAGE/nbdtest"
717		if [ "$size" != "0" ]; then
718			return 0
719		else
720			sleep 0.1
721		fi
722	done
723
724	return 1
725}
726
727function waitforbdev() {
728	local bdev_name=$1
729	local i
730
731	for ((i = 1; i <= 20; i++)); do
732		if $rpc_py bdev_get_bdevs | jq -r '.[] .name' | grep -qw $bdev_name; then
733			return 0
734		fi
735
736		if $rpc_py bdev_get_bdevs | jq -r '.[] .aliases' | grep -qw $bdev_name; then
737			return 0
738		fi
739
740		sleep 0.1
741	done
742
743	return 1
744}
745
746function make_filesystem() {
747	local fstype=$1
748	local dev_name=$2
749	local i=0
750	local force
751
752	if [ $fstype = ext4 ]; then
753		force=-F
754	else
755		force=-f
756	fi
757
758	while ! mkfs.${fstype} $force ${dev_name}; do
759		if [ $i -ge 15 ]; then
760			return 1
761		fi
762		i=$((i + 1))
763		sleep 1
764	done
765
766	return 0
767}
768
769function killprocess() {
770	# $1 = process pid
771	if [ -z "$1" ]; then
772		exit 1
773	fi
774
775	if kill -0 $1; then
776		if [ $(uname) = Linux ]; then
777			process_name=$(ps --no-headers -o comm= $1)
778		else
779			process_name=$(ps -c -o command $1 | tail -1)
780		fi
781		if [ "$process_name" = "sudo" ]; then
782			# kill the child process, which is the actual app
783			# (assume $1 has just one child)
784			local child
785			child="$(pgrep -P $1)"
786			echo "killing process with pid $child"
787			kill $child
788		else
789			echo "killing process with pid $1"
790			kill $1
791		fi
792
793		# wait for the process regardless if its the dummy sudo one
794		# or the actual app - it should terminate anyway
795		wait $1
796	else
797		# the process is not there anymore
798		echo "Process with pid $1 is not found"
799		exit 1
800	fi
801}
802
803function iscsicleanup() {
804	echo "Cleaning up iSCSI connection"
805	iscsiadm -m node --logout || true
806	iscsiadm -m node -o delete || true
807	rm -rf /var/lib/iscsi/nodes/*
808}
809
810function stop_iscsi_service() {
811	if cat /etc/*-release | grep Ubuntu; then
812		service open-iscsi stop
813	else
814		service iscsid stop
815	fi
816}
817
818function start_iscsi_service() {
819	if cat /etc/*-release | grep Ubuntu; then
820		service open-iscsi start
821	else
822		service iscsid start
823	fi
824}
825
826function rbd_setup() {
827	# $1 = monitor ip address
828	# $2 = name of the namespace
829	if [ -z "$1" ]; then
830		echo "No monitor IP address provided for ceph"
831		exit 1
832	fi
833	if [ -n "$2" ]; then
834		if ip netns list | grep "$2"; then
835			NS_CMD="ip netns exec $2"
836		else
837			echo "No namespace $2 exists"
838			exit 1
839		fi
840	fi
841
842	if hash ceph; then
843		export PG_NUM=128
844		export RBD_POOL=rbd
845		export RBD_NAME=foo
846		$NS_CMD $rootdir/scripts/ceph/stop.sh || true
847		$NS_CMD $rootdir/scripts/ceph/start.sh $1
848
849		$NS_CMD ceph osd pool create $RBD_POOL $PG_NUM || true
850		$NS_CMD rbd create $RBD_NAME --size 1000
851	fi
852}
853
854function rbd_cleanup() {
855	if hash ceph; then
856		$rootdir/scripts/ceph/stop.sh || true
857		rm -f /var/tmp/ceph_raw.img
858	fi
859}
860
861function _start_stub() {
862	# Disable ASLR for multi-process testing.  SPDK does support using DPDK multi-process,
863	# but ASLR can still be unreliable in some cases.
864	# We will reenable it again after multi-process testing is complete in kill_stub().
865	# Save current setting so it can be restored upon calling kill_stub().
866	_randomize_va_space=$(< /proc/sys/kernel/randomize_va_space)
867	echo 0 > /proc/sys/kernel/randomize_va_space
868	$rootdir/test/app/stub/stub $1 &
869	stubpid=$!
870	echo Waiting for stub to ready for secondary processes...
871	while ! [ -e /var/run/spdk_stub0 ]; do
872		# If stub dies while we wait, bail
873		[[ -e /proc/$stubpid ]] || return 1
874		sleep 1s
875	done
876	echo done.
877}
878
879function start_stub() {
880	if ! _start_stub "$@"; then
881		echo "stub failed" >&2
882		return 1
883	fi
884}
885
886function kill_stub() {
887	if [[ -e /proc/$stubpid ]]; then
888		kill $1 $stubpid
889		wait $stubpid
890	fi 2> /dev/null || :
891	rm -f /var/run/spdk_stub0
892	# Re-enable ASLR now that we are done with multi-process testing
893	# Note: "1" enables ASLR w/o randomizing data segments, "2" adds data segment
894	#  randomizing and is the default on all recent Linux kernels
895	echo "${_randomize_va_space:-2}" > /proc/sys/kernel/randomize_va_space
896}
897
898function run_test() {
899	if [ $# -le 1 ]; then
900		echo "Not enough parameters"
901		echo "usage: run_test test_name test_script [script_params]"
902		exit 1
903	fi
904
905	xtrace_disable
906	local test_name="$1"
907	shift
908
909	if [ -n "$test_domain" ]; then
910		export test_domain="${test_domain}.${test_name}"
911	else
912		export test_domain="$test_name"
913	fi
914
915	timing_enter $test_name
916	echo "************************************"
917	echo "START TEST $test_name"
918	echo "************************************"
919	xtrace_restore
920	time "$@"
921	xtrace_disable
922	echo "************************************"
923	echo "END TEST $test_name"
924	echo "************************************"
925	timing_exit $test_name
926
927	export test_domain=${test_domain%"$test_name"}
928	if [ -n "$test_domain" ]; then
929		export test_domain=${test_domain%?}
930	fi
931
932	if [ -z "$test_domain" ]; then
933		echo "top_level $test_name" >> $output_dir/test_completions.txt
934	else
935		echo "$test_domain $test_name" >> $output_dir/test_completions.txt
936	fi
937	xtrace_restore
938}
939
940function skip_run_test_with_warning() {
941	echo "WARNING: $1"
942	echo "Test run may fail if run with autorun.sh"
943	echo "Please check your $rootdir/test/common/skipped_tests.txt"
944}
945
946function print_backtrace() {
947	# if errexit is not enabled, don't print a backtrace
948	[[ "$-" =~ e ]] || return 0
949
950	local args=("${BASH_ARGV[@]}")
951
952	xtrace_disable
953	echo "========== Backtrace start: =========="
954	echo ""
955	for i in $(seq 1 $((${#FUNCNAME[@]} - 1))); do
956		local func="${FUNCNAME[$i]}"
957		local line_nr="${BASH_LINENO[$((i - 1))]}"
958		local src="${BASH_SOURCE[$i]}"
959		local bt="" cmdline=()
960
961		if [[ -f $src ]]; then
962			bt=$(nl -w 4 -ba -nln $src | grep -B 5 -A 5 "^${line_nr}[^0-9]" \
963				| sed "s/^/   /g" | sed "s/^   $line_nr /=> $line_nr /g")
964		fi
965
966		# If extdebug set the BASH_ARGC[i], try to fetch all the args
967		if ((BASH_ARGC[i] > 0)); then
968			# Use argc as index to reverse the stack
969			local argc=${BASH_ARGC[i]} arg
970			for arg in "${args[@]::BASH_ARGC[i]}"; do
971				cmdline[argc--]="[\"$arg\"]"
972			done
973			args=("${args[@]:BASH_ARGC[i]}")
974		fi
975
976		echo "in $src:$line_nr -> $func($(
977			IFS=","
978			printf '%s\n' "${cmdline[*]:-[]}"
979		))"
980		echo "     ..."
981		echo "${bt:-backtrace unavailable}"
982		echo "     ..."
983	done
984	echo ""
985	echo "========== Backtrace end =========="
986	xtrace_restore
987	return 0
988}
989
990function waitforserial() {
991	local i=0
992	local nvme_device_counter=1
993	if [[ -n "$2" ]]; then
994		nvme_device_counter=$2
995	fi
996
997	while [ $(lsblk -l -o NAME,SERIAL | grep -c $1) -lt $nvme_device_counter ]; do
998		[ $i -lt 15 ] || break
999		i=$((i + 1))
1000		echo "Waiting for devices"
1001		sleep 1
1002	done
1003
1004	if [[ $(lsblk -l -o NAME,SERIAL | grep -c $1) -lt $nvme_device_counter ]]; then
1005		return 1
1006	fi
1007
1008	return 0
1009}
1010
1011function waitforserial_disconnect() {
1012	local i=0
1013	while lsblk -o NAME,SERIAL | grep -q -w $1; do
1014		[ $i -lt 15 ] || break
1015		i=$((i + 1))
1016		echo "Waiting for disconnect devices"
1017		sleep 1
1018	done
1019
1020	if lsblk -l -o NAME | grep -q -w $1; then
1021		return 1
1022	fi
1023
1024	return 0
1025}
1026
1027function waitforblk() {
1028	local i=0
1029	while ! lsblk -l -o NAME | grep -q -w $1; do
1030		[ $i -lt 15 ] || break
1031		i=$((i + 1))
1032		sleep 1
1033	done
1034
1035	if ! lsblk -l -o NAME | grep -q -w $1; then
1036		return 1
1037	fi
1038
1039	return 0
1040}
1041
1042function waitforblk_disconnect() {
1043	local i=0
1044	while lsblk -l -o NAME | grep -q -w $1; do
1045		[ $i -lt 15 ] || break
1046		i=$((i + 1))
1047		sleep 1
1048	done
1049
1050	if lsblk -l -o NAME | grep -q -w $1; then
1051		return 1
1052	fi
1053
1054	return 0
1055}
1056
1057function waitforfile() {
1058	local i=0
1059	while [ ! -e $1 ]; do
1060		[ $i -lt 200 ] || break
1061		i=$((i + 1))
1062		sleep 0.1
1063	done
1064
1065	if [ ! -e $1 ]; then
1066		return 1
1067	fi
1068
1069	return 0
1070}
1071
1072function fio_config_gen() {
1073	local config_file=$1
1074	local workload=$2
1075	local bdev_type=$3
1076	local fio_dir=$CONFIG_FIO_SOURCE_DIR
1077
1078	if [ -e "$config_file" ]; then
1079		echo "Configuration File Already Exists!: $config_file"
1080		return 1
1081	fi
1082
1083	if [ -z "$workload" ]; then
1084		workload=randrw
1085	fi
1086
1087	touch $1
1088
1089	cat > $1 << EOL
1090[global]
1091thread=1
1092group_reporting=1
1093direct=1
1094norandommap=1
1095percentile_list=50:99:99.9:99.99:99.999
1096time_based=1
1097ramp_time=0
1098EOL
1099
1100	if [ "$workload" == "verify" ]; then
1101		cat <<- EOL >> $config_file
1102			verify=sha1
1103			verify_backlog=1024
1104			rw=randwrite
1105		EOL
1106
1107		# To avoid potential data race issue due to the AIO device
1108		# flush mechanism, add the flag to serialize the writes.
1109		# This is to fix the intermittent IO failure issue of #935
1110		if [ "$bdev_type" == "AIO" ]; then
1111			if [[ $($fio_dir/fio --version) == *"fio-3"* ]]; then
1112				echo "serialize_overlap=1" >> $config_file
1113			fi
1114		fi
1115	elif [ "$workload" == "trim" ]; then
1116		echo "rw=trimwrite" >> $config_file
1117	else
1118		echo "rw=$workload" >> $config_file
1119	fi
1120}
1121
1122function fio_bdev() {
1123	# Setup fio binary cmd line
1124	local fio_dir=$CONFIG_FIO_SOURCE_DIR
1125	local bdev_plugin="$rootdir/build/fio/spdk_bdev"
1126
1127	# Preload AddressSanitizer library to fio if fio_plugin was compiled with it
1128	local asan_lib
1129	asan_lib=$(ldd $bdev_plugin | grep libasan | awk '{print $3}')
1130
1131	LD_PRELOAD="$asan_lib $bdev_plugin" "$fio_dir"/fio "$@"
1132}
1133
1134function fio_nvme() {
1135	# Setup fio binary cmd line
1136	local fio_dir=$CONFIG_FIO_SOURCE_DIR
1137	local nvme_plugin="$rootdir/build/fio/spdk_nvme"
1138
1139	# Preload AddressSanitizer library to fio if fio_plugin was compiled with it
1140	asan_lib=$(ldd $nvme_plugin | grep libasan | awk '{print $3}')
1141
1142	LD_PRELOAD="$asan_lib $nvme_plugin" "$fio_dir"/fio "$@"
1143}
1144
1145function get_lvs_free_mb() {
1146	local lvs_uuid=$1
1147	local lvs_info
1148	local fc
1149	local cs
1150	lvs_info=$($rpc_py bdev_lvol_get_lvstores)
1151	fc=$(jq ".[] | select(.uuid==\"$lvs_uuid\") .free_clusters" <<< "$lvs_info")
1152	cs=$(jq ".[] | select(.uuid==\"$lvs_uuid\") .cluster_size" <<< "$lvs_info")
1153
1154	# Change to MB's
1155	free_mb=$((fc * cs / 1024 / 1024))
1156	echo "$free_mb"
1157}
1158
1159function get_bdev_size() {
1160	local bdev_name=$1
1161	local bdev_info
1162	local bs
1163	local nb
1164	bdev_info=$($rpc_py bdev_get_bdevs -b $bdev_name)
1165	bs=$(jq ".[] .block_size" <<< "$bdev_info")
1166	nb=$(jq ".[] .num_blocks" <<< "$bdev_info")
1167
1168	# Change to MB's
1169	bdev_size=$((bs * nb / 1024 / 1024))
1170	echo "$bdev_size"
1171}
1172
1173function autotest_cleanup() {
1174	$rootdir/scripts/setup.sh reset
1175	$rootdir/scripts/setup.sh cleanup
1176	if [ $(uname -s) = "Linux" ]; then
1177		if grep -q '#define SPDK_CONFIG_IGB_UIO_DRIVER 1' $rootdir/include/spdk/config.h; then
1178			[[ -e /sys/module/igb_uio ]] && rmmod igb_uio
1179		else
1180			modprobe -r uio_pci_generic
1181		fi
1182	fi
1183	rm -rf "$asan_suppression_file"
1184	if [[ -n $old_core_pattern ]]; then
1185		echo "$old_core_pattern" > /proc/sys/kernel/core_pattern
1186	fi
1187	if [[ -e /proc/$udevadm_pid/status ]]; then
1188		kill "$udevadm_pid" || :
1189	fi
1190	revert_soft_roce
1191}
1192
1193function freebsd_update_contigmem_mod() {
1194	if [ $(uname) = FreeBSD ]; then
1195		kldunload contigmem.ko || true
1196		if [ -n "$SPDK_RUN_EXTERNAL_DPDK" ]; then
1197			echo "Warning: SPDK only works on FreeBSD with patches that only exist in SPDK's dpdk submodule"
1198			cp -f "$SPDK_RUN_EXTERNAL_DPDK/kmod/contigmem.ko" /boot/modules/
1199			cp -f "$SPDK_RUN_EXTERNAL_DPDK/kmod/contigmem.ko" /boot/kernel/
1200			cp -f "$SPDK_RUN_EXTERNAL_DPDK/kmod/nic_uio.ko" /boot/modules/
1201			cp -f "$SPDK_RUN_EXTERNAL_DPDK/kmod/nic_uio.ko" /boot/kernel/
1202		else
1203			cp -f "$rootdir/dpdk/build/kmod/contigmem.ko" /boot/modules/
1204			cp -f "$rootdir/dpdk/build/kmod/contigmem.ko" /boot/kernel/
1205			cp -f "$rootdir/dpdk/build/kmod/nic_uio.ko" /boot/modules/
1206			cp -f "$rootdir/dpdk/build/kmod/nic_uio.ko" /boot/kernel/
1207		fi
1208	fi
1209}
1210
1211function get_nvme_name_from_bdf() {
1212	blkname=()
1213
1214	nvme_devs=$(lsblk -d --output NAME | grep "^nvme") || true
1215	if [ -z "$nvme_devs" ]; then
1216		return
1217	fi
1218	for dev in $nvme_devs; do
1219		link_name=$(readlink /sys/block/$dev/device/device) || true
1220		if [ -z "$link_name" ]; then
1221			link_name=$(readlink /sys/block/$dev/device)
1222		fi
1223		bdf=$(basename "$link_name")
1224		if [ "$bdf" = "$1" ]; then
1225			blkname+=($dev)
1226		fi
1227	done
1228
1229	printf '%s\n' "${blkname[@]}"
1230}
1231
1232function get_nvme_ctrlr_from_bdf() {
1233	bdf_sysfs_path=$(readlink -f /sys/class/nvme/nvme* | grep "$1/nvme/nvme")
1234	if [[ -z "$bdf_sysfs_path" ]]; then
1235		return
1236	fi
1237
1238	printf '%s\n' "$(basename $bdf_sysfs_path)"
1239}
1240
1241# Get BDF addresses of all NVMe drives currently attached to
1242# uio-pci-generic or vfio-pci
1243function get_nvme_bdfs() {
1244	xtrace_disable
1245	bdfs=$(jq -r .config[].params.traddr <<< $($rootdir/scripts/gen_nvme.sh))
1246	if [[ -z $bdfs ]]; then
1247		echo "No devices to test on!"
1248		exit 1
1249	fi
1250	echo "$bdfs"
1251	xtrace_restore
1252}
1253
1254# Same as function above, but just get the first disks BDF address
1255function get_first_nvme_bdf() {
1256	head -1 <<< "$(get_nvme_bdfs)"
1257}
1258
1259function nvme_namespace_revert() {
1260	$rootdir/scripts/setup.sh
1261	sleep 1
1262	bdfs=$(get_nvme_bdfs)
1263
1264	$rootdir/scripts/setup.sh reset
1265
1266	for bdf in $bdfs; do
1267		nvme_ctrlr=/dev/$(get_nvme_ctrlr_from_bdf ${bdf})
1268		if [[ -z "$nvme_ctrlr" ]]; then
1269			continue
1270		fi
1271
1272		# Check Optional Admin Command Support for Namespace Management
1273		oacs=$(nvme id-ctrl ${nvme_ctrlr} | grep oacs | cut -d: -f2)
1274		oacs_ns_manage=$((oacs & 0x8))
1275
1276		if [[ "$oacs_ns_manage" -ne 0 ]]; then
1277			# This assumes every NVMe controller contains single namespace,
1278			# encompassing Total NVM Capacity and formatted as 512 block size.
1279			# 512 block size is needed for test/vhost/vhost_boot.sh to
1280			# succesfully run.
1281
1282			unvmcap=$(nvme id-ctrl ${nvme_ctrlr} | grep unvmcap | cut -d: -f2)
1283			if [[ "$unvmcap" -eq 0 ]]; then
1284				# All available space already used
1285				continue
1286			fi
1287			tnvmcap=$(nvme id-ctrl ${nvme_ctrlr} | grep tnvmcap | cut -d: -f2)
1288			blksize=512
1289
1290			size=$((tnvmcap / blksize))
1291
1292			nvme detach-ns ${nvme_ctrlr} -n 0xffffffff -c 0 || true
1293			nvme delete-ns ${nvme_ctrlr} -n 0xffffffff || true
1294			nvme create-ns ${nvme_ctrlr} -s ${size} -c ${size} -b ${blksize}
1295			nvme attach-ns ${nvme_ctrlr} -n 1 -c 0
1296			nvme reset ${nvme_ctrlr}
1297			waitforfile "${nvme_ctrlr}n1"
1298		fi
1299	done
1300}
1301
1302# Get BDFs based on device ID, such as 0x0a54
1303function get_nvme_bdfs_by_id() {
1304	local bdfs=()
1305
1306	for bdf in $(get_nvme_bdfs); do
1307		device=$(cat /sys/bus/pci/devices/$bdf/device) || true
1308		if [[ "$device" == "$1" ]]; then
1309			bdfs+=($bdf)
1310		fi
1311	done
1312
1313	printf '%s\n' "${bdfs[@]}"
1314}
1315
1316function opal_revert_cleanup() {
1317	# The OPAL CI tests is only used for P4510 devices.
1318	mapfile -t bdfs < <(get_nvme_bdfs_by_id 0x0a54)
1319	if [[ -z ${bdfs[0]} ]]; then
1320		return 0
1321	fi
1322
1323	$SPDK_BIN_DIR/spdk_tgt &
1324	spdk_tgt_pid=$!
1325	waitforlisten $spdk_tgt_pid
1326
1327	bdf_id=0
1328	for bdf in "${bdfs[@]}"; do
1329		$rootdir/scripts/rpc.py bdev_nvme_attach_controller -b "nvme"${bdf_id} -t "pcie" -a ${bdf}
1330		# Ignore if this fails.
1331		$rootdir/scripts/rpc.py bdev_nvme_opal_revert -b "nvme"${bdf_id} -p test || true
1332		((++bdf_id))
1333	done
1334
1335	killprocess $spdk_tgt_pid
1336}
1337
1338function pap() {
1339	while read -r file; do
1340		cat <<- FILE
1341			--- $file ---
1342			$(<"$file")
1343			--- $file ---
1344		FILE
1345		rm -f "$file"
1346	done < <(find "$@" -type f | sort -u)
1347}
1348
1349# Define temp storage for all the tests. Look for 2GB at minimum
1350set_test_storage "${TEST_MIN_STORAGE_SIZE:-$((1 << 31))}"
1351
1352set -o errtrace
1353shopt -s extdebug
1354trap "trap - ERR; print_backtrace >&2" ERR
1355
1356PS4=' \t	\$ '
1357if $SPDK_AUTOTEST_X; then
1358	# explicitly enable xtraces, overriding any tracking information.
1359	unset XTRACE_DISABLED
1360	unset XTRACE_NESTING_LEVEL
1361	set -x
1362	xtrace_enable
1363else
1364	xtrace_restore
1365fi
1366