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 19use strict; 20use warnings; 21use File::Basename; 22use Getopt::Long; 23 24my $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 35my %coregrind_dirs = ( 36 "include" => 1, 37 "coregrind" => 1, 38 ); 39 40my %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 55my %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 72my %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 79my $usage=<<EOF; 80USAGE 81 82 $this_script 83 84 [--debug] Debugging output 85 86 dir ... Directories to process 87EOF 88 89my $debug = 0; 90my $num_errors = 0; 91 92&main; 93 94sub 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 111sub 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#--------------------------------------------------------------------- 158sub 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#--------------------------------------------------------------------- 167sub 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#--------------------------------------------------------------------- 176sub 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#--------------------------------------------------------------------- 188sub 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#--------------------------------------------------------------------- 210sub 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#--------------------------------------------------------------------- 240sub 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#--------------------------------------------------------------------- 281sub 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 308sub 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 322sub error { 323 my ($message) = @_; 324 print STDERR "*** $message"; 325 ++$num_errors; 326} 327