tools/rb/make-tree.pl

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rwxr-xr-x

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 #!/usr/bin/perl -w
michael@0 2 #
michael@0 3 # This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 # License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 6
michael@0 7 use 5.004;
michael@0 8 use strict;
michael@0 9 use Getopt::Long;
michael@0 10
michael@0 11 $::opt_prune_depth = 0;
michael@0 12 $::opt_subtree_size = 0;
michael@0 13 $::opt_reverse = 0;
michael@0 14
michael@0 15 # GetOption will create $opt_object & $opt_exclude, so ignore the
michael@0 16 # warning that gets spit out about those vbls.
michael@0 17 GetOptions("object=s", "exclude=s", "comptrs=s", "ignore-balanced", "subtree-size=i", "prune-depth=i",
michael@0 18 "collapse-to-method", "collapse-to-class", "old-style", "reverse");
michael@0 19
michael@0 20 $::opt_object ||
michael@0 21 die qq{
michael@0 22 usage: leak.pl < logfile
michael@0 23 --object <obj> The address of the object to examine (required)
michael@0 24 --exclude <file> Exclude routines listed in <file>
michael@0 25 --comptrs <file> Subtract all the data in the balanced COMPtr log <file>
michael@0 26 --ignore-balanced Ignore balanced subtrees
michael@0 27 --subtree-size <n> Print subtrees with more than <n> nodes separately
michael@0 28 --prune-depth <depth> Prune the tree to <depth>
michael@0 29 --collapse-to-method Aggregate data by method
michael@0 30 --collapse-to-class Aggregate data by class (subsumes --collapse-to-method)
michael@0 31 --reverse Reverse call stacks, showing leaves first
michael@0 32 --old-style Old-style formatting
michael@0 33 };
michael@0 34
michael@0 35 $::opt_prune_depth = 0 if $::opt_prune_depth < 0;
michael@0 36 $::opt_subtree_size = 0 if $::opt_subtree_size < 0;
michael@0 37
michael@0 38 warn "object $::opt_object\n";
michael@0 39 warn "ignoring balanced subtrees\n" if $::opt_ignore_balanced;
michael@0 40 warn "prune depth $::opt_prune_depth\n" if $::opt_prune_depth;
michael@0 41 warn "collapsing to class\n" if $::opt_collapse_to_class;
michael@0 42 warn "collapsing to method\n" if $::opt_collapse_to_method && !$::opt_collapse_to_class;
michael@0 43 warn "reversing call stacks\n" if $::opt_reverse;
michael@0 44
michael@0 45
michael@0 46 # The 'excludes' are functions that, if detected in a particular call
michael@0 47 # stack, will cause the _entire_ call stack to be ignored. You might,
michael@0 48 # for example, explicitly exclude two functions that have a matching
michael@0 49 # AddRef/Release pair.
michael@0 50
michael@0 51 my %excludes;
michael@0 52
michael@0 53 if ($::opt_exclude) {
michael@0 54 open(EXCLUDE, "<".$::opt_exclude)
michael@0 55 || die "unable to open $::opt_exclude";
michael@0 56
michael@0 57 while (<EXCLUDE>) {
michael@0 58 chomp $_;
michael@0 59 warn "excluding $_\n";
michael@0 60 $excludes{$_} = 1;
michael@0 61 }
michael@0 62 }
michael@0 63
michael@0 64 # Each entry in the tree rooted by callGraphRoot contains the following:
michael@0 65 # #name# This call's name+offset string
michael@0 66 # #refcount# The net reference count of this call
michael@0 67 # #label# The label used for this subtree; only defined for labeled nodes
michael@0 68 # #children# List of children in alphabetical order
michael@0 69 # zero or more children indexed by method name+offset strings.
michael@0 70
michael@0 71 my $callGraphRoot;
michael@0 72 $callGraphRoot = { '#name#' => '.root', '#refcount#' => 'n/a' };
michael@0 73
michael@0 74 # The 'imbalance' is a gross count of how balanced a particular
michael@0 75 # callsite is. It is used to prune away callsites that are detected to
michael@0 76 # be balanced; that is, that have matching AddRef/Release() pairs.
michael@0 77
michael@0 78 my %imbalance;
michael@0 79 $imbalance{'.root'} = 'n/a';
michael@0 80
michael@0 81 # The main read loop.
michael@0 82
michael@0 83 sub read_data($$$) {
michael@0 84 my ($INFILE, $plus, $minus) = @_;
michael@0 85
michael@0 86 LINE: while (<$INFILE>) {
michael@0 87 next LINE if (! /^</);
michael@0 88 my @fields = split(/ /, $_);
michael@0 89
michael@0 90 my $class = shift(@fields);
michael@0 91 my $obj = shift(@fields);
michael@0 92 my $sno = shift(@fields);
michael@0 93 next LINE unless ($obj eq $::opt_object);
michael@0 94
michael@0 95 my $op = shift(@fields);
michael@0 96 next LINE unless ($op eq $plus || $op eq $minus);
michael@0 97
michael@0 98 my $cnt = shift(@fields);
michael@0 99
michael@0 100 # Collect the remaining lines to create a stack trace.
michael@0 101 my @stack;
michael@0 102 CALLSITE: while (<$INFILE>) {
michael@0 103 chomp;
michael@0 104 last CALLSITE if (/^$/);
michael@0 105 $stack[++$#stack] = $_;
michael@0 106 }
michael@0 107
michael@0 108 # Reverse the remaining fields to produce the call stack, with the
michael@0 109 # oldest frame at the front of the array.
michael@0 110 if (! $::opt_reverse) {
michael@0 111 @stack = reverse(@stack);
michael@0 112 }
michael@0 113
michael@0 114 my $call;
michael@0 115
michael@0 116 # If any of the functions in the stack are supposed to be excluded,
michael@0 117 # march on to the next line.
michael@0 118 foreach $call (@stack) {
michael@0 119 next LINE if exists($excludes{$call});
michael@0 120 }
michael@0 121
michael@0 122
michael@0 123 # Add the callstack as a path through the call graph, updating
michael@0 124 # refcounts at each node.
michael@0 125
michael@0 126 my $caller = $callGraphRoot;
michael@0 127
michael@0 128 foreach $call (@stack) {
michael@0 129
michael@0 130 # Chop the method offset if we're 'collapsing to method' or
michael@0 131 # 'collapsing to class'.
michael@0 132 $call =~ s/\+0x.*$//g if ($::opt_collapse_to_method || $::opt_collapse_to_class);
michael@0 133
michael@0 134 # Chop the method name if we're 'collapsing to class'.
michael@0 135 $call =~ s/::.*$//g if ($::opt_collapse_to_class);
michael@0 136
michael@0 137 my $site = $caller->{$call};
michael@0 138 if (!$site) {
michael@0 139 # This is the first time we've seen this callsite. Add a
michael@0 140 # new entry to the call tree.
michael@0 141
michael@0 142 $site = { '#name#' => $call, '#refcount#' => 0 };
michael@0 143 $caller->{$call} = $site;
michael@0 144 }
michael@0 145
michael@0 146 if ($op eq $plus) {
michael@0 147 ++($site->{'#refcount#'});
michael@0 148 ++($imbalance{$call});
michael@0 149 } elsif ($op eq $minus) {
michael@0 150 --($site->{'#refcount#'});
michael@0 151 --($imbalance{$call});
michael@0 152 } else {
michael@0 153 die "Bad operation $op";
michael@0 154 }
michael@0 155
michael@0 156 $caller = $site;
michael@0 157 }
michael@0 158 }
michael@0 159 }
michael@0 160
michael@0 161 read_data(*STDIN, "AddRef", "Release");
michael@0 162
michael@0 163 if ($::opt_comptrs) {
michael@0 164 warn "Subtracting comptr log ". $::opt_comptrs . "\n";
michael@0 165 open(COMPTRS, "<".$::opt_comptrs)
michael@0 166 || die "unable to open $::opt_comptrs";
michael@0 167
michael@0 168 # read backwards to subtract
michael@0 169 read_data(*COMPTRS, "nsCOMPtrRelease", "nsCOMPtrAddRef");
michael@0 170 }
michael@0 171
michael@0 172 sub num_alpha {
michael@0 173 my ($aN, $aS, $bN, $bS);
michael@0 174 ($aN, $aS) = ($1, $2) if $a =~ /^(\d+) (.+)$/;
michael@0 175 ($bN, $bS) = ($1, $2) if $b =~ /^(\d+) (.+)$/;
michael@0 176 return $a cmp $b unless defined $aN && defined $bN;
michael@0 177 return $aN <=> $bN unless $aN == $bN;
michael@0 178 return $aS cmp $bS;
michael@0 179 }
michael@0 180
michael@0 181 # Given a subtree and its nesting level, return true if that subtree should be pruned.
michael@0 182 # If it shouldn't be pruned, destructively attempt to prune its children.
michael@0 183 # Also compute the #children# properties of unpruned nodes.
michael@0 184 sub prune($$) {
michael@0 185 my ($site, $nest) = @_;
michael@0 186
michael@0 187 # If they want us to prune the tree's depth, do so here.
michael@0 188 return 1 if ($::opt_prune_depth && $nest >= $::opt_prune_depth);
michael@0 189
michael@0 190 # If the subtree is balanced, ignore it.
michael@0 191 return 1 if ($::opt_ignore_balanced && !$site->{'#refcount#'});
michael@0 192
michael@0 193 my $name = $site->{'#name#'};
michael@0 194
michael@0 195 # If the symbol isn't imbalanced, then prune here (and warn)
michael@0 196 if ($::opt_ignore_balanced && !$imbalance{$name}) {
michael@0 197 warn "discarding " . $name . "\n";
michael@0 198 # return 1;
michael@0 199 }
michael@0 200
michael@0 201 my @children;
michael@0 202 foreach my $child (sort num_alpha keys(%$site)) {
michael@0 203 if (substr($child, 0, 1) ne '#') {
michael@0 204 if (prune($site->{$child}, $nest + 1)) {
michael@0 205 delete $site->{$child};
michael@0 206 } else {
michael@0 207 push @children, $site->{$child};
michael@0 208 }
michael@0 209 }
michael@0 210 }
michael@0 211 $site->{'#children#'} = \@children;
michael@0 212 return 0;
michael@0 213 }
michael@0 214
michael@0 215
michael@0 216 # Compute the #label# properties of this subtree.
michael@0 217 # Return the subtree's number of nodes, not counting nodes reachable
michael@0 218 # through a labeled node.
michael@0 219 sub createLabels($) {
michael@0 220 my ($site) = @_;
michael@0 221 my @children = @{$site->{'#children#'}};
michael@0 222 my $nChildren = @children;
michael@0 223 my $nDescendants = 0;
michael@0 224
michael@0 225 foreach my $child (@children) {
michael@0 226 my $childDescendants = createLabels($child);
michael@0 227 if ($nChildren > 1 && $childDescendants > $::opt_subtree_size) {
michael@0 228 die "Internal error" if defined($child->{'#label#'});
michael@0 229 $child->{'#label#'} = "__label__";
michael@0 230 $childDescendants = 1;
michael@0 231 }
michael@0 232 $nDescendants += $childDescendants;
michael@0 233 }
michael@0 234 return $nDescendants + 1;
michael@0 235 }
michael@0 236
michael@0 237
michael@0 238 my $nextLabel = 0;
michael@0 239 my @labeledSubtrees;
michael@0 240
michael@0 241 sub list($$$$$) {
michael@0 242 my ($site, $nest, $nestStr, $childrenLeft, $root) = @_;
michael@0 243 my $label = !$root && $site->{'#label#'};
michael@0 244
michael@0 245 # Assign a unique number to the label.
michael@0 246 if ($label) {
michael@0 247 die unless $label eq "__label__";
michael@0 248 $label = "__" . ++$nextLabel . "__";
michael@0 249 $site->{'#label#'} = $label;
michael@0 250 push @labeledSubtrees, $site;
michael@0 251 }
michael@0 252
michael@0 253 print $nestStr;
michael@0 254 if ($::opt_old_style) {
michael@0 255 print $label, " " if $label;
michael@0 256 print $site->{'#name#'}, ": bal=", $site->{'#refcount#'}, "\n";
michael@0 257 } else {
michael@0 258 my $refcount = $site->{'#refcount#'};
michael@0 259 my $l = 8 - length $refcount;
michael@0 260 $l = 1 if $l < 1;
michael@0 261 print $refcount, " " x $l;
michael@0 262 print $label, " " if $label;
michael@0 263 print $site->{'#name#'}, "\n";
michael@0 264 }
michael@0 265
michael@0 266 $nestStr .= $childrenLeft && !$::opt_old_style ? "| " : " ";
michael@0 267 if (!$label) {
michael@0 268 my @children = @{$site->{'#children#'}};
michael@0 269 $childrenLeft = @children;
michael@0 270 foreach my $child (@children) {
michael@0 271 $childrenLeft--;
michael@0 272 list($child, $nest + 1, $nestStr, $childrenLeft);
michael@0 273 }
michael@0 274 }
michael@0 275 }
michael@0 276
michael@0 277
michael@0 278 if (!prune($callGraphRoot, 0)) {
michael@0 279 createLabels $callGraphRoot if ($::opt_subtree_size);
michael@0 280 list $callGraphRoot, 0, "", 0, 1;
michael@0 281 while (@labeledSubtrees) {
michael@0 282 my $labeledSubtree = shift @labeledSubtrees;
michael@0 283 print "\n------------------------------\n",
michael@0 284 $labeledSubtree->{'#label#'}, "\n";
michael@0 285 list $labeledSubtree, 0, "", 0, 1;
michael@0 286 }
michael@0 287 print "\n------------------------------\n" if @labeledSubtrees;
michael@0 288 }
michael@0 289
michael@0 290 print qq{
michael@0 291 Imbalance
michael@0 292 ---------
michael@0 293 };
michael@0 294
michael@0 295 foreach my $call (sort num_alpha keys(%imbalance)) {
michael@0 296 print $call . " " . $imbalance{$call} . "\n";
michael@0 297 }
michael@0 298

mercurial