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