1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/tools/rb/bloatdiff.pl Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,372 @@ 1.4 +#!/usr/bin/perl -w 1.5 +# This Source Code Form is subject to the terms of the Mozilla Public 1.6 +# License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 +# file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.8 + 1.9 + 1.10 +################################################################################ 1.11 + 1.12 +sub usage() { 1.13 + print <<EOUSAGE; 1.14 +# bloatdiff.pl - munges the output from 1.15 +# XPCOM_MEM_BLOAT_LOG=1 1.16 +# firefox-bin -P default resource:///res/bloatcycle.html 1.17 +# so that it does some summary and stats stuff. 1.18 +# 1.19 +# To show leak test results for a set of changes, do something like this: 1.20 +# 1.21 +# XPCOM_MEM_BLOAT_LOG=1 1.22 +# firefox-bin -P default resource:///res/bloatcycle.html > a.out 1.23 +# **make change** 1.24 +# firefox-bin -P default resource:///res/bloatcycle.html > b.out 1.25 +# bloatdiff.pl a.out b.out 1.26 + 1.27 +EOUSAGE 1.28 +} 1.29 + 1.30 +$OLDFILE = $ARGV[0]; 1.31 +$NEWFILE = $ARGV[1]; 1.32 +#$LABEL = $ARGV[2]; 1.33 + 1.34 +if (!$OLDFILE or 1.35 + ! -e $OLDFILE or 1.36 + -z $OLDFILE) { 1.37 + print "\nError: Previous log file not specified, does not exist, or is empty.\n\n"; 1.38 + &usage(); 1.39 + exit 1; 1.40 +} 1.41 + 1.42 +if (!$NEWFILE or 1.43 + ! -e $NEWFILE or 1.44 + -z $NEWFILE) { 1.45 + print "\nError: Current log file not specified, does not exist, or is empty.\n\n"; 1.46 + &usage(); 1.47 + exit 1; 1.48 +} 1.49 + 1.50 +sub processFile { 1.51 + my ($filename, $map, $prevMap) = @_; 1.52 + open(FH, $filename); 1.53 + while (<FH>) { 1.54 + if (m{ 1.55 + ^\s*(\d+)\s # Line number 1.56 + ([\w:]+)\s+ # Name 1.57 + (-?\d+)\s+ # Size 1.58 + (-?\d+)\s+ # Leaked 1.59 + (-?\d+)\s+ # Objects Total 1.60 + (-?\d+)\s+ # Objects Rem 1.61 + \(\s*(-?[\d.]+)\s+ # Objects Mean 1.62 + \+/-\s+ 1.63 + ([\w.]+)\)\s+ # Objects StdDev 1.64 + (-?\d+)\s+ # Reference Total 1.65 + (-?\d+)\s+ # Reference Rem 1.66 + \(\s*(-?[\d.]+)\s+ # Reference Mean 1.67 + \+/-\s+ 1.68 + ([\w\.]+)\) # Reference StdDev 1.69 + }x) { 1.70 + $$map{$2} = { name => $2, 1.71 + size => $3, 1.72 + leaked => $4, 1.73 + objTotal => $5, 1.74 + objRem => $6, 1.75 + objMean => $7, 1.76 + objStdDev => $8, 1.77 + refTotal => $9, 1.78 + refRem => $10, 1.79 + refMean => $11, 1.80 + refStdDev => $12, 1.81 + bloat => $3 * $5 # size * objTotal 1.82 + }; 1.83 + } else { 1.84 +# print "failed to parse: $_\n"; 1.85 + } 1.86 + } 1.87 + close(FH); 1.88 +} 1.89 + 1.90 +%oldMap = (); 1.91 +processFile($OLDFILE, \%oldMap); 1.92 + 1.93 +%newMap = (); 1.94 +processFile($NEWFILE, \%newMap); 1.95 + 1.96 +################################################################################ 1.97 + 1.98 +$inf = 9999999.99; 1.99 + 1.100 +sub getLeaksDelta { 1.101 + my ($key) = @_; 1.102 + my $oldLeaks = $oldMap{$key}{leaked} || 0; 1.103 + my $newLeaks = $newMap{$key}{leaked}; 1.104 + my $percentLeaks = 0; 1.105 + if ($oldLeaks == 0) { 1.106 + if ($newLeaks != 0) { 1.107 + # there weren't any leaks before, but now there are! 1.108 + $percentLeaks = $inf; 1.109 + } 1.110 + } 1.111 + else { 1.112 + $percentLeaks = ($newLeaks - $oldLeaks) / $oldLeaks * 100; 1.113 + } 1.114 + # else we had no record of this class before 1.115 + return ($newLeaks - $oldLeaks, $percentLeaks); 1.116 +} 1.117 + 1.118 +################################################################################ 1.119 + 1.120 +sub getBloatDelta { 1.121 + my ($key) = @_; 1.122 + my $newBloat = $newMap{$key}{bloat}; 1.123 + my $percentBloat = 0; 1.124 + my $oldSize = $oldMap{$key}{size} || 0; 1.125 + my $oldTotal = $oldMap{$key}{objTotal} || 0; 1.126 + my $oldBloat = $oldTotal * $oldSize; 1.127 + if ($oldBloat == 0) { 1.128 + if ($newBloat != 0) { 1.129 + # this class wasn't used before, but now it is 1.130 + $percentBloat = $inf; 1.131 + } 1.132 + } 1.133 + else { 1.134 + $percentBloat = ($newBloat - $oldBloat) / $oldBloat * 100; 1.135 + } 1.136 + # else we had no record of this class before 1.137 + return ($newBloat - $oldBloat, $percentBloat); 1.138 +} 1.139 + 1.140 +################################################################################ 1.141 + 1.142 +foreach $key (keys %newMap) { 1.143 + my ($newLeaks, $percentLeaks) = getLeaksDelta($key); 1.144 + my ($newBloat, $percentBloat) = getBloatDelta($key); 1.145 + $newMap{$key}{leakDelta} = $newLeaks; 1.146 + $newMap{$key}{leakPercent} = $percentLeaks; 1.147 + $newMap{$key}{bloatDelta} = $newBloat; 1.148 + $newMap{$key}{bloatPercent} = $percentBloat; 1.149 +} 1.150 + 1.151 +################################################################################ 1.152 + 1.153 +# Print a value of bytes out in a reasonable 1.154 +# KB, MB, or GB form. Copied from build-seamonkey-util.pl, sorry. -mcafee 1.155 +sub PrintSize($) { 1.156 + 1.157 + # print a number with 3 significant figures 1.158 + sub PrintNum($) { 1.159 + my ($num) = @_; 1.160 + my $rv; 1.161 + if ($num < 1) { 1.162 + $rv = sprintf "%.3f", ($num); 1.163 + } elsif ($num < 10) { 1.164 + $rv = sprintf "%.2f", ($num); 1.165 + } elsif ($num < 100) { 1.166 + $rv = sprintf "%.1f", ($num); 1.167 + } else { 1.168 + $rv = sprintf "%d", ($num); 1.169 + } 1.170 + } 1.171 + 1.172 + my ($size) = @_; 1.173 + my $rv; 1.174 + if ($size > 1000000000) { 1.175 + $rv = PrintNum($size / 1000000000.0) . "G"; 1.176 + } elsif ($size > 1000000) { 1.177 + $rv = PrintNum($size / 1000000.0) . "M"; 1.178 + } elsif ($size > 1000) { 1.179 + $rv = PrintNum($size / 1000.0) . "K"; 1.180 + } else { 1.181 + $rv = PrintNum($size); 1.182 + } 1.183 +} 1.184 + 1.185 + 1.186 +print "Bloat/Leak Delta Report\n"; 1.187 +print "--------------------------------------------------------------------------------------\n"; 1.188 +print "Current file: $NEWFILE\n"; 1.189 +print "Previous file: $OLDFILE\n"; 1.190 +print "----------------------------------------------leaks------leaks%------bloat------bloat%\n"; 1.191 + 1.192 + if (! $newMap{"TOTAL"} or 1.193 + ! $newMap{"TOTAL"}{bloat}) { 1.194 + # It's OK if leaked or leakPercent are 0 (in fact, that would be good). 1.195 + # If bloatPercent is zero, it is also OK, because we may have just had 1.196 + # two runs exactly the same or with no new bloat. 1.197 + print "\nError: unable to calculate bloat/leak data.\n"; 1.198 + print "There is no data present.\n\n"; 1.199 + print "HINT - Did your test run complete successfully?\n"; 1.200 + print "HINT - Are you pointing at the right log files?\n\n"; 1.201 + &usage(); 1.202 + exit 1; 1.203 + } 1.204 + 1.205 +printf "%-40s %10s %10.2f%% %10s %10.2f%%\n", 1.206 + ("TOTAL", 1.207 + $newMap{"TOTAL"}{leaked}, $newMap{"TOTAL"}{leakPercent}, 1.208 + $newMap{"TOTAL"}{bloat}, $newMap{"TOTAL"}{bloatPercent}); 1.209 + 1.210 +################################################################################ 1.211 + 1.212 +sub percentStr { 1.213 + my ($p) = @_; 1.214 + if ($p == $inf) { 1.215 + return "-"; 1.216 + } 1.217 + else { 1.218 + return sprintf "%10.2f%%", $p; 1.219 + } 1.220 +} 1.221 + 1.222 +# NEW LEAKS 1.223 +@keys = sort { $newMap{$b}{leakPercent} <=> $newMap{$a}{leakPercent} } keys %newMap; 1.224 +my $needsHeading = 1; 1.225 +my $total = 0; 1.226 +foreach $key (@keys) { 1.227 + my $percentLeaks = $newMap{$key}{leakPercent}; 1.228 + my $leaks = $newMap{$key}{leaked}; 1.229 + if ($percentLeaks > 0 && $key !~ /TOTAL/) { 1.230 + if ($needsHeading) { 1.231 + printf "--NEW-LEAKS-----------------------------------leaks------leaks%%-----------------------\n"; 1.232 + $needsHeading = 0; 1.233 + } 1.234 + printf "%-40s %10s %10s\n", ($key, $leaks, percentStr($percentLeaks)); 1.235 + $total += $leaks; 1.236 + } 1.237 +} 1.238 +if (!$needsHeading) { 1.239 + printf "%-40s %10s\n", ("TOTAL", $total); 1.240 +} 1.241 + 1.242 +# FIXED LEAKS 1.243 +@keys = sort { $newMap{$b}{leakPercent} <=> $newMap{$a}{leakPercent} } keys %newMap; 1.244 +$needsHeading = 1; 1.245 +$total = 0; 1.246 +foreach $key (@keys) { 1.247 + my $percentLeaks = $newMap{$key}{leakPercent}; 1.248 + my $leaks = $newMap{$key}{leaked}; 1.249 + if ($percentLeaks < 0 && $key !~ /TOTAL/) { 1.250 + if ($needsHeading) { 1.251 + printf "--FIXED-LEAKS---------------------------------leaks------leaks%%-----------------------\n"; 1.252 + $needsHeading = 0; 1.253 + } 1.254 + printf "%-40s %10s %10s\n", ($key, $leaks, percentStr($percentLeaks)); 1.255 + $total += $leaks; 1.256 + } 1.257 +} 1.258 +if (!$needsHeading) { 1.259 + printf "%-40s %10s\n", ("TOTAL", $total); 1.260 +} 1.261 + 1.262 +# NEW BLOAT 1.263 +@keys = sort { $newMap{$b}{bloatPercent} <=> $newMap{$a}{bloatPercent} } keys %newMap; 1.264 +$needsHeading = 1; 1.265 +$total = 0; 1.266 +foreach $key (@keys) { 1.267 + my $percentBloat = $newMap{$key}{bloatPercent}; 1.268 + my $bloat = $newMap{$key}{bloat}; 1.269 + if ($percentBloat > 0 && $key !~ /TOTAL/) { 1.270 + if ($needsHeading) { 1.271 + printf "--NEW-BLOAT-----------------------------------bloat------bloat%%-----------------------\n"; 1.272 + $needsHeading = 0; 1.273 + } 1.274 + printf "%-40s %10s %10s\n", ($key, $bloat, percentStr($percentBloat)); 1.275 + $total += $bloat; 1.276 + } 1.277 +} 1.278 +if (!$needsHeading) { 1.279 + printf "%-40s %10s\n", ("TOTAL", $total); 1.280 +} 1.281 + 1.282 +# ALL LEAKS 1.283 +@keys = sort { $newMap{$b}{leaked} <=> $newMap{$a}{leaked} } keys %newMap; 1.284 +$needsHeading = 1; 1.285 +$total = 0; 1.286 +foreach $key (@keys) { 1.287 + my $leaks = $newMap{$key}{leaked}; 1.288 + my $percentLeaks = $newMap{$key}{leakPercent}; 1.289 + if ($leaks > 0) { 1.290 + if ($needsHeading) { 1.291 + printf "--ALL-LEAKS-----------------------------------leaks------leaks%%-----------------------\n"; 1.292 + $needsHeading = 0; 1.293 + } 1.294 + printf "%-40s %10s %10s\n", ($key, $leaks, percentStr($percentLeaks)); 1.295 + if ($key !~ /TOTAL/) { 1.296 + $total += $leaks; 1.297 + } 1.298 + } 1.299 +} 1.300 +if (!$needsHeading) { 1.301 +# printf "%-40s %10s\n", ("TOTAL", $total); 1.302 +} 1.303 + 1.304 +# ALL BLOAT 1.305 +@keys = sort { $newMap{$b}{bloat} <=> $newMap{$a}{bloat} } keys %newMap; 1.306 +$needsHeading = 1; 1.307 +$total = 0; 1.308 +foreach $key (@keys) { 1.309 + my $bloat = $newMap{$key}{bloat}; 1.310 + my $percentBloat = $newMap{$key}{bloatPercent}; 1.311 + if ($bloat > 0) { 1.312 + if ($needsHeading) { 1.313 + printf "--ALL-BLOAT-----------------------------------bloat------bloat%%-----------------------\n"; 1.314 + $needsHeading = 0; 1.315 + } 1.316 + printf "%-40s %10s %10s\n", ($key, $bloat, percentStr($percentBloat)); 1.317 + if ($key !~ /TOTAL/) { 1.318 + $total += $bloat; 1.319 + } 1.320 + } 1.321 +} 1.322 +if (!$needsHeading) { 1.323 +# printf "%-40s %10s\n", ("TOTAL", $total); 1.324 +} 1.325 + 1.326 +# NEW CLASSES 1.327 +@keys = sort { $newMap{$b}{bloatDelta} <=> $newMap{$a}{bloatDelta} } keys %newMap; 1.328 +$needsHeading = 1; 1.329 +my $ltotal = 0; 1.330 +my $btotal = 0; 1.331 +foreach $key (@keys) { 1.332 + my $leaks = $newMap{$key}{leaked}; 1.333 + my $bloat = $newMap{$key}{bloat}; 1.334 + my $percentBloat = $newMap{$key}{bloatPercent}; 1.335 + if ($percentBloat == $inf && $key !~ /TOTAL/) { 1.336 + if ($needsHeading) { 1.337 + printf "--CLASSES-NOT-REPORTED-LAST-TIME--------------leaks------bloat------------------------\n"; 1.338 + $needsHeading = 0; 1.339 + } 1.340 + printf "%-40s %10s %10s\n", ($key, $leaks, $bloat); 1.341 + if ($key !~ /TOTAL/) { 1.342 + $ltotal += $leaks; 1.343 + $btotal += $bloat; 1.344 + } 1.345 + } 1.346 +} 1.347 +if (!$needsHeading) { 1.348 + printf "%-40s %10s %10s\n", ("TOTAL", $ltotal, $btotal); 1.349 +} 1.350 + 1.351 +# OLD CLASSES 1.352 +@keys = sort { ($oldMap{$b}{bloat} || 0) <=> ($oldMap{$a}{bloat} || 0) } keys %oldMap; 1.353 +$needsHeading = 1; 1.354 +$ltotal = 0; 1.355 +$btotal = 0; 1.356 +foreach $key (@keys) { 1.357 + if (!defined($newMap{$key})) { 1.358 + my $leaks = $oldMap{$key}{leaked}; 1.359 + my $bloat = $oldMap{$key}{bloat}; 1.360 + if ($needsHeading) { 1.361 + printf "--CLASSES-THAT-WENT-AWAY----------------------leaks------bloat------------------------\n"; 1.362 + $needsHeading = 0; 1.363 + } 1.364 + printf "%-40s %10s %10s\n", ($key, $leaks, $bloat); 1.365 + if ($key !~ /TOTAL/) { 1.366 + $ltotal += $leaks; 1.367 + $btotal += $bloat; 1.368 + } 1.369 + } 1.370 +} 1.371 +if (!$needsHeading) { 1.372 + printf "%-40s %10s %10s\n", ("TOTAL", $ltotal, $btotal); 1.373 +} 1.374 + 1.375 +print "--------------------------------------------------------------------------------------\n";