| #!/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"; |