123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373 |
- #!/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";
|