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