Test script updates to cater for the revised tree layout. Allows for separate user...
[xfstests-dev.git] / tools / srcdiff
1 #!/usr/bin/perl -w
2 use strict;
3
4 # srcdiff is used to compare current user level code with the current
5 # kernel code and advise of any differences between files which are
6 # sharing some or all of their content.
7
8 # There are two classes of sharing which we will check - header files
9 # in the include directory, which must be exactly the same (use diff)
10 # and source files which contain routines which must be exactly the
11 # same (but the userland file is always a subset of the kernel file,
12 # and hence a more flexible mechanism to "diff" is required).
13
14 # NB: to cross check that srcdiff is finding all the functions in the
15 #     user source file, providing you have "mkproto" installed, you
16 #     can "cd xfsprogs/libxfs" and cut&paste this in a bourne shell:
17 #     $ for file in xfs_*.c; do
18 #     > mkproto -nps < $file | perl -ne '
19 #     > END { print "    $count\t- " }
20 #     > s/^.* (xfs\w+|\*xfs\w+|xlog\w+|\*xlog\w+) \(.*/\1/ && { $count++ }'
21 #     > echo $file
22 #     > done
23 # (compare this to "srcdiff | fgrep Total:")
24
25
26 die "WORKAREA not set" unless defined $ENV{'WORKAREA'};
27 die "KWORKAREA not set" unless defined $ENV{'KWORKAREA'};
28 chdir $ENV{'WORKAREA'};
29 my $kworkarea = $ENV{'KWORKAREA'};
30 my $xdiff = $ENV{'XDIFF'};
31 my $quiet=0;
32 my $usage=0;
33
34 foreach (@ARGV) {
35         if (/^-q$/) {
36                 $quiet++;
37         } else {
38                 print STDERR "Illegal option $_\n";
39                 $usage++;
40         }
41 }
42
43 if ($usage) {
44     print STDERR "Usage: $0 [-q]\n";
45     exit 1;
46 }
47
48 my @pkglist = qw( xfstests attr acl dmapi xfsdump xfsprogs );
49 my @difflist = qw(
50         xfs_ag.h  xfs_alloc.h  xfs_alloc_btree.h xfs_arch.h
51         xfs_attr_leaf.h  xfs_attr_sf.h  xfs_bit.h  xfs_bmap.h
52         xfs_bmap_btree.h  xfs_btree.h  xfs_buf_item.h
53         xfs_da_btree.h  xfs_dfrag.h  xfs_dinode.h  xfs_dir.h
54         xfs_dir2.h  xfs_dir2_block.h  xfs_dir2_data.h
55         xfs_dir2_leaf.h  xfs_dir2_node.h  xfs_dir2_sf.h
56         xfs_dir_leaf.h  xfs_dir_sf.h  xfs_extfree_item.h  xfs_ialloc.h
57         xfs_imap.h  xfs_ialloc_btree.h  xfs_inode.h  xfs_inode_item.h
58         xfs_inum.h  xfs_log.h  xfs_log_priv.h  xfs_log_recover.h
59         xfs_mount.h  xfs_quota.h  xfs_rtalloc.h
60         xfs_sb.h  xfs_trans.h  xfs_trans_space.h  xfs_types.h
61         xfs_fs.h xfs_acl.h xfs_cap.h xfs_mac.h
62 );
63
64 sub straightdiff {
65         my ( $file, $prefix1, $prefix2 ) = @_;
66
67         `diff $prefix1/$file $prefix2/$file >/dev/null 2>&1`;
68         if (!$quiet) {
69                 print sprintf("\t%-35s ... ", $file);
70                 if ($? != 0)    { printf("FAILED\n(%s/%s differs to %s/%s)\n",
71                                          $prefix1, $file, $prefix2, $file); }
72                 else            { print "ok\n"; }
73         
74         } elsif ($? != 0) {
75                 printf("\t%-35s ... FAILED\n(%s/%s differs to %s/%s)\n",
76                         $file, $prefix1, $file, $prefix2, $file);
77         }
78 }
79
80 #
81 # xfstests directory m4 directory is a repository of all of the
82 # custom m4 macros used in the packages we look after.
83 #
84 sub m4macrodiff {
85         my ( $package ) = @_;
86
87         foreach (`ls $package/m4/*.m4`) {
88                 my $m4 = `basename $_`;
89                 chomp($m4);
90                 straightdiff $m4, "$package/m4", "xfstests/m4";
91         }
92 }
93
94 my $first = shift @pkglist;
95 foreach (@pkglist) {
96         print "\n=== Checking $_ package ===\n";
97         m4macrodiff $_;
98         straightdiff 'buildrules', "$first/include", "$_/include";
99         straightdiff 'buildmacros', "$first/include", "$_/include";
100         straightdiff 'Makefile', "$first/build", "$_/build";
101         straightdiff 'Makefile', "$first/build/rpm", "$_/build/rpm";
102         straightdiff 'Makefile', "$first/build/tar", "$_/build/tar";
103 }
104 print "\n=== Checking headers ===\n";
105 foreach (@difflist) {
106         straightdiff $_, 'xfsprogs/include', "$kworkarea/fs/xfs";
107 }
108 straightdiff 'dmapi_kern.h', 'dmapi/include', "$kworkarea/fs/xfs/dmapi";
109 straightdiff 'dmapi.h', 'dmapi/include', "$kworkarea/fs/xfs/dmapi";
110
111
112 # setstate
113 # Implements a tri-state FSA, see comments for state transitions
114 #  (knows about the way the XFS kernel code is written, & makes
115 #   some assumptions so as to not need to parse generic C code).
116 # Accepts one line at a time from a source file, picking out the
117 #   function bodies so they can be subsequently compared.
118
119
120 my $line;       # line number in current source file
121 my $state;      # current FSA state
122 my $funcbody;   # current function body (contents)
123
124 sub setstate {
125         my ( $newline ) = @_;
126         $line++;
127
128         # - state 0:
129         #       if line looks like start of a function, transition to 1
130         #               & squirrel line away as line 1 of current function
131         if ($state == 0) {
132                 if ($newline =~ m/^[xfs|xlog]/) {
133                         $state = 1;
134                         $funcbody = $newline;
135                 }
136         }
137
138         # - state 1:
139         #       if line looks like start of a function, stay here
140         #               & squirrel line away as line 1 of current function
141         #       otherwise if line isn't start of function body,
142         #               squirrel line away as next line of current function
143         #               (args/..., but not sure this is a real function yet)
144         #       otherwise (start of function)
145         #               squirrel line away as next line of current function
146         #               transition to state 2
147         elsif ($state == 1) {
148                 if ($newline =~ m/^[xfs|xlog]/) {
149                         $funcbody = $newline;
150                 }
151                 elsif ($newline =~ m/^\{/) {
152                         $state = 2;
153                         $funcbody .= $newline;
154                 }
155         }
156
157         # - state 2:
158         #       if line looks like end of function body,
159         #               squirrel line away as last line of current function
160         #               tell someone we have a complete function ready
161         #               transition to state 0
162         #       otherwise
163         #               squirrel line away as next line of current function
164         elsif ($state == 2) {
165                 $funcbody .= $newline;
166                 if ($newline =~ m/^\}/) {
167                         $state = 0;
168                         return $funcbody;
169                 }
170         }
171
172         else {
173                 die "unknown state transition";
174         }
175         return undef;   # i.e. not at end of a function
176 }
177
178 sub listfuncs {
179         my ( $file ) = @_;
180         my @funcs;
181
182         $funcbody = '';
183         $state = $line = 0;
184
185         open(USER, "$file") || die "cannot open $file";
186         while (<USER>) {
187                 my $func = setstate($_);
188                 push @funcs, $func if (defined($func)); # store function away
189         }
190         close USER;
191         return @funcs;
192 }
193
194 sub hashfuncs {
195         my ( $file ) = @_;
196         my %funcs;
197
198         $funcbody = '';
199         $state = $line = 0;
200
201         open(KERN, "$file") || die "cannot open $file";
202         while (<KERN>) {
203                 my $func = setstate($_);
204                 if (defined($func)) {
205                         $func =~ m/^([xfs|xlog]\w+)\s*\(/;
206                         next unless defined($1);
207                         my $name = $1;
208                         if (defined($func)) {
209                                 $funcs{$name} = $func;  # store function away
210                         }
211                 }
212         }
213         close KERN;
214         return %funcs;
215 }
216
217 sub diffme {
218         my ( $sa, $sb ) = @_;
219
220         return unless defined($xdiff);
221
222         open(FILEA, "> /tmp/diff.user.$$") || die "cannot write to /tmp/diff.user.$$";
223         open(FILEB, "> /tmp/diff.kern.$$") || die "cannot write to /tmp/diff.kern.$$";
224         print FILEA $sa;
225         print FILEB $sb;
226         close FILEA;
227         close FILEB;
228         `$xdiff /tmp/diff.user.$$ /tmp/diff.kern.$$`;
229         unlink ("/tmp/diff.user.$$","/tmp/diff.kern.$$");
230 }
231
232 sub functiondiff {
233         my ( $file, $prefix1, $prefix2 ) = @_;
234         my $plural = '';
235         my $count = 0;
236         my $name;
237         my $found = 0;
238
239         print "\n=== Checking $file routines ===\n" unless ($quiet);
240
241         # iterate over user funcs, match up to kernel funcs
242         # 
243         my @user = listfuncs "$prefix1/$file";
244         my %kern = hashfuncs "$prefix2/$file";
245
246         foreach my $userfunc (@user) {
247                 
248                 $userfunc =~ m/^([xfs|xlog]\w+)\s*\(/;
249                 next unless (defined($1));
250                 $name = $1;
251                 $count++;
252
253                 if (exists($kern{$name})) {
254                         if ($userfunc ne $kern{$name}) {
255                                 print "\n=== $file routines ===\n"
256                                     if (!$found++ && $quiet);
257                                     
258                                 printf("\t%-35s ... ", $name);
259                                 print "FAILED\n";
260                                 diffme $userfunc, $kern{$name};
261                         }
262                         elsif (!$quiet) {
263                                 printf("\t%-35s ... ", $name);
264                                 print "ok\n";
265                         }
266                 }
267                 else {
268                         print "Cannot find kernel function $userfunc";
269                         print " in file $prefix2/$file\n";
270                 }
271         }
272         ($count != 1) && ( $plural = 's' );
273         print "( Total: $count routine$plural checked in $file )\n" unless ($quiet);
274 }
275
276 # xfsprogs/{libxfs,libxlog}/* fs/xfs/*
277 my @funclist = qw(
278         xfs_alloc.c  xfs_alloc_btree.c  xfs_attr_leaf.c
279         xfs_bmap.c  xfs_bmap_btree.c  xfs_btree.c  xfs_da_btree.c
280         xfs_dir.c  xfs_dir2.c  xfs_dir2_block.c  xfs_dir2_data.c
281         xfs_dir2_leaf.c  xfs_dir2_node.c  xfs_dir2_sf.c
282         xfs_dir_leaf.c  xfs_ialloc.c  xfs_ialloc_btree.c
283         xfs_inode.c  xfs_rtalloc.c  xfs_mount.c  xfs_trans.c
284 );
285
286 print "\n=== Checking libxfs code ===\n";
287 foreach (@funclist) {
288         functiondiff $_, 'xfsprogs/libxfs', "$kworkarea/fs/xfs";
289 }
290 print "\n=== Checking libxlog code ===\n";
291 functiondiff 'xfs_log_recover.c', 'xfsprogs/libxlog', "$kworkarea/fs/xfs";