Home | History | Annotate | Download | only in tests
      1 #!/usr/bin/env perl
      2 
      3 #-------------------------------------------------------------------
      4 # Check header files and #include directives
      5 #
      6 # (1) include/*.h must not include pub_core_...h
      7 # (2) coregrind/pub_core_xyzzy.h may include pub_tool_xyzzy.h
      8 #     other coregrind headers may not include pub_tool_xyzzy.h
      9 # (3) coregrind/ *.c must not include pub_tool_xyzzy.h
     10 # (4) tool *.[ch] files must not include pub_core_...h
     11 # (5) include pub_core/tool_clreq.h instead of valgrind.h except in tools'
     12 #     export headers
     13 # (6) coregrind/ *.[ch] must not use tl_assert
     14 # (7) include/*.h and tool *.[ch] must not use vg_assert
     15 # (8) coregrind/ *.[ch] must not use VG_(tool_panic)
     16 # (9) include/*.h and tool *.[ch] must not use VG_(core_panic)
     17 # (10) *.S must unconditionally instantiate MARK_STACK_NO_EXEC
     18 #
     19 # There can be false positives as we don't really parse the source files.
     20 # Instead we only match regular expressions.
     21 #-------------------------------------------------------------------
     22 
     23 use strict;
     24 use warnings;
     25 use File::Basename;
     26 use Getopt::Long;
     27 
     28 my $this_script = basename($0);
     29 
     30 # The list of top-level directories is divided into three sets:
     31 #
     32 # (1) coregrind directories
     33 # (2) tool directories
     34 # (3) directories to ignore
     35 #
     36 # If a directory is found that does not belong to any of those sets, the
     37 # script will terminate unsuccessfully.
     38 
     39 my %coregrind_dirs = (
     40     "include" => 1,
     41     "coregrind" => 1,
     42     );
     43 
     44 my %tool_dirs = (
     45     "none" => 1,
     46     "lackey" => 1,
     47     "massif" => 1,
     48     "memcheck" => 1,
     49     "drd" => 1,
     50     "helgrind", => 1,
     51     "callgrind" => 1,
     52     "cachegrind" => 1,
     53     "shared" => 1,
     54     "exp-bbv" => 1,
     55     "exp-dhat" => 1,
     56     "exp-sgcheck" => 1
     57     );
     58 
     59 my %dirs_to_ignore = (
     60     ".deps" => 1,
     61     ".svn" => 1,
     62     ".git" => 1,            # allow git mirrors of the svn repo
     63     ".in_place" => 1,
     64     "Inst" => 1,            # the nightly scripts creates this
     65     "VEX" => 1,
     66     "docs" => 1,
     67     "auxprogs" => 1,
     68     "autom4te.cache" => 1,
     69     "nightly" => 1,
     70     "perf" => 1,
     71     "tests" => 1,
     72     "gdbserver_tests" => 1,
     73     "mpi" => 1,
     74     "solaris" => 1
     75     );
     76 
     77 my %tool_export_header = (
     78     "drd/drd.h" => 1,
     79     "helgrind/helgrind.h" => 1,
     80     "memcheck/memcheck.h" => 1,
     81     "callgrind/callgrind.h" => 1
     82     );
     83 
     84 my $usage=<<EOF;
     85 USAGE
     86 
     87   $this_script
     88 
     89     [--debug]          Debugging output
     90 
     91     dir ...            Directories to process
     92 EOF
     93 
     94 my $debug = 0;
     95 my $num_errors = 0;
     96 
     97 &main;
     98 
     99 sub main {
    100     GetOptions( "debug"  => \$debug ) || die $usage;
    101 
    102     my $argc = $#ARGV + 1;
    103 
    104     if ($argc < 1) {
    105         die $usage;
    106     }
    107 
    108     foreach my $dir (@ARGV) {
    109         process_dir(undef, $dir, 0);
    110     }
    111 
    112     my $rc = ($num_errors == 0) ? 0 : 1;
    113     exit $rc;
    114 }
    115 
    116 sub process_dir {
    117     my ($path, $dir, $depth) = @_;
    118     my $hdir;
    119 
    120     if ($depth == 0) {
    121 # The root directory is always processed
    122     } elsif ($depth == 1) {
    123 # Toplevel directories
    124         return if ($dirs_to_ignore{$dir});
    125 
    126         if (! $tool_dirs{$dir} && ! $coregrind_dirs{$dir}) {
    127             die "Unknown directory '$dir'. Please update $this_script\n";
    128         }
    129     } else {
    130 # Subdirectories
    131         return if ($dirs_to_ignore{$dir});
    132     }
    133 
    134     print "DIR = $dir   DEPTH = $depth\n" if ($debug);
    135 
    136     chdir($dir) || die "Cannot chdir '$dir'\n";
    137 
    138     opendir($hdir, ".") || die "cannot open directory '.'";
    139 
    140     while (my $file = readdir($hdir)) {
    141         next if ($file eq ".");
    142         next if ($file eq "..");
    143 
    144 # Subdirectories
    145         if (-d $file) {
    146             my $full_path = defined $path ? "$path/$file" : $file;
    147             process_dir($full_path, $file, $depth + 1);
    148             next;
    149         }
    150 
    151 # Regular files; only interested in *.c, *.S and *.h
    152         next if (! ($file =~ /\.[cSh]$/));
    153         my $path_name = defined $path ? "$path/$file" : $file;
    154         process_file($path_name);
    155     }
    156     close($hdir);
    157     chdir("..") || die "Cannot chdir '..'\n";
    158 }
    159 
    160 #---------------------------------------------------------------------
    161 # Return 1, if file is located in <valgrind>/include
    162 #---------------------------------------------------------------------
    163 sub is_coregrind_export_header {
    164     my ($path_name) = @_;
    165 
    166     return ($path_name =~ /^include\//) ? 1 : 0;
    167 }
    168 
    169 #---------------------------------------------------------------------
    170 # Return 1, if file is located underneath <valgrind>/coregrind
    171 #---------------------------------------------------------------------
    172 sub is_coregrind_file {
    173     my ($path_name) = @_;
    174 
    175     return ($path_name =~ /^coregrind\//) ? 1 : 0;
    176 }
    177 
    178 #---------------------------------------------------------------------
    179 # Return 1, if file is located underneath <valgrind>/<tool>
    180 #---------------------------------------------------------------------
    181 sub is_tool_file {
    182     my ($path_name) = @_;
    183 
    184     for my $tool (keys %tool_dirs) {
    185         return 1 if ($path_name =~ /^$tool\//);
    186     }
    187     return 0
    188 }
    189 
    190 #---------------------------------------------------------------------
    191 # Return array of files #include'd by file.
    192 #---------------------------------------------------------------------
    193 sub get_included_files {
    194     my ($path_name) = @_;
    195     my @includes = ();
    196     my $file = basename($path_name);
    197 
    198     open(FILE, "<$file") || die "Cannot open file '$file'";
    199 
    200     while (my $line = <FILE>) {
    201         if ($line =~ /^\s*#\s*include "([^"]*)"/) {
    202             push @includes, $1;
    203         }
    204         if ($line =~ /^\s*#\s*include <([^>]*)>/) {
    205             push @includes, $1;
    206         }
    207     }
    208     close FILE;
    209     return @includes;
    210 }
    211 
    212 #---------------------------------------------------------------------
    213 # Check a file from <valgrind>/include
    214 #---------------------------------------------------------------------
    215 sub check_coregrind_export_header {
    216     my ($path_name) = @_;
    217     my $file = basename($path_name);
    218 
    219     foreach my $inc (get_included_files($path_name)) {
    220         $inc = basename($inc);
    221 # Must not include pub_core_....
    222         if ($inc =~ /pub_core_/) {
    223             error("File $path_name must not include $inc\n");
    224         }
    225 # Only pub_tool_clreq.h may include valgrind.h
    226         if (($inc eq "valgrind.h") && ($path_name ne "include/pub_tool_clreq.h")) {
    227             error("File $path_name should include pub_tool_clreq.h instead of $inc\n");
    228         }
    229     }
    230 # Must not use vg_assert
    231     my $assert = `grep vg_assert $file`;
    232     if ($assert ne "") {
    233         error("File $path_name must not use vg_assert\n");
    234     }
    235 # Must not use VG_(core_panic)
    236     my $panic = `grep 'VG_(core_panic)' $file`;
    237     if ($panic ne "") {
    238         error("File $path_name must not use VG_(core_panic)\n");
    239     }
    240 }
    241 
    242 #---------------------------------------------------------------------
    243 # Check a file from <valgrind>/coregrind
    244 #---------------------------------------------------------------------
    245 sub check_coregrind_file {
    246     my ($path_name) = @_;
    247     my $file = basename($path_name);
    248 
    249     foreach my $inc (get_included_files($path_name)) {
    250         print "\tINCLUDE $inc\n" if ($debug);
    251 # Only pub_tool_xyzzy.h may include pub_core_xyzzy.h
    252         if ($inc =~ /pub_tool_/) {
    253             my $buddy = $inc;
    254             $buddy =~ s/pub_tool/pub_core/;
    255             if ($file ne $buddy) {
    256                 error("File $path_name must not include $inc\n");
    257             }
    258         }
    259 # Must not include valgrind.h
    260         if ($inc eq "valgrind.h") {
    261             error("File $path_name should include pub_core_clreq.h instead of $inc\n");
    262         }
    263     }
    264 # Must not use tl_assert
    265     my $assert = `grep tl_assert $file`;
    266     if ($assert ne "") {
    267         error("File $path_name must not use tl_assert\n");
    268     }
    269 # Must not use VG_(tool_panic)
    270     my $panic = `grep 'VG_(tool_panic)' $file`;
    271     if ($panic ne "") {
    272         chomp($panic);
    273 # Do not complain about the definition of VG_(tool_panic)
    274         if (($path_name eq "coregrind/m_libcassert.c") &&
    275             ($panic eq "void VG_(tool_panic) ( const HChar* str )")) {
    276 # OK
    277         } else {
    278             error("File $path_name must not use VG_(tool_panic)\n");
    279         }
    280     }
    281 }
    282 
    283 #---------------------------------------------------------------------
    284 # Check a file from <valgrind>/<tool>
    285 #---------------------------------------------------------------------
    286 sub check_tool_file {
    287     my ($path_name) = @_;
    288     my $file = basename($path_name);
    289 
    290     foreach my $inc (get_included_files($path_name)) {
    291         print "\tINCLUDE $inc\n" if ($debug);
    292 # Must not include pub_core_...
    293         if ($inc =~ /pub_core_/) {
    294             error("File $path_name must not include $inc\n");
    295         }
    296 # Must not include valgrind.h unless this is an export header
    297         if ($inc eq "valgrind.h" && ! $tool_export_header{$path_name}) {
    298             error("File $path_name should include pub_tool_clreq.h instead of $inc\n");
    299         }
    300     }
    301 # Must not use vg_assert
    302     my $assert = `grep vg_assert $file`;
    303     if ($assert ne "") {
    304         error("File $path_name must not use vg_assert\n");
    305     }
    306 # Must not use VG_(core_panic)
    307     my $panic = `grep 'VG_(core_panic)' $file`;
    308     if ($panic ne "") {
    309         error("File $path_name must not use VG_(core_panic)\n");
    310     }
    311 }
    312 
    313 #---------------------------------------------------------------------
    314 # Check an assembler file
    315 #---------------------------------------------------------------------
    316 sub check_assembler_file {
    317     my ($path_name) = @_;
    318     my $file = basename($path_name);
    319     my $found = 0;
    320 
    321     open(FILE, "<$file") || die "Cannot open file '$file'";
    322 
    323     while (my $line = <FILE>) {
    324         if ($line =~ /^\s*MARK_STACK_NO_EXEC/) {
    325             $found = 1;
    326             last;
    327         }
    328     }
    329     if ($found == 0) {
    330         error("File $path_name does not instantiate MARK_STACK_NO_EXEC\n");
    331     } else {
    332         while (my $line = <FILE>) {
    333             if ($line =~ /^\s*#\s*endif/) {
    334                 error("File $path_name instantiates MARK_STACK_NO_EXEC"
    335                       . " under a condition\n");
    336                 last;
    337             }
    338         }
    339     }
    340     close FILE;
    341 }
    342 
    343 sub process_file {
    344     my ($path_name) = @_;
    345 
    346     print "FILE = $path_name\n" if ($debug);
    347 
    348     if (is_coregrind_export_header($path_name)) {
    349         check_coregrind_export_header($path_name);
    350     } elsif (is_coregrind_file($path_name)) {
    351         check_coregrind_file($path_name);
    352     } elsif (is_tool_file($path_name)) {
    353         check_tool_file($path_name);
    354     }
    355 
    356     if ($path_name =~ /\.S$/) {
    357         check_assembler_file($path_name);
    358     }
    359 }
    360 
    361 sub error {
    362     my ($message) = @_;
    363     print STDERR "*** $message";
    364     ++$num_errors;
    365 }
    366