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