| #!/usr/bin/perl -w | |
| # | |
| # Copyright (c) 2001-2008 Silicon Graphics, Inc. All Rights Reserved. | |
| # | |
| # This program is free software; you can redistribute it and/or | |
| # modify it under the terms of the GNU General Public License as | |
| # published by the Free Software Foundation. | |
| # | |
| # This program is distributed in the hope that it would be useful, | |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| # GNU General Public License for more details. | |
| # | |
| # You should have received a copy of the GNU General Public License | |
| # along with this program; if not, write the Free Software Foundation, | |
| # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
| # | |
| # | |
| # srcdiff is used to compare current user level code with the current | |
| # kernel code and advise of any differences between files which are | |
| # sharing some or all of their content. | |
| # | |
| # There are two classes of sharing which we will check - header files | |
| # in the include directory, which must be exactly the same (use diff) | |
| # and source files which contain routines which must be exactly the | |
| # same (but the userland file is always a subset of the kernel file, | |
| # and hence a more flexible mechanism to "diff" is required). | |
| # | |
| # NB: to cross check that srcdiff is finding all the functions in the | |
| # user source file, providing you have "mkproto" installed, you | |
| # can "cd xfsprogs/libxfs" and cut&paste this in a bourne shell: | |
| # $ for file in xfs_*.c; do | |
| # > mkproto -nps < $file | perl -ne ' | |
| # > END { print " $count\t- " } | |
| # > s/^.* (xfs\w+|\*xfs\w+|xlog\w+|\*xlog\w+) \(.*/\1/ && { $count++ }' | |
| # > echo $file | |
| # > done | |
| # (compare this to "srcdiff | fgrep Total:") | |
| # | |
| use strict; | |
| die "WORKAREA not set" unless defined $ENV{'WORKAREA'}; | |
| die "KWORKAREA not set" unless defined $ENV{'KWORKAREA'}; | |
| die "DMWORKAREA not set" unless defined $ENV{'DMWORKAREA'}; | |
| chdir $ENV{'WORKAREA'}; | |
| my $dmworkarea = $ENV{'DMWORKAREA'}; | |
| my $kworkarea = $ENV{'KWORKAREA'}; | |
| my $xdiff = $ENV{'XDIFF'}; | |
| my $quiet = 0; | |
| my $usage = 0; | |
| if ( ! -f "$kworkarea/xfs_fs.h" ) { | |
| $kworkarea .= '/fs/xfs'; | |
| die "Cannot find XFS in KWORKAREA" unless -f "$kworkarea/xfs_fs.h"; | |
| } | |
| die "Cannot find DMAPI in DMWORKAREA" unless -f "$dmworkarea/dmapi.h"; | |
| foreach (@ARGV) { | |
| if (/^-q$/) { | |
| $quiet++; | |
| } else { | |
| print STDERR "Illegal option $_\n"; | |
| $usage++; | |
| } | |
| } | |
| if ($usage) { | |
| print STDERR "Usage: $0 [-q]\n"; | |
| exit 1; | |
| } | |
| my @pkglist = qw( xfstests attr acl dmapi xfsdump xfsprogs ); | |
| my @difflist = qw( | |
| xfs_ag.h xfs_alloc.h xfs_alloc_btree.h xfs_arch.h | |
| xfs_attr_leaf.h xfs_attr_sf.h xfs_bit.h xfs_bmap.h | |
| xfs_bmap_btree.h xfs_btree.h xfs_buf_item.h | |
| xfs_da_btree.h xfs_dfrag.h xfs_dinode.h | |
| xfs_dir2.h xfs_dir2_block.h xfs_dir2_data.h | |
| xfs_dir2_leaf.h xfs_dir2_node.h xfs_dir2_sf.h | |
| xfs_extfree_item.h xfs_ialloc.h | |
| xfs_imap.h xfs_ialloc_btree.h xfs_inode.h xfs_inode_item.h | |
| xfs_inum.h xfs_log.h xfs_log_priv.h xfs_log_recover.h | |
| xfs_mount.h xfs_quota.h xfs_rtalloc.h | |
| xfs_sb.h xfs_trans.h xfs_trans_space.h xfs_types.h xfs_fs.h | |
| ); | |
| sub straightdiff { | |
| my ( $file, $prefix1, $prefix2 ) = @_; | |
| `diff $prefix1/$file $prefix2/$file >/dev/null 2>&1`; | |
| if (!$quiet) { | |
| print sprintf("\t%-35s ... ", $file); | |
| if ($? != 0) { printf("FAILED\n(%s/%s differs to %s/%s)\n", | |
| $prefix1, $file, $prefix2, $file); } | |
| else { print "ok\n"; } | |
| } elsif ($? != 0) { | |
| printf("\t%-35s ... FAILED\n(%s/%s differs to %s/%s)\n", | |
| $file, $prefix1, $file, $prefix2, $file); | |
| if (defined($xdiff)) { | |
| `$xdiff $prefix1/$file $prefix2/$file`; | |
| } | |
| } | |
| } | |
| # | |
| # xfstests directory m4 directory is a repository of all of the | |
| # custom m4 macros used in the packages we look after. | |
| # | |
| sub m4macrodiff { | |
| my ( $package ) = @_; | |
| foreach (`ls $package/m4/*.m4`) { | |
| my $m4 = `basename $_`; | |
| chomp($m4); | |
| straightdiff $m4, "$package/m4", "xfstests/m4"; | |
| } | |
| } | |
| my $first = shift @pkglist; | |
| foreach (@pkglist) { | |
| print "\n=== Checking $_ package ===\n"; | |
| m4macrodiff $_; | |
| straightdiff 'buildrules', "$first/include", "$_/include"; | |
| straightdiff 'buildmacros', "$first/include", "$_/include"; | |
| straightdiff 'Makefile', "$first/build", "$_/build"; | |
| straightdiff 'Makefile', "$first/build/rpm", "$_/build/rpm"; | |
| straightdiff 'Makefile', "$first/build/tar", "$_/build/tar"; | |
| } | |
| print "\n=== Checking headers ===\n"; | |
| foreach (@difflist) { | |
| straightdiff $_, 'xfsprogs/include', "$kworkarea"; | |
| } | |
| straightdiff 'dmapi_kern.h', 'dmapi/include', "$dmworkarea"; | |
| straightdiff 'dmapi.h', 'dmapi/include', "$dmworkarea"; | |
| # | |
| # setstate | |
| # Implements a tri-state FSA, see comments for state transitions | |
| # (knows about the way the XFS kernel code is written, & makes | |
| # some assumptions so as to not need to parse generic C code). | |
| # Accepts one line at a time from a source file, picking out the | |
| # function bodies so they can be subsequently compared. | |
| # | |
| my $line; # line number in current source file | |
| my $state; # current FSA state | |
| my $funcbody; # current function body (contents) | |
| sub setstate { | |
| my ( $newline ) = @_; | |
| $line++; | |
| # - state 0: | |
| # if line looks like start of a function, transition to 1 | |
| # & squirrel line away as line 1 of current function | |
| if ($state == 0) { | |
| if ($newline =~ m/^[xfs|xlog]/) { | |
| $state = 1; | |
| $funcbody = $newline; | |
| } | |
| } | |
| # - state 1: | |
| # if line looks like start of a function, stay here | |
| # & squirrel line away as line 1 of current function | |
| # otherwise if line isn't start of function body, | |
| # squirrel line away as next line of current function | |
| # (args/..., but not sure this is a real function yet) | |
| # otherwise (start of function) | |
| # squirrel line away as next line of current function | |
| # transition to state 2 | |
| elsif ($state == 1) { | |
| if ($newline =~ m/^[xfs|xlog]/) { | |
| $funcbody = $newline; | |
| } | |
| elsif ($newline =~ m/^\{/) { | |
| $state = 2; | |
| $funcbody .= $newline; | |
| } | |
| } | |
| # - state 2: | |
| # if line looks like end of function body, | |
| # squirrel line away as last line of current function | |
| # tell someone we have a complete function ready | |
| # transition to state 0 | |
| # otherwise | |
| # squirrel line away as next line of current function | |
| elsif ($state == 2) { | |
| $funcbody .= $newline; | |
| if ($newline =~ m/^\}/) { | |
| $state = 0; | |
| return $funcbody; | |
| } | |
| } | |
| else { | |
| die "unknown state transition"; | |
| } | |
| return undef; # i.e. not at end of a function | |
| } | |
| sub listfuncs { | |
| my ( $file ) = @_; | |
| my @funcs; | |
| $funcbody = ''; | |
| $state = $line = 0; | |
| open(USER, "$file") || die "cannot open $file"; | |
| while (<USER>) { | |
| my $func = setstate($_); | |
| push @funcs, $func if (defined($func)); # store function away | |
| } | |
| close USER; | |
| return @funcs; | |
| } | |
| sub hashfuncs { | |
| my ( $file ) = @_; | |
| my %funcs; | |
| $funcbody = ''; | |
| $state = $line = 0; | |
| open(KERN, "$file") || die "cannot open $file"; | |
| while (<KERN>) { | |
| my $func = setstate($_); | |
| if (defined($func)) { | |
| $func =~ m/^([xfs|xlog]\w+)\s*\(/; | |
| next unless defined($1); | |
| my $name = $1; | |
| if (defined($func)) { | |
| $funcs{$name} = $func; # store function away | |
| } | |
| } | |
| } | |
| close KERN; | |
| return %funcs; | |
| } | |
| sub diffme { | |
| my ( $sa, $sb ) = @_; | |
| return unless defined($xdiff); | |
| open(FILEA, "> /tmp/diff.user.$$") || die "cannot write to /tmp/diff.user.$$"; | |
| open(FILEB, "> /tmp/diff.kern.$$") || die "cannot write to /tmp/diff.kern.$$"; | |
| print FILEA $sa; | |
| print FILEB $sb; | |
| close FILEA; | |
| close FILEB; | |
| `$xdiff /tmp/diff.user.$$ /tmp/diff.kern.$$`; | |
| unlink ("/tmp/diff.user.$$","/tmp/diff.kern.$$"); | |
| } | |
| sub functiondiff { | |
| my ( $file, $prefix1, $prefix2 ) = @_; | |
| my $plural = ''; | |
| my $count = 0; | |
| my $name; | |
| my $found = 0; | |
| print "\n=== Checking $file routines ===\n" unless ($quiet); | |
| # iterate over user funcs, match up to kernel funcs | |
| # | |
| my @user = listfuncs "$prefix1/$file"; | |
| my %kern = hashfuncs "$prefix2/$file"; | |
| foreach my $userfunc (@user) { | |
| $userfunc =~ m/^([xfs|xlog]\w+)\s*\(/; | |
| next unless (defined($1)); | |
| $name = $1; | |
| $count++; | |
| if (exists($kern{$name})) { | |
| if ($userfunc ne $kern{$name}) { | |
| print "\n=== $file routines ===\n" | |
| if (!$found++ && $quiet); | |
| printf("\t%-35s ... ", $name); | |
| print "FAILED\n"; | |
| diffme $userfunc, $kern{$name}; | |
| } | |
| elsif (!$quiet) { | |
| printf("\t%-35s ... ", $name); | |
| print "ok\n"; | |
| } | |
| } | |
| else { | |
| print "Cannot find kernel function $userfunc"; | |
| print " in file $prefix2/$file\n"; | |
| } | |
| } | |
| ($count != 1) && ( $plural = 's' ); | |
| print "( Total: $count routine$plural checked in $file )\n" unless ($quiet); | |
| } | |
| # xfsprogs/{libxfs,libxlog}/* fs/xfs/* | |
| my @funclist = qw( | |
| xfs_alloc.c xfs_alloc_btree.c xfs_attr.c xfs_attr_leaf.c | |
| xfs_bmap.c xfs_bmap_btree.c xfs_btree.c xfs_da_btree.c | |
| xfs_dir2.c xfs_dir2_block.c xfs_dir2_data.c xfs_dir2_leaf.c | |
| xfs_dir2_node.c xfs_dir2_sf.c xfs_ialloc.c xfs_ialloc_btree.c | |
| xfs_inode.c xfs_mount.c xfs_rtalloc.c xfs_trans.c | |
| ); | |
| print "\n=== Checking libxfs code ===\n"; | |
| foreach (@funclist) { | |
| functiondiff $_, 'xfsprogs/libxfs', "$kworkarea"; | |
| } | |
| print "\n=== Checking libxlog code ===\n"; | |
| functiondiff 'xfs_log_recover.c', 'xfsprogs/libxlog', "$kworkarea"; |