michael@0: #!/usr/bin/perl -w michael@0: # michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: # michael@0: # Process output of TraceMallocDumpAllocations() to produce a table michael@0: # that attributes memory to the allocators using call stack. michael@0: # michael@0: michael@0: use 5.004; michael@0: use strict; michael@0: michael@0: # A table of all ancestors. Key is function name, value is an michael@0: # array of ancestors, each attributed with a number of calls and michael@0: # the amount of memory allocated. michael@0: my %Ancestors; michael@0: michael@0: # Ibid, for descendants. michael@0: my %Descendants; michael@0: michael@0: # A table that keeps the total amount of memory allocated by each michael@0: # function michael@0: my %Totals; michael@0: $Totals{".root"} = { "#memory#" => 0, "#calls#" => 0 }; michael@0: michael@0: # A table that maps the long ugly function name to a unique number so michael@0: # that the HTML we generate isn't too fat michael@0: my %Ids; michael@0: my $NextId = 0; michael@0: michael@0: $Ids{".root"} = ++$NextId; michael@0: michael@0: michael@0: LINE: while (<>) { michael@0: # The line'll look like: michael@0: # michael@0: # 0x4000a008 16 PR_Malloc+16; nsMemoryImpl::Alloc(unsigned int)+12; ... michael@0: michael@0: # Ignore any lines that don't start with an address michael@0: next LINE unless /^0x/; michael@0: michael@0: # Parse it michael@0: my ($address, $size, $rest) = /^(0x\S*)\s*(\d+)\s*(.*)$/; michael@0: my @stack = reverse(split /; /, $rest); michael@0: michael@0: # Accumulate at the root michael@0: $Totals{".root"}->{"#memory#"} += $size; michael@0: ++$Totals{".root"}->{"#calls#"}; michael@0: michael@0: my $caller = ".root"; michael@0: foreach my $callee (@stack) { michael@0: # Strip the offset from the callsite information. I don't michael@0: # think we care. michael@0: $callee =~ s/\+\d+$//g; michael@0: michael@0: # Accumulate the total for the callee michael@0: if (! $Totals{$callee}) { michael@0: $Totals{$callee} = { "#memory#" => 0, "#calls#" => 0 }; michael@0: } michael@0: michael@0: $Totals{$callee}->{"#memory#"} += $size; michael@0: ++$Totals{$callee}->{"#calls#"}; michael@0: michael@0: # Descendants michael@0: my $descendants = $Descendants{$caller}; michael@0: if (! $descendants) { michael@0: $descendants = $Descendants{$caller} = [ ]; michael@0: } michael@0: michael@0: # Manage the list of descendants michael@0: { michael@0: my $wasInserted = 0; michael@0: DESCENDANT: foreach my $item (@$descendants) { michael@0: if ($item->{"#name#"} eq $callee) { michael@0: $item->{"#memory#"} += $size; michael@0: ++$item->{"#calls#"}; michael@0: $wasInserted = 1; michael@0: last DESCENDANT; michael@0: } michael@0: } michael@0: michael@0: if (! $wasInserted) { michael@0: $descendants->[@$descendants] = { michael@0: "#name#" => $callee, michael@0: "#memory#" => $size, michael@0: "#calls#" => 1 michael@0: }; michael@0: } michael@0: } michael@0: michael@0: # Ancestors michael@0: my $ancestors = $Ancestors{$callee}; michael@0: if (! $ancestors) { michael@0: $ancestors = $Ancestors{$callee} = [ ]; michael@0: } michael@0: michael@0: # Manage the list of ancestors michael@0: { michael@0: my $wasInserted = 0; michael@0: ANCESTOR: foreach my $item (@$ancestors) { michael@0: if ($item->{"#name#"} eq $caller) { michael@0: $item->{"#memory#"} += $size; michael@0: ++$item->{"#calls#"}; michael@0: $wasInserted = 1; michael@0: last ANCESTOR; michael@0: } michael@0: } michael@0: michael@0: if (! $wasInserted) { michael@0: $ancestors->[@$ancestors] = { michael@0: "#name#" => $caller, michael@0: "#memory#" => $size, michael@0: "#calls#" => 1 michael@0: }; michael@0: } michael@0: } michael@0: michael@0: # Make a new "id", if necessary michael@0: if (! $Ids{$callee}) { michael@0: $Ids{$callee} = ++$NextId; michael@0: } michael@0: michael@0: # On to the next one... michael@0: $caller = $callee; michael@0: } michael@0: } michael@0: michael@0: michael@0: # Change the manky looking callsite into a pretty function; strip argument michael@0: # types and offset information. michael@0: sub pretty($) { michael@0: $_ = $_[0]; michael@0: s/&/&/g; michael@0: s/</g; michael@0: s/>/>/g; michael@0: michael@0: if (/([^\(]*)(\(.*\))/) { michael@0: return $1 . "()"; michael@0: } michael@0: else { michael@0: return $_[0]; michael@0: } michael@0: } michael@0: michael@0: # Dump a web page! michael@0: print "\n"; michael@0: print "
\n"; michael@0: print "Function | Ancestors | Descendants |
$pretty $memory ($calls) | \n"; michael@0: } michael@0: michael@0: # Ancestors, sorted descending by amount of memory allocated michael@0: print "\n";
michael@0: my $ancestors = $Ancestors{$node};
michael@0: if ($ancestors) {
michael@0: foreach my $ancestor (sort { $b->{"#memory#"} <=> $a->{"#memory#"} } @$ancestors) {
michael@0: my ($name, $memory, $calls) =
michael@0: ($ancestor->{"#name#"},
michael@0: $ancestor->{"#memory#"},
michael@0: $ancestor->{"#calls#"});
michael@0:
michael@0: my $pretty = pretty($name);
michael@0:
michael@0: print " $pretty $memory ($calls) \n"; michael@0: } michael@0: } michael@0: michael@0: print " | \n";
michael@0:
michael@0: # Descendants, sorted descending by amount of memory allocated
michael@0: print " \n";
michael@0: my $descendants = $Descendants{$node};
michael@0: if ($descendants) {
michael@0: foreach my $descendant (sort { $b->{"#memory#"} <=> $a->{"#memory#"} } @$descendants) {
michael@0: my ($name, $memory, $calls) =
michael@0: ($descendant->{"#name#"},
michael@0: $descendant->{"#memory#"},
michael@0: $descendant->{"#calls#"});
michael@0:
michael@0: my $pretty = pretty($name);
michael@0:
michael@0: print " $pretty $memory ($calls) \n"; michael@0: } michael@0: } michael@0: print " |
Function | Ancestors | Descendants |