70c1fdf7c3d796c612b2741286ab7c518da26ce8
[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 cmd/xfs/libxfs" and cut&paste this into 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:") ... repeat for logprint.
24
25
26 die "WORKAREA not set" unless defined $ENV{'WORKAREA'};
27 chdir $ENV{'WORKAREA'};
28 my $xdiff = $ENV{'XDIFF'};
29 my $quiet=0;
30 my $usage=0;
31
32 foreach (@ARGV) {
33     if (/^-q$/) {
34         $quiet++;
35     } else {
36         print STDERR "Illegal option $_\n";
37         $usage++;
38     }
39 }
40
41 if ($usage) {
42     print STDERR "Usage: $0 [-q]\n";
43     exit 1;
44 }
45
46 my @difflist = qw(
47         xfs_ag.h  xfs_alloc.h  xfs_alloc_btree.h xfs_arch.h
48         xfs_attr_leaf.h  xfs_attr_sf.h  xfs_bit.h  xfs_bmap.h
49         xfs_bmap_btree.h  xfs_btree.h  xfs_buf_item.h
50         xfs_da_btree.h  xfs_dfrag.h  xfs_dinode.h  xfs_dir.h
51         xfs_dir2.h  xfs_dir2_block.h  xfs_dir2_data.h
52         xfs_dir2_leaf.h  xfs_dir2_node.h  xfs_dir2_sf.h
53         xfs_dir_leaf.h  xfs_dir_sf.h  xfs_dqblk.h  xfs_dquot_item.h
54         xfs_extfree_item.h  xfs_ialloc.h  xfs_imap.h
55         xfs_ialloc_btree.h  xfs_inode.h  xfs_inode_item.h
56         xfs_inum.h  xfs_log.h  xfs_log_priv.h  xfs_log_recover.h
57         xfs_mount.h  xfs_quota.h  xfs_rtalloc.h
58         xfs_sb.h  xfs_trans.h  xfs_trans_space.h  xfs_types.h
59 );
60
61 sub straightdiff {
62         my ( $file, $prefix1, $prefix2 ) = @_;
63
64         `diff $prefix1/$file $prefix2/$file >/dev/null 2>&1`;
65         if (!$quiet) {
66             print sprintf("\t%-35s ... ", $file);
67             if ($? != 0)        { print "FAILED\n"; }
68             else                { print "ok\n"; }
69         } elsif ($? != 0) { 
70                 printf("\t%-35s ... ", $file);
71                 print "FAILED\n"; 
72         }
73 }
74
75 print "\n=== Checking headers ===\n";
76 foreach (@difflist) {
77         straightdiff $_, 'cmd/xfs/include', 'linux/fs/xfs';
78 }
79 straightdiff 'xfs_cred.h', 'cmd/xfs/include', 'linux/fs/xfs/linux';
80 straightdiff 'xfs_fs.h', 'cmd/xfs/include', 'linux/include/linux';
81 straightdiff 'attributes.h', 'cmd/xfs/include', 'linux/include/linux';
82 straightdiff 'acl.h', 'cmd/xfs/include', 'linux/fs/xfs/pseudo-inc/sys';
83 straightdiff 'arch.h', 'cmd/xfs/include', 'linux/fs/xfs/support';
84 straightdiff 'xqm.h', 'cmd/xfs/include', 'linux/include/linux';
85
86
87 # setstate
88 # Implements a tri-state FSA, see comments for state transitions
89 #  (knows about the way the XFS kernel code is written, & makes
90 #   some assumptions so as to not need to parse generic C code).
91 # Accepts one line at a time from a source file, picking out the
92 #   function bodies so they can be subsequently compared.
93
94
95 my $line;       # line number in current source file
96 my $state;      # current FSA state
97 my $funcbody;   # current function body (contents)
98
99 sub setstate {
100         my ( $newline ) = @_;
101         $line++;
102
103         # - state 0:
104         #       if line looks like start of a function, transition to 1
105         #               & squirrel line away as line 1 of current function
106         if ($state == 0) {
107                 if ($newline =~ m/^[xfs|xlog]/) {
108                         $state = 1;
109                         $funcbody = $newline;
110                 }
111         }
112
113         # - state 1:
114         #       if line looks like start of a function, stay here
115         #               & squirrel line away as line 1 of current function
116         #       otherwise if line isn't start of function body,
117         #               squirrel line away as next line of current function
118         #               (args/..., but not sure this is a real function yet)
119         #       otherwise (start of function)
120         #               squirrel line away as next line of current function
121         #               transition to state 2
122         elsif ($state == 1) {
123                 if ($newline =~ m/^[xfs|xlog]/) {
124                         $funcbody = $newline;
125                 }
126                 elsif ($newline =~ m/^\{/) {
127                         $state = 2;
128                         $funcbody .= $newline;
129                 }
130         }
131
132         # - state 2:
133         #       if line looks like end of function body,
134         #               squirrel line away as last line of current function
135         #               tell someone we have a complete function ready
136         #               transition to state 0
137         #       otherwise
138         #               squirrel line away as next line of current function
139         elsif ($state == 2) {
140                 $funcbody .= $newline;
141                 if ($newline =~ m/^\}/) {
142                         $state = 0;
143                         return $funcbody;
144                 }
145         }
146
147         else {
148                 die "unknown state transition";
149         }
150         return undef;   # i.e. not at end of a function
151 }
152
153 sub listfuncs {
154         my ( $file ) = @_;
155         my @funcs;
156
157         $funcbody = '';
158         $state = $line = 0;
159
160         open(USER, "$file") || die "cannot open $file";
161         while (<USER>) {
162                 my $func = setstate($_);
163                 push @funcs, $func if (defined($func)); # store function away
164         }
165         close USER;
166         return @funcs;
167 }
168
169 sub hashfuncs {
170         my ( $file ) = @_;
171         my %funcs;
172
173         $funcbody = '';
174         $state = $line = 0;
175
176         open(KERN, "$file") || die "cannot open $file";
177         while (<KERN>) {
178                 my $func = setstate($_);
179                 if (defined($func)) {
180                         $func =~ m/^([xfs|xlog]\w+)\s*\(/;
181                         next unless defined($1);
182                         my $name = $1;
183                         if (defined($func)) {
184                                 $funcs{$name} = $func;  # store function away
185                         }
186                 }
187         }
188         close KERN;
189         return %funcs;
190 }
191
192 sub diffme {
193         my ( $sa, $sb ) = @_;
194
195         return unless defined($xdiff);
196
197         open(FILEA, "> /tmp/diff.user.$$") || die "cannot write to /tmp/diff.user.$$";
198         open(FILEB, "> /tmp/diff.kern.$$") || die "cannot write to /tmp/diff.kern.$$";
199         print FILEA $sa;
200         print FILEB $sb;
201         close FILEA;
202         close FILEB;
203         `$xdiff /tmp/diff.user.$$ /tmp/diff.kern.$$`;
204         unlink ("/tmp/diff.user.$$","/tmp/diff.kern.$$");
205 }
206
207 sub functiondiff {
208         my ( $file, $prefix1, $prefix2 ) = @_;
209         my $plural = '';
210         my $count = 0;
211         my $name;
212         my $found = 0;
213
214         print "\n=== Checking $file routines ===\n" unless ($quiet);
215
216         # iterate over user funcs, match up to kernel funcs
217         # 
218         my @user = listfuncs "$prefix1/$file";
219         my %kern = hashfuncs "$prefix2/$file";
220
221         foreach my $userfunc (@user) {
222                 
223                 $userfunc =~ m/^([xfs|xlog]\w+)\s*\(/;
224                 next unless (defined($1));
225                 $name = $1;
226                 $count++;
227
228                 if (exists($kern{$name})) {
229                         if ($userfunc ne $kern{$name}) {
230                                 print "\n=== $file routines ===\n"
231                                     if (!$found++ && $quiet);
232                                     
233                                 printf("\t%-35s ... ", $name);
234                                 print "FAILED\n";
235                                 diffme $userfunc, $kern{$name};
236                         }
237                         elsif (!$quiet) {
238                                 printf("\t%-35s ... ", $name);
239                                 print "ok\n";
240                         }
241                 }
242                 else {
243                         print "Cannot find kernel function $userfunc";
244                         print " in file $prefix2/$file\n";
245                 }
246         }
247         ($count != 1) && ( $plural = 's' );
248         print "( Total: $count routine$plural checked in $file )\n" unless ($quiet);
249 }
250
251 # cmd/xfs/{libxfs,logprint}/* fs/xfs/*
252 my @funclist = qw(
253         xfs_alloc.c  xfs_alloc_btree.c  xfs_attr_leaf.c  xfs_bit.c
254         xfs_bmap.c  xfs_bmap_btree.c  xfs_btree.c  xfs_da_btree.c
255         xfs_dir.c  xfs_dir2.c  xfs_dir2_block.c  xfs_dir2_data.c
256         xfs_dir2_leaf.c  xfs_dir2_node.c  xfs_dir2_sf.c
257         xfs_dir_leaf.c  xfs_ialloc.c  xfs_ialloc_btree.c
258         xfs_inode.c  xfs_rtalloc.c  xfs_rtbit.c xfs_mount.c
259         xfs_trans.c
260 );
261
262 print "\n=== Checking libxfs code ===\n";
263 foreach (@funclist) {
264         functiondiff $_, 'cmd/xfs/libxfs', 'linux/fs/xfs';
265 }
266 print "\n=== Checking logprint code ===\n";
267 functiondiff 'xfs_log_recover.c', 'cmd/xfs/logprint', 'linux/fs/xfs';