#!/usr/bin/perl use strict; use warnings; # Copyright (C) 2012 Glen Pitt-Pladdy # # 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; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will 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 to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # # See: http://www.pitt-pladdy.com/blog/_20120215-111422_0000_Filesystems_Fragmentation/ # Inspired by: http://forums.gentoo.org/viewtopic-t-429915.html my $FIND = '/usr/bin/find'; my $FILEFRAG = '/usr/sbin/filefrag'; my $totalbytes = 0; my $totalfrags = 0; my $files = 0; my $fraggedfiles = 0; my %histogram; my %worstfiles; my $WORSTFILES = 100; # search for all files on this device my $path = $ARGV[0]; $path =~ s/\\/\\\\/g; $path =~ s/`/\\`/g; $path =~ s/\$/\\\$/g; $path =~ s/"/\\"/g; open FND, '-|', "$FIND \"$path\" -xdev -type f" or die "$0: FATAL - can't execute \"$FIND \"$path\" -type f -xdev\": $!\n"; my $count = 0; while ( defined ( my $file = ) ) { chomp $file; # find the fraggyness my $fileesc = $file; $fileesc =~ s/\\/\\\\/g; $fileesc =~ s/`/\\`/g; $fileesc =~ s/\$/\\\$/g; $fileesc =~ s/"/\\"/g; open FFRAG, '-|', "$FILEFRAG \"$fileesc\"" or die "$0: FATAL - can't run \"$FILEFRAG \"$fileesc\"\": $!\n"; my $result = ; close FFRAG; chomp $result; if ( $result !~ /: (\d+) extents? found$/ ) { die "$0: FATAL - don't understand $FILEFRAG output: $result\n"; } my $extents = $1; # prepare stats $totalbytes += -s $file; ++$files; my $frags = 0; if ( $extents > 1 ) { $frags = $extents - 1; $totalfrags += $frags; ++$fraggedfiles; # keep track of worst files $worstfiles{$file} = $frags; # keep %worstfiles under control periodically if ( $count % 1000 == 0 ) { # time for a cleaning cycle my $toremove = keys ( %worstfiles ) - $WORSTFILES; foreach my $worstfile (sort {$worstfiles{$a}<=>$worstfiles{$b}} keys %worstfiles) { if ( $toremove-- <= 0 ) { last; } delete $worstfiles{$worstfile}; } } } if ( ! exists $histogram{$frags} ) { $histogram{$frags} = 0; } ++$histogram{$frags}; ++$count; } close FND; # report print "Non-contigous files: $fraggedfiles / $files = ".(int($fraggedfiles / $files * 1000 + 0.5)/10)."%\n"; print "Bytes per frag: ".int($totalbytes / $totalfrags + 0.5)."\n"; print "\n"; # show $WORSTFILES worst files $count = 0; print "Worst files:\n"; foreach my $worstfile (sort {$worstfiles{$b}<=>$worstfiles{$a}} keys %worstfiles) { if ( $count++ >= $WORSTFILES ) { last; } print "$worstfiles{$worstfile} frags: $worstfile\n"; } print "\n"; # histogram: frags, # files print "\n"; print "Frags,#Files,%Files\n"; foreach my $frags (sort {$a<=>$b} keys %histogram) { print "$frags,$histogram{$frags},".(int($histogram{$frags} / $files * 1000 + 0.5)/10)."\n"; }