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