diff options
Diffstat (limited to 'cddl/contrib/opensolaris/cmd/dtrace/test/cmd/scripts/dtest.pl')
-rwxr-xr-x | cddl/contrib/opensolaris/cmd/dtrace/test/cmd/scripts/dtest.pl | 704 |
1 files changed, 704 insertions, 0 deletions
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/cmd/scripts/dtest.pl b/cddl/contrib/opensolaris/cmd/dtrace/test/cmd/scripts/dtest.pl new file mode 100755 index 0000000..7b47580 --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/cmd/scripts/dtest.pl @@ -0,0 +1,704 @@ +#!/usr/local/bin/perl +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +require 5.8.4; + +use File::Find; +use File::Basename; +use Getopt::Std; +use Cwd; +use Cwd 'abs_path'; + +$PNAME = $0; +$PNAME =~ s:.*/::; +$OPTSTR = 'abd:fghi:jlnqsx:'; +$USAGE = "Usage: $PNAME [-abfghjlnqs] [-d dir] [-i isa] " + . "[-x opt[=arg]] [file | dir ...]\n"; +($MACH = `uname -p`) =~ s/\W*\n//; +($PLATFORM = `uname -i`) =~ s/\W*\n//; + +@dtrace_argv = (); + +$ksh_path = '/usr/local/bin/ksh'; + +@files = (); +%exceptions = (); +%results = (); +$errs = 0; + +# +# If no test files are specified on the command-line, execute a find on "." +# and append any tst.*.d, tst.*.ksh, err.*.d or drp.*.d files found within +# the directory tree. +# +sub wanted +{ + push(@files, $File::Find::name) + if ($_ =~ /^(tst|err|drp)\..+\.(d|ksh)$/ && -f "$_"); +} + +sub dirname { + my($s) = @_; + my($i); + + $s = substr($s, 0, $i) if (($i = rindex($s, '/')) != -1); + return $i == -1 ? '.' : $i == 0 ? '/' : $s; +} + +sub usage +{ + print $USAGE; + print "\t -a execute test suite using anonymous enablings\n"; + print "\t -b execute bad ioctl test program\n"; + print "\t -d specify directory for test results files and cores\n"; + print "\t -g enable libumem debugging when running tests\n"; + print "\t -f force bypassed tests to run\n"; + print "\t -h display verbose usage message\n"; + print "\t -i specify ISA to test instead of isaexec(3C) default\n"; + print "\t -j execute test suite using jdtrace (Java API) only\n"; + print "\t -l save log file of results and PIDs used by tests\n"; + print "\t -n execute test suite using dtrace(1m) only\n"; + print "\t -q set quiet mode (only report errors and summary)\n"; + print "\t -s save results files even for tests that pass\n"; + print "\t -x pass corresponding -x argument to dtrace(1M)\n"; + exit(2); +} + +sub errmsg +{ + my($msg) = @_; + + print STDERR $msg; + print LOG $msg if ($opt_l); + $errs++; +} + +sub fail +{ + my(@parms) = @_; + my($msg) = $parms[0]; + my($errfile) = $parms[1]; + my($n) = 0; + my($dest) = basename($file); + + while (-d "$opt_d/failure.$n") { + $n++; + } + + unless (mkdir "$opt_d/failure.$n") { + warn "ERROR: failed to make directory $opt_d/failure.$n: $!\n"; + exit(125); + } + + open(README, ">$opt_d/failure.$n/README"); + print README "ERROR: " . $file . " " . $msg; + + if (scalar @parms > 1) { + print README "; see $errfile\n"; + } else { + if (-f "$opt_d/$pid.core") { + print README "; see $pid.core\n"; + } else { + print README "\n"; + } + } + + close(README); + + if (-f "$opt_d/$pid.out") { + rename("$opt_d/$pid.out", "$opt_d/failure.$n/$pid.out"); + link("$file.out", "$opt_d/failure.$n/$dest.out"); + } + + if (-f "$opt_d/$pid.err") { + rename("$opt_d/$pid.err", "$opt_d/failure.$n/$pid.err"); + link("$file.err", "$opt_d/failure.$n/$dest.err"); + } + + if (-f "$opt_d/$pid.core") { + rename("$opt_d/$pid.core", "$opt_d/failure.$n/$pid.core"); + } + + link("$file", "$opt_d/failure.$n/$dest"); + + $msg = "ERROR: " . $dest . " " . $msg; + + if (scalar @parms > 1) { + $msg = $msg . "; see $errfile in failure.$n\n"; + } else { + $msg = $msg . "; details in failure.$n\n"; + } + + errmsg($msg); +} + +sub logmsg +{ + my($msg) = @_; + + print STDOUT $msg unless ($opt_q); + print LOG $msg if ($opt_l); +} + +# Trim leading and trailing whitespace +sub trim { + my($s) = @_; + + $s =~ s/^\s*//; + $s =~ s/\s*$//; + return $s; +} + +# Load exception set of skipped tests from the file at the given +# pathname. The test names are assumed to be paths relative to $dt_tst, +# for example: common/aggs/tst.neglquant.d, and specify tests to be +# skipped. +sub load_exceptions { + my($listfile) = @_; + my($line) = ""; + + %exceptions = (); + if (length($listfile) > 0) { + exit(123) unless open(STDIN, "<$listfile"); + while (<STDIN>) { + chomp; + $line = $_; + # line is non-empty and not a comment + if ((length($line) > 0) && ($line =~ /^\s*[^\s#]/ )) { + $exceptions{trim($line)} = 1; + } + } + } +} + +# Return 1 if the test is found in the exception set, 0 otherwise. +sub is_exception { + my($file) = @_; + my($i) = -1; + + if (scalar(keys(%exceptions)) == 0) { + return 0; + } + + # hash absolute pathname after $dt_tst/ + $file = abs_path($file); + $i = index($file, $dt_tst); + if ($i == 0) { + $file = substr($file, length($dt_tst) + 1); + return $exceptions{$file}; + } + return 0; +} + +# +# Iterate over the set of test files specified on the command-line or by a find +# on "$defdir/common", "$defdir/$MACH" and "$defdir/$PLATFORM" and execute each +# one. If the test file is executable, we fork and exec it. If the test is a +# .ksh file, we run it with $ksh_path. Otherwise we run dtrace -s on it. If +# the file is named tst.* we assume it should return exit status 0. If the +# file is named err.* we assume it should return exit status 1. If the file is +# named err.D_[A-Z0-9]+[.*].d we use dtrace -xerrtags and examine stderr to +# ensure that a matching error tag was produced. If the file is named +# drp.[A-Z0-9]+[.*].d we use dtrace -xdroptags and examine stderr to ensure +# that a matching drop tag was produced. If any *.out or *.err files are found +# we perform output comparisons. +# +# run_tests takes two arguments: The first is the pathname of the dtrace +# command to invoke when running the tests. The second is the pathname +# of a file (may be the empty string) listing tests that ought to be +# skipped (skipped tests are listed as paths relative to $dt_tst, for +# example: common/aggs/tst.neglquant.d). +# +sub run_tests { + my($dtrace, $exceptions_path) = @_; + my($passed) = 0; + my($bypassed) = 0; + my($failed) = $errs; + my($total) = 0; + + die "$PNAME: $dtrace not found\n" unless (-x "$dtrace"); + logmsg($dtrace . "\n"); + + load_exceptions($exceptions_path); + + foreach $file (sort @files) { + $file =~ m:.*/((.*)\.(\w+)):; + $name = $1; + $base = $2; + $ext = $3; + + $dir = dirname($file); + $isksh = 0; + $tag = 0; + $droptag = 0; + + if ($name =~ /^tst\./) { + $isksh = ($ext eq 'ksh'); + $status = 0; + } elsif ($name =~ /^err\.(D_[A-Z0-9_]+)\./) { + $status = 1; + $tag = $1; + } elsif ($name =~ /^err\./) { + $status = 1; + } elsif ($name =~ /^drp\.([A-Z0-9_]+)\./) { + $status = 0; + $droptag = $1; + } else { + errmsg("ERROR: $file is not a valid test file name\n"); + next; + } + + $fullname = "$dir/$name"; + $exe = "./$base.exe"; + $exe_pid = -1; + + if ($opt_a && ($status != 0 || $tag != 0 || $droptag != 0 || + -x $exe || $isksh || -x $fullname)) { + $bypassed++; + next; + } + + if (!$opt_f && is_exception("$dir/$name")) { + $bypassed++; + next; + } + + if (!$isksh && -x $exe) { + if (($exe_pid = fork()) == -1) { + errmsg( + "ERROR: failed to fork to run $exe: $!\n"); + next; + } + + if ($exe_pid == 0) { + open(STDIN, '</dev/null'); + + exec($exe); + + warn "ERROR: failed to exec $exe: $!\n"; + } + } + + logmsg("testing $file ... "); + + if (($pid = fork()) == -1) { + errmsg("ERROR: failed to fork to run test $file: $!\n"); + next; + } + + if ($pid == 0) { + open(STDIN, '</dev/null'); + exit(125) unless open(STDOUT, ">$opt_d/$$.out"); + exit(125) unless open(STDERR, ">$opt_d/$$.err"); + + unless (chdir($dir)) { + warn "ERROR: failed to chdir for $file: $!\n"; + exit(126); + } + + push(@dtrace_argv, '-xerrtags') if ($tag); + push(@dtrace_argv, '-xdroptags') if ($droptag); + push(@dtrace_argv, $exe_pid) if ($exe_pid != -1); + + if ($isksh) { + exit(123) unless open(STDIN, "<$name"); + exec("$ksh_path /dev/stdin $dtrace"); + } elsif (-x $name) { + warn "ERROR: $name is executable\n"; + exit(1); + } else { + if ($tag == 0 && $status == $0 && $opt_a) { + push(@dtrace_argv, '-A'); + } + + push(@dtrace_argv, '-C'); + push(@dtrace_argv, '-s'); + push(@dtrace_argv, $name); + exec($dtrace, @dtrace_argv); + } + + warn "ERROR: failed to exec for $file: $!\n"; + exit(127); + } + + if (waitpid($pid, 0) == -1) { + errmsg("ERROR: timed out waiting for $file\n"); + kill(9, $exe_pid) if ($exe_pid != -1); + kill(9, $pid); + next; + } + + kill(9, $exe_pid) if ($exe_pid != -1); + + if ($tag == 0 && $status == $0 && $opt_a) { + # + # We can chuck the earler output. + # + unlink($pid . '.out'); + unlink($pid . '.err'); + + # + # This is an anonymous enabling. We need to get + # the module unloaded. + # + system("dtrace -ae 1> /dev/null 2> /dev/null"); + system("svcadm disable -s " . + "svc:/network/nfs/mapid:default"); + system("modunload -i 0 ; modunload -i 0 ; " . + "modunload -i 0"); + if (!system("modinfo | grep dtrace")) { + warn "ERROR: couldn't unload dtrace\n"; + system("svcadm enable " . + "-s svc:/network/nfs/mapid:default"); + exit(124); + } + + # + # DTrace is gone. Now update_drv(1M), and rip + # everything out again. + # + system("update_drv dtrace"); + system("dtrace -ae 1> /dev/null 2> /dev/null"); + system("modunload -i 0 ; modunload -i 0 ; " . + "modunload -i 0"); + if (!system("modinfo | grep dtrace")) { + warn "ERROR: couldn't unload dtrace\n"; + system("svcadm enable " . + "-s svc:/network/nfs/mapid:default"); + exit(124); + } + + # + # Now bring DTrace back in. + # + system("sync ; sync"); + system("dtrace -l -n bogusprobe 1> /dev/null " . + "2> /dev/null"); + system("svcadm enable -s " . + "svc:/network/nfs/mapid:default"); + + # + # That should have caused DTrace to reload with + # the new configuration file. Now we can try to + # snag our anonymous state. + # + if (($pid = fork()) == -1) { + errmsg("ERROR: failed to fork to run " . + "test $file: $!\n"); + next; + } + + if ($pid == 0) { + open(STDIN, '</dev/null'); + exit(125) unless open(STDOUT, ">$opt_d/$$.out"); + exit(125) unless open(STDERR, ">$opt_d/$$.err"); + + push(@dtrace_argv, '-a'); + + unless (chdir($dir)) { + warn "ERROR: failed to chdir " . + "for $file: $!\n"; + exit(126); + } + + exec($dtrace, @dtrace_argv); + warn "ERROR: failed to exec for $file: $!\n"; + exit(127); + } + + if (waitpid($pid, 0) == -1) { + errmsg("ERROR: timed out waiting for $file\n"); + kill(9, $pid); + next; + } + } + + logmsg("[$pid]\n"); + $wstat = $?; + $wifexited = ($wstat & 0xFF) == 0; + $wexitstat = ($wstat >> 8) & 0xFF; + $wtermsig = ($wstat & 0x7F); + + if (!$wifexited) { + fail("died from signal $wtermsig"); + next; + } + + if ($wexitstat == 125) { + die "$PNAME: failed to create output file in $opt_d " . + "(cd elsewhere or use -d)\n"; + } + + if ($wexitstat != $status) { + fail("returned $wexitstat instead of $status"); + next; + } + + if (-f "$file.out" && + system("cmp -s $file.out $opt_d/$pid.out") != 0) { + fail("stdout mismatch", "$pid.out"); + next; + } + + if (-f "$file.err" && + system("cmp -s $file.err $opt_d/$pid.err") != 0) { + fail("stderr mismatch: see $pid.err"); + next; + } + + if ($tag) { + open(TSTERR, "<$opt_d/$pid.err"); + $tsterr = <TSTERR>; + close(TSTERR); + + unless ($tsterr =~ /: \[$tag\] line \d+:/) { + fail("errtag mismatch: see $pid.err"); + next; + } + } + + if ($droptag) { + $found = 0; + open(TSTERR, "<$opt_d/$pid.err"); + + while (<TSTERR>) { + if (/\[$droptag\] /) { + $found = 1; + last; + } + } + + close (TSTERR); + + unless ($found) { + fail("droptag mismatch: see $pid.err"); + next; + } + } + + unless ($opt_s) { + unlink($pid . '.out'); + unlink($pid . '.err'); + } + } + + if ($opt_a) { + # + # If we're running with anonymous enablings, we need to + # restore the .conf file. + # + system("dtrace -A 1> /dev/null 2> /dev/null"); + system("dtrace -ae 1> /dev/null 2> /dev/null"); + system("modunload -i 0 ; modunload -i 0 ; modunload -i 0"); + system("update_drv dtrace"); + } + + $total = scalar(@files); + $failed = $errs - $failed; + $passed = ($total - $failed - $bypassed); + $results{$dtrace} = { + "passed" => $passed, + "bypassed" => $bypassed, + "failed" => $failed, + "total" => $total + }; +} + +die $USAGE unless (getopts($OPTSTR)); +usage() if ($opt_h); + +foreach $arg (@ARGV) { + if (-f $arg) { + push(@files, $arg); + } elsif (-d $arg) { + find(\&wanted, $arg); + } else { + die "$PNAME: $arg is not a valid file or directory\n"; + } +} + +$dt_tst = '/opt/SUNWdtrt/tst'; +$dt_bin = '/opt/SUNWdtrt/bin'; +$defdir = -d $dt_tst ? $dt_tst : '.'; +$bindir = -d $dt_bin ? $dt_bin : '.'; + +find(\&wanted, "$defdir/common") if (scalar(@ARGV) == 0); +find(\&wanted, "$defdir/$MACH") if (scalar(@ARGV) == 0); +find(\&wanted, "$defdir/$PLATFORM") if (scalar(@ARGV) == 0); +die $USAGE if (scalar(@files) == 0); + +$dtrace_path = '/usr/sbin/dtrace'; +$jdtrace_path = "$bindir/jdtrace"; + +%exception_lists = ("$jdtrace_path" => "$bindir/exception.lst"); + +if ($opt_j || $opt_n || $opt_i) { + @dtrace_cmds = (); + push(@dtrace_cmds, $dtrace_path) if ($opt_n); + push(@dtrace_cmds, $jdtrace_path) if ($opt_j); + push(@dtrace_cmds, "/usr/sbin/$opt_i/dtrace") if ($opt_i); +} else { + @dtrace_cmds = ($dtrace_path, $jdtrace_path); +} + +if ($opt_d) { + die "$PNAME: -d arg must be absolute path\n" unless ($opt_d =~ /^\//); + die "$PNAME: -d arg $opt_d is not a directory\n" unless (-d "$opt_d"); + system("coreadm -p $opt_d/%p.core"); +} else { + my $dir = getcwd; + system("coreadm -p $dir/%p.core"); + $opt_d = '.'; +} + +if ($opt_x) { + push(@dtrace_argv, '-x'); + push(@dtrace_argv, $opt_x); +} + +die "$PNAME: failed to open $PNAME.$$.log: $!\n" + unless (!$opt_l || open(LOG, ">$PNAME.$$.log")); + +if ($opt_g) { + $ENV{'UMEM_DEBUG'} = 'default,verbose'; + $ENV{'UMEM_LOGGING'} = 'fail,contents'; + $ENV{'LD_PRELOAD'} = 'libumem.so'; +} + +# +# Ensure that $PATH contains a cc(1) so that we can execute the +# test programs that require compilation of C code. +# +#$ENV{'PATH'} = $ENV{'PATH'} . ':/ws/onnv-tools/SUNWspro/SS11/bin'; + +if ($opt_b) { + logmsg("badioctl'ing ... "); + + if (($badioctl = fork()) == -1) { + errmsg("ERROR: failed to fork to run badioctl: $!\n"); + next; + } + + if ($badioctl == 0) { + open(STDIN, '</dev/null'); + exit(125) unless open(STDOUT, ">$opt_d/$$.out"); + exit(125) unless open(STDERR, ">$opt_d/$$.err"); + + exec($bindir . "/badioctl"); + warn "ERROR: failed to exec badioctl: $!\n"; + exit(127); + } + + + logmsg("[$badioctl]\n"); + + # + # If we're going to be bad, we're just going to iterate over each + # test file. + # + foreach $file (sort @files) { + ($name = $file) =~ s:.*/::; + $dir = dirname($file); + + if (!($name =~ /^tst\./ && $name =~ /\.d$/)) { + next; + } + + logmsg("baddof'ing $file ... "); + + if (($pid = fork()) == -1) { + errmsg("ERROR: failed to fork to run baddof: $!\n"); + next; + } + + if ($pid == 0) { + open(STDIN, '</dev/null'); + exit(125) unless open(STDOUT, ">$opt_d/$$.out"); + exit(125) unless open(STDERR, ">$opt_d/$$.err"); + + unless (chdir($dir)) { + warn "ERROR: failed to chdir for $file: $!\n"; + exit(126); + } + + exec($bindir . "/baddof", $name); + + warn "ERROR: failed to exec for $file: $!\n"; + exit(127); + } + + sleep 60; + kill(9, $pid); + waitpid($pid, 0); + + logmsg("[$pid]\n"); + + unless ($opt_s) { + unlink($pid . '.out'); + unlink($pid . '.err'); + } + } + + kill(9, $badioctl); + waitpid($badioctl, 0); + + unless ($opt_s) { + unlink($badioctl . '.out'); + unlink($badioctl . '.err'); + } + + exit(0); +} + +# +# Run all the tests specified on the command-line (the entire test suite +# by default) once for each dtrace command tested, skipping any tests +# not valid for that command. +# +foreach $dtrace_cmd (@dtrace_cmds) { + run_tests($dtrace_cmd, $exception_lists{$dtrace_cmd}); +} + +$opt_q = 0; # force final summary to appear regardless of -q option + +logmsg("\n==== TEST RESULTS ====\n"); +foreach $key (keys %results) { + my $passed = $results{$key}{"passed"}; + my $bypassed = $results{$key}{"bypassed"}; + my $failed = $results{$key}{"failed"}; + my $total = $results{$key}{"total"}; + + logmsg("\n mode: " . $key . "\n"); + logmsg(" passed: " . $passed . "\n"); + if ($bypassed) { + logmsg(" bypassed: " . $bypassed . "\n"); + } + logmsg(" failed: " . $failed . "\n"); + logmsg(" total: " . $total . "\n"); +} + +exit($errs != 0); |