• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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