• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/bin/sh
2#
3# Copyright (c) 2011-2016 Dmitry V. Levin <ldv@altlinux.org>
4# Copyright (c) 2011-2018 The strace developers.
5# All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions
9# are met:
10# 1. Redistributions of source code must retain the above copyright
11#    notice, this list of conditions and the following disclaimer.
12# 2. Redistributions in binary form must reproduce the above copyright
13#    notice, this list of conditions and the following disclaimer in the
14#    documentation and/or other materials provided with the distribution.
15# 3. The name of the author may not be used to endorse or promote products
16#    derived from this software without specific prior written permission.
17#
18# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29export LC_ALL=C
30ME_="${0##*/}"
31LOG="log"
32OUT="out"
33EXP="exp"
34
35warn_() { printf >&2 '%s\n' "$*"; }
36fail_() { warn_ "$ME_: failed test: $*"; exit 1; }
37skip_() { warn_ "$ME_: skipped test: $*"; exit 77; }
38framework_failure_() { warn_ "$ME_: framework failure: $*"; exit 99; }
39framework_skip_() { warn_ "$ME_: framework skip: $*"; exit 77; }
40
41check_prog()
42{
43	type "$@" > /dev/null 2>&1 ||
44		framework_skip_ "$* is not available"
45}
46
47dump_log_and_fail_with()
48{
49	cat < "$LOG" >&2
50	fail_ "$*"
51}
52
53run_prog()
54{
55	if [ $# -eq 0 ]; then
56		set -- "../$NAME"
57	fi
58	args="$*"
59	"$@" || {
60		rc=$?
61		if [ $rc -eq 77 ]; then
62			skip_ "$args exited with code 77"
63		else
64			fail_ "$args failed with code $rc"
65		fi
66	}
67}
68
69
70run_prog_skip_if_failed()
71{
72	args="$*"
73	"$@" || framework_skip_ "$args failed with code $?"
74}
75
76try_run_prog()
77{
78	local rc
79
80	"$@" > /dev/null || {
81		rc=$?
82		if [ $rc -eq 77 ]; then
83			return 1
84		else
85			fail_ "$* failed with code $rc"
86		fi
87	}
88}
89
90run_strace()
91{
92	> "$LOG" || fail_ "failed to write $LOG"
93	args="$*"
94	$STRACE -o "$LOG" "$@" ||
95		dump_log_and_fail_with "$STRACE $args failed with code $?"
96}
97
98run_strace_merge()
99{
100	rm -f -- "$LOG".[0-9]*
101	run_strace -ff -tt "$@"
102	"$srcdir"/../strace-log-merge "$LOG" > "$LOG" ||
103		dump_log_and_fail_with 'strace-log-merge failed with code $?'
104	rm -f -- "$LOG".[0-9]*
105}
106
107check_gawk()
108{
109	check_prog gawk
110	check_prog grep
111
112	local program="$1"; shift
113	if grep '^@include[[:space:]]' < "$program" > /dev/null; then
114		gawk '@include "/dev/null"' < /dev/null ||
115			framework_skip_ 'gawk does not support @include'
116	fi
117}
118
119# Usage: [FILE_TO_CHECK [AWK_PROGRAM [ERROR_MESSAGE [EXTRA_AWK_OPTIONS...]]]]
120# Check whether AWK_PROGRAM matches FILE_TO_CHECK using gawk.
121# If it doesn't, dump FILE_TO_CHECK and fail with ERROR_MESSAGE.
122match_awk()
123{
124	local output program error
125	if [ $# -eq 0 ]; then
126		output="$LOG"
127	else
128		output="$1"; shift
129	fi
130	if [ $# -eq 0 ]; then
131		program="$srcdir/$NAME.awk"
132	else
133		program="$1"; shift
134	fi
135	if [ $# -eq 0 ]; then
136		error="$STRACE $args output mismatch"
137	else
138		error="$1"; shift
139	fi
140
141	check_gawk "$program"
142
143	AWKPATH="$srcdir" gawk -f "$program" "$@" < "$output" || {
144		cat < "$output"
145		fail_ "$error"
146	}
147}
148
149# Usage: [FILE_TO_CHECK [FILE_TO_COMPATE_WITH [ERROR_MESSAGE]]]
150# Check whether FILE_TO_CHECK differs from FILE_TO_COMPATE_WITH.
151# If it does, dump the difference and fail with ERROR_MESSAGE.
152match_diff()
153{
154	local output expected error
155	if [ $# -eq 0 ]; then
156		output="$LOG"
157	else
158		output="$1"; shift
159	fi
160	if [ $# -eq 0 ]; then
161		expected="$srcdir/$NAME.expected"
162	else
163		expected="$1"; shift
164	fi
165	if [ $# -eq 0 ]; then
166		error="$STRACE $args output mismatch"
167	else
168		error="$1"; shift
169	fi
170
171	check_prog diff
172
173	diff -u -- "$expected" "$output" ||
174		fail_ "$error"
175}
176
177# Usage: [FILE_TO_CHECK [FILE_WITH_PATTERNS [ERROR_MESSAGE]]]
178# Check whether all patterns listed in FILE_WITH_PATTERNS
179# match FILE_TO_CHECK using egrep.
180# If at least one of these patterns does not match,
181# dump both files and fail with ERROR_MESSAGE.
182match_grep()
183{
184	local output patterns error pattern cnt failed=
185	if [ $# -eq 0 ]; then
186		output="$LOG"
187	else
188		output="$1"; shift
189	fi
190	if [ $# -eq 0 ]; then
191		patterns="$srcdir/$NAME.expected"
192	else
193		patterns="$1"; shift
194	fi
195	if [ $# -eq 0 ]; then
196		error="$STRACE $args output mismatch"
197	else
198		error="$1"; shift
199	fi
200
201	check_prog wc
202	check_prog grep
203
204	cnt=1
205	while read -r pattern; do
206		LC_ALL=C grep -E -x -e "$pattern" < "$output" > /dev/null || {
207			test -n "$failed" || {
208				echo 'Failed patterns of expected output:'
209				failed=1
210			}
211			printf '#%d: %s\n' "$cnt" "$pattern"
212		}
213		cnt=$(($cnt + 1))
214	done < "$patterns"
215	test -z "$failed" || {
216		echo 'Actual output:'
217		cat < "$output"
218		fail_ "$error"
219	}
220}
221
222# Usage: run_strace_match_diff [args to run_strace]
223run_strace_match_diff()
224{
225	args="$*"
226	[ -n "$args" -a -z "${args##*-e trace=*}" ] ||
227		set -- -e trace="$NAME" "$@"
228	run_prog > /dev/null
229	run_strace "$@" $args > "$EXP"
230	match_diff "$LOG" "$EXP"
231}
232
233# Usage: run_strace_match_grep [args to run_strace]
234run_strace_match_grep()
235{
236	args="$*"
237	[ -n "$args" -a -z "${args##*-e trace=*}" ] ||
238		set -- -e trace="$NAME" "$@"
239	run_prog > /dev/null
240	run_strace "$@" $args > "$EXP"
241	match_grep "$LOG" "$EXP"
242}
243
244# Print kernel version code.
245# usage: kernel_version_code $(uname -r)
246kernel_version_code()
247{
248	(
249		set -f
250		IFS=.
251		set -- $1 0 0
252		v1="${1%%[!0-9]*}" && [ -n "$v1" ] || v1=0
253		v2="${2%%[!0-9]*}" && [ -n "$v2" ] || v2=0
254		v3="${3%%[!0-9]*}" && [ -n "$v3" ] || v3=0
255		echo "$(($v1 * 65536 + $v2 * 256 + $v3))"
256	)
257}
258
259# Usage: require_min_kernel_version_or_skip 3.0
260require_min_kernel_version_or_skip()
261{
262	local uname_r
263	uname_r="$(uname -r)"
264
265	[ "$(kernel_version_code "$uname_r")" -ge \
266	  "$(kernel_version_code "$1")" ] ||
267		skip_ "the kernel release $uname_r is not $1 or newer"
268}
269
270# Usage: grep_pid_status $pid GREP-OPTIONS...
271grep_pid_status()
272{
273	local pid
274	pid=$1; shift
275	cat < "/proc/$pid/status" | grep "$@"
276}
277
278# Subtracts one program set from another.
279# If an optional regular expression is specified, the lines in the minuend file
280# that match this regular expression are elso excluded from the output.
281#
282# Usage: prog_set_subtract minuend_file subtrahend_file [subtrahend_regexp]
283prog_set_subtract()
284{
285	local min sub re pat
286	min="$1"; shift
287	sub="$1"; shift
288	re="${1-}"
289	pat="$re|$(sed 's/[[:space:]].*//' < "$sub" | tr -s '\n' '|')"
290	grep -E -v -x -e "$pat" < "$min"
291}
292
293# Usage: test_pure_prog_set [--expfile FILE] COMMON_ARGS < tests_file
294# stdin should consist of lines in "test_name strace_args..." format.
295test_pure_prog_set()
296{
297	local expfile
298
299	expfile="$EXP"
300
301	while [ -n "$1" ]; do
302		case "$1" in
303		--expfile)
304			shift
305			expfile="$1"
306			shift
307			;;
308		*)
309			break
310			;;
311		esac
312	done
313
314	while read -r t prog_args; do {
315		# skip lines beginning with "#" symbol
316		[ "${t###}" = "$t" ] || continue
317
318		try_run_prog "../$t" || continue
319		run_strace $prog_args "$@" "../$t" > "$expfile"
320		match_diff "$LOG" "$expfile"
321	} < /dev/null; done
322}
323
324# Run strace against list of programs put in "$NAME.in" and then against the
325# rest of pure_executables.list with the expectation of empty output in the
326# latter case.
327#
328# Usage: source this file after init.sh and call:
329#   test_trace_expr subtrahend_regexp strace_args
330# Environment:
331#   $NAME:	test name, used for "$NAME.in" file containing list of tests
332#		for positive trace expression match;
333#   $srcdir:	used to find pure_executables.list and "$NAME.in" files.
334# Files created:
335#   negative.list: File containing list of tests for negative match.
336test_trace_expr()
337{
338	local subtrahend_regexp
339	subtrahend_regexp="$1"; shift
340	test_pure_prog_set "$@" < "$srcdir/$NAME.in"
341	prog_set_subtract "$srcdir/pure_executables.list" "$srcdir/$NAME.in" \
342		"$subtrahend_regexp" > negative.list
343	test_pure_prog_set --expfile /dev/null -qq -esignal=none "$@" \
344		< negative.list
345}
346
347check_prog cat
348check_prog rm
349
350case "$ME_" in
351	*.gen.test) NAME="${ME_%.gen.test}" ;;
352	*.test) NAME="${ME_%.test}" ;;
353	*) NAME=
354esac
355
356STRACE_EXE=
357if [ -n "$NAME" ]; then
358	TESTDIR="$NAME.dir"
359	rm -rf -- "$TESTDIR"
360	mkdir -- "$TESTDIR"
361	cd "$TESTDIR"
362
363	case "$srcdir" in
364		/*) ;;
365		*) srcdir="../$srcdir" ;;
366	esac
367
368	[ -n "${STRACE-}" ] || {
369		STRACE=../../strace
370		case "${LOG_COMPILER-} ${LOG_FLAGS-}" in
371			*--suppressions=*--error-exitcode=*--tool=*)
372			STRACE_EXE="$STRACE"
373			# add valgrind command prefix
374			STRACE="${LOG_COMPILER-} ${LOG_FLAGS-} $STRACE"
375			;;
376		esac
377	}
378
379	trap 'dump_log_and_fail_with "time limit ($TIMEOUT_DURATION) exceeded"' XCPU
380else
381	: "${STRACE:=../strace}"
382fi
383
384# Export $STRACE_EXE to check_PROGRAMS.
385: "${STRACE_EXE:=$STRACE}"
386export STRACE_EXE
387
388: "${TIMEOUT_DURATION:=600}"
389: "${SLEEP_A_BIT:=sleep 1}"
390
391[ -z "${VERBOSE-}" ] ||
392	set -x
393