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();