#!/usr/bin/perl -w
#
# Usage:
# 1) run command with:
#    $ strace -f  -e open,close,read,write,fsync -otrace.log ...your command...
# 2) analyze I/Os with:
#    $ io.pl < trace.log
#
# Author: Dominique Pelle <dominique.pelle@gmail.com>

while (<>) {
  if (/^\d+ +open\("(.*)", [^"]* *= *(\d+)$/) {
    $file_descr_to_name{$2} = $1;
    $open_count{$1}++;

  # Measure reads...
  } elsif (/^\d+ +read\((\d+), ".* *= *(\d+)$/) {
    if (exists $file_descr_to_name{$1}) {
      $file_name = $file_descr_to_name{$1};
      $read_bytes{$file_name} += $2;
      $read_count{$file_name}++;
    }
  } elsif (/^\d+ +read\((\d+), *<unfinished *\.\.\.>$/) {
    $unfinished_read_filename = $file_descr_to_name{$1};
  } elsif (/^\d+ +<\.\.\. *read resumed> *".*= *(\d+)$/) {
    if ($unfinished_read_filename) {
      $read_bytes{$unfinished_read_filename} += $1;
      $read_count{$unfinished_read_filename}++;
    }

  # Measure writes...
  } elsif (/^\d+ +write\((\d+), ".* = *(\d+)$/) {
    if (exists $file_descr_to_name{$1}) {
      $file_name = $file_descr_to_name{$1};
      $write_bytes{$file_name} += $2;
      $write_count{$file_name}++;
    }
  } elsif (/^\d+ +write\((\d+), *<unfinished *\.\.\.>$/) {
    $unfinished_write_filename = $file_descr_to_name{$1};
  } elsif (/^\d+ +<\.\.\. write resumed>.*= *(\d+)$/) {
    if ($unfinished_write_filename) {
      $read_bytes{$unfinished_write_filename} += $1;
      $read_count{$unfinished_write_filename}++;
    }
  } elsif (/^\d+ +fsync\((\d+)\)/) {
    if (exists $file_descr_to_name{$1}) {
      $file_name = $file_descr_to_name{$1};
      $fsync_count{$file_name}++;
    }
  }
}

print "read_bytes   #read  write_bytes  #write  #open  #fsync  filename\n";
print "----------   -----  -----------  ------  -----  ------  --------\n";


# open_count should contain all file names, including the files which we only
# open, but never read from or write to.
foreach my $key (sort {($read_bytes{$b} || 0) <=> ($read_bytes{$a} || 0)} keys %open_count) {
  printf "%10d %7d %10d %7d %7d %7d  %s\n",
          ($read_bytes{$key}  || 0), ($read_count{$key}  || 0),
          ($write_bytes{$key} || 0), ($write_count{$key} || 0),
          ($open_count{$key}  || 0), ($fsync_count{$key} || 0),
          $key;
  $total_read_bytes  += ($read_bytes{$key}  || 0);
  $total_read_count  += ($read_count{$key}  || 0);
  $total_write_bytes += ($write_bytes{$key} || 0);
  $total_write_count += ($write_count{$key} || 0);
  $total_open_count  += ($open_count{$key}  || 0);
  $total_fsync_count += ($fsync_count{$key} || 0);
}
print "----------   -----  -----------  ------  ------  -----  TOTAL\n";
printf "%10d %7d %10d %7d %7d %7d\n",
        $total_read_bytes,  $total_read_count,
        $total_write_bytes, $total_write_count,
        $total_open_count, $total_fsync_count;
