1 #!/bin/sh 2 # 3 # Copyright (c) 2011-2016 Dmitry V. Levin <ldv (at] 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 29 export LC_ALL=C 30 ME_="${0##*/}" 31 LOG="log" 32 OUT="out" 33 EXP="exp" 34 35 warn_() { printf >&2 '%s\n' "$*"; } 36 fail_() { warn_ "$ME_: failed test: $*"; exit 1; } 37 skip_() { warn_ "$ME_: skipped test: $*"; exit 77; } 38 framework_failure_() { warn_ "$ME_: framework failure: $*"; exit 99; } 39 framework_skip_() { warn_ "$ME_: framework skip: $*"; exit 77; } 40 41 check_prog() 42 { 43 type "$@" > /dev/null 2>&1 || 44 framework_skip_ "$* is not available" 45 } 46 47 dump_log_and_fail_with() 48 { 49 cat < "$LOG" >&2 50 fail_ "$*" 51 } 52 53 run_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 70 run_prog_skip_if_failed() 71 { 72 args="$*" 73 "$@" || framework_skip_ "$args failed with code $?" 74 } 75 76 try_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 90 run_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 98 run_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 107 check_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. 122 match_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. 152 match_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. 182 match_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] 223 run_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] 234 run_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) 246 kernel_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 260 require_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... 271 grep_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] 283 prog_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. 295 test_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. 336 test_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 347 check_prog cat 348 check_prog rm 349 350 case "$ME_" in 351 *.gen.test) NAME="${ME_%.gen.test}" ;; 352 *.test) NAME="${ME_%.test}" ;; 353 *) NAME= 354 esac 355 356 STRACE_EXE= 357 if [ -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 380 else 381 : "${STRACE:=../strace}" 382 fi 383 384 # Export $STRACE_EXE to check_PROGRAMS. 385 : "${STRACE_EXE:=$STRACE}" 386 export STRACE_EXE 387 388 : "${TIMEOUT_DURATION:=600}" 389 : "${SLEEP_A_BIT:=sleep 1}" 390 391 [ -z "${VERBOSE-}" ] || 392 set -x 393