#!/usr/bin/perl -w
use strict;

my %G_opts = ( sortby => "name", verbose => 0 );

my @opts = @ARGV; @ARGV = ();
while (@opts) {
  my $opt = shift @opts;
  if( $opt !~ /^-/ ) { push( @ARGV, $opt ); }
  elsif( $opt eq "-v" ) { $G_opts{verbose} = 1; }
  elsif( $opt eq "-n" ) { $G_opts{sortby} = "name"; }
  elsif( $opt eq "-s" ) { $G_opts{sortby} = "size"; }
  else { die "Unknown command line opion $opt\n"; }
}

my $SortKeys = $G_opts{sortby} eq "name" ? \&keysbyname : \&keysbysize;

if( @ARGV < 1 ) {
  print "usage: module_ram_usage (-v|-s|-n) [exe] (module) (module) (...)\n";
  exit 0;
}

my $exe = shift @ARGV;
my %modules = map { $_ => 1 } @ARGV;

my %total = ();
my %vars = ();

my $cmd = "avr-objdump -x \"$exe\"";
open( FH, "$cmd |" ) or die "ERROR, $cmd failed: $!\n";

while( <FH> ) {
  if( /^\S+\s+\w+\s+\w+\s+\.bss\s+(\S+)\s+(\S+)/ ) {
    my ($size,$name) = ($1,$2);
    (my $module = $name) =~ s/\$.*//;
    $total{$module} += hex $size;
    if( $name =~ /\$(.*)/ ) {
      $vars{$module} = {} unless exists $vars{$module};
      $vars{$module}->{$1} += hex $size;
    }
  }
}

close( FH );

my $sum = 0;
for my $v (values %total) { $sum += $v; }

print "$sum bytes RAM in $exe, usage by " . (%modules?"selected ":"") . "module:\n";

for my $module (&$SortKeys(\%total)) {
  next if %modules && not $modules{$module};
  printf( "%8d  %s\n", $total{$module}, $module ) if $total{$module};
  if( $G_opts{verbose} ) {
    for my $var (&$SortKeys($vars{$module})) {
      printf( "%8s >%6d  %s\n", "", $vars{$module}->{$var}, $var );
    }
  }
}


sub keysbyname {
  my $h = shift;
  return sort keys %$h;
}

sub keysbysize {
  my $h = shift;
  return sort {$h->{$a} <=> $h->{$b} || $a cmp $b} keys %$h;
}

