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