#!/usr/bin/perl
#
# Copyright 2014 Gregory Soutade
#

use Getopt::Long;

my $filename = "";
my $filter_filename = "";
my $filter_files = 0;
my $filter_functions = 0;
my $help = 0;

GetOptions ("in=s" => \$filename,
	    "filter_files:i" => \$filter_files,
	    "filter_functions:i" => \$filter_functions,
	    "filter_filename:s" => \$filter_filename,
	    "help" => \$help)
or die("Error in command line arguments\n");

if ($help == 1 || length($filename) == 0)
{
    print "Displays statistics from objdump's output\n";
    print "Binary must be compiled with -ggdb\n\n";
    
    print "Usage: $0 --in <filename> [--filter_files <percentage>] [--filter_functions <min_instructions>] [--filter_filename <filename>] [--help]\n";
    print "\n";
    print "--filter_files:\t\tDisplays only files that have a percentage of code >= <percentage>\n";
    print "--filter_functions:\tDisplays functions that have a number of instructions >= <min_instructions>\n";
    print "--filter_filename:\tOnly analyse filenames starting by <filename> (can be a regexp)\n";
	
    exit(0);
}

$filter_filename .= ".*";

if( ! open(FIC,"<$filename") ) {
    print "Error opening '$filename'\n";
    exit(1);
}

my $nb_total_instructions = 0;
my %files;
my $filename, $function;

# Skip "Disassembly of section .text:"
while( defined( $l = <FIC> ) ) {
   chomp $l;

   last if ($l =~ /Disassembly.*/);
}

while( defined( $l = <FIC> ) ) {
   chomp $l;
   if ($l =~ /[0-9a-f]{8} <(.*)>/)
   {
       $function = $1;
       $filename = "";

       $l = <FIC>; # Skip function name
       $l = <FIC>; # Filename or code
       chomp $l;

       if ($l =~ /$filter_filename$/ && $l =~ /([^:]+):[0-9]+/)
       {
	   $filename = $1;

	   $nb_instructions = 0;
	   if (exists($files{$filename}))
	   {
	       $nb_instructions_per_file = $files{$filename}{ff_nb_instructions};
	   }
	   else
	   {
	       %{$files{$filename}} = (ff_nb_instructions => 0);
	       $nb_instructions_per_file = 0;
	   }

	   while( defined( $l = <FIC> ) )
	   {
	       chomp $l;
	       last if (length($l) == 0);
	       # Skip filename
	       next if ($l =~ /([^:]+):[0-9]+/);
	       $nb_instructions_per_file = $nb_instructions_per_file+1;
	       $nb_instructions = $nb_instructions+1;
	       $nb_total_instructions = $nb_total_instructions+1;
	   }
	   
	   $files{$filename}{ff_nb_instructions} = $nb_instructions_per_file;
	   $files{$filename}{$function} = $nb_instructions;
       }
       else
       {
	   # Skip
	   while( defined( $l = <FIC> ) )
	   {
	       chomp $l;
	       last if (length($l) == 0);
	   }
       }
   }
}

print "Total instructions $nb_total_instructions\n\n";

foreach my $file (sort { $files{$b}{ff_nb_instructions} <=> $files{$a}{ff_nb_instructions} } keys %files)
{
    $nb_instructions = $files{$file}{ff_nb_instructions};
    $percentage = (($nb_instructions/$nb_total_instructions)*100);

    next if $percentage < $filter_files;

    if ($percentage >= 10)
    {
	printf("%d\t(%.2f%%)   %s\n", $nb_instructions, $percentage, $file);
    }
    else
    {
	printf("%d\t(0%.2f%%)   %s\n", $nb_instructions, $percentage, $file);
    }

    foreach my $function (sort { $files{$file}{$b} <=> $files{$file}{$a} } keys %{$files{$file}})
    {
	next if $files{$file}{$function} < $filter_functions;
	if ($function ne "ff_nb_instructions")
	{
	    print "\t" . $files{$file}{$function} . "\t $function\n";
	}
    }
    print "\n";
}

close( FIC );
