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