dtneededsize: Fixes.
[massrebuild.git] / dtneededsize
1 #! /usr/bin/perl
2 use strict;
3 use warnings;
4 use File::Basename qw(&dirname);
5 use File::Find;
6 use Data::Dumper;
7 use Statistics::Basic::StdDev;
8 use List::Util;
9
10 $|=1;
11 sub readfile {
12   my($fname)=@_;
13   local *F;
14   open F,"$fname" or die $fname;
15   local $/=undef();
16   defined(my $r=<F>) or die $fname;
17   close F or die $fname;
18   return $r;
19 }
20
21 my %R;
22 if (($ARGV[0]||"") eq "-r") {
23   shift;
24   my $r=readfile shift;
25   $R{$1}=1 while $r=~s/^(.*?)\n//;
26   die $r if $r;
27 }
28
29 my %D;
30 # ==> build/Cadence-1.0.0-0.12.20200504git5787908.fc33.src.rpm.dtneeded <==
31 # /usr/lib/debug/.dwz/Cadence isdwzcommon 479079 NA
32 # /usr/bin/cadence-jackmeter /usr/lib/debug/.dwz/Cadence 717792 1329040
33 # /usr/bin/cadence-xycontroller /usr/lib/debug/.dwz/Cadence 1763616 2369832
34 # ==> build/CVector-1.0.3.1-21.fc33.src.rpm.dtneeded <==
35 # /usr/lib64/libCVector-1.0.3.so.2.0.0 nodwzcommon 28088 28896
36 for my $dtneededfn (glob "build/*.dtneeded") {
37   next if %R && !$R{($dtneededfn=~m{^build/(.*?)-[^-]*-[^-]*$})[0]."-debuginfo"};
38   local *F;
39   open F,$dtneededfn or die "$dtneededfn: $1";
40   local $_;
41   while (<F>) {
42     chomp;
43     my($fn,$dwzcommon,$dwzsize,$dtsize)=/^(\S+) (\S+) (\S+) (\S+)$/ or die "$dtneededfn: $_";
44     die if $dwzcommon eq "isdwzcommon" && $dtsize ne "NA";
45     my $ref=\$D{$fn};
46     if ($$ref) {
47       die if $dwzcommon eq "isdwzcommon";
48       $$ref=[];
49     } else {
50       $$ref=[$dwzcommon,$dwzsize,$dtsize];
51     }
52   }
53   close F or die "close $dtneededfn: $1";
54 }
55
56 chdir "dtneeded.out" or die "dtneeded.out: $!";
57
58 my %F;
59 my %SONAME;
60 my @DEBUG;
61 find {
62   "no_chdir"=>1,
63   "wanted"=>sub {
64     die $File::Find::dir if $File::Find::dir=~m{/$};
65     my $binfn=$File::Find::name;
66     return if $binfn eq ".";
67     die $binfn if $binfn!~m{^[.]/};
68     return if -l $binfn;
69     return if !-f $binfn;
70     my $bin=readfile $binfn;
71     $binfn=~s{^[.]}{} or die;
72     return if $bin=~m{^\s*0x0{8}\s+\Q(NULL)\E\s+0x0$}m;
73     return if $bin!~m{^\s*0x0{16}\s+\Q(NULL)\E\s+0x0$}m;
74     my $rpath=($bin=~m{^.*\Q(R\E(?:UN)?\QPATH)\E\s*Library r(?:un)?path: \Q[\E(.*)\Q]\E$}m)[0];
75     if ($rpath) {
76       my $dirname=$File::Find::dir;
77       die if $dirname!~s{^[.]/}{/};
78       $rpath=~s{\$ORIGIN}{$dirname}g;
79     }
80     my $soname=($bin=~m{^.*\Q(SONAME)\E\s*Library soname: \Q[\E(.*)\Q]\E$}m)[0];
81     my $h={
82       "binfn"=>$binfn,
83       "needed"=>[$bin=~m{^\s*0x0000000000000001\s*\Q(NEEDED)\E\s*\QShared library: [\E(.*)\Q]\E$}gm],
84     };
85     $h->{"rpath"}=$rpath if $rpath;
86     $F{$binfn}=$h;
87 #warn "$binfn=".Dumper($h)."\n" if $binfn=~/libc.so.6/;
88 #    my $total=keys %F; warn "$total...\n" if 0==$total%1000;
89     if ($soname) {
90       my $sonamefn=$File::Find::dir."/".$soname;
91       $sonamefn=~s{^[.]/}{/} or die $sonamefn;
92       my $ref=\$SONAME{$binfn};
93 #      warn "soname=<$sonamefn> <$binfn> vs. <".$$ref->{"binfn"}.">\n" if $$ref;
94 #warn "soname=<$sonamefn> <$binfn>" if $soname=~/libc/;
95       $$ref=$h;
96     }
97     push @DEBUG,$binfn if $bin=~m{ \Q(DEBUG) \E };
98   },
99 },".";
100
101 sub resolve($) {
102   my($binfn)=@_;
103   die if $binfn!~m{^/};
104   return $binfn if !-l ".$binfn";
105   $binfn=".$binfn";
106   my $target=readlink $binfn or die $binfn;
107   $binfn=~s{^[.]}{};
108   my $final;
109   if ($target=~m{^/}) {
110     $final=$target;
111 #warn "$binfn,target=final=$final\n";
112   } else {
113     my $base=dirname(".$binfn");
114     my $abs=File::Spec->rel2abs($target,$base);
115     $final="/".File::Spec->abs2rel($abs);
116 #warn "$binfn,base=$base,target=$target,abs=$abs,final=$final\n";
117   }
118   1 while $final=~s{/[^/]+/[.][.]/}{/};
119   1 while $final=~s{/[^/]+/[.][.]$}{};
120   if ($final=~m{/[.][.]}) {
121     warn "$binfn,target=$target,final=$final\n";
122     return;
123   }
124 #warn "$binfn,final=$final\n";
125   return &resolve($final);
126 }
127
128 my $dwzsizeall=0;
129 my $dwzsizeduplall=0;
130 my $dtsizeall=0;
131 my @ratioall;
132 my @ratioduplall;
133 my $computed=0;
134 #@DEBUG="/usr/bin/elfedit";
135 BINFN: for my $binfn (@DEBUG) {
136 #  warn "$binfn...\n".Dumper([sort @{$F{$binfn}{"needed"}}]);
137   die $binfn if -l ".$binfn";
138   my @l=$binfn;
139   my %l=($binfn=>1);
140   while (@l) {
141     my $l=shift @l;
142     my $h=$F{$l};
143 #warn "$binfn: $l: ".Dumper($h);
144     for my $needed (@{$h->{"needed"}}) {
145       my $found;
146       if ($needed=~m{^/}) {
147         $found=resolve $needed;
148       } else {
149 #       die "$binfn: $l: $needed" if $needed=~m{/};
150         my @rpath;
151         my $rpath=$h->{"rpath"};
152         push @rpath,split /:/,$rpath if $rpath;
153         push @rpath,qw(/lib64 /usr/lib64);
154         for my $rpath (@rpath) {
155           if ($rpath!~m{^/}) {
156             warn "$binfn: $l: $rpath";
157             next;
158           }
159           my $fn="$rpath/$needed";
160 #warn "A:$fn\n" if $fn=~/libc/;
161           $fn=resolve $fn;
162 #warn "B:$fn\n" if $fn=~/libc/;
163           next if !$SONAME{$fn};
164 #warn "C:$fn\n" if $fn=~/libc/;
165           $found=$fn;
166           last;
167         }
168         warn "$binfn: $l: $needed not found; rpath=".join(":",@rpath)."\n" if !$found;
169       }
170       push @l,$found if $found&&!$l{$found}++;
171     }
172   }
173 #warn Dumper $binfn,\%l;
174   my $dwzsizetot=0;
175   my $dwzsizedupltot=0;
176   my $dtsizetot=0;
177   my %dwzcommons;
178 #warn "$binfn: ".Dumper(\%l);
179   for my $l (keys(%l)) {
180     my $ref=$D{$l};
181     if (!defined $ref) {
182       warn "$binfn: $l: missing\n";
183       next BINFN;
184     }
185     if (0==@$ref) {
186       warn "$binfn: $l: ambiguous\n";
187       next BINFN;
188     }
189     my $dtsize=$ref->[2];
190     die if !defined $dtsize;
191     $dtsizetot+=$dtsize;
192     my $dwzsize=$ref->[1];
193     die if !defined $dwzsize;
194     $dwzsizetot+=$dwzsize;
195     $dwzsizedupltot+=$dwzsize;
196     my $dwzcommon=$ref->[0];
197     next if $dwzcommon eq "nodwzcommon";
198     die if $dwzcommon eq "isdwzcommon";
199     my $duplicate=$dwzcommons{$dwzcommon}++;
200     my $dwzcommonref=$D{$dwzcommon};
201     die if !$dwzcommonref;
202     die if $dwzcommonref->[0] ne "isdwzcommon";
203     die if $dwzcommonref->[2] ne "NA";
204     my $dwzcommonsize=$dwzcommonref->[1];
205     $dwzsizetot+=$dwzcommonsize if !$duplicate;
206     $dwzsizedupltot+=$dwzcommonsize;
207   }
208   print "$binfn: dwzsizetot=$dwzsizetot dtsizetot=$dtsizetot\n";
209 #warn "$binfn: ".Dumper(\%dwzcommons);
210   $computed++;
211   $dwzsizeall+=$dwzsizetot;
212   $dwzsizeduplall+=$dwzsizedupltot;
213   $dtsizeall+=$dtsizetot;
214 warn "ratio ".$dwzsizetot    /$dtsizetot." dwzsizetot=$dwzsizetot dtsizetot=$dtsizetot: $binfn\n";
215   push @ratioall    ,$dwzsizetot    /$dtsizetot;
216   push @ratioduplall,$dwzsizedupltot/$dtsizetot;
217 #  warn "$binfn done\n".Dumper([sort keys(%l)]);
218 }
219 print "dwzsizeall    =$dwzsizeall"    ." dtsizeall=$dtsizeall =".$dwzsizeall    /$dtsizeall." avg=".List::Util::sum(@ratioall    )/@ratioall    ." stddev=".Statistics::Basic::stddev(\@ratioall    )."\n";
220 print "dwzsizeduplall=$dwzsizeduplall"." dtsizeall=$dtsizeall =".$dwzsizeduplall/$dtsizeall." avg=".List::Util::sum(@ratioduplall)/@ratioduplall." stddev=".Statistics::Basic::stddev(\@ratioduplall)."\n";
221 print "computed=$computed of DEBUG=".(0+@DEBUG)." =".$computed/@DEBUG."\n";
222 #print Dumper \@ratioall;