This commit is contained in:
alecf%netscape.com 2002-02-12 23:57:07 +00:00
Родитель 9359a45df5
Коммит 1e81d170f3
1 изменённых файлов: 89 добавлений и 26 удалений

Просмотреть файл

@ -1,20 +1,30 @@
#!/bin/env perl
use strict;
my $export_file;
my @import_files;
my $show_class_usage = 0;
my $infrequent_use_threshold = 1;
# first argument is the file that's exporting
# all further arguments are the input files
my $arg;
foreach $arg (@ARGV) {
if (!$export_file) {
if ($arg =~ /^-(.*)/) {
$show_class_usage=1 if ($1 eq "c");
}
elsif (!$export_file) {
$export_file = $arg;
} else {
push @import_files, $arg;
}
}
if (!$export_file|| $#import_files == 0) {
if (!$export_file || $#import_files == -1) {
print "$export_file, $#import_files\n";
print "Usage: $ARGV[0] <exporter>.dll [ importer [ importer ..]]\n";
print " Builds up a list of exporters from <exporter>.dll,\n";
print " and then lists all exports that are rarely or never used.\n";
@ -23,7 +33,12 @@ if (!$export_file|| $#import_files == 0) {
open EXPORTS, "dumpbin /exports $export_file|";
my %exports;
# $demangle{foo} is the demangled name of "foo"
my %demangle;
# $import_count{foo} is the number of exports of "foo"
my %import_count;
my %last_consumer_of;
my $found_exports;
while (<EXPORTS>) {
@ -37,7 +52,7 @@ while (<EXPORTS>) {
my $ordinal = $words[1];
my $name = $words[4];
$exports{$name} = 0;
$import_count{$name} = 0;
} elsif (/ordinal\s+hint\s+RVA/) {
$found_exports = 1;
@ -49,11 +64,15 @@ close EXPORTS;
#
# now read in all imports, and ++ the imports from the exported file
#
my $filename;
foreach $filename (@import_files) {
print STDERR "gathering data for $filename\n";
open IMPORTS, "dumpbin /imports $filename|";
my $current_import_dll, $awaiting_import_dll, $importing_from;
my $current_import_dll;
my $awaiting_import_dll;
my $importing_from;
my $awaiting_import_header;
while (<IMPORTS>) {
chop;
if (/Dump of file ([^\s]+)/) {
@ -82,12 +101,12 @@ foreach $filename (@import_files) {
}
if ($export_file eq $importing_from) {
# must be an import line
@imports = split /\s+/;
my @imports = split /\s+/;
next if ($#imports != 2);
my $name = $imports[2];
#print STDERR "... importing $name from $export_file\n";
$exports{$name}++;
$last_consumer_of{$name} = $current_import_dll;
my $func = $imports[2];
#print STDERR "... importing $func from $export_file\n";
$import_count{$func}++;
$last_consumer_of{$func} = $current_import_dll;
# print STDERR "Got import $importing_from\[$imports[2]\]\n";
}
@ -96,12 +115,18 @@ foreach $filename (@import_files) {
close IMPORTS;
}
my $func;
my @funcs_to_demangle;
print STDERR "finding infrequently imported symbols..\n";
foreach $func (sort keys %exports) {
if ($exports{$func} < 2) {
foreach $func (sort keys %import_count) {
# we need to demangle all symbols if we're showing unused classes
# otherwise, we only need to demangle infrequently-used symbols
if ($show_class_usage ||
$import_count{$func} <= $infrequent_use_threshold) {
push @funcs_to_demangle, $func;
} else {
#print STDERR "Found $exports{$func} consumers of $func\n";
#print STDERR "Found $import_count{$func} consumers of $func\n";
}
}
@ -109,13 +134,19 @@ print STDERR "Demangling..$#funcs_to_demangle unused symbols\n";
my $max_per_run = 20;
my $demangle_queue;
my $count = 0;
while ($func = pop @funcs_to_demangle) {
# skip functions that are used often
#next if ($import_count{$func} > 1);
$count++;
$funcs_to_demangle .= " $func";
$demangle_queue .= " $func";
if ($count == $max_per_run || ($#funcs_to_demangle == 0)) {
# set up the initial slot where the demangled name goes
open UNDNAME, "undname -f $funcs_to_demangle|";
open UNDNAME, "undname -f $demangle_queue|";
while (<UNDNAME>) {
chop;
@ -128,30 +159,62 @@ while ($func = pop @funcs_to_demangle) {
}
}
close UNDNAME;
$funcs_to_demangle = "";
$demangle_queue = "";
$count = 0;
}
}
print STDERR "Done.\n";
my %class_methods; # number of methods
my %class_method_unused; # number of unused methods in this class
my %class_method_infreq; # number of infrequently-used methods
my %class_method_used; # number of used method
foreach $func (sort keys %demangle ) {
# skip vtables, they don't matter
next if ($demangle{$func} =~ /vftable/);
if ($exports{$func} == 0) {
my ($class) = ($demangle{$func} =~ /(\S+)::\S+/);
$class_methods{$class}++ if ($class);
if ($import_count{$func} == 0) {
$class_method_unused{$class}++ if ($class);
print "$demangle{$func}: No importers\n";
} elsif ($exports{$func} == 1) {
print "$demangle{$func}: One consumer: $last_consumer_of{$func}\n";
}
if ($demangle{$func} =~ /(\S+)::\S+/) {
$classes{$1} ++;
elsif ($import_count{$func} <= $infrequent_use_threshold) {
$class_method_infreq{$class}++ if ($class);
print "$demangle{$func}: $import_count{$func} consumer(s) ($last_consumer_of{$func})\n";
}
else {
$class_method_used{$class}++ if ($class);
}
}
print "\n";
print "Classes that might be worth trimming:\n";
foreach $class (sort keys %classes) {
print "$class: $classes{$class} unused/minorly used methods\n"
if ($classes{$class} > 3)
print "Checking class usage ($show_class_usage)\n";
if ($show_class_usage) {
print "\n";
print "Classes that might be worth trimming:\n";
my $class;
foreach $class (sort keys %class_methods) {
if ($class_method_unused{$class} == $class_methods{$class}) {
print "$class is never used! ($class_methods{$class} methods)\n";
}
elsif ($class_method_unused{$class} == $class_methods{$class}) {
# print "$class is well-used\n";
}
else {
print "$class: $class_methods{$class} methods.\n";
print " Used: $class_method_used{$class}\n";
print " Infreq: $class_method_infreq{$class}\n";
print " Unused: $class_method_unused{$class}\n";
}
}
}