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