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 23use strict; 24use warnings; 25use File::Basename; 26use Getopt::Long; 27 28my $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 39my %coregrind_dirs = ( 40 "include" => 1, 41 "coregrind" => 1, 42 ); 43 44my %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 59my %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 77my %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 84my $usage=<<EOF; 85USAGE 86 87 $this_script 88 89 [--debug] Debugging output 90 91 dir ... Directories to process 92EOF 93 94my $debug = 0; 95my $num_errors = 0; 96 97&main; 98 99sub 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 116sub 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#--------------------------------------------------------------------- 163sub 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#--------------------------------------------------------------------- 172sub 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#--------------------------------------------------------------------- 181sub 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#--------------------------------------------------------------------- 193sub 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#--------------------------------------------------------------------- 215sub 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#--------------------------------------------------------------------- 245sub 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#--------------------------------------------------------------------- 286sub 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#--------------------------------------------------------------------- 316sub 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 343sub 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 361sub error { 362 my ($message) = @_; 363 print STDERR "*** $message"; 364 ++$num_errors; 365} 366