1#!/usr/perl5/bin/perl 2# 3# CDDL HEADER START 4# 5# The contents of this file are subject to the terms of the 6# Common Development and Distribution License (the "License"). 7# You may not use this file except in compliance with the License. 8# 9# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10# or http://www.opensolaris.org/os/licensing. 11# See the License for the specific language governing permissions 12# and limitations under the License. 13# 14# When distributing Covered Code, include this CDDL HEADER in each 15# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16# If applicable, add the following below this CDDL HEADER, with the 17# fields enclosed by brackets "[]" replaced with your own identifying 18# information: Portions Copyright [yyyy] [name of copyright owner] 19# 20# CDDL HEADER END 21# 22 23# 24# Copyright 2006 Sun Microsystems, Inc. All rights reserved. 25# Use is subject to license terms. 26# 27# ident "%Z%%M% %I% %E% SMI" 28 29require 5.6.1; 30 31use File::Find; 32use File::Basename; 33use Getopt::Std; 34use Cwd; 35use Cwd 'abs_path'; 36 37$PNAME = $0; 38$PNAME =~ s:.*/::; 39$OPTSTR = 'abd:ghi:lqsux:'; 40$USAGE = "Usage: $PNAME [-abghlqsu] [-d dir] [-i isa] " 41 . "[-x opt[=arg]] [file | dir ...]\n"; 42($MACH = `uname -p`) =~ s/\W*\n//; 43 44$dtrace_path = '/usr/sbin/dtrace'; 45@dtrace_argv = (); 46 47$ksh_path = '/usr/bin/ksh'; 48 49@files = (); 50%exceptions = (); 51$errs = 0; 52$bypassed = 0; 53 54# 55# If no test files are specified on the command-line, execute a find on "." 56# and append any tst.*.d, tst.*.ksh, err.*.d or drp.*.d files found within 57# the directory tree. 58# 59sub wanted 60{ 61 push(@files, $File::Find::name) 62 if ($_ =~ /^(tst|err|drp)\..+\.(d|ksh)$/ && -f "$_"); 63} 64 65sub dirname { 66 my($s) = @_; 67 my($i); 68 69 $s = substr($s, 0, $i) if (($i = rindex($s, '/')) != -1); 70 return $i == -1 ? '.' : $i == 0 ? '/' : $s; 71} 72 73sub usage 74{ 75 print $USAGE; 76 print "\t -a execute test suite using anonymous enablings\n"; 77 print "\t -b execute bad ioctl test program\n"; 78 print "\t -d specify directory for test results files and cores\n"; 79 print "\t -g enable libumem debugging when running tests\n"; 80 print "\t -h display verbose usage message\n"; 81 print "\t -i specify ISA to test instead of isaexec(3C) default\n"; 82 print "\t -l save log file of results and PIDs used by tests\n"; 83 print "\t -q set quiet mode (only report errors and summary)\n"; 84 print "\t -s save results files even for tests that pass\n"; 85 print "\t -x pass corresponding -x argument to dtrace(1M)\n"; 86 print "\n\tUse \"-i java\" to run tests using the "; 87 print "Java DTrace API.\n"; 88 exit(2); 89} 90 91sub errmsg 92{ 93 my($msg) = @_; 94 95 print STDERR $msg; 96 print LOG $msg if ($opt_l); 97 $errs++; 98} 99 100sub fail 101{ 102 my(@parms) = @_; 103 my($msg) = $parms[0]; 104 my($errfile) = $parms[1]; 105 my($n) = 0; 106 my($dest) = basename($file); 107 108 while (-d "$opt_d/failure.$n") { 109 $n++; 110 } 111 112 unless (mkdir "$opt_d/failure.$n") { 113 warn "ERROR: failed to make directory $opt_d/failure.$n: $!\n"; 114 exit(125); 115 } 116 117 open(README, ">$opt_d/failure.$n/README"); 118 print README "ERROR: " . $file . " " . $msg; 119 120 if (scalar @parms > 1) { 121 print README "; see $errfile\n"; 122 } else { 123 if (-f "$opt_d/$pid.core") { 124 print README "; see $pid.core\n"; 125 } else { 126 print README "\n"; 127 } 128 } 129 130 close(README); 131 132 if (-f "$opt_d/$pid.out") { 133 rename("$opt_d/$pid.out", "$opt_d/failure.$n/$pid.out"); 134 link("$file.out", "$opt_d/failure.$n/$dest.out"); 135 } 136 137 if (-f "$opt_d/$pid.err") { 138 rename("$opt_d/$pid.err", "$opt_d/failure.$n/$pid.err"); 139 link("$file.err", "$opt_d/failure.$n/$dest.err"); 140 } 141 142 if (-f "$opt_d/$pid.core") { 143 rename("$opt_d/$pid.core", "$opt_d/failure.$n/$pid.core"); 144 } 145 146 link("$file", "$opt_d/failure.$n/$dest"); 147 148 $msg = "ERROR: " . $dest . " " . $msg; 149 150 if (scalar @parms > 1) { 151 $msg = $msg . "; see $errfile in failure.$n\n"; 152 } else { 153 $msg = $msg . "; details in failure.$n\n"; 154 } 155 156 errmsg($msg); 157} 158 159sub logmsg 160{ 161 my($msg) = @_; 162 163 print STDOUT $msg unless ($opt_q); 164 print LOG $msg if ($opt_l); 165} 166 167# Trim leading and trailing whitespace 168sub trim { 169 my($s) = @_; 170 171 $s =~ s/^\s*//; 172 $s =~ s/\s*$//; 173 return $s; 174} 175 176# Loads exception set of skipped tests 177sub load_exceptions { 178 my($listfile) = @_; 179 my($line) = ""; 180 181 exit(123) unless open(STDIN, "<$listfile"); 182 while (<STDIN>) { 183 chomp; 184 $line = $_; 185 # line is non-empty and not a comment 186 if ((length($line) > 0) && ($line =~ /^\s*[^\s#]/ )) { 187 $exceptions{trim($line)} = 1; 188 } 189 } 190 return 0; 191} 192 193# Return 1 if file name found in exception set, 0 otherwise 194sub is_exception { 195 my($file) = @_; 196 my($i) = -1; 197 198 # hash absolute pathname after $dt_tst/ 199 $file = abs_path($file); 200 $i = index($file, $dt_tst); 201 if ($i == 0) { 202 $file = substr($file, length($dt_tst) + 1); 203 return $exceptions{$file}; 204 } 205 return 0; 206} 207 208die $USAGE unless (getopts($OPTSTR)); 209usage() if ($opt_h); 210 211foreach $arg (@ARGV) { 212 if (-f $arg) { 213 push(@files, $arg); 214 } elsif (-d $arg) { 215 find(\&wanted, $arg); 216 } else { 217 die "$PNAME: $arg is not a valid file or directory\n"; 218 } 219} 220 221$dt_tst = '/opt/SUNWdtrt/tst'; 222$dt_bin = '/opt/SUNWdtrt/bin'; 223$defdir = -d $dt_tst ? $dt_tst : '.'; 224$bindir = -d $dt_bin ? $dt_bin : '.'; 225 226find(\&wanted, "$defdir/common") if (scalar(@ARGV) == 0); 227find(\&wanted, "$defdir/$MACH") if (scalar(@ARGV) == 0); 228die $USAGE if (scalar(@files) == 0); 229 230if ($opt_d) { 231 die "$PNAME: -d arg must be absolute path\n" unless ($opt_d =~ /^\//); 232 die "$PNAME: -d arg $opt_d is not a directory\n" unless (-d "$opt_d"); 233 system("coreadm -p $opt_d/%p.core"); 234} else { 235 my $dir = getcwd; 236 system("coreadm -p $dir/%p.core"); 237 $opt_d = '.'; 238} 239 240if ($opt_i) { 241 if ($opt_i eq "java") { 242 $dtrace_path = $bindir . "/jdtrace"; 243 die "$PNAME: jdtrace not found\n" 244 unless (-x "$dtrace_path"); 245 load_exceptions($bindir . "/exception.lst"); 246 } else { 247 $dtrace_path = "/usr/sbin/$opt_i/dtrace"; 248 die "$PNAME: dtrace(1M) for ISA $opt_i not found\n" 249 unless (-x "$dtrace_path"); 250 } 251} 252 253if ($opt_x) { 254 push(@dtrace_argv, '-x'); 255 push(@dtrace_argv, $opt_x); 256} 257 258die "$PNAME: failed to open $PNAME.$$.log: $!\n" 259 unless (!$opt_l || open(LOG, ">$PNAME.$$.log")); 260 261if ($opt_g) { 262 $ENV{'UMEM_DEBUG'} = 'default,verbose'; 263 $ENV{'UMEM_LOGGING'} = 'fail,contents'; 264 $ENV{'LD_PRELOAD'} = 'libumem.so'; 265} 266 267# 268# Ensure that $PATH contains a cc(1) so that we can execute the 269# test programs that require compilation of C code. 270# 271$ENV{'PATH'} = $ENV{'PATH'} . ':/ws/onnv-tools/SUNWspro/SS11/bin'; 272 273if ($opt_b) { 274 logmsg("badioctl'ing ... "); 275 276 if (($badioctl = fork()) == -1) { 277 errmsg("ERROR: failed to fork to run badioctl: $!\n"); 278 next; 279 } 280 281 if ($badioctl == 0) { 282 open(STDIN, '</dev/null'); 283 exit(125) unless open(STDOUT, ">$opt_d/$$.out"); 284 exit(125) unless open(STDERR, ">$opt_d/$$.err"); 285 286 exec($bindir . "/badioctl"); 287 warn "ERROR: failed to exec badioctl: $!\n"; 288 exit(127); 289 } 290 291 292 logmsg("[$badioctl]\n"); 293 294 # 295 # If we're going to be bad, we're just going to iterate over each 296 # test file. 297 # 298 foreach $file (sort @files) { 299 ($name = $file) =~ s:.*/::; 300 $dir = dirname($file); 301 302 if (!($name =~ /^tst\./ && $name =~ /\.d$/)) { 303 next; 304 } 305 306 logmsg("baddof'ing $file ... "); 307 308 if (($pid = fork()) == -1) { 309 errmsg("ERROR: failed to fork to run baddof: $!\n"); 310 next; 311 } 312 313 if ($pid == 0) { 314 open(STDIN, '</dev/null'); 315 exit(125) unless open(STDOUT, ">$opt_d/$$.out"); 316 exit(125) unless open(STDERR, ">$opt_d/$$.err"); 317 318 unless (chdir($dir)) { 319 warn "ERROR: failed to chdir for $file: $!\n"; 320 exit(126); 321 } 322 323 exec($bindir . "/baddof", $name); 324 325 warn "ERROR: failed to exec for $file: $!\n"; 326 exit(127); 327 } 328 329 sleep 60; 330 kill(9, $pid); 331 waitpid($pid, 0); 332 333 logmsg("[$pid]\n"); 334 335 unless ($opt_s) { 336 unlink($pid . '.out'); 337 unlink($pid . '.err'); 338 } 339 } 340 341 kill(9, $badioctl); 342 waitpid($badioctl, 0); 343 344 unless ($opt_s) { 345 unlink($badioctl . '.out'); 346 unlink($badioctl . '.err'); 347 } 348 349 exit(0); 350} 351 352if ($opt_u) { 353 logmsg "spawning module unloading process... "; 354 355 $unloader = fork; 356 357 if ($unloader != 0 && !defined $unloader) { 358 # 359 # Couldn't fork for some reason. 360 # 361 die "couldn't fork: $!\n"; 362 } 363 364 if ($unloader == 0) { 365 # 366 # We're in the child. Go modunload krazy. 367 # 368 for (;;) { 369 system("modunload -i 0"); 370 } 371 } else { 372 logmsg "[$unloader]\n"; 373 374 $SIG{INT} = sub { 375 kill 9, $unloader; 376 exit($errs != 0); 377 }; 378 } 379} 380 381# 382# Iterate over the set of test files specified on the command-line or located 383# by a find on "." and execute each one. If the test file is executable, we 384# assume it is a #! script and run it. Otherwise we run dtrace -s on it. 385# If the file is named tst.* we assume it should return exit status 0. 386# If the file is named err.* we assume it should return exit status 1. 387# If the file is named err.D_[A-Z0-9]+[.*].d we use dtrace -xerrtags and 388# examine stderr to ensure that a matching error tag was produced. 389# If the file is named drp.[A-Z0-9]+[.*].d we use dtrace -xdroptags and 390# examine stderr to ensure that a matching drop tag was produced. 391# If any *.out or *.err files are found we perform output comparisons. 392# 393foreach $file (sort @files) { 394 $file =~ m:.*/((.*)\.(\w+)):; 395 $name = $1; 396 $base = $2; 397 $ext = $3; 398 399 $dir = dirname($file); 400 $isksh = 0; 401 $tag = 0; 402 $droptag = 0; 403 404 if ($name =~ /^tst\./) { 405 $isksh = ($ext eq 'ksh'); 406 $status = 0; 407 } elsif ($name =~ /^err\.(D_[A-Z0-9_]+)\./) { 408 $status = 1; 409 $tag = $1; 410 } elsif ($name =~ /^err\./) { 411 $status = 1; 412 } elsif ($name =~ /^drp\.([A-Z0-9_]+)\./) { 413 $status = 0; 414 $droptag = $1; 415 } else { 416 errmsg("ERROR: $file is not a valid test file name\n"); 417 next; 418 } 419 420 $fullname = "$dir/$name"; 421 $exe = "$dir/$base.exe"; 422 $exe_pid = -1; 423 424 if ($opt_a && ($status != 0 || $tag != 0 || $droptag != 0 || 425 -x $exe || $isksh || -x $fullname)) { 426 $bypassed++; 427 next; 428 } 429 430 if ($opt_i eq "java") { 431 if (is_exception("$dir/$name")) { 432 $bypassed++; 433 next; 434 } 435 } 436 437 if (!$isksh && -x $exe) { 438 if (($exe_pid = fork()) == -1) { 439 errmsg("ERROR: failed to fork to run $exe: $!\n"); 440 next; 441 } 442 443 if ($exe_pid == 0) { 444 open(STDIN, '</dev/null'); 445 446 exec($exe); 447 448 warn "ERROR: failed to exec $exe: $!\n"; 449 } 450 } 451 452 logmsg("testing $file ... "); 453 454 if (($pid = fork()) == -1) { 455 errmsg("ERROR: failed to fork to run test $file: $!\n"); 456 next; 457 } 458 459 if ($pid == 0) { 460 open(STDIN, '</dev/null'); 461 exit(125) unless open(STDOUT, ">$opt_d/$$.out"); 462 exit(125) unless open(STDERR, ">$opt_d/$$.err"); 463 464 unless (chdir($dir)) { 465 warn "ERROR: failed to chdir for $file: $!\n"; 466 exit(126); 467 } 468 469 push(@dtrace_argv, '-xerrtags') if ($tag); 470 push(@dtrace_argv, '-xdroptags') if ($droptag); 471 push(@dtrace_argv, $exe_pid) if ($exe_pid != -1); 472 473 if ($isksh) { 474 exit(123) unless open(STDIN, "<$name"); 475 exec("$ksh_path /dev/stdin $dtrace_path"); 476 } elsif (-x $name) { 477 warn "ERROR: $name is executable\n"; 478 exit(1); 479 } else { 480 if ($tag == 0 && $status == $0 && $opt_a) { 481 push(@dtrace_argv, '-A'); 482 } 483 484 push(@dtrace_argv, '-C'); 485 push(@dtrace_argv, '-s'); 486 push(@dtrace_argv, $name); 487 exec($dtrace_path, @dtrace_argv); 488 } 489 490 warn "ERROR: failed to exec for $file: $!\n"; 491 exit(127); 492 } 493 494 if (waitpid($pid, 0) == -1) { 495 errmsg("ERROR: timed out waiting for $file\n"); 496 kill(9, $exe_pid) if ($exe_pid != -1); 497 kill(9, $pid); 498 next; 499 } 500 501 kill(9, $exe_pid) if ($exe_pid != -1); 502 503 if ($tag == 0 && $status == $0 && $opt_a) { 504 # 505 # We can chuck the earler output. 506 # 507 unlink($pid . '.out'); 508 unlink($pid . '.err'); 509 510 # 511 # This is an anonymous enabling. We need to get the module 512 # unloaded. 513 # 514 system("dtrace -ae 1> /dev/null 2> /dev/null"); 515 system("svcadm disable -s svc:/network/nfs/mapid:default"); 516 system("modunload -i 0 ; modunload -i 0 ; modunload -i 0"); 517 if (!system("modinfo | grep dtrace")) { 518 warn "ERROR: couldn't unload dtrace\n"; 519 system("svcadm enable " . 520 "-s svc:/network/nfs/mapid:default"); 521 exit(124); 522 } 523 524 # 525 # DTrace is gone. Now update_drv(1M), and rip everything out 526 # again. 527 # 528 system("update_drv dtrace"); 529 system("dtrace -ae 1> /dev/null 2> /dev/null"); 530 system("modunload -i 0 ; modunload -i 0 ; modunload -i 0"); 531 if (!system("modinfo | grep dtrace")) { 532 warn "ERROR: couldn't unload dtrace\n"; 533 system("svcadm enable " . 534 "-s svc:/network/nfs/mapid:default"); 535 exit(124); 536 } 537 538 # 539 # Now bring DTrace back in. 540 # 541 system("sync ; sync"); 542 system("dtrace -l -n bogusprobe 1> /dev/null 2> /dev/null"); 543 system("svcadm enable -s svc:/network/nfs/mapid:default"); 544 545 # 546 # That should have caused DTrace to reload with the new 547 # configuration file. Now we can try to snag our anonymous 548 # state. 549 # 550 if (($pid = fork()) == -1) { 551 errmsg("ERROR: failed to fork to run test $file: $!\n"); 552 next; 553 } 554 555 if ($pid == 0) { 556 open(STDIN, '</dev/null'); 557 exit(125) unless open(STDOUT, ">$opt_d/$$.out"); 558 exit(125) unless open(STDERR, ">$opt_d/$$.err"); 559 560 push(@dtrace_argv, '-a'); 561 562 unless (chdir($dir)) { 563 warn "ERROR: failed to chdir for $file: $!\n"; 564 exit(126); 565 } 566 567 exec($dtrace_path, @dtrace_argv); 568 warn "ERROR: failed to exec for $file: $!\n"; 569 exit(127); 570 } 571 572 if (waitpid($pid, 0) == -1) { 573 errmsg("ERROR: timed out waiting for $file\n"); 574 kill(9, $pid); 575 next; 576 } 577 } 578 579 logmsg("[$pid]\n"); 580 $wstat = $?; 581 $wifexited = ($wstat & 0xFF) == 0; 582 $wexitstat = ($wstat >> 8) & 0xFF; 583 $wtermsig = ($wstat & 0x7F); 584 585 if (!$wifexited) { 586 fail("died from signal $wtermsig"); 587 next; 588 } 589 590 if ($wexitstat == 125) { 591 die "$PNAME: failed to create output file in $opt_d " . 592 "(cd elsewhere or use -d)\n"; 593 } 594 595 if ($wexitstat != $status) { 596 fail("returned $wexitstat instead of $status"); 597 next; 598 } 599 600 if (-f "$file.out" && system("cmp -s $file.out $opt_d/$pid.out") != 0) { 601 fail("stdout mismatch", "$pid.out"); 602 next; 603 } 604 605 if (-f "$file.err" && system("cmp -s $file.err $opt_d/$pid.err") != 0) { 606 fail("stderr mismatch: see $pid.err"); 607 next; 608 } 609 610 if ($tag) { 611 open(TSTERR, "<$opt_d/$pid.err"); 612 $tsterr = <TSTERR>; 613 close(TSTERR); 614 615 unless ($tsterr =~ /: \[$tag\] line \d+:/) { 616 fail("errtag mismatch: see $pid.err"); 617 next; 618 } 619 } 620 621 if ($droptag) { 622 $found = 0; 623 open(TSTERR, "<$opt_d/$pid.err"); 624 625 while (<TSTERR>) { 626 if (/\[$droptag\] /) { 627 $found = 1; 628 last; 629 } 630 } 631 632 close (TSTERR); 633 634 unless ($found) { 635 fail("droptag mismatch: see $pid.err"); 636 next; 637 } 638 } 639 640 unless ($opt_s) { 641 unlink($pid . '.out'); 642 unlink($pid . '.err'); 643 } 644} 645 646if ($opt_a) { 647 # 648 # If we're running with anonymous enablings, we need to restore the 649 # .conf file. 650 # 651 system("dtrace -A 1> /dev/null 2> /dev/null"); 652 system("dtrace -ae 1> /dev/null 2> /dev/null"); 653 system("modunload -i 0 ; modunload -i 0 ; modunload -i 0"); 654 system("update_drv dtrace"); 655} 656 657$opt_q = 0; # force final summary to appear regardless of -q option 658 659logmsg("\n==== TEST RESULTS ====\n"); 660logmsg(" passed: " . (scalar(@files) - $errs - $bypassed) . "\n"); 661 662if ($bypassed) { 663 logmsg(" bypassed: " . $bypassed . "\n"); 664} 665 666logmsg(" failed: " . $errs . "\n"); 667logmsg(" total: " . scalar(@files) . "\n"); 668 669if ($opt_u) { 670 kill 9, $unloader; 671 waitpid $unloader, 0; 672} 673 674exit($errs != 0); 675