3 # ***** BEGIN LICENSE BLOCK *****
4 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 # The contents of this file are subject to the Mozilla Public License Version
7 # 1.1 (the "License"); you may not use this file except in compliance with
8 # the License. You may obtain a copy of the License at
9 # http://www.mozilla.org/MPL/
11 # Software distributed under the License is distributed on an "AS IS" basis,
12 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 # for the specific language governing rights and limitations under the
16 # The Original Code is mozilla.org Code.
18 # The Initial Developer of the Original Code is
19 # Netscape Communications Corporation.
20 # Portions created by the Initial Developer are Copyright (C) 1998
21 # the Initial Developer. All Rights Reserved.
25 # Alternatively, the contents of this file may be used under the terms of
26 # either the GNU General Public License Version 2 or later (the "GPL"), or
27 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 # in which case the provisions of the GPL or the LGPL are applicable instead
29 # of those above. If you wish to allow use of your version of this file only
30 # under the terms of either the GPL or the LGPL, and not to allow others to
31 # use your version of this file under the terms of the MPL, indicate your
32 # decision by deleting the provisions above and replace them with the notice
33 # and other provisions required by the GPL or the LGPL. If you do not delete
34 # the provisions above, a recipient may use your version of this file under
35 # the terms of any one of the MPL, the GPL or the LGPL.
37 # ***** END LICENSE BLOCK *****
43 $::opt_prune_depth
= 0;
44 $::opt_subtree_size
= 0;
47 # GetOption will create $opt_object & $opt_exclude, so ignore the
48 # warning that gets spit out about those vbls.
49 GetOptions
("object=s", "exclude=s", "comptrs=s", "ignore-balanced", "subtree-size=i", "prune-depth=i",
50 "collapse-to-method", "collapse-to-class", "old-style", "reverse");
54 usage
: leak
.pl
< logfile
55 --object
<obj
> The address of the object to examine
(required
)
56 --exclude
<file
> Exclude routines listed
in <file
>
57 --comptrs
<file
> Subtract all the data
in the balanced COMPtr
log <file
>
58 --ignore
-balanced Ignore balanced subtrees
59 --subtree
-size
<n
> Print subtrees with more than
<n
> nodes separately
60 --prune
-depth
<depth
> Prune the tree to
<depth
>
61 --collapse
-to
-method Aggregate data by method
62 --collapse
-to
-class Aggregate data by
class (subsumes
--collapse
-to
-method
)
63 --reverse Reverse call stacks
, showing leaves first
64 --old
-style Old
-style formatting
67 $::opt_prune_depth
= 0 if $::opt_prune_depth
< 0;
68 $::opt_subtree_size
= 0 if $::opt_subtree_size
< 0;
70 warn "object $::opt_object\n";
71 warn "ignoring balanced subtrees\n" if $::opt_ignore_balanced
;
72 warn "prune depth $::opt_prune_depth\n" if $::opt_prune_depth
;
73 warn "collapsing to class\n" if $::opt_collapse_to_class
;
74 warn "collapsing to method\n" if $::opt_collapse_to_method
&& !$::opt_collapse_to_class
;
75 warn "reversing call stacks\n" if $::opt_reverse
;
78 # The 'excludes' are functions that, if detected in a particular call
79 # stack, will cause the _entire_ call stack to be ignored. You might,
80 # for example, explicitly exclude two functions that have a matching
81 # AddRef/Release pair.
86 open(EXCLUDE
, "<".$::opt_exclude
)
87 || die "unable to open $::opt_exclude";
91 warn "excluding $_\n";
96 # Each entry in the tree rooted by callGraphRoot contains the following:
97 # #name# This call's name+offset string
98 # #refcount# The net reference count of this call
99 # #label# The label used for this subtree; only defined for labeled nodes
100 # #children# List of children in alphabetical order
101 # zero or more children indexed by method name+offset strings.
104 $callGraphRoot = { '#name#' => '.root', '#refcount#' => 'n/a' };
106 # The 'imbalance' is a gross count of how balanced a particular
107 # callsite is. It is used to prune away callsites that are detected to
108 # be balanced; that is, that have matching AddRef/Release() pairs.
111 $imbalance{'.root'} = 'n/a';
113 # The main read loop.
116 my ($INFILE, $plus, $minus) = @_;
118 LINE
: while (<$INFILE>) {
119 next LINE
if (! /^</);
120 my @fields = split(/ /, $_);
122 my $class = shift(@fields);
123 my $obj = shift(@fields);
124 my $sno = shift(@fields);
125 next LINE
unless ($obj eq $::opt_object
);
127 my $op = shift(@fields);
128 next LINE
unless ($op eq $plus || $op eq $minus);
130 my $cnt = shift(@fields);
132 # Collect the remaining lines to create a stack trace.
134 CALLSITE
: while (<$INFILE>) {
136 last CALLSITE
if (/^$/);
137 $stack[++$#stack] = $_;
140 # Reverse the remaining fields to produce the call stack, with the
141 # oldest frame at the front of the array.
142 if (! $::opt_reverse
) {
143 @stack = reverse(@stack);
148 # If any of the functions in the stack are supposed to be excluded,
149 # march on to the next line.
150 foreach $call (@stack) {
151 next LINE
if exists($excludes{$call});
155 # Add the callstack as a path through the call graph, updating
156 # refcounts at each node.
158 my $caller = $callGraphRoot;
160 foreach $call (@stack) {
162 # Chop the method offset if we're 'collapsing to method' or
163 # 'collapsing to class'.
164 $call =~ s/\+0x.*$//g if ($::opt_collapse_to_method
|| $::opt_collapse_to_class
);
166 # Chop the method name if we're 'collapsing to class'.
167 $call =~ s/::.*$//g if ($::opt_collapse_to_class
);
169 my $site = $caller->{$call};
171 # This is the first time we've seen this callsite. Add a
172 # new entry to the call tree.
174 $site = { '#name#' => $call, '#refcount#' => 0 };
175 $caller->{$call} = $site;
179 ++($site->{'#refcount#'});
180 ++($imbalance{$call});
181 } elsif ($op eq $minus) {
182 --($site->{'#refcount#'});
183 --($imbalance{$call});
185 die "Bad operation $op";
193 read_data
(*STDIN
, "AddRef", "Release");
195 if ($::opt_comptrs
) {
196 warn "Subtracting comptr log ". $::opt_comptrs
. "\n";
197 open(COMPTRS
, "<".$::opt_comptrs
)
198 || die "unable to open $::opt_comptrs";
200 # read backwards to subtract
201 read_data
(*COMPTRS
, "nsCOMPtrRelease", "nsCOMPtrAddRef");
205 my ($aN, $aS, $bN, $bS);
206 ($aN, $aS) = ($1, $2) if $a =~ /^(\d+) (.+)$/;
207 ($bN, $bS) = ($1, $2) if $b =~ /^(\d+) (.+)$/;
208 return $a cmp $b unless defined $aN && defined $bN;
209 return $aN <=> $bN unless $aN == $bN;
213 # Given a subtree and its nesting level, return true if that subtree should be pruned.
214 # If it shouldn't be pruned, destructively attempt to prune its children.
215 # Also compute the #children# properties of unpruned nodes.
217 my ($site, $nest) = @_;
219 # If they want us to prune the tree's depth, do so here.
220 return 1 if ($::opt_prune_depth
&& $nest >= $::opt_prune_depth
);
222 # If the subtree is balanced, ignore it.
223 return 1 if ($::opt_ignore_balanced
&& !$site->{'#refcount#'});
225 my $name = $site->{'#name#'};
227 # If the symbol isn't imbalanced, then prune here (and warn)
228 if ($::opt_ignore_balanced
&& !$imbalance{$name}) {
229 warn "discarding " . $name . "\n";
234 foreach my $child (sort num_alpha
keys(%$site)) {
235 if (substr($child, 0, 1) ne '#') {
236 if (prune
($site->{$child}, $nest + 1)) {
237 delete $site->{$child};
239 push @children, $site->{$child};
243 $site->{'#children#'} = \
@children;
248 # Compute the #label# properties of this subtree.
249 # Return the subtree's number of nodes, not counting nodes reachable
250 # through a labeled node.
251 sub createLabels
($) {
253 my @children = @
{$site->{'#children#'}};
254 my $nChildren = @children;
255 my $nDescendants = 0;
257 foreach my $child (@children) {
258 my $childDescendants = createLabels
($child);
259 if ($nChildren > 1 && $childDescendants > $::opt_subtree_size
) {
260 die "Internal error" if defined($child->{'#label#'});
261 $child->{'#label#'} = "__label__";
262 $childDescendants = 1;
264 $nDescendants += $childDescendants;
266 return $nDescendants + 1;
274 my ($site, $nest, $nestStr, $childrenLeft, $root) = @_;
275 my $label = !$root && $site->{'#label#'};
277 # Assign a unique number to the label.
279 die unless $label eq "__label__";
280 $label = "__" . ++$nextLabel . "__";
281 $site->{'#label#'} = $label;
282 push @labeledSubtrees, $site;
286 if ($::opt_old_style
) {
287 print $label, " " if $label;
288 print $site->{'#name#'}, ": bal=", $site->{'#refcount#'}, "\n";
290 my $refcount = $site->{'#refcount#'};
291 my $l = 8 - length $refcount;
293 print $refcount, " " x
$l;
294 print $label, " " if $label;
295 print $site->{'#name#'}, "\n";
298 $nestStr .= $childrenLeft && !$::opt_old_style ?
"| " : " ";
300 my @children = @
{$site->{'#children#'}};
301 $childrenLeft = @children;
302 foreach my $child (@children) {
304 list
($child, $nest + 1, $nestStr, $childrenLeft);
310 if (!prune
($callGraphRoot, 0)) {
311 createLabels
$callGraphRoot if ($::opt_subtree_size
);
312 list
$callGraphRoot, 0, "", 0, 1;
313 while (@labeledSubtrees) {
314 my $labeledSubtree = shift @labeledSubtrees;
315 print "\n------------------------------\n",
316 $labeledSubtree->{'#label#'}, "\n";
317 list
$labeledSubtree, 0, "", 0, 1;
319 print "\n------------------------------\n" if @labeledSubtrees;
327 foreach my $call (sort num_alpha
keys(%imbalance)) {
328 print $call . " " . $imbalance{$call} . "\n";