tools/trace-malloc/diffbloatdump.pl

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/tools/trace-malloc/diffbloatdump.pl	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,207 @@
     1.4 +#!/usr/bin/perl -w
     1.5 +# vim:cindent:ts=8:et:sw=4:
     1.6 +# This Source Code Form is subject to the terms of the Mozilla Public
     1.7 +# License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 +# file, You can obtain one at http://mozilla.org/MPL/2.0/.
     1.9 +
    1.10 +# This script produces a diff between two files that are the result of
    1.11 +# calling NS_TraceMallocDumpAllocations.  Such files can be created
    1.12 +# through the command-line option --shutdown-leaks=<filename> or through
    1.13 +# the DOM function window.TraceMallocDumpAllocations(<filename>).  Both
    1.14 +# methods will work only if --trace-malloc=<malloc-log> is also given on
    1.15 +# the command line.
    1.16 +
    1.17 +use 5.004;
    1.18 +use strict;
    1.19 +use Getopt::Long;
    1.20 +
    1.21 +$::opt_help = 0;
    1.22 +$::opt_depth = 6;
    1.23 +$::opt_include_zero = 0;
    1.24 +$::opt_allocation_count = 0;
    1.25 +$::opt_use_address = 0;
    1.26 +
    1.27 +# XXX Change --use-address to be the default and remove the option
    1.28 +# once tinderbox is no longer using it without --use-address.
    1.29 +
    1.30 +Getopt::Long::Configure("pass_through");
    1.31 +Getopt::Long::GetOptions("help", "allocation-count", "depth=i",
    1.32 +                         "include-zero", "use-address");
    1.33 +
    1.34 +if ($::opt_help) {
    1.35 +    die "usage: diffbloatdump.pl [options] <dump1> <dump2>
    1.36 +  --help                 Display this message
    1.37 +
    1.38 +  --allocation-count     Use allocation count rather than size (i.e., treat
    1.39 +                           all sizes as 1).
    1.40 +  --depth=<num>          Only display <num> frames at top of allocation stack.
    1.41 +  --include-zero         Display subtrees totalling zero.
    1.42 +  --use-address          Don't ignore the address part of the stack trace
    1.43 +                           (can make comparison more accurate when comparing
    1.44 +                           results from the same build)
    1.45 +
    1.46 +  The input files (<dump1> and <dump2> above) are either trace-malloc
    1.47 +  memory dumps OR this script's output.  (If this script's output,
    1.48 +  --allocation-count and --use-address are ignored.)  If the input files
    1.49 +  have .gz or .bz2 extension, they are uncompressed.
    1.50 +";
    1.51 +}
    1.52 +
    1.53 +my $calltree = { count => 0 }; # leave children undefined
    1.54 +
    1.55 +sub get_child($$) {
    1.56 +    my ($node, $frame) = @_;
    1.57 +    if (!defined($node->{children})) {
    1.58 +        $node->{children} = {};
    1.59 +    }
    1.60 +    if (!defined($node->{children}->{$frame})) {
    1.61 +        $node->{children}->{$frame} = { count => 0 };
    1.62 +    }
    1.63 +    return $node->{children}->{$frame};
    1.64 +}
    1.65 +
    1.66 +sub add_tree_file($$$) {
    1.67 +    my ($infile, $firstline, $factor) = @_;
    1.68 +
    1.69 +    my @nodestack;
    1.70 +    $nodestack[1] = $calltree;
    1.71 +    $firstline =~ /^(-?\d+) malloc$/;
    1.72 +    $calltree->{count} += $1 * $factor;
    1.73 +
    1.74 +    my $lineno = 1;
    1.75 +    while (!eof($infile)) {
    1.76 +        my $line = <$infile>;
    1.77 +        ++$lineno;
    1.78 +        $line =~ /^( *)(-?\d+) (.*)$/ || die "malformed input, line $lineno";
    1.79 +        my $depth = length($1);
    1.80 +        my $count = $2;
    1.81 +        my $frame = $3;
    1.82 +        die "malformed input, line $lineno" if ($depth % 2 != 0);
    1.83 +        $depth /= 2;
    1.84 +        die "malformed input, line $lineno" if ($depth > $#nodestack);
    1.85 +        $#nodestack = $depth;
    1.86 +        my $node = get_child($nodestack[$depth], $frame);
    1.87 +        push @nodestack, $node;
    1.88 +        $node->{count} += $count * $factor;
    1.89 +    }
    1.90 +}
    1.91 +
    1.92 +sub add_file($$) {
    1.93 +    # Takes (1) a reference to a file descriptor for input and (2) the
    1.94 +    # factor to multiply the stacks by (generally +1 or -1).
    1.95 +    # Returns a reference to an array representing the stack, allocation
    1.96 +    # site in array[0].
    1.97 +    sub read_stack($) {
    1.98 +        my ($infile) = @_;
    1.99 +        my $line;
   1.100 +        my @stack;
   1.101 +
   1.102 +        # read the data at the memory location
   1.103 +        while ( defined($infile) && ($line = <$infile>) && substr($line,0,1) eq "\t" ) {
   1.104 +            # do nothing
   1.105 +        }
   1.106 +
   1.107 +        # read the stack
   1.108 +        do {
   1.109 +            chomp($line);
   1.110 +            if ( ! $::opt_use_address &&
   1.111 +                 $line =~ /(.*)\[(.*)\]/) {
   1.112 +                $line = $1;
   1.113 +            }
   1.114 +            $stack[$#stack+1] = $line;
   1.115 +        } while ( defined($infile) && ($line = <$infile>) && $line ne "\n" && $line ne "\r\n" );
   1.116 +
   1.117 +        return \@stack;
   1.118 +    }
   1.119 +
   1.120 +    # adds the stack given as a parameter (reference to array, $stack[0] is
   1.121 +    # allocator) to $calltree, with the call count multiplied by $factor
   1.122 +    # (typically +1 or -1).
   1.123 +    sub add_stack($$) {
   1.124 +        my @stack = @{$_[0]};
   1.125 +        my $factor = $_[1];
   1.126 +
   1.127 +        my $i = 0;
   1.128 +        my $node = $calltree;
   1.129 +        while ($i < $#stack && $i < $::opt_depth) {
   1.130 +            $node->{count} += $factor;
   1.131 +            $node = get_child($node, $stack[$i]);
   1.132 +            ++$i;
   1.133 +        }
   1.134 +        $node->{count} += $factor;
   1.135 +    }
   1.136 +
   1.137 +    my ($infile, $factor) = @_;
   1.138 +
   1.139 +    if ($infile =~ /\.bz2$/) {
   1.140 +        # XXX This doesn't propagate errors from bzip2.
   1.141 +        open (INFILE, "bzip2 -cd '$infile' |") || die "Can't open input \"$infile\"";
   1.142 +    } elsif ($infile =~ /\.gz$/) {
   1.143 +        # XXX This doesn't propagate errors from gzip.
   1.144 +        open (INFILE, "gzip -cd '$infile' |") || die "Can't open input \"$infile\"";
   1.145 +    } else {
   1.146 +        open (INFILE, "<$infile") || die "Can't open input \"$infile\"";
   1.147 +    }
   1.148 +    my $first = 1;
   1.149 +    while ( ! eof(INFILE) ) {
   1.150 +        # read the type and address
   1.151 +        my $line = <INFILE>;
   1.152 +        if ($first) {
   1.153 +            $first = 0;
   1.154 +            if ($line =~ /^-?\d+ malloc$/) {
   1.155 +                # We're capable of reading in our own output as well.
   1.156 +                add_tree_file(\*INFILE, $line, $factor);
   1.157 +                close INFILE;
   1.158 +                return;
   1.159 +            }
   1.160 +        }
   1.161 +        unless ($line =~ /.*\((\d*)\)[\r|\n]/) {
   1.162 +            die "badly formed allocation header in $infile";
   1.163 +        }
   1.164 +        my $size;
   1.165 +        if ($::opt_allocation_count) {
   1.166 +            $size = 1;
   1.167 +        } else {
   1.168 +            $size = $1;
   1.169 +        }
   1.170 +
   1.171 +        add_stack(read_stack(\*INFILE), $size * $factor);
   1.172 +    }
   1.173 +    close INFILE;
   1.174 +}
   1.175 +
   1.176 +sub print_node_indent($$$);
   1.177 +
   1.178 +sub print_calltree() {
   1.179 +    sub print_indent($) {
   1.180 +        my ($i) = @_;
   1.181 +        while (--$i >= 0) {
   1.182 +            print "  ";
   1.183 +        }
   1.184 +    }
   1.185 +
   1.186 +    sub print_node_indent($$$) {
   1.187 +        my ($nodename, $node, $indent) = @_;
   1.188 +
   1.189 +        if (!$::opt_include_zero && $node->{count} == 0) {
   1.190 +            return;
   1.191 +        }
   1.192 +
   1.193 +        print_indent($indent);
   1.194 +        print "$node->{count} $nodename\n";
   1.195 +        if (defined($node->{children})) {
   1.196 +            my %kids = %{$node->{children}};
   1.197 +            ++$indent;
   1.198 +            foreach my $kid (sort { $kids{$b}->{count} <=> $kids{$a}->{count} }
   1.199 +                                  keys (%kids)) {
   1.200 +                print_node_indent($kid, $kids{$kid}, $indent);
   1.201 +            }
   1.202 +        }
   1.203 +    }
   1.204 +
   1.205 +    print_node_indent("malloc", $calltree, 0);
   1.206 +}
   1.207 +
   1.208 +add_file($ARGV[0], -1);
   1.209 +add_file($ARGV[1],  1);
   1.210 +print_calltree();

mercurial