use File::Basename qw(&dirname);
use File::Find;
use Data::Dumper;
+use Statistics::Basic::StdDev;
+use List::Util;
$|=1;
sub readfile {
return $r;
}
+my %R;
+if (($ARGV[0]||"") eq "-r") {
+ shift;
+ my $r=readfile shift;
+ $R{$1}=1 while $r=~s/^(.*?)\n//;
+ die $r if $r;
+}
+
+my %D;
+# ==> build/Cadence-1.0.0-0.12.20200504git5787908.fc33.src.rpm.dtneeded <==
+# /usr/lib/debug/.dwz/Cadence isdwzcommon 479079 NA
+# /usr/bin/cadence-jackmeter /usr/lib/debug/.dwz/Cadence 717792 1329040
+# /usr/bin/cadence-xycontroller /usr/lib/debug/.dwz/Cadence 1763616 2369832
+# ==> build/CVector-1.0.3.1-21.fc33.src.rpm.dtneeded <==
+# /usr/lib64/libCVector-1.0.3.so.2.0.0 nodwzcommon 28088 28896
+for my $dtneededfn (glob "build/*.dtneeded") {
+ next if %R && !$R{($dtneededfn=~m{^build/(.*?)-[^-]*-[^-]*$})[0]."-debuginfo"};
+ local *F;
+ open F,$dtneededfn or die "$dtneededfn: $1";
+ local $_;
+ while (<F>) {
+ chomp;
+ my($fn,$dwzcommon,$dwzsize,$dtsize)=/^(\S+) (\S+) (\S+) (\S+)$/ or die "$dtneededfn: $_";
+ die if $dwzcommon eq "isdwzcommon" && $dtsize ne "NA";
+ my $ref=\$D{$fn};
+ if ($$ref) {
+ die if $dwzcommon eq "isdwzcommon";
+ $$ref=[];
+ } else {
+ $$ref=[$dwzcommon,$dwzsize,$dtsize];
+ }
+ }
+ close F or die "close $dtneededfn: $1";
+}
+
chdir "dtneeded.out" or die "dtneeded.out: $!";
my %F;
"wanted"=>sub {
die $File::Find::dir if $File::Find::dir=~m{/$};
my $binfn=$File::Find::name;
- return if !-f $binfn;
+ return if $binfn eq ".";
die $binfn if $binfn!~m{^[.]/};
+ return if -l $binfn;
+ return if !-f $binfn;
my $bin=readfile $binfn;
+ $binfn=~s{^[.]}{} or die;
+ return if $bin=~m{^\s*0x0{8}\s+\Q(NULL)\E\s+0x0$}m;
+ return if $bin!~m{^\s*0x0{16}\s+\Q(NULL)\E\s+0x0$}m;
my $rpath=($bin=~m{^.*\Q(R\E(?:UN)?\QPATH)\E\s*Library r(?:un)?path: \Q[\E(.*)\Q]\E$}m)[0];
if ($rpath) {
my $dirname=$File::Find::dir;
};
$h->{"rpath"}=$rpath if $rpath;
$F{$binfn}=$h;
+#warn "$binfn=".Dumper($h)."\n" if $binfn=~/libc.so.6/;
# my $total=keys %F; warn "$total...\n" if 0==$total%1000;
if ($soname) {
- my $ref=\$SONAME{$File::Find::dir."/".$soname};
-# warn "soname=<$soname> <$binfn> vs. <".$$ref->{"binfn"}.">\n" if $$ref;
+ my $sonamefn=$File::Find::dir."/".$soname;
+ $sonamefn=~s{^[.]/}{/} or die $sonamefn;
+ my $ref=\$SONAME{$binfn};
+# warn "soname=<$sonamefn> <$binfn> vs. <".$$ref->{"binfn"}.">\n" if $$ref;
+#warn "soname=<$sonamefn> <$binfn>" if $soname=~/libc/;
$$ref=$h;
}
push @DEBUG,$binfn if $bin=~m{ \Q(DEBUG) \E };
},
},".";
-for my $binfn (@DEBUG) {
+sub resolve($) {
+ my($binfn)=@_;
+ die if $binfn!~m{^/};
+ return $binfn if !-l ".$binfn";
+ $binfn=".$binfn";
+ my $target=readlink $binfn or die $binfn;
+ $binfn=~s{^[.]}{};
+ my $final;
+ if ($target=~m{^/}) {
+ $final=$target;
+#warn "$binfn,target=final=$final\n";
+ } else {
+ my $base=dirname(".$binfn");
+ my $abs=File::Spec->rel2abs($target,$base);
+ $final="/".File::Spec->abs2rel($abs);
+#warn "$binfn,base=$base,target=$target,abs=$abs,final=$final\n";
+ }
+ 1 while $final=~s{/[^/]+/[.][.]/}{/};
+ 1 while $final=~s{/[^/]+/[.][.]$}{};
+ if ($final=~m{/[.][.]}) {
+ warn "$binfn,target=$target,final=$final\n";
+ return;
+ }
+#warn "$binfn,final=$final\n";
+ return &resolve($final);
+}
+
+my $dwzsizeall=0;
+my $dwzsizeduplall=0;
+my $dtsizeall=0;
+my @ratioall;
+my @ratioduplall;
+my $computed=0;
+#@DEBUG="/usr/bin/elfedit";
+BINFN: for my $binfn (@DEBUG) {
# warn "$binfn...\n".Dumper([sort @{$F{$binfn}{"needed"}}]);
+ die $binfn if -l ".$binfn";
my @l=$binfn;
my %l=($binfn=>1);
while (@l) {
my $l=shift @l;
my $h=$F{$l};
+#warn "$binfn: $l: ".Dumper($h);
for my $needed (@{$h->{"needed"}}) {
my $found;
if ($needed=~m{^/}) {
- $found=$needed;
+ $found=resolve $needed;
} else {
# die "$binfn: $l: $needed" if $needed=~m{/};
my @rpath;
warn "$binfn: $l: $rpath";
next;
}
- my $fn=".$rpath/$needed";
+ my $fn="$rpath/$needed";
+#warn "A:$fn\n" if $fn=~/libc/;
+ $fn=resolve $fn;
+#warn "B:$fn\n" if $fn=~/libc/;
next if !$SONAME{$fn};
+#warn "C:$fn\n" if $fn=~/libc/;
$found=$fn;
last;
}
push @l,$found if $found&&!$l{$found}++;
}
}
+#warn Dumper $binfn,\%l;
+ my $dwzsizetot=0;
+ my $dwzsizedupltot=0;
+ my $dtsizetot=0;
+ my %dwzcommons;
+#warn "$binfn: ".Dumper(\%l);
+ for my $l (keys(%l)) {
+ my $ref=$D{$l};
+ if (!defined $ref) {
+ warn "$binfn: $l: missing\n";
+ next BINFN;
+ }
+ if (0==@$ref) {
+ warn "$binfn: $l: ambiguous\n";
+ next BINFN;
+ }
+ my $dtsize=$ref->[2];
+ die if !defined $dtsize;
+ $dtsizetot+=$dtsize;
+ my $dwzsize=$ref->[1];
+ die if !defined $dwzsize;
+ $dwzsizetot+=$dwzsize;
+ $dwzsizedupltot+=$dwzsize;
+ my $dwzcommon=$ref->[0];
+ next if $dwzcommon eq "nodwzcommon";
+ die if $dwzcommon eq "isdwzcommon";
+ my $duplicate=$dwzcommons{$dwzcommon}++;
+ my $dwzcommonref=$D{$dwzcommon};
+ die if !$dwzcommonref;
+ die if $dwzcommonref->[0] ne "isdwzcommon";
+ die if $dwzcommonref->[2] ne "NA";
+ my $dwzcommonsize=$dwzcommonref->[1];
+ $dwzsizetot+=$dwzcommonsize if !$duplicate;
+ $dwzsizedupltot+=$dwzcommonsize;
+ }
+ print "$binfn: dwzsizetot=$dwzsizetot dtsizetot=$dtsizetot\n";
+#warn "$binfn: ".Dumper(\%dwzcommons);
+ $computed++;
+ $dwzsizeall+=$dwzsizetot;
+ $dwzsizeduplall+=$dwzsizedupltot;
+ $dtsizeall+=$dtsizetot;
+warn "ratio ".$dwzsizetot /$dtsizetot." dwzsizetot=$dwzsizetot dtsizetot=$dtsizetot: $binfn\n";
+ push @ratioall ,$dwzsizetot /$dtsizetot;
+ push @ratioduplall,$dwzsizedupltot/$dtsizetot;
# warn "$binfn done\n".Dumper([sort keys(%l)]);
}
+print "dwzsizeall =$dwzsizeall" ." dtsizeall=$dtsizeall =".$dwzsizeall /$dtsizeall." avg=".List::Util::sum(@ratioall )/@ratioall ." stddev=".Statistics::Basic::stddev(\@ratioall )."\n";
+print "dwzsizeduplall=$dwzsizeduplall"." dtsizeall=$dtsizeall =".$dwzsizeduplall/$dtsizeall." avg=".List::Util::sum(@ratioduplall)/@ratioduplall." stddev=".Statistics::Basic::stddev(\@ratioduplall)."\n";
+print "computed=$computed of DEBUG=".(0+@DEBUG)." =".$computed/@DEBUG."\n";
+#print Dumper \@ratioall;