michael@0: #!/usr/bin/perl -w michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: michael@0: ################################################################################ michael@0: michael@0: sub usage() { michael@0: print < a.out michael@0: # **make change** michael@0: # firefox-bin -P default resource:///res/bloatcycle.html > b.out michael@0: # bloatdiff.pl a.out b.out michael@0: michael@0: EOUSAGE michael@0: } michael@0: michael@0: $OLDFILE = $ARGV[0]; michael@0: $NEWFILE = $ARGV[1]; michael@0: #$LABEL = $ARGV[2]; michael@0: michael@0: if (!$OLDFILE or michael@0: ! -e $OLDFILE or michael@0: -z $OLDFILE) { michael@0: print "\nError: Previous log file not specified, does not exist, or is empty.\n\n"; michael@0: &usage(); michael@0: exit 1; michael@0: } michael@0: michael@0: if (!$NEWFILE or michael@0: ! -e $NEWFILE or michael@0: -z $NEWFILE) { michael@0: print "\nError: Current log file not specified, does not exist, or is empty.\n\n"; michael@0: &usage(); michael@0: exit 1; michael@0: } michael@0: michael@0: sub processFile { michael@0: my ($filename, $map, $prevMap) = @_; michael@0: open(FH, $filename); michael@0: while () { michael@0: if (m{ michael@0: ^\s*(\d+)\s # Line number michael@0: ([\w:]+)\s+ # Name michael@0: (-?\d+)\s+ # Size michael@0: (-?\d+)\s+ # Leaked michael@0: (-?\d+)\s+ # Objects Total michael@0: (-?\d+)\s+ # Objects Rem michael@0: \(\s*(-?[\d.]+)\s+ # Objects Mean michael@0: \+/-\s+ michael@0: ([\w.]+)\)\s+ # Objects StdDev michael@0: (-?\d+)\s+ # Reference Total michael@0: (-?\d+)\s+ # Reference Rem michael@0: \(\s*(-?[\d.]+)\s+ # Reference Mean michael@0: \+/-\s+ michael@0: ([\w\.]+)\) # Reference StdDev michael@0: }x) { michael@0: $$map{$2} = { name => $2, michael@0: size => $3, michael@0: leaked => $4, michael@0: objTotal => $5, michael@0: objRem => $6, michael@0: objMean => $7, michael@0: objStdDev => $8, michael@0: refTotal => $9, michael@0: refRem => $10, michael@0: refMean => $11, michael@0: refStdDev => $12, michael@0: bloat => $3 * $5 # size * objTotal michael@0: }; michael@0: } else { michael@0: # print "failed to parse: $_\n"; michael@0: } michael@0: } michael@0: close(FH); michael@0: } michael@0: michael@0: %oldMap = (); michael@0: processFile($OLDFILE, \%oldMap); michael@0: michael@0: %newMap = (); michael@0: processFile($NEWFILE, \%newMap); michael@0: michael@0: ################################################################################ michael@0: michael@0: $inf = 9999999.99; michael@0: michael@0: sub getLeaksDelta { michael@0: my ($key) = @_; michael@0: my $oldLeaks = $oldMap{$key}{leaked} || 0; michael@0: my $newLeaks = $newMap{$key}{leaked}; michael@0: my $percentLeaks = 0; michael@0: if ($oldLeaks == 0) { michael@0: if ($newLeaks != 0) { michael@0: # there weren't any leaks before, but now there are! michael@0: $percentLeaks = $inf; michael@0: } michael@0: } michael@0: else { michael@0: $percentLeaks = ($newLeaks - $oldLeaks) / $oldLeaks * 100; michael@0: } michael@0: # else we had no record of this class before michael@0: return ($newLeaks - $oldLeaks, $percentLeaks); michael@0: } michael@0: michael@0: ################################################################################ michael@0: michael@0: sub getBloatDelta { michael@0: my ($key) = @_; michael@0: my $newBloat = $newMap{$key}{bloat}; michael@0: my $percentBloat = 0; michael@0: my $oldSize = $oldMap{$key}{size} || 0; michael@0: my $oldTotal = $oldMap{$key}{objTotal} || 0; michael@0: my $oldBloat = $oldTotal * $oldSize; michael@0: if ($oldBloat == 0) { michael@0: if ($newBloat != 0) { michael@0: # this class wasn't used before, but now it is michael@0: $percentBloat = $inf; michael@0: } michael@0: } michael@0: else { michael@0: $percentBloat = ($newBloat - $oldBloat) / $oldBloat * 100; michael@0: } michael@0: # else we had no record of this class before michael@0: return ($newBloat - $oldBloat, $percentBloat); michael@0: } michael@0: michael@0: ################################################################################ michael@0: michael@0: foreach $key (keys %newMap) { michael@0: my ($newLeaks, $percentLeaks) = getLeaksDelta($key); michael@0: my ($newBloat, $percentBloat) = getBloatDelta($key); michael@0: $newMap{$key}{leakDelta} = $newLeaks; michael@0: $newMap{$key}{leakPercent} = $percentLeaks; michael@0: $newMap{$key}{bloatDelta} = $newBloat; michael@0: $newMap{$key}{bloatPercent} = $percentBloat; michael@0: } michael@0: michael@0: ################################################################################ michael@0: michael@0: # Print a value of bytes out in a reasonable michael@0: # KB, MB, or GB form. Copied from build-seamonkey-util.pl, sorry. -mcafee michael@0: sub PrintSize($) { michael@0: michael@0: # print a number with 3 significant figures michael@0: sub PrintNum($) { michael@0: my ($num) = @_; michael@0: my $rv; michael@0: if ($num < 1) { michael@0: $rv = sprintf "%.3f", ($num); michael@0: } elsif ($num < 10) { michael@0: $rv = sprintf "%.2f", ($num); michael@0: } elsif ($num < 100) { michael@0: $rv = sprintf "%.1f", ($num); michael@0: } else { michael@0: $rv = sprintf "%d", ($num); michael@0: } michael@0: } michael@0: michael@0: my ($size) = @_; michael@0: my $rv; michael@0: if ($size > 1000000000) { michael@0: $rv = PrintNum($size / 1000000000.0) . "G"; michael@0: } elsif ($size > 1000000) { michael@0: $rv = PrintNum($size / 1000000.0) . "M"; michael@0: } elsif ($size > 1000) { michael@0: $rv = PrintNum($size / 1000.0) . "K"; michael@0: } else { michael@0: $rv = PrintNum($size); michael@0: } michael@0: } michael@0: michael@0: michael@0: print "Bloat/Leak Delta Report\n"; michael@0: print "--------------------------------------------------------------------------------------\n"; michael@0: print "Current file: $NEWFILE\n"; michael@0: print "Previous file: $OLDFILE\n"; michael@0: print "----------------------------------------------leaks------leaks%------bloat------bloat%\n"; michael@0: michael@0: if (! $newMap{"TOTAL"} or michael@0: ! $newMap{"TOTAL"}{bloat}) { michael@0: # It's OK if leaked or leakPercent are 0 (in fact, that would be good). michael@0: # If bloatPercent is zero, it is also OK, because we may have just had michael@0: # two runs exactly the same or with no new bloat. michael@0: print "\nError: unable to calculate bloat/leak data.\n"; michael@0: print "There is no data present.\n\n"; michael@0: print "HINT - Did your test run complete successfully?\n"; michael@0: print "HINT - Are you pointing at the right log files?\n\n"; michael@0: &usage(); michael@0: exit 1; michael@0: } michael@0: michael@0: printf "%-40s %10s %10.2f%% %10s %10.2f%%\n", michael@0: ("TOTAL", michael@0: $newMap{"TOTAL"}{leaked}, $newMap{"TOTAL"}{leakPercent}, michael@0: $newMap{"TOTAL"}{bloat}, $newMap{"TOTAL"}{bloatPercent}); michael@0: michael@0: ################################################################################ michael@0: michael@0: sub percentStr { michael@0: my ($p) = @_; michael@0: if ($p == $inf) { michael@0: return "-"; michael@0: } michael@0: else { michael@0: return sprintf "%10.2f%%", $p; michael@0: } michael@0: } michael@0: michael@0: # NEW LEAKS michael@0: @keys = sort { $newMap{$b}{leakPercent} <=> $newMap{$a}{leakPercent} } keys %newMap; michael@0: my $needsHeading = 1; michael@0: my $total = 0; michael@0: foreach $key (@keys) { michael@0: my $percentLeaks = $newMap{$key}{leakPercent}; michael@0: my $leaks = $newMap{$key}{leaked}; michael@0: if ($percentLeaks > 0 && $key !~ /TOTAL/) { michael@0: if ($needsHeading) { michael@0: printf "--NEW-LEAKS-----------------------------------leaks------leaks%%-----------------------\n"; michael@0: $needsHeading = 0; michael@0: } michael@0: printf "%-40s %10s %10s\n", ($key, $leaks, percentStr($percentLeaks)); michael@0: $total += $leaks; michael@0: } michael@0: } michael@0: if (!$needsHeading) { michael@0: printf "%-40s %10s\n", ("TOTAL", $total); michael@0: } michael@0: michael@0: # FIXED LEAKS michael@0: @keys = sort { $newMap{$b}{leakPercent} <=> $newMap{$a}{leakPercent} } keys %newMap; michael@0: $needsHeading = 1; michael@0: $total = 0; michael@0: foreach $key (@keys) { michael@0: my $percentLeaks = $newMap{$key}{leakPercent}; michael@0: my $leaks = $newMap{$key}{leaked}; michael@0: if ($percentLeaks < 0 && $key !~ /TOTAL/) { michael@0: if ($needsHeading) { michael@0: printf "--FIXED-LEAKS---------------------------------leaks------leaks%%-----------------------\n"; michael@0: $needsHeading = 0; michael@0: } michael@0: printf "%-40s %10s %10s\n", ($key, $leaks, percentStr($percentLeaks)); michael@0: $total += $leaks; michael@0: } michael@0: } michael@0: if (!$needsHeading) { michael@0: printf "%-40s %10s\n", ("TOTAL", $total); michael@0: } michael@0: michael@0: # NEW BLOAT michael@0: @keys = sort { $newMap{$b}{bloatPercent} <=> $newMap{$a}{bloatPercent} } keys %newMap; michael@0: $needsHeading = 1; michael@0: $total = 0; michael@0: foreach $key (@keys) { michael@0: my $percentBloat = $newMap{$key}{bloatPercent}; michael@0: my $bloat = $newMap{$key}{bloat}; michael@0: if ($percentBloat > 0 && $key !~ /TOTAL/) { michael@0: if ($needsHeading) { michael@0: printf "--NEW-BLOAT-----------------------------------bloat------bloat%%-----------------------\n"; michael@0: $needsHeading = 0; michael@0: } michael@0: printf "%-40s %10s %10s\n", ($key, $bloat, percentStr($percentBloat)); michael@0: $total += $bloat; michael@0: } michael@0: } michael@0: if (!$needsHeading) { michael@0: printf "%-40s %10s\n", ("TOTAL", $total); michael@0: } michael@0: michael@0: # ALL LEAKS michael@0: @keys = sort { $newMap{$b}{leaked} <=> $newMap{$a}{leaked} } keys %newMap; michael@0: $needsHeading = 1; michael@0: $total = 0; michael@0: foreach $key (@keys) { michael@0: my $leaks = $newMap{$key}{leaked}; michael@0: my $percentLeaks = $newMap{$key}{leakPercent}; michael@0: if ($leaks > 0) { michael@0: if ($needsHeading) { michael@0: printf "--ALL-LEAKS-----------------------------------leaks------leaks%%-----------------------\n"; michael@0: $needsHeading = 0; michael@0: } michael@0: printf "%-40s %10s %10s\n", ($key, $leaks, percentStr($percentLeaks)); michael@0: if ($key !~ /TOTAL/) { michael@0: $total += $leaks; michael@0: } michael@0: } michael@0: } michael@0: if (!$needsHeading) { michael@0: # printf "%-40s %10s\n", ("TOTAL", $total); michael@0: } michael@0: michael@0: # ALL BLOAT michael@0: @keys = sort { $newMap{$b}{bloat} <=> $newMap{$a}{bloat} } keys %newMap; michael@0: $needsHeading = 1; michael@0: $total = 0; michael@0: foreach $key (@keys) { michael@0: my $bloat = $newMap{$key}{bloat}; michael@0: my $percentBloat = $newMap{$key}{bloatPercent}; michael@0: if ($bloat > 0) { michael@0: if ($needsHeading) { michael@0: printf "--ALL-BLOAT-----------------------------------bloat------bloat%%-----------------------\n"; michael@0: $needsHeading = 0; michael@0: } michael@0: printf "%-40s %10s %10s\n", ($key, $bloat, percentStr($percentBloat)); michael@0: if ($key !~ /TOTAL/) { michael@0: $total += $bloat; michael@0: } michael@0: } michael@0: } michael@0: if (!$needsHeading) { michael@0: # printf "%-40s %10s\n", ("TOTAL", $total); michael@0: } michael@0: michael@0: # NEW CLASSES michael@0: @keys = sort { $newMap{$b}{bloatDelta} <=> $newMap{$a}{bloatDelta} } keys %newMap; michael@0: $needsHeading = 1; michael@0: my $ltotal = 0; michael@0: my $btotal = 0; michael@0: foreach $key (@keys) { michael@0: my $leaks = $newMap{$key}{leaked}; michael@0: my $bloat = $newMap{$key}{bloat}; michael@0: my $percentBloat = $newMap{$key}{bloatPercent}; michael@0: if ($percentBloat == $inf && $key !~ /TOTAL/) { michael@0: if ($needsHeading) { michael@0: printf "--CLASSES-NOT-REPORTED-LAST-TIME--------------leaks------bloat------------------------\n"; michael@0: $needsHeading = 0; michael@0: } michael@0: printf "%-40s %10s %10s\n", ($key, $leaks, $bloat); michael@0: if ($key !~ /TOTAL/) { michael@0: $ltotal += $leaks; michael@0: $btotal += $bloat; michael@0: } michael@0: } michael@0: } michael@0: if (!$needsHeading) { michael@0: printf "%-40s %10s %10s\n", ("TOTAL", $ltotal, $btotal); michael@0: } michael@0: michael@0: # OLD CLASSES michael@0: @keys = sort { ($oldMap{$b}{bloat} || 0) <=> ($oldMap{$a}{bloat} || 0) } keys %oldMap; michael@0: $needsHeading = 1; michael@0: $ltotal = 0; michael@0: $btotal = 0; michael@0: foreach $key (@keys) { michael@0: if (!defined($newMap{$key})) { michael@0: my $leaks = $oldMap{$key}{leaked}; michael@0: my $bloat = $oldMap{$key}{bloat}; michael@0: if ($needsHeading) { michael@0: printf "--CLASSES-THAT-WENT-AWAY----------------------leaks------bloat------------------------\n"; michael@0: $needsHeading = 0; michael@0: } michael@0: printf "%-40s %10s %10s\n", ($key, $leaks, $bloat); michael@0: if ($key !~ /TOTAL/) { michael@0: $ltotal += $leaks; michael@0: $btotal += $bloat; michael@0: } michael@0: } michael@0: } michael@0: if (!$needsHeading) { michael@0: printf "%-40s %10s %10s\n", ("TOTAL", $ltotal, $btotal); michael@0: } michael@0: michael@0: print "--------------------------------------------------------------------------------------\n";