tools/trace-malloc/diffbloatdump.pl

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rwxr-xr-x

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 #!/usr/bin/perl -w
     2 # vim:cindent:ts=8:et:sw=4:
     3 # This Source Code Form is subject to the terms of the Mozilla Public
     4 # License, v. 2.0. If a copy of the MPL was not distributed with this
     5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
     7 # This script produces a diff between two files that are the result of
     8 # calling NS_TraceMallocDumpAllocations.  Such files can be created
     9 # through the command-line option --shutdown-leaks=<filename> or through
    10 # the DOM function window.TraceMallocDumpAllocations(<filename>).  Both
    11 # methods will work only if --trace-malloc=<malloc-log> is also given on
    12 # the command line.
    14 use 5.004;
    15 use strict;
    16 use Getopt::Long;
    18 $::opt_help = 0;
    19 $::opt_depth = 6;
    20 $::opt_include_zero = 0;
    21 $::opt_allocation_count = 0;
    22 $::opt_use_address = 0;
    24 # XXX Change --use-address to be the default and remove the option
    25 # once tinderbox is no longer using it without --use-address.
    27 Getopt::Long::Configure("pass_through");
    28 Getopt::Long::GetOptions("help", "allocation-count", "depth=i",
    29                          "include-zero", "use-address");
    31 if ($::opt_help) {
    32     die "usage: diffbloatdump.pl [options] <dump1> <dump2>
    33   --help                 Display this message
    35   --allocation-count     Use allocation count rather than size (i.e., treat
    36                            all sizes as 1).
    37   --depth=<num>          Only display <num> frames at top of allocation stack.
    38   --include-zero         Display subtrees totalling zero.
    39   --use-address          Don't ignore the address part of the stack trace
    40                            (can make comparison more accurate when comparing
    41                            results from the same build)
    43   The input files (<dump1> and <dump2> above) are either trace-malloc
    44   memory dumps OR this script's output.  (If this script's output,
    45   --allocation-count and --use-address are ignored.)  If the input files
    46   have .gz or .bz2 extension, they are uncompressed.
    47 ";
    48 }
    50 my $calltree = { count => 0 }; # leave children undefined
    52 sub get_child($$) {
    53     my ($node, $frame) = @_;
    54     if (!defined($node->{children})) {
    55         $node->{children} = {};
    56     }
    57     if (!defined($node->{children}->{$frame})) {
    58         $node->{children}->{$frame} = { count => 0 };
    59     }
    60     return $node->{children}->{$frame};
    61 }
    63 sub add_tree_file($$$) {
    64     my ($infile, $firstline, $factor) = @_;
    66     my @nodestack;
    67     $nodestack[1] = $calltree;
    68     $firstline =~ /^(-?\d+) malloc$/;
    69     $calltree->{count} += $1 * $factor;
    71     my $lineno = 1;
    72     while (!eof($infile)) {
    73         my $line = <$infile>;
    74         ++$lineno;
    75         $line =~ /^( *)(-?\d+) (.*)$/ || die "malformed input, line $lineno";
    76         my $depth = length($1);
    77         my $count = $2;
    78         my $frame = $3;
    79         die "malformed input, line $lineno" if ($depth % 2 != 0);
    80         $depth /= 2;
    81         die "malformed input, line $lineno" if ($depth > $#nodestack);
    82         $#nodestack = $depth;
    83         my $node = get_child($nodestack[$depth], $frame);
    84         push @nodestack, $node;
    85         $node->{count} += $count * $factor;
    86     }
    87 }
    89 sub add_file($$) {
    90     # Takes (1) a reference to a file descriptor for input and (2) the
    91     # factor to multiply the stacks by (generally +1 or -1).
    92     # Returns a reference to an array representing the stack, allocation
    93     # site in array[0].
    94     sub read_stack($) {
    95         my ($infile) = @_;
    96         my $line;
    97         my @stack;
    99         # read the data at the memory location
   100         while ( defined($infile) && ($line = <$infile>) && substr($line,0,1) eq "\t" ) {
   101             # do nothing
   102         }
   104         # read the stack
   105         do {
   106             chomp($line);
   107             if ( ! $::opt_use_address &&
   108                  $line =~ /(.*)\[(.*)\]/) {
   109                 $line = $1;
   110             }
   111             $stack[$#stack+1] = $line;
   112         } while ( defined($infile) && ($line = <$infile>) && $line ne "\n" && $line ne "\r\n" );
   114         return \@stack;
   115     }
   117     # adds the stack given as a parameter (reference to array, $stack[0] is
   118     # allocator) to $calltree, with the call count multiplied by $factor
   119     # (typically +1 or -1).
   120     sub add_stack($$) {
   121         my @stack = @{$_[0]};
   122         my $factor = $_[1];
   124         my $i = 0;
   125         my $node = $calltree;
   126         while ($i < $#stack && $i < $::opt_depth) {
   127             $node->{count} += $factor;
   128             $node = get_child($node, $stack[$i]);
   129             ++$i;
   130         }
   131         $node->{count} += $factor;
   132     }
   134     my ($infile, $factor) = @_;
   136     if ($infile =~ /\.bz2$/) {
   137         # XXX This doesn't propagate errors from bzip2.
   138         open (INFILE, "bzip2 -cd '$infile' |") || die "Can't open input \"$infile\"";
   139     } elsif ($infile =~ /\.gz$/) {
   140         # XXX This doesn't propagate errors from gzip.
   141         open (INFILE, "gzip -cd '$infile' |") || die "Can't open input \"$infile\"";
   142     } else {
   143         open (INFILE, "<$infile") || die "Can't open input \"$infile\"";
   144     }
   145     my $first = 1;
   146     while ( ! eof(INFILE) ) {
   147         # read the type and address
   148         my $line = <INFILE>;
   149         if ($first) {
   150             $first = 0;
   151             if ($line =~ /^-?\d+ malloc$/) {
   152                 # We're capable of reading in our own output as well.
   153                 add_tree_file(\*INFILE, $line, $factor);
   154                 close INFILE;
   155                 return;
   156             }
   157         }
   158         unless ($line =~ /.*\((\d*)\)[\r|\n]/) {
   159             die "badly formed allocation header in $infile";
   160         }
   161         my $size;
   162         if ($::opt_allocation_count) {
   163             $size = 1;
   164         } else {
   165             $size = $1;
   166         }
   168         add_stack(read_stack(\*INFILE), $size * $factor);
   169     }
   170     close INFILE;
   171 }
   173 sub print_node_indent($$$);
   175 sub print_calltree() {
   176     sub print_indent($) {
   177         my ($i) = @_;
   178         while (--$i >= 0) {
   179             print "  ";
   180         }
   181     }
   183     sub print_node_indent($$$) {
   184         my ($nodename, $node, $indent) = @_;
   186         if (!$::opt_include_zero && $node->{count} == 0) {
   187             return;
   188         }
   190         print_indent($indent);
   191         print "$node->{count} $nodename\n";
   192         if (defined($node->{children})) {
   193             my %kids = %{$node->{children}};
   194             ++$indent;
   195             foreach my $kid (sort { $kids{$b}->{count} <=> $kids{$a}->{count} }
   196                                   keys (%kids)) {
   197                 print_node_indent($kid, $kids{$kid}, $indent);
   198             }
   199         }
   200     }
   202     print_node_indent("malloc", $calltree, 0);
   203 }
   205 add_file($ARGV[0], -1);
   206 add_file($ARGV[1],  1);
   207 print_calltree();

mercurial