#!/usr/bin/perl -w use strict; # # 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 cmd/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:") # die "WORKAREA not set" unless defined $ENV{'WORKAREA'}; chdir $ENV{'WORKAREA'}; my $xdiff = $ENV{'XDIFF'}; my $quiet=0; my $usage=0; 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_dir.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_dir_leaf.h xfs_dir_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 xfs_acl.h xfs_cap.h xfs_mac.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); } } # # 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 cmd/$package/m4/*.m4`) { my $m4 = `basename $_`; chomp($m4); straightdiff $m4, "cmd/$package/m4", "cmd/xfstests/m4"; } } my $first = shift @pkglist; foreach (@pkglist) { print "\n=== Checking $_ package ===\n"; m4macrodiff $_; straightdiff 'buildrules', "cmd/$first/include", "cmd/$_/include"; straightdiff 'buildmacros', "cmd/$first/include", "cmd/$_/include"; straightdiff 'Makefile', "cmd/$first/build", "cmd/$_/build"; straightdiff 'Makefile', "cmd/$first/build/rpm", "cmd/$_/build/rpm"; straightdiff 'Makefile', "cmd/$first/build/tar", "cmd/$_/build/tar"; } print "\n=== Checking headers ===\n"; foreach (@difflist) { straightdiff $_, 'cmd/xfsprogs/include', 'linux/fs/xfs'; } straightdiff 'dmapi_kern.h', 'cmd/dmapi/include', 'linux/fs/xfs/dmapi'; straightdiff 'dmapi.h', 'cmd/dmapi/include', 'linux/fs/xfs/dmapi'; # # 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 () { 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 () { 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); } # cmd/xfsprogs/{libxfs,libxlog}/* fs/xfs/* my @funclist = qw( xfs_alloc.c xfs_alloc_btree.c xfs_attr_leaf.c xfs_bmap.c xfs_bmap_btree.c xfs_btree.c xfs_da_btree.c xfs_dir.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_dir_leaf.c xfs_ialloc.c xfs_ialloc_btree.c xfs_inode.c xfs_rtalloc.c xfs_mount.c xfs_trans.c ); print "\n=== Checking libxfs code ===\n"; foreach (@funclist) { functiondiff $_, 'cmd/xfsprogs/libxfs', 'linux/fs/xfs'; } print "\n=== Checking libxlog code ===\n"; functiondiff 'xfs_log_recover.c', 'cmd/xfsprogs/libxlog', 'linux/fs/xfs';