blob: 47491b2597dcb8fca4982241679884839641cbba [file] [log] [blame]
florian43c56332013-09-19 14:55:09 +00001#!/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
floriane2800c92014-09-15 20:57:45 +000013# (6) coregrind/ *.[ch] must not use tl_assert
florianbc251982014-09-16 07:20:21 +000014# (7) include/*.h and tool *.[ch] must not use vg_assert
floriana4ca4fe2014-09-16 09:28:12 +000015# (8) coregrind/ *.[ch] must not use VG_(tool_panic)
16# (9) include/*.h and tool *.[ch] must not use VG_(core_panic)
florian29801632015-09-30 20:58:36 +000017# (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.
florian43c56332013-09-19 14:55:09 +000021#-------------------------------------------------------------------
22
23use strict;
24use warnings;
bart727033d2013-11-17 10:06:09 +000025use File::Basename;
florian43c56332013-09-19 14:55:09 +000026use Getopt::Long;
27
bart727033d2013-11-17 10:06:09 +000028my $this_script = basename($0);
florian43c56332013-09-19 14:55:09 +000029
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,
bart9c7779b2013-11-24 17:48:13 +000053 "shared" => 1,
florian43c56332013-09-19 14:55:09 +000054 "exp-bbv" => 1,
55 "exp-dhat" => 1,
56 "exp-sgcheck" => 1
57 );
58
59my %dirs_to_ignore = (
60 ".deps" => 1,
61 ".svn" => 1,
mjw782f3702013-10-02 11:39:41 +000062 ".git" => 1, # allow git mirrors of the svn repo
florian43c56332013-09-19 14:55:09 +000063 ".in_place" => 1,
floriandf4aec22013-09-20 12:12:52 +000064 "Inst" => 1, # the nightly scripts creates this
florian43c56332013-09-19 14:55:09 +000065 "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,
sewardj8eb8bab2015-07-21 14:44:28 +000073 "mpi" => 1,
74 "solaris" => 1
florian43c56332013-09-19 14:55:09 +000075 );
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
florian29801632015-09-30 20:58:36 +0000151# Regular files; only interested in *.c, *.S and *.h
152 next if (! ($file =~ /\.[cSh]$/));
florian43c56332013-09-19 14:55:09 +0000153 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#---------------------------------------------------------------------
florian43c56332013-09-19 14:55:09 +0000161# 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) = @_;
floriane2800c92014-09-15 20:57:45 +0000217 my $file = basename($path_name);
florian43c56332013-09-19 14:55:09 +0000218
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 }
floriane2800c92014-09-15 20:57:45 +0000230# 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 }
floriana4ca4fe2014-09-16 09:28:12 +0000235# 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 }
florian43c56332013-09-19 14:55:09 +0000240}
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 }
floriane2800c92014-09-15 20:57:45 +0000264# 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 }
floriana4ca4fe2014-09-16 09:28:12 +0000269# 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 }
florian43c56332013-09-19 14:55:09 +0000281}
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 }
floriane2800c92014-09-15 20:57:45 +0000301# 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 }
floriana4ca4fe2014-09-16 09:28:12 +0000306# 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 }
florian43c56332013-09-19 14:55:09 +0000311}
312
florian29801632015-09-30 20:58:36 +0000313#---------------------------------------------------------------------
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
florian43c56332013-09-19 14:55:09 +0000343sub 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 }
florian29801632015-09-30 20:58:36 +0000355
356 if ($path_name =~ /\.S$/) {
357 check_assembler_file($path_name);
358 }
florian43c56332013-09-19 14:55:09 +0000359}
360
361sub error {
362 my ($message) = @_;
363 print STDERR "*** $message";
364 ++$num_errors;
365}