| #!/usr/bin/env perl |
| |
| #------------------------------------------------------------------- |
| # Check header files and #include directives |
| # |
| # (1) include/*.h must not include pub_core_...h |
| # (2) coregrind/pub_core_xyzzy.h may include pub_tool_xyzzy.h |
| # other coregrind headers may not include pub_tool_xyzzy.h |
| # (3) coregrind/ *.c must not include pub_tool_xyzzy.h |
| # (4) tool *.[ch] files must not include pub_core_...h |
| # (5) include pub_core/tool_clreq.h instead of valgrind.h except in tools' |
| # export headers |
| # (6) coregrind/ *.[ch] must not use tl_assert |
| # (7) include/*.h and tool *.[ch] must not use vg_assert |
| # (8) coregrind/ *.[ch] must not use VG_(tool_panic) |
| # (9) include/*.h and tool *.[ch] must not use VG_(core_panic) |
| #------------------------------------------------------------------- |
| |
| use strict; |
| use warnings; |
| use File::Basename; |
| use Getopt::Long; |
| |
| my $this_script = basename($0); |
| |
| # The list of top-level directories is divided into three sets: |
| # |
| # (1) coregrind directories |
| # (2) tool directories |
| # (3) directories to ignore |
| # |
| # If a directory is found that does not belong to any of those sets, the |
| # script will terminate unsuccessfully. |
| |
| my %coregrind_dirs = ( |
| "include" => 1, |
| "coregrind" => 1, |
| ); |
| |
| my %tool_dirs = ( |
| "none" => 1, |
| "lackey" => 1, |
| "massif" => 1, |
| "memcheck" => 1, |
| "drd" => 1, |
| "helgrind", => 1, |
| "callgrind" => 1, |
| "cachegrind" => 1, |
| "shared" => 1, |
| "exp-bbv" => 1, |
| "exp-dhat" => 1, |
| "exp-sgcheck" => 1 |
| ); |
| |
| my %dirs_to_ignore = ( |
| ".deps" => 1, |
| ".svn" => 1, |
| ".git" => 1, # allow git mirrors of the svn repo |
| ".in_place" => 1, |
| "Inst" => 1, # the nightly scripts creates this |
| "VEX" => 1, |
| "docs" => 1, |
| "auxprogs" => 1, |
| "autom4te.cache" => 1, |
| "nightly" => 1, |
| "perf" => 1, |
| "tests" => 1, |
| "gdbserver_tests" => 1, |
| "mpi" => 1 |
| ); |
| |
| my %tool_export_header = ( |
| "drd/drd.h" => 1, |
| "helgrind/helgrind.h" => 1, |
| "memcheck/memcheck.h" => 1, |
| "callgrind/callgrind.h" => 1 |
| ); |
| |
| my $usage=<<EOF; |
| USAGE |
| |
| $this_script |
| |
| [--debug] Debugging output |
| |
| dir ... Directories to process |
| EOF |
| |
| my $debug = 0; |
| my $num_errors = 0; |
| |
| &main; |
| |
| sub main { |
| GetOptions( "debug" => \$debug ) || die $usage; |
| |
| my $argc = $#ARGV + 1; |
| |
| if ($argc < 1) { |
| die $usage; |
| } |
| |
| foreach my $dir (@ARGV) { |
| process_dir(undef, $dir, 0); |
| } |
| |
| my $rc = ($num_errors == 0) ? 0 : 1; |
| exit $rc; |
| } |
| |
| sub process_dir { |
| my ($path, $dir, $depth) = @_; |
| my $hdir; |
| |
| if ($depth == 0) { |
| # The root directory is always processed |
| } elsif ($depth == 1) { |
| # Toplevel directories |
| return if ($dirs_to_ignore{$dir}); |
| |
| if (! $tool_dirs{$dir} && ! $coregrind_dirs{$dir}) { |
| die "Unknown directory '$dir'. Please update $this_script\n"; |
| } |
| } else { |
| # Subdirectories |
| return if ($dirs_to_ignore{$dir}); |
| } |
| |
| print "DIR = $dir DEPTH = $depth\n" if ($debug); |
| |
| chdir($dir) || die "Cannot chdir '$dir'\n"; |
| |
| opendir($hdir, ".") || die "cannot open directory '.'"; |
| |
| while (my $file = readdir($hdir)) { |
| next if ($file eq "."); |
| next if ($file eq ".."); |
| |
| # Subdirectories |
| if (-d $file) { |
| my $full_path = defined $path ? "$path/$file" : $file; |
| process_dir($full_path, $file, $depth + 1); |
| next; |
| } |
| |
| # Regular files; only interested in *.c and *.h |
| next if (! ($file =~ /\.[ch]$/)); |
| my $path_name = defined $path ? "$path/$file" : $file; |
| process_file($path_name); |
| } |
| close($hdir); |
| chdir("..") || die "Cannot chdir '..'\n"; |
| } |
| |
| #--------------------------------------------------------------------- |
| # Return 1, if file is located in <valgrind>/include |
| #--------------------------------------------------------------------- |
| sub is_coregrind_export_header { |
| my ($path_name) = @_; |
| |
| return ($path_name =~ /^include\//) ? 1 : 0; |
| } |
| |
| #--------------------------------------------------------------------- |
| # Return 1, if file is located underneath <valgrind>/coregrind |
| #--------------------------------------------------------------------- |
| sub is_coregrind_file { |
| my ($path_name) = @_; |
| |
| return ($path_name =~ /^coregrind\//) ? 1 : 0; |
| } |
| |
| #--------------------------------------------------------------------- |
| # Return 1, if file is located underneath <valgrind>/<tool> |
| #--------------------------------------------------------------------- |
| sub is_tool_file { |
| my ($path_name) = @_; |
| |
| for my $tool (keys %tool_dirs) { |
| return 1 if ($path_name =~ /^$tool\//); |
| } |
| return 0 |
| } |
| |
| #--------------------------------------------------------------------- |
| # Return array of files #include'd by file. |
| #--------------------------------------------------------------------- |
| sub get_included_files { |
| my ($path_name) = @_; |
| my @includes = (); |
| my $file = basename($path_name); |
| |
| open(FILE, "<$file") || die "Cannot open file '$file'"; |
| |
| while (my $line = <FILE>) { |
| if ($line =~ /^\s*#\s*include "([^"]*)"/) { |
| push @includes, $1; |
| } |
| if ($line =~ /^\s*#\s*include <([^>]*)>/) { |
| push @includes, $1; |
| } |
| } |
| close FILE; |
| return @includes; |
| } |
| |
| #--------------------------------------------------------------------- |
| # Check a file from <valgrind>/include |
| #--------------------------------------------------------------------- |
| sub check_coregrind_export_header { |
| my ($path_name) = @_; |
| my $file = basename($path_name); |
| |
| foreach my $inc (get_included_files($path_name)) { |
| $inc = basename($inc); |
| # Must not include pub_core_.... |
| if ($inc =~ /pub_core_/) { |
| error("File $path_name must not include $inc\n"); |
| } |
| # Only pub_tool_clreq.h may include valgrind.h |
| if (($inc eq "valgrind.h") && ($path_name ne "include/pub_tool_clreq.h")) { |
| error("File $path_name should include pub_tool_clreq.h instead of $inc\n"); |
| } |
| } |
| # Must not use vg_assert |
| my $assert = `grep vg_assert $file`; |
| if ($assert ne "") { |
| error("File $path_name must not use vg_assert\n"); |
| } |
| # Must not use VG_(core_panic) |
| my $panic = `grep 'VG_(core_panic)' $file`; |
| if ($panic ne "") { |
| error("File $path_name must not use VG_(core_panic)\n"); |
| } |
| } |
| |
| #--------------------------------------------------------------------- |
| # Check a file from <valgrind>/coregrind |
| #--------------------------------------------------------------------- |
| sub check_coregrind_file { |
| my ($path_name) = @_; |
| my $file = basename($path_name); |
| |
| foreach my $inc (get_included_files($path_name)) { |
| print "\tINCLUDE $inc\n" if ($debug); |
| # Only pub_tool_xyzzy.h may include pub_core_xyzzy.h |
| if ($inc =~ /pub_tool_/) { |
| my $buddy = $inc; |
| $buddy =~ s/pub_tool/pub_core/; |
| if ($file ne $buddy) { |
| error("File $path_name must not include $inc\n"); |
| } |
| } |
| # Must not include valgrind.h |
| if ($inc eq "valgrind.h") { |
| error("File $path_name should include pub_core_clreq.h instead of $inc\n"); |
| } |
| } |
| # Must not use tl_assert |
| my $assert = `grep tl_assert $file`; |
| if ($assert ne "") { |
| error("File $path_name must not use tl_assert\n"); |
| } |
| # Must not use VG_(tool_panic) |
| my $panic = `grep 'VG_(tool_panic)' $file`; |
| if ($panic ne "") { |
| chomp($panic); |
| # Do not complain about the definition of VG_(tool_panic) |
| if (($path_name eq "coregrind/m_libcassert.c") && |
| ($panic eq "void VG_(tool_panic) ( const HChar* str )")) { |
| # OK |
| } else { |
| error("File $path_name must not use VG_(tool_panic)\n"); |
| } |
| } |
| } |
| |
| #--------------------------------------------------------------------- |
| # Check a file from <valgrind>/<tool> |
| #--------------------------------------------------------------------- |
| sub check_tool_file { |
| my ($path_name) = @_; |
| my $file = basename($path_name); |
| |
| foreach my $inc (get_included_files($path_name)) { |
| print "\tINCLUDE $inc\n" if ($debug); |
| # Must not include pub_core_... |
| if ($inc =~ /pub_core_/) { |
| error("File $path_name must not include $inc\n"); |
| } |
| # Must not include valgrind.h unless this is an export header |
| if ($inc eq "valgrind.h" && ! $tool_export_header{$path_name}) { |
| error("File $path_name should include pub_tool_clreq.h instead of $inc\n"); |
| } |
| } |
| # Must not use vg_assert |
| my $assert = `grep vg_assert $file`; |
| if ($assert ne "") { |
| error("File $path_name must not use vg_assert\n"); |
| } |
| # Must not use VG_(core_panic) |
| my $panic = `grep 'VG_(core_panic)' $file`; |
| if ($panic ne "") { |
| error("File $path_name must not use VG_(core_panic)\n"); |
| } |
| } |
| |
| sub process_file { |
| my ($path_name) = @_; |
| |
| print "FILE = $path_name\n" if ($debug); |
| |
| if (is_coregrind_export_header($path_name)) { |
| check_coregrind_export_header($path_name); |
| } elsif (is_coregrind_file($path_name)) { |
| check_coregrind_file($path_name); |
| } elsif (is_tool_file($path_name)) { |
| check_tool_file($path_name); |
| } |
| } |
| |
| sub error { |
| my ($message) = @_; |
| print STDERR "*** $message"; |
| ++$num_errors; |
| } |