1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564
|
#!/bin/bash
#
# runtest.sh -- A dtrace tester and regression tester.
#
# Runs a bunch of .d scripts and tests their results.
# If more than one dtrace build directory is available,
# runs them each in turn, comparing their output
# and generated intermediate representation.
#
# Oracle Linux DTrace.
# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
# Licensed under the Universal Permissive License v 1.0 as shown at
# http://oss.oracle.com/licenses/upl.
# Sanitize the shell and get configuration info.
shopt -s nullglob extglob
unset CDPATH
unset POSIXLY_CORRECT # Interferes with 'wait'
export LC_COLLATE="C"
export dtrace="/usr/sbin/dtrace"
arch="$(uname -m)"
load_modules()
{
# If running as root, pull in appropriate modules
if [[ "x$(id -u)" = "x0" ]]; then
cat test/modules | xargs -rn 1 modprobe 2>/dev/null
fi
}
#
# Utility function to ensure a given provider is present.
# Uses the providers list written out at start time.
#
check_provider()
{
grep -q "^$1\$" $tmpdir/providers
}
export -f check_provider
# get_dir_name
#
# Pick a unique temporary directory name to stick things in.
# Returns results via `echo'; must be called via substitution.
#
# Respect TMPDIR if set, as POSIX requires.
get_dir_name()
{
local tmpname; # A subdir name being tested for uniqueness
false; # Starting state - reset $?
while [[ $? -ne 0 ]]; do
tmpname=${TMPDIR:-/tmp}/runtest.${RANDOM}
mkdir $tmpname # Test-and-set it
done
echo $tmpname # Name is unique, return it
}
# find_next_numeric_dir DIR
#
# Create and return the first empty numerically-named directory under DIR.
find_next_numeric_dir()
{
local i;
mkdir -p $1
for ((i=0; ; i++)); do
if [[ ! -e $1/$i ]]; then
mkdir -p $1/$i
chattr +D $1/$i 2>/dev/null
rm -f $1/current
ln -s $i $1/current
echo $1/$i
return
fi
done
}
declare -a ZAPTHESE=
# run_with_timeout TIMEOUT CMD [ARGS ...]
#
# Run command CMD with argument ARGS, with the specified timeout.
# Return the exitcode of the command, or $TIMEOUTRET if a timeout occurred.
# Also send a KILL signal if the command is still running TIMEOUTKIL seconds
# after the initial signal is sent, but start with TIMEOUTSIG so that DTrace
# can perform an orderly shutdown, if possible.
TIMEOUTSIG=TERM
TIMEOUTRET=124
TIMEOUTKIL=5
run_with_timeout()
{
log "Running timeout --signal=$TIMEOUTSIG $@ ${explicit_arg:+"$explicit_arg"}\n"
timeout --signal=$TIMEOUTSIG --kill-after=$TIMEOUTKIL $@ ${explicit_arg:+"$explicit_arg"}
local status=$?
# short wait to allow any coredump to trickle out to disk.
[[ $status -ne 0 ]] && sleep 1
# status 128+9 indicates timeout via KILL(=9)
if [[ $status -eq 137 ]]; then
status=$TIMEOUTRET
fi
return $status
}
# exist_options NAME FILE
#
# Return 0 iff a set of runtest options named NAME exists in FILE,
# or in the test.options file in its directory or in any parent
# up to test/.
#
# The options have the form @@NAME.
exist_options()
{
local retval=1
local path=$2
local file=$2
while [[ $retval -ne 0 ]]; do
if [[ -e "$file" ]]; then
grep -Eq "@@"$1 $file
retval=$?
fi
path="$(dirname $path)"
file="$path/test.options"
if [[ -e "$path/test.$arch.options" ]]; then
file="$path/test.$arch.options"
fi
# Halt at top level.
if [[ -e $path/Makecheck ]]; then
break
fi
done
return $retval
}
# extract_options NAME FILE EXPAND ACCUMULATE
#
# Extract the options named NAME from FILE, or in the test.options file in its
# directory or any parent up to test/, and return them on stdout.
#
# If EXPAND is set, do a round of shell evaluation on them first.
#
# If ACCUMULATE is set, return the concatenated values of all options named
# NAME up to test/.
extract_options()
{
local retval=1
local path=$2
local file=$2
local expand=$3
local accumulate=$4
local val=
while :; do
# This horrible sequence of patterns catches @@foo, @@foo: bar, and @@foo */,
# while not catching @@foo-bar or @@foo-bar: baz. */
if [[ -f $file ]] && grep -Eq -e "@@"$1' *:' -e "@@"$1" " -e "@@"$1'$' $file; then
local val="$val$(grep -E -e "@@"$1' *:' -e "@@"$1" " -e "@@"$1'$' $file | sed 's,.*@@'$1' *: ,,; s,\*/$,,; s, *$,,')"
if [[ -n $accumulate ]]; then
val="$val "
fi
fi
path="$(dirname $path)"
file="$path/test.options"
if [[ -e "$path/test.$arch.options" ]]; then
file="$path/test.$arch.options"
fi
# Halt when we have any tags if not accumulating, or at top level anyway.
if { [[ -z $accumulate ]] && [[ -n $val ]]; } || [[ -e $path/Makecheck ]]; then
# Force the $_pid to expand to itself.
_pid='$_pid'
# This trick is because printf with a straight eval ends up called
# with more args than format string items and squashes out spaces,
# echo -e -e foo prints 'foo', and bash's builtin echo -e -- "foo"
# prints "-- foo". coreutils echo does the right thing, but is more
# expensive than the builtin printf, so use it only when needed.
if [[ -n $val ]]; then
if [[ -n $expand ]]; then
eval printf "%s" \""$val"\"
else
printf "%s" ''"$val"
fi
fi
return
fi
done
}
# is_interpreter_file FILE
is_interpreter_file()
{
if [[ ! -e $1 ]]; then
return 1
fi
if [[ $(gawk '{print $1; exit}' $1) != "#!dtrace" ]]; then
return 1
fi
# At this point, perhaps we can also check that the file is executable.
# But we will copy it anyhow. So do not bother.
return 0
}
DEFAULT_TIMEOUT=41
usage()
{
cat <<EOF
$0 -- testsuite engine for DTrace
Usage: runtest options [TEST ...]
Options:
--timeout=TIME: Time out test runs after TIME seconds (default
$DEFAULT_TIMEOUT). (May cause many test failures if lowered.)
--skip-longer[=TIME]: Skip all tests with expected timeouts longer than
TIME (if not specified, $DEFAULT_TIMEOUT or whatever is
specified by --timeout, whichever is longer).
--ignore-timeouts: Treat all timeouts as not being failures. (For use when
testing under load.)
--testsuites=SUITES: Which testsuites (directories under test/) to run,
as a comma-separated list.
--[no-]execute: Execute probes with associated triggers.
--[no-]comparison: (Do not) compare results against expected results.
If result comparison is suppressed, XPASSes are
silently converted to PASSes (catering for expected
failures which are shown only via a miscomparison).
--[no-]capture-expected: Record results of tests that exit successfully
or with a timeout, but have no expected results,
as the expected results. (Only useful if
--no-comparison is not specified.)
--[no-]use-installed: Use an installed dtrace rather than a copy in the
source tree.
--quiet: Only show unexpected output (FAILs and XPASSes).
--verbose: The opposite of --quiet (and the default).
--[no-]tag=TAG: Run only tests with[out] TAG.
Multiple such options are ANDed together.
--help: This message.
If one or more TESTs is provided, they must be the name of .d files existing
under the test/ directory. If none is provided, all available tests are run.
EOF
exit 0
}
# Parameters.
CAPTURE_EXPECTED=${DTRACE_TEST_CAPTURE_EXPECTED:+t}
OVERWRITE_RESULTS=
NOEXEC=${DTRACE_TEST_NO_EXECUTE:+t}
USE_INSTALLED=${DTRACE_TEST_USE_INSTALLED:+t}
VALGRIND=${DTRACE_TEST_VALGRIND:+t}
COMPARISON=t
SKIP_LONGER=
if [[ -n $DTRACE_TEST_TESTSUITES ]]; then
TESTSUITES="${DTRACE_TEST_TESTSUITES}"
else
TESTSUITES="unittest internals stress demo"
fi
ONLY_TESTS=
TESTS=
QUIET=
USER_TAGS=
if [[ -n $DTRACE_TEST_TIMEOUT ]]; then
TIMEOUT="$DTRACE_TEST_TIMEOUT"
else
TIMEOUT=$DEFAULT_TIMEOUT
fi
IGNORE_TIMEOUTS=${DTRACE_TEST_IGNORE_TIMEOUTS:+t}
ERRORS=
while [[ $# -gt 0 ]]; do
case $1 in
--capture-expected) CAPTURE_EXPECTED=t;;
--no-capture-expected) CAPTURE_EXPECTED=;;
--execute) NOEXEC=;;
--no-execute) NOEXEC=t;;
--comparison) COMPARISON=t;;
--no-comparison) COMPARISON=;;
--valgrind) VALGRIND=t;;
--no-valgrind) VALGRIND=;;
--use-installed) USE_INSTALLED=t;;
--no-use-installed) USE_INSTALLED=;;
--timeout=*) TIMEOUT="$(printf -- $1 | cut -d= -f2-)";;
--ignore-timeouts) IGNORE_TIMEOUTS=t;;
--skip-longer) SKIP_LONGER=$TIMEOUT;;
--skip-longer=*) SKIP_LONGER="$(printf -- $1 | cut -d= -f2-)";;
--testsuites=*) TESTSUITES="$(printf -- $1 | cut -d= -f2- | tr "," " ")";;
--quiet) QUIET=t;;
--verbose) QUIET=;;
--tag=*) USER_TAGS="$USER_TAGS $(printf -- $1 | cut -d= -f2-)";;
--no-tag=*) USER_TAGS="$USER_TAGS !$(printf -- $1 | cut -d= -f2-)";;
--help|--*) usage;;
*) ONLY_TESTS=t
OVERWRITE_RESULTS=t
if [[ -f $1 ]]; then
TESTS="$TESTS $1"
else
echo "Test $1 not found, ignored." >&2
fi;;
esac
shift
done
# Store test results in a directory with an incrementing name.
# We arbitrarily assume that we are being run from ~: since
# this is what 'make check' does, that's a reasonable
# assumption.
cd $(dirname $0)
logdir=$(find_next_numeric_dir test/log)
LOGFILE=$logdir/runtest.log
SUMFILE=$logdir/runtest.sum
# Set a locked-memory limit that should be big enough for the test suite.
ulimit -l $((256 * 1024 * 1024))
# If running as root, remember and turn off core_pattern, and set the
# coredumpsize to a biggish value.
orig_core_pattern=
orig_core_uses_pid=
if [[ "x$(id -u)" = "x0" ]]; then
orig_core_pattern="$(cat /proc/sys/kernel/core_pattern)"
orig_core_uses_pid="$(cat /proc/sys/kernel/core_uses_pid)"
echo core > /proc/sys/kernel/core_pattern
echo 0 > /proc/sys/kernel/core_uses_pid
ulimit -c 1000000
fi
# Make a temporary directory for intermediate result storage.
# Make a binary directory within it, for wrapper scripts.
export tmpdir="$(get_dir_name)"
mkdir $tmpdir/bin
export PATH=$tmpdir/bin:$PATH
# Log and failure functions.
out()
{
if [[ -z $QUIET ]]; then
printf "%s" "$*" | sed 's,%,%%,g' | xargs -0n 1 printf
fi
sum "$@"
}
force_out_only()
{
printf "%s" "$*" | sed 's,%,%%,g' | xargs -0n 1 printf
}
force_out()
{
force_out_only "$@"
sum "$@"
}
sum()
{
printf "%s" "$*" | sed 's,%,%%,g' | xargs -0n 1 printf >> $SUMFILE
log "$@"
}
log()
{
printf "%s" "$*" | sed 's,%,%%,g' | xargs -0n 1 printf >> $LOGFILE
}
# At shutdown, delete this directory, kill requested processes, and restore
# core_pattern. When this is specifically an interruption, report as much in
# the log.
closedown()
{
for pid in ${ZAPTHESE[@]}; do
kill -9 -- $pid
done
if [[ -z $orig_core_pattern ]]; then
echo $orig_core_pattern > /proc/sys/kernel/core_pattern
echo $orig_core_uses_pid > /proc/sys/kernel/core_uses_pid
fi
declare -ga ZAPTHESE=
}
trap 'rm -rf ${tmpdir}; closedown; exit' EXIT
trap 'force_out "Test interrupted.\n"; closedown' INT HUP QUIT TERM
# fail XFAIL XFAILMSG FAILMSG
#
# Report a test failure, whether expected or otherwise, with the given
# message. If XFAIL is unset, XFAILMSG is ignored. If FAILMSG
# would be printed but is unset, $testmsg is used instead.
fail()
{
local xfail="$1"
local xfailmsg="$2"
# In quiet mode, we may need to print out the test name too.
[[ -n $QUIET ]] && [[ -z $xfail ]] && force_out_only "$_test: "
shift 2
local failmsg="$(echo ''"$@" | sed 's,^ *,,; s, $,,;')"
if [[ -z $failmsg ]]; then
failmsg="$testmsg"
fi
if [[ -n "$xfail" ]] && [[ -n "$xfailmsg" ]]; then
out "XFAIL: $@ ($xfailmsg).\n"
elif [[ -n "$xfail" ]]; then
out "XFAIL: $@.\n"
elif [[ -n $failmsg ]]; then
ERRORS=t
force_out "FAIL: $failmsg.\n"
else
ERRORS=t
force_out "FAIL.\n"
fi
}
# pass XFAIL MSG
#
# Report a test pass, possibly unexpected. The output format is subtly
# different from failures, to emphasise that the message is less important
# than the fact of passing. If MSG would be printed but is unset, $testmsg
# is used instead.
pass()
{
local xpass="$1"
local xout=out
# In quiet mode, we may need to print out the test name, and will
# want to print out the PASS message if and only if this was an
# XPASS.
if [[ -n $xpass ]] && [[ -n $COMPARISON ]]; then
[[ -n $QUIET ]] && force_out_only "$_test: "
force_out "X"
xout=force_out
fi
shift
local msg="$(echo ''"$@" | sed 's,^ *,,; s, $,,; s,^# *,,')"
if [[ -z $msg ]]; then
msg="$testmsg"
fi
if [[ $# -gt 0 ]] && [[ -n "$msg" ]]; then
$xout "PASS ($msg).\n"
else
$xout "PASS.\n"
fi
}
# postprocess POSTPROCESSOR OUTPUT FINAL
#
# Run postprocessing over some test OUTPUT, yielding a file named
# FINAL. Possibly run the POSTPROCESSOR first.
postprocess()
{
local postprocessor=$1
local output=$2
local final=$3
local retval=0
cp -f $output $tmpdir/pp.out
# Postprocess the output, if need be.
if [[ -x $postprocessor ]]; then
if $postprocessor < $output > $tmpdir/output.post; then
mv -f $tmpdir/output.post $tmpdir/pp.out
else
retval=$?
cp -f $output $tmpdir/pp.out
fi
fi
# Transform any printed hex strings into a fixed string, excepting
# only valgrind errors.
# TODO: may need adjustment or making optional if scripts emit hex
# values which are not continuously variable.
sed -e '/^==[0-9][0-9]*== /!s,0x[0-9a-f][0-9a-f]*,{ptr},g' \
-e 's,at BPF pc [1-9][0-9]*,at BPF pc NNN,' < $tmpdir/pp.out > $final
return $retval
}
# Log results. (Purely to reduce duplication further down.)
# The single argument indicates whether to write to the sumfile:
# writes to the logfile due to reinvocations should not go to the sumfile,
# since that is just noise and throws off results counting.
resultslog()
{
want_sum=$1
# Always log the unpostprocessed test output.
cat $testout >> $LOGFILE
rm -f $testout
if [[ -n $want_sum ]]; then
if [[ -n $want_all_output ]]; then
cat $tmpdir/test.out >> $SUMFILE
elif [[ -n $want_expected_diff ]]; then
log "Diff against expected:\n"
diff -au $rfile $tmpdir/test.out | tee -a $LOGFILE >> $SUMFILE
fi
fi
# Finally, write the debugging output, if any, to the logfile.
if [[ -s $testdebug ]]; then
echo "-- @@debug --" >> $LOGFILE
cat $testdebug >> $LOGFILE
fi
rm -f $testdebug
}
# Flip a few env vars to tell dtrace to produce reproducible output.
export _DTRACE_TESTING=t
export LANGUAGE=C
if [[ -z $USE_INSTALLED ]]; then
dtrace="$(pwd)/build*/dtrace"
test_libdir="$(pwd)/build/dlibs"
test_ldflags="-L$(pwd)/build"
test_cppflags="-I$(pwd)/include -I$(pwd)/uts/common -I$(pwd)/build -I$(pwd)/libdtrace -DARCH_$arch"
helper_device="dtrace/test-$$"
# Pre-existing directories from earlier tests are just fine!
# dtprobed will clean things up.
mkdir -p $tmpdir/run/dtrace
dtprobed_flags="-n $helper_device -s${tmpdir}/run/dtrace -F"
export DTRACE_DOF_INIT_DEVNAME="/dev/$helper_device"
export DTRACE_OPT_DOFSTASHPATH="${tmpdir}/run/dtrace"
if [[ -z $(eval echo $dtrace) ]]; then
echo "No dtraces available." >&2
exit 1
fi
build/dtprobed $dtprobed_flags &
export dtprobed_pid=$!
ZAPTHESE+=($dtprobed_pid)
else
# If we don't have a pkg-config path, try to point at a plausible
# one given DTrace's default install paths. This will fail if
# the pkg-config path is changed as well: in that case, presumably
# the person changing that path has pointed pkg-config at it anyway.
if [[ -z $PKG_CONFIG_PATH ]]; then
export PKG_CONFIG_PATH="$(pwd)/../../../share/pkgconfig"
fi
dtrace="$(pkg-config --variable=dtrace dtrace)"
test_libdir="installed"
test_ldflags=""
test_cppflags="-DARCH_$arch $(pkg-config --cflags dtrace_sdt) $(pkg-config --cflags dtrace)"
if [[ ! -x $dtrace ]]; then
echo "$dtrace not available." >&2
exit 1
fi
fi
core_raw_dt_flags="$test_cppflags"
export test_cppflags="$test_cppflags -fno-lto"
export test_ldflags="$test_ldflags -fno-lto"
export test_libdir
# Figure out if the preprocessor supports -fno-diagnostics-show-option: if it
# does, add a bunch of options designed to make GCC output look like it used
# to.
# Regardless, turn off display of column numbers, if possible: they vary
# between GCC releases.
if ! /usr/bin/cpp -x c -fno-diagnostics-show-caret - /dev/null < /dev/null 2>&1 | \
grep -q 'unrecognized command line option'; then
export DTRACE_OPT_CPPARGS="-fno-diagnostics-show-caret -fdiagnostics-color=never -fno-diagnostics-show-option -fno-show-column"
elif ! /usr/bin/cpp -x c -fno-show-column - /dev/null < /dev/null 2>&1 | \
grep -q 'unrecognized command line option'; then
export DTRACE_OPT_CPPARGS="-fno-show-column"
fi
# More than one dtrace tree -> run tests for all dtraces, and verify identical
# intermediate code is produced by each dtrace.
regression=
first_dtest_dir=
if [[ $(echo $dtrace | wc -w) -gt 1 ]]; then
regression=t
fi
# If not testing an installed dtrace, we must look for our system .d files
# in the right place.
if [[ "x$test_libdir" != "xinstalled" ]]; then
export DTRACE_OPT_SYSLIBDIR="$test_libdir"
fi
# Determine the list of available providers
for dt in $dtrace; do
# Look for our shared libraries in the right place.
if [[ "x$test_libdir" != "xinstalled" ]]; then
export LD_LIBRARY_PATH="$(dirname $dt)"
fi
# Write out a list of loaded providers.
DTRACE_DEBUG= $dt -l | tail -n +2 | gawk '{print $2;}' | sort -u > $tmpdir/providers
unset LD_LIBRARY_PATH
break
done
# Initialize test coverage.
for name in build*; do
if [[ -n "$(echo $name/*.gcno)" ]]; then
rm -rf $logdir/coverage
mkdir -p $logdir/coverage
lcov --zerocounters --directory $name --quiet
lcov --capture --base-directory . --directory $name --initial \
--quiet -o $logdir/coverage/initial.lcov 2>/dev/null
fi
done
load_modules
# Export some variables so triggers and .sh scripts can get at them.
export _test _pid dt_flags
export CC=${CC:-/usr/bin/gcc}
export NM=${NM:-/usr/bin/nm}
export OBJCOPY=${OBJCOPY:-/usr/bin/objcopy}
export OBJDUMP=${OBJDUMP:-/usr/bin/objdump}
export READELF=${READELF:-/usr/bin/readelf}
# Arrange to do (relatively expensive) mutex debugging.
export DTRACE_OPT_DEBUGASSERT="mutexes"
# If running DTrace inside valgrind, make sure valgrind works, and multiply
# the timeout by ten.
if [[ -n $VALGRIND ]]; then
if ! valgrind -q /bin/true >/dev/null 2>&1; then
echo "valgrind does not work: cannot run in --valgrind mode." >&2
exit 1
fi
TIMEOUT=$((TIMEOUT * 10))
fi
# Close unexpectedly open FDs.
for fd in /proc/$$/fd/*; do
fd="${fd##*/}"
case $fd in
0|1|2|255) continue;;
*) exec {fd}>&-;;
esac
done
# Mark the log and sumfiles as fsynced always.
touch $LOGFILE
touch $SUMFILE
chattr +S $LOGFILE $SUMFILE 2>/dev/null
# Form run and skip files from multiple sources of user-specified tags:
# - default-tag file (test/tags.default)
# - per-arch default-tag file (test/tags.$arch.default
# - environment variable (TEST_TAGS)
# - command-line options (--[no-]tag=)
USER_TAGS="$(cat test/tags.default test/tags.$arch.default 2>/dev/null) $TEST_TAGS $USER_TAGS"
rm -f $tmpdir/run.tags > /dev/null 2>&1
rm -f $tmpdir/skip.tags > /dev/null 2>&1
printf "%s\n" $USER_TAGS | grep -v '^!' | sort -u > $tmpdir/run.tags
printf "%s\n" $USER_TAGS | grep '^!' | sed 's/^!//' | sort -u > $tmpdir/skip.tags
# Free ourselves of the hassles of empty (but existing) files.
for name in $tmpdir/run.tags $tmpdir/skip.tags; do
if [[ `cat $name | wc -w` -eq 0 ]]; then
rm $name
fi
done
# Loop over each test in turn, or the specified subset if test names were passed
# on the command line.
# Tests are .d files, representing D programs passed to dtrace, or .sh files,
# representing shell scripts which invoke dtrace and analyze the results.
for dt in $dtrace; do
if [[ -n $regression ]]; then
force_out "\nTest of $dt:\n"
fi
# Look for our shared libraries in the right place.
if [[ "x$test_libdir" != "xinstalled" ]]; then
export LD_LIBRARY_PATH="$(dirname $dt)"
fi
# Log versions.
DTRACE_DEBUG= $dt -vV | tee -a $LOGFILE >> $SUMFILE
uname -a | tee -a $LOGFILE >> $SUMFILE
if [[ -f .git-version ]]; then
sum "testsuite version-control ID: $(cat .git-version)\n\n"
elif [[ -f .git-archive-version ]]; then
sum "Testsuite version-control ID: $(cat .git-archive-version)\n\n"
fi
# If we are running valgrind, invoke dtrace via a wrapper.
if [[ -n $VALGRIND ]]; then
cat >> $tmpdir/bin/$(basename $dt) <<-EOT
#!/bin/bash
# Wrap DTrace calls inside valgrind.
valgrind -q $dt "\$@"
EOT
dt="$tmpdir/bin/$(basename $dt)"
vg="valgrind -q "
chmod a+x $tmpdir/bin/$(basename $dt)
else
vg=""
fi
for _test in $(if [[ $ONLY_TESTS ]]; then
echo $TESTS | sed 's,\.r$,\.d,g; s,\.r ,.d ,g'
else
for name in $TESTSUITES; do
find test/$name \( -name "*.d" -o -name "*.sh" -o -name "*.c" \) | sort -u
done
fi); do
base=${_test%.d}
base=${base%.sh}
base=${base%.c}
testonly="$(basename $_test)"
export timeout="$TIMEOUT"
# Hidden files and editor backup files are not tests.
if [[ $_test =~ /\. ]] || [[ $test =~ ~$ ]]; then
continue
fi
if [[ ! -e $_test ]]; then
force_out "$_test: Not found.\n"
continue
fi
# Various options can be set in the test .d script, usually embedded in
# comments. In addition, any options listed in a file test.options in
# any directory from test/ on down will automatically be imposed on
# any tests in that directory tree which do not themselves impose a
# value for that option. If a file named test.$(uname -m).options
# exists at a given level, it is consulted instead of test.options at
# that level.
#
# @@runtest-opts: A set of options to be added to the default set
# (normally -S -e -s, where the -S and -e may not
# necessarily be provided.) Subjected to shell
# expansion.
#
# @@timeout: The timeout to use for this test. Overrides the --timeout
# parameter, iff the @@timeout is greater.
#
# @@skip: If true, the test is skipped.
#
# @@tags: Tag test case with specified tags, separated by spaces.
# Tags named after all subdirectories below 'test' are
# automatically added. (e.g. all tests in test/unittest/proc
# will gain the tags test/unittest/proc and test/unittest.)
#
# @@xfail: A single line containing a reason for this test's expected
# failure. (If the test passes unexpectedly, this message is
# not printed.) See '.x'.
#
# @@no-xfail: If present, this means that a test is *not* expected to
# fail, even though a test.options in a directory above
# this test says otherwise.
#
# @@timeout-success: If present, this means that a timeout is
# considered a test pass, not a test failure.
#
# @@deadman-success: If present, this means that a deadman timer
# firing is considered a test pass, not a test
# failure.
#
# @@reinvoke-failure: If present, this is an integer indicating the
# number of times to reinvoke failed tests: only
# if they fail every time will they be considered
# a FAIL rather than a PASS.
#
# @@trigger: A single line containing the name of a program in
# test/triggers which is executed after dtrace is started.
# When this program exits, dtrace will be killed. If the
# timeout expires, both programs are killed. Arguments may
# be provided as normal, and are subjected to shell
# expansion. For the sake of reproducible testcases, only
# tests with a trigger, or that explicitly declare that they
# can produce reproducible output via /* @@trigger: none */
# are ever executed against the kernel. Triggers should not
# emit anything to stdout or stderr, as their output is not
# currently trapped and it will spoil the screen display.
# See '.trigger'.
#
# @@trigger-timing: A single line containing 'before', 'after',
# 'synchro', or a number. If 'synchro' (the
# default), the trigger will be passed to dtrace via
# -c and left for dtrace to invoke. If 'after', the
# trigger will be exec()ed (from a pre-existing PID)
# after DTrace has started. If 'before', the trigger
# will already be running when DTrace starts. If a
# number, it is a count in seconds to delay trigger
# execution (to wait for DTrace to start).
#
# @@link: A library to link .c programs against (see below). -ldtrace
# by default.
#
# @@nosort: If present, this means do not sort before comparing results
# with a .r file. Results are sorted by default, since many
# test results do not have a well-defined order -- notably,
# when generated on different CPUs. Some tests, however,
# care about strict ordering.
#
# Certain filenames of test .d script are treated specially:
#
# tst.*.d: These are assumed to have /* @@trigger: none */ by default.
#
# err.*.d: As with tst.*.d, only these are additionally expected to
# return 1. (This is not the same as an XFAIL, which suggests
# that this test is known to be not passing: this indicates
# that this test is marked PASS if it returns 1, which would
# normally indicate failure.)
#
# err.D_*.d: As with err.*.d, only run with -xerrtags.
#
# drp.*.d: As with tst.*.d, only run -xdroptags.
# Various other files can exist with the same basename as the test.
# Many of these serve the same purpose as embedded comments: both exist
# to permit identical D scripts to be symlinked together while still
# permitting e.g. different triggering programs to be used.
#
# The full list of suffxes is as follows:
#
# .d: The D program itself.
#
# .sh: If executable, a shell script that is run instead of dtrace with
# a single argument, the path to dtrace. All other features are still
# provided, including timeouts. The script is run as if with
# /* @@trigger: none */. Arguments specified by @@runtest-opts, and
# a minimal set without which no tests will work, are passed in
# the environment variable dt_flags. An exit code of 67 will cause
# the test to be considered an expected failure.
#
# .c: A C program that is linked against the libraries listed in @@link,
# or -ldtrace by default, then run.
#
# .r: Expected results, after postprocessing. If not present,
# no expected-result comparison is done (the results are
# merely dumped into the logfile, and the test is deemed to
# pass as long as dtrace exits with a zero exitcode or
# times out). If any output is expected on standard error, it
# should be appended to this file, after a single line reading
# only "-- @@stderr --". Expected-result comparison can also
# be suppressed with the --no-expected command-line parameter.
#
# Files suffixed $(uname -m).r serve to override the results for a
# particular architecture.
#
# .r.p: Expected-results postprocessor: a filter run before various
# hardwired pointer-value-replacement preprocessors which
# transform all 0x[a-f]* strings into the string {ptr} and edit
# out CPU and probe IDs.
# Receives one argument, the name of the testcase.
#
# .x: If executable, a program which when executed indicates whether
# the test is expected to succeed or not in this environment.
# 0 indicates expected success, 1 expected failure, or 2 that the
# test is unreliable and should be skipped; in the latter two cases
# it can emit to standard output a one-line message describing the
# reason for failure or skipping. This permits tests to inspect
# their environment and determine whether failure is expected, or
# whether they can safely run at all. See '@@xfail' and '@@skip'.
# If both .x and @@xfail exist, only the .x is respected.
#
# Files suffixed $(uname -m).x allow xfails or skips for a
# particular architecture.
#
# As with test.options, executable files named test.x and
# test.$(uname -m).x can exist at any level in the hierarchy:
# the one that is 'most faily' wins (so any SKIP overrides any
# XFAIL, which overrides any PASS, no matter what level of the
# hierarchy it is found at).
#
# .t: If executable, serves the same purpose as the '@@trigger'
# option above. If both .t and @@trigger exist, only the .t is
# respected.
_exit="${NOEXEC:+-e}"
# Optionally skip this test.
if exist_options skip $_test; then
sum "$_test: SKIP: $(extract_options skip $_test)\n"
continue
fi
# Check if the user specified any tags.
if [[ -n "$USER_TAGS" ]]; then
# Extract tags from test, and add tags derived from the test's containing
# directory.
rm -f $tmpdir/test.tags >/dev/null 2>&1
test_tags="$(extract_options tags $_test "" t)"
test_tags="$test_tags $(shrinktest=$_test
while :; do
shrinktest="${shrinktest%/*}"
[[ "$shrinktest" =~ / ]] || break
printf "%s " "$shrinktest"
done)"
printf "%s\n" $test_tags | sort -u > $tmpdir/test.tags
# If user specified run tags, check for them.
if [[ -e $tmpdir/run.tags ]]; then
if [[ -n "$(comm -23 $tmpdir/run.tags $tmpdir/test.tags)" ]]; then
# Some user-specified run tags are not in test. Skip test.
tagdiff=$(comm -23 $tmpdir/run.tags $tmpdir/test.tags | xargs)
sum "$_test: SKIP: test lacks requested tags: $tagdiff\n"
continue
fi
fi
# If user specified skip tags, check for them.
if [[ -e $tmpdir/skip.tags && -n $test_tags ]]; then
if [[ -n "$(comm -12 $tmpdir/skip.tags $tmpdir/test.tags)" ]]; then
# Some user-specified skip tags are in test. Skip test.
tagdiff=$(comm -12 $tmpdir/skip.tags $tmpdir/test.tags | xargs)
sum "$_test: SKIP: test has excluded tags: $tagdiff\n"
continue
fi
fi
fi
# Per-test timeout.
if exist_options timeout $_test; then
if [[ "$(extract_options timeout $_test)" -gt $timeout ]]; then
timeout="$(extract_options timeout $_test)"
if [[ -n $VALGRIND ]]; then
timeout=$((timeout * 10))
fi
fi
fi
# Timeout-based skipping.
if [[ -n $SKIP_LONGER ]] && [[ $timeout -gt $SKIP_LONGER ]]; then
sum "$_test: SKIP: would take too long\n"
continue
fi
# Note if this is expected to fail.
xfail=0
xfailpath="$_test"
xbase=$base
xfailmsg=
if exist_options xfail $_test && ! exist_options no-xfail $_test; then
xfail=1
xfailmsg="$(extract_options xfail $_test | sed 's, *$,,')"
fi
# Scan for $test.x, $test.$arch.x, then test.x and test.arch.x
# all the way up to the top level, much as for test.options.
# Whichever .x file returns the highest value is used (SKIP >
# XFAIL > PASS).
while [[ $xfail -lt 2 ]]; do
xfile=$xbase.$arch.x
[[ -e $xfile ]] || xfile=$xbase.x
if [[ -x $xfile ]]; then
# xfail program. Run, and capture its output, clamping the
# exitcode at 2. (The output of whichever program has the
# highest exitcode is used.)
xfailmsgthisfile="$($xfile)"
xfailthisfile=$?
if [[ $xfailthisfile -gt 2 ]]; then
echo "$xfile: Unexpected return value $xfailthisfile." >&2
xfailthisfile=2
fi
if [[ $xfailthisfile -gt $xfail ]]; then
xfail=$xfailthisfile
xfailmsg="$xfailmsgthisfile"
fi
elif [[ -e $xfile ]] && [[ $xfail -eq 0 ]]; then
xfail=1
fi
xfailpath="$(dirname $xfailpath)"
xbase="$xfailpath/test"
if [[ -e "$xfailpath/test.$arch.x" ]]; then
xbase="$xfailpath/test.$arch"
fi
# Halt at top level.
if [[ -e $xfailpath/Makecheck ]]; then
break
fi
done
case $xfail in
0) xfail="";; # no failure expected
1) xfail=t;; # failure expected
2) sum "$_test: SKIP${xfailmsg:+: $xfailmsg}.\n" # skip
continue;;
*) xfail="";
echo "$xfile: Unexpected return value $?." >&2;;
esac
# Failure reinvocation.
reinvoke_failure=0
reinvoked=0
if exist_options reinvoke-failure $_test; then
reinvoke_failure="$(extract_options reinvoke-failure $_test)"
fi
# Check for a trigger.
trigger=
# tst.*, err.* and drp.* default to running despite the absence of
# a trigger.
if [[ $testonly =~ ^(tst|err|drp)\. ]]; then
trigger=none
fi
# Get a trigger from @@trigger, if possible.
if exist_options trigger $_test; then
trigger="$(extract_options trigger $_test t)"
fi
# Non-'none' @@triggers must exist in test/triggers.
if [[ "x$trigger" = "xnone" ]] || [[ -z $trigger ]]; then
:
elif [[ ! -x test/triggers/$(printf '%s' "$trigger" | cut -d\ -f1) ]]; then
out "$_test: "
fail=t
trigger=$(printf '%s' "$trigger" | cut -d\ -f1)
fail "$xfail" "$xfailmsg" "trigger $trigger not found in test/triggers"
continue
else
trigger="test/triggers/$trigger"
fi
# Finally, look for a .t file.
if [[ -x $base.t ]]; then
trigger=$base.t
fi
# Note the expected exitcode of this test.
expected_exitcode=0
if [[ $testonly =~ ^err\. ]]; then
expected_exitcode=1
fi
# Default and substitute in flags. The raw_dt_flags apply even to a
# sh invocation.
raw_dt_flags="$core_raw_dt_flags"
expected_tag=
if [[ $testonly =~ ^err\.D_ ]]; then
raw_dt_flags="$raw_dt_flags -xerrtags"
expected_tag="$(echo ''"$testonly" | sed 's,^err\.,,; s,\..*$,,')"
elif [[ $testonly =~ ^drp\. ]]; then
raw_dt_flags="$raw_dt_flags -xdroptags"
expected_tag="$(echo ''"$testonly" | sed 's,^drp\.,,; s,\..*$,,')"
fi
dt_flags="$_exit $raw_dt_flags -s $_test"
if exist_options runtest-opts $_test; then
opts="$(extract_options runtest-opts $_test t)"
# The flags go after the dt_flags above, and also land in
# raw_dt_flags for the sake of shell-script runners.
if [[ -n $opts ]] && [[ -z $override ]]; then
dt_flags="$dt_flags $opts"
raw_dt_flags="$raw_dt_flags $opts"
fi
fi
# Handle an executable .sh.
progtype=d
run=$dt
interpreter_file=""
if is_interpreter_file $base.d; then
interpreter_file=$tmpdir/test.interpreter.$RANDOM.d
rm -f $interpreter_file
sed 's:^#!dtrace :#!'$dt' :' $base.d > $interpreter_file
chmod +x $interpreter_file
progtype=shell
run="$interpreter_file $raw_dt_flags"
elif [[ -x $base.sh ]]; then
progtype=shell
run="$base.sh $dt"
if [[ -z $trigger ]]; then
trigger=none
fi
dt_flags="$raw_dt_flags"
elif [[ -e $base.c ]]; then
progtype=c
run="$vg $tmpdir/$(basename $base)"
fi
# Erase any pre-existing coredump, then run dtrace, with a
# timeout, without permitting execution, recording the output and
# exitcode into temporary files. (We use a different temporary file on
# every invocation, so that hanging subprocesses emitting output into
# these files do not mess up subsequent tests. This won't strictly fix
# the problem, but will drive its probability of occurrence way down,
# even in the presence of a hanging test. stdout and stderr go into
# different files, and any debugging output is split into a third.)
rm -f core
fail=
this_noexec=$NOEXEC
testmsg=
testout=$tmpdir/test.out.$RANDOM
testerr=$tmpdir/test.err.$RANDOM
testdebug=$tmpdir/test.debug.$RANDOM
out "$_test: "
if [[ "$progtype" = "c" ]]; then
if [[ -z "$(extract_options link $_test)" ]]; then
link="-ldtrace"
else
link="$(extract_options link $_test)"
fi
CC="${CC:-gcc}"
CCline="$CC -std=gnu99 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64"
CCline="$CCline $test_cppflags $CFLAGS $test_ldflags $LDFLAGS"
CCline="$CCline -o $tmpdir/$(basename $base) $_test $link"
log "Compiling $CCline\n"
if ! $CCline >/dev/null 2>$tmpdir/cc.err; then
fail=t
fail "$xfail" "$xfailmsg" "compilation failure"
cat $tmpdir/cc.err >> $LOGFILE
continue
fi
fi
orig_dt_flags="$dt_flags"
while [[ $reinvoke_failure -ge 0 ]]; do
fail=
tst=$base
export tst
if [[ -z $trigger ]] || [[ "$trigger" = "none" ]]; then
# No trigger.
case $progtype in
d) eflag=
if [[ -z $trigger ]]; then
eflag=-e
this_noexec=t
fi
run_with_timeout $timeout $run $dt_flags $eflag > $testout 2> $testerr;;
shell) run_with_timeout $timeout $run > $testout 2> $testerr;;
c) run_with_timeout $timeout $run > $testout 2> $testerr;;
*) out "$_test: Internal error: unknown program type $progtype";;
esac
exitcode=$?
else
# A trigger. Run dtrace with timeout, and permit execution. First,
# run the trigger with a 1s delay before invocation, record
# its PID, and note it in the dt_flags if $_pid is there.
#
# We have to run each of these in separate subprocesses to avoid
# the SIGCHLD from the sleep 1's death leaking into run_with_timeout
# and confusing it. (This happens even if disowned.)
#
# For .c tests, none of this is supported: we just run the trigger
# first and kill it afterwards, as if @@trigger-timing: before,
# passing the PID to the C program as its lone parameter.
trigger_timing=synchro
trigger_delay=
needszap=
if exist_options trigger-timing $_test &&
[[ "x$(extract_options trigger-timing $_test)" != "xsynchro" ]]; then
trigger_timing="$(extract_options trigger-timing $_test)"
case $trigger_timing in
after) trigger_delay=1;;
before|synchro) ;;
*) trigger_delay=$trigger_timing;;
esac
fi
explicit_arg=
if [[ "$trigger_timing" == "synchro" ]] && [[ $progtype != "c" ]]; then
# This is an unbearably ugly hack to try to kludge around
# shell quoting difficulties.
dt_flags="$dt_flags -c "
explicit_arg="$trigger"
_pid=
else
log "Running trigger $trigger${trigger_delay:+ with delay $trigger_delay}\n"
( [[ -n $trigger_delay ]] && sleep $trigger_delay; exec $trigger; ) &
_pid=$!
disown %-
needszap=t
ZAPTHESE+=($_pid)
if [[ $dt_flags =~ \$_pid ]]; then
dt_flags="$(echo ''"$dt_flags" | sed 's,\$_pid[^a-zA-Z],'$_pid',g; s,\$_pid$,'$_pid',g')"
fi
fi
case $progtype in
d) ( run_with_timeout $timeout $run $dt_flags > $testout 2> $testerr
echo $? > $tmpdir/dtrace.exit; );;
shell) ( run_with_timeout $timeout $run > $testout 2> $testerr
echo $? > $tmpdir/dtrace.exit; );;
c) ( run_with_timeout $timeout $run $_pid > $testout 2> $testerr
echo $? > $tmpdir/dtrace.exit; );;
*) out "$_test: Internal error: unknown program type $progtype";;
esac
exitcode="$(cat $tmpdir/dtrace.exit)"
explicit_arg=
# If the trigger is still running, kill it, and wait for it, to
# quiesce the background-process-kill noise the shell would
# otherwise emit.
if [[ -n $_pid ]] && [[ "$(ps -p $_pid -o ppid=)" -eq $BASHPID ]]; then
kill $_pid >/dev/null 2>&1
fi
if [[ -n $needszap ]]; then
unset "ZAPTHESE[$((${#ZAPTHESE[@]}-1))]"
fi
unset _pid
fi
# Translate $interpreter_file back into name of test file.
if [[ -n $interpreter_file ]]; then
sed -i 's:'$interpreter_file':'$base.d':' $testout
sed -i 's:'$interpreter_file':'$base.d':' $testerr
fi
# Split debugging info out of the test output.
grep -E '^[a-z_]+ DEBUG [0-9]+: ' $testerr > $testdebug
grep -vE '^[a-z_]+ DEBUG [0-9]+: ' $testerr > $testerr.tmp
# Account for an error message change in CTF
sed -e 's/Invalid member name/Member name not found/' $testerr.tmp > $testerr
rm -f $testerr.tmp
# Note if dtrace mentions running out of memory at any point.
# If it does, this test quietly becomes an expected failure
# (without transforming an err.* test into an XPASS).
if grep -q "Cannot allocate memory" $testout $testerr; then
xfailmsg="out of memory"
if [[ $expected_exitcode -eq 0 ]]; then
xfail=t
fi
fi
# If deadman-success is on, then a deadman timer firing turns
# a test into an XFAIL.
if grep -q "Abort due to systemic unresponsiveness" $testerr &&
[[ "x$(extract_options deadman-success $_test)" != "x" ]]; then
testmsg="(expected) systemic unresponsiveness"
if [[ $expected_exitcode -eq 0 ]]; then
xfail=t
fi
fi
# If we are expecting a specific tag in the error output, make sure
# it is present. If found, we clear expected_tag to signal that
# all is fine.
if [[ -n $expected_tag ]] &&
grep -q "\[$expected_tag\]" $testerr; then
expected_tag=
fi
if [[ -n $this_noexec ]]; then
testmsg="no execution"
fi
if [[ -s $testerr ]]; then
echo "-- @@stderr --" >> $testout
cat $testerr >> $testout
fi
if ! postprocess $base.r.p $testout $tmpdir/test.out; then
testmsg="results postprocessor failed with exitcode $?"
fail=t
fi
rm -f $testerr
# Note if we will certainly capture results.
capturing=
if [[ -n $CAPTURE_EXPECTED ]] && [[ -n $OVERWRITE_RESULTS ]] &&
[[ -n $COMPARISON ]]; then
capturing="results captured"
fi
# Results processing is complicated by the existence of a
# class-of-failure hierarchy (e.g. coredumps should be reported in
# preference to comparison failures, but we still want comparison
# failures to be logged) and by the need to print a message about each
# failure at most once, and by our desire to print any expected-results
# diff after the logged output (so it stands out).
want_expected_diff=
want_all_output=
failmsg=
# By default, sort before comparing since test results often
# have indeterminate order (generated on different CPUs, etc.).
if exist_options nosort $_test; then
sortcmd=cat
else
sortcmd=sort
fi
# Compare results, if available, and log the diff.
rfile=$base.$arch.r
[[ -e $rfile ]] || rfile=$base.r
if [[ -e $rfile ]] && [[ -n $COMPARISON ]] &&
! diff -u <($sortcmd $rfile) <($sortcmd $tmpdir/test.out) >/dev/null; then
fail=t
failmsg="expected results differ"
want_expected_diff=t
elif [[ ! -e $base.r ]]; then
# No expected results? Write all the output to the sumfile.
# (It is also always written to the logfile: see below.)
want_all_output=t
fi
if [[ -f core ]] || [[ "$(find $tmpdir -name core -print)" != "" ]] || [[ $exitcode = 139 ]]; then
# A coredump in the current directory or under the tmpdir.
# Preserve it in the logdir, if possible.
mv core $logdir/$(echo $base | tr '/' '-').core 2>/dev/null || true
find $tmpdir -name core -type f -print0 | xargs -0r -I'{}' mv --backup=numbered '{}' $logdir/$(echo $base | tr '/' '-').core
fail=t
failmsg="core dumped"
# Expected tag not found.
elif [[ -n $expected_tag ]]; then
fail=t
failmsg="expected tag $expected_tag"
reinvoke_failure=-1
# Detect a timeout.
elif [[ $exitcode -eq $TIMEOUTRET ]] && [[ -z $IGNORE_TIMEOUTS ]] &&
[[ "x$(extract_options timeout-success $_test)" = "x" ]]; then
fail=t
failmsg="timed out"
# Exitcode of 67 == XFAIL.
elif [[ $exitcode -eq 67 ]]; then
[[ $expected_exitcode -eq 1 ]] && fail=t
xfail=t
failmsg="requested by test"
# Exitcodes are not useful if there's been a coredump, but otherwise...
elif [[ $exitcode != $expected_exitcode ]] && [[ $exitcode -ne $TIMEOUTRET ]]; then
# Some sort of exitcode error. Assume that errors in the
# range 129 -- 193 (a common value of SIGRTMAX) are signal
# exits.
fail=t
if [[ $exitcode -lt 129 ]] || [[ $exitcode -gt 193 ]]; then
failmsg="erroneous exitcode ($exitcode)"
else
failmsg="hit by signal $((exitcode - 128))"
fi
fi
reinvoke_failure=$((reinvoke_failure-1))
if [[ -z $fail ]]; then
# No reinvocation if we didn't fail.
reinvoke_failure=-1
elif [[ -n $fail ]] && [[ $reinvoke_failure -ge 0 ]]; then
reinvoked=$((reinvoked+1))
testmsg="after $reinvoked reinvocations"
resultslog
fi
dt_flags="$orig_dt_flags"
done
# If it xpasses but is unstable, just call it xfail.
if [[ -z $fail ]] && [[ -n $xfail ]] && [[ "$(extract_options tags $_test)" == *unstable* ]]; then
fail=t
if [[ -z $xfailmsg ]]; then
xfailmsg="xpassed but unstable"
else
xfailmsg="xpassed but unstable; $xfailmsg"
fi
fi
if [[ -z $fail ]]; then
# Success!
# If results don't already exist and we are in capture mode, then
# capture them even if forcible capturing is off.
if [[ -n $CAPTURE_EXPECTED ]] && [[ -n $COMPARISON ]] &&
[[ ! -e $base.r ]]; then
capturing="results captured"
fi
pass "$xfail" "$xfailmsg" "$capturing"
else
# If it fails, is not xfail, and is unstable, just call it xfail.
if [[ -z $xfail ]] && [[ "$(extract_options tags $_test)" == *unstable* ]]; then
xfail=t
if [[ -z $xfailmsg ]]; then
xfailmsg="unstable"
else
xfailmsg="unstable; $xfailmsg"
fi
fi
fail "$xfail" "$xfailmsg" "$failmsg${capturing:+, $capturing}"
fi
resultslog t
# If capturing results is requested, capture them now.
if [[ -n $capturing ]]; then
cp -f $tmpdir/test.out $base.r
fi
test/utils/clean_probes.sh >> $LOGFILE
log "\n"
test/utils/get_remote.sh cleanup
if [[ -n $regression ]]; then
# If regtesting, we run a second time, with intermediate results
# displayed, and output redirected to a per-test, per-dtrace
# temporary file. We always ban execution during regtesting:
# our real interest is whether intermediate results have changed.
dtest_dir="$tmpdir/regtest/$(basename $(dirname $dt))"
reglog="$dtest_dir/$(echo $_test | sed 's,/,-,g').log"
mkdir -p $dtest_dir
echo "$dt_flags -e" > $reglog
echo >> $reglog
DTRACE_DEBUG= $dt -S $dt_flags -e > $tmpdir/regtest.out 2>&1
postprocess $base.r.p $tmpdir/regtest.out $tmpdir/regtest.out
cat $tmpdir/regtest.out >> $reglog
if [[ -z $first_dtest_dir ]]; then
first_dtest_dir=$dtest_dir
fi
fi
done
done
if [[ -n $regression ]]; then
# Regtest comparison. Skip one directory and diff all the others
# against it.
force_out "\n\nRegression test comparison.\n"
for name in $tmpdir/regtest/*; do
[[ $name = $first_dtest_dir ]] && continue
if ! diff -urN $first_dtest_dir $name | tee -a $LOGFILE $SUMFILE; then
force_out "Regression test comparison failed."
fi
done
else
# Test summary.
gawk -f - $SUMFILE <<'EOF' | tee -a $LOGFILE $SUMFILE
{
rc = 0;
}
match($0, /: X?(FAIL|PASS|SKIP)/) {
rc = substr($0, RSTART + 2, RLENGTH - 2);
count[rc]++;
total++;
}
rc && match($0, /after [0-9]+ reinvocations/) {
$0 = substr($0, RSTART);
count["REINVOKES"] += int($2);
}
END {
if (count["REINVOKES"] == 0)
printf("%d cases (%d PASS, %d FAIL, %d XPASS, %d XFAIL, %d SKIP)\n",
total, count["PASS"], count["FAIL"], count["XPASS"],
count["XFAIL"], count["SKIP"]);
else
printf("%d cases (%d PASS (%d reinvocations), %d FAIL, %d XPASS, %d XFAIL, %d SKIP)\n",
total, count["PASS"], count["REINVOKES"], count["FAIL"],
count["XPASS"], count["XFAIL"], count["SKIP"]);
}
EOF
fi
# Test coverage.
for name in build*; do
if [[ -n "$(echo $name/*.gcda)" ]]; then
force_out "Coverage info for $(echo $name | sed 's,build,userspace,'):\n"
lcov --capture --base-directory . --directory $name \
--quiet -o $logdir/coverage/runtest.lcov
lcov --add-tracefile $logdir/coverage/initial.lcov \
--add-tracefile $logdir/coverage/runtest.lcov \
--quiet -o $logdir/coverage/coverage.lcov
genhtml --frames --show-details -o $logdir/coverage \
--title "DTrace userspace coverage" \
--highlight --legend $logdir/coverage/coverage.lcov | \
gawk 'BEGIN { quiet=1; } { if (!quiet) { print ($0); } } /^Overall coverage rate:$/ { quiet=0; }' | \
tee -a $LOGFILE $SUMFILE
fi
done
if [[ -n $ERRORS ]]; then
exit 1
fi
exit 0
|