#!/usr/bin/perl -w
# Richard W.M. Jones
# License: GPLv2+

use strict;

die "Usage: v2v-binary-size.pl v2v/virt-v2v\n" unless @ARGV == 1;
my $binary = $ARGV[0];

# Count various features.
my $frametables = 0;      # Used to track which stack slots are GC roots.
my $caml_internal = 0;    # Any OCaml internal stuff, GC, start-up, etc.
my $guestfs_int = 0;      # Libguestfs internal symbols.
my $guestfs_bindings = 0; # OCaml libguestfs bindings.
my $libvirt_bindings = 0; # OCaml libvirt bindings.
my $libnbd_bindings = 0;  # OCaml libnbd bindings.
my %modules;              # OCaml modules.
my $c_statics;            # Any C static functions or variables.
my $unknown = 0;          # Anything else we didn't match.

open NM, "nm --print-size --size-sort $binary |" or die "nm: $!";
while (<NM>) {
    my @fields = split;
    if (@fields == 4) {
        my $size = hex($fields[1]);
        my $type = $fields[2];
        my $sym = $fields[3];
        if ($sym =~ /^caml.*__frametable$/) {
            $frametables += $size;
        }
        elsif ($sym =~ /^camlCamlinternal/ ||
               $sym =~ /^[cC]aml_/) {
            $caml_internal += $size;
        }
        elsif ($sym =~ /^guestfs_int_ocaml_/) {
            $guestfs_bindings += $size;
        }
        elsif ($sym =~ /^guestfs_int_/) {
            $guestfs_int += $size;
        }
        elsif ($sym =~ /^ocaml_libvirt_/) {
            $libvirt_bindings += $size;
        }
        elsif ($sym =~ /^nbd_internal_ocaml_/) {
            $libnbd_bindings += $size;
        }
        elsif ($sym =~ /^caml([A-Za-z0-9_]+)__/) {
            my $name = $1;
            $name =~ s/__/./;
            $modules{$name} = 0 unless exists $modules{$name};
            $modules{$name} += $size;
        }
        elsif ($type eq "t" || $type eq "b" || $type eq "d") {
            $c_statics += $size;
        }
        else {
            $unknown += $size;
            warn "unknown symbol $sym (type $type)\n"
        }

    }
}
close NM;

print "Report on $binary\n";
print "----------------------\n";
print "\n";
print "Frametables               $frametables\n";
print "OCaml internal stuff      $caml_internal\n";
print "Libguestfs internal       $guestfs_int\n";
print "Libguestfs bindings       $guestfs_bindings\n";
print "Libvirt bindings          $libvirt_bindings\n";
print "Libnbd bindings           $libnbd_bindings\n";
print "Unknown                   $unknown\n";
print "\n";

print "Modules (largest first)\n";
my @modules = sort { $modules{$b} <=> $modules{$a} } keys(%modules);
foreach my $name (@modules) {
    my $size = $modules{$name};
    printf ("%-24s  %d\n", $name, $size);
}
