Home | History | Annotate | Download | only in scripts
      1 #!/usr/bin/perl
      2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 #
      6 # Blame callstacks for each memory allocation.
      7 # Similar to memprof.pl, will also try to filter out unuseful stacks.
      8 # TODO: better describe how these tools differ.
      9 #
     10 # Usage:
     11 #
     12 #   memtrace.pl <logfile>
     13 #
     14 #      logfile -- The memwatcher.logXXXX file to summarize.
     15 #
     16 #
     17 #
     18 # Sample output:
     19 #
     20 # 41,975,368    77.64%      f:\sp\vctools\crt_bld\self_x86\crt\src\malloc.c (163): malloc
     21 #  2,097,152    3.88%       c:\src\chrome1\src\webkit\pending\frameloader.cpp (3300): WebCore::FrameLoader::committedLoad
     22 #  1,572,864    2.91%       c:\src\chrome1\src\webkit\port\bridge\v8bridge.cpp (214): WebCore::V8Bridge::evaluate
     23 #  1,572,864    2.91%       c:\src\chrome1\src\webkit\glue\webframeloaderclient_impl.cc (1071): WebFrameLoaderClient::committedLoad
     24 #  1,572,864    2.91%       c:\src\chrome1\src\v8\src\ast.h (1181): v8::internal::Visitor::Visit
     25 #
     26 #
     27 #
     28 
     29 
     30 sub process_raw($) {
     31   my $file = shift;
     32 
     33   my %leaks = ();
     34 
     35   my $location_bytes = 0;
     36   my $location_hits = 0;
     37   my $location_blame = "";
     38   my $location_last = "";
     39   my $contains_load_lib = 0;
     40   my $total_bytes = 0;
     41   open (LOGFILE, "$file") or die("could not open $file");
     42   while(<LOGFILE>) {
     43     my $line = $_;
     44 #print "$line";
     45     chomp($line);
     46     if ($line =~ m/([0-9]*) bytes, ([0-9]*) allocs/) {
     47 
     48 #print "START\n";
     49       # Dump "prior" frame here
     50       if ($location_bytes > 0) {
     51 #print("GOTLEAK: $location_bytes ($location_hits) $location_blame\n");
     52         if ($location_blame eq "") {
     53           $location_blame = $location_last;
     54         }
     55         if (!$contains_load_lib) {
     56           $leaks{$location_blame} += $location_bytes;
     57         }
     58         $location_bytes = 0;
     59         $location_blame = "";
     60         $contains_load_lib = 0;
     61       }
     62 
     63       #print("stackframe " . $1 . ", " . $2 . "\n");
     64       $location_hits = $2;
     65       $location_bytes = $1;
     66     }
     67     elsif ($line =~ m/Total Bytes:[ ]*([0-9]*)/) {
     68       $total_bytes += $1;
     69     }
     70     elsif ($line =~ m/LoadLibrary/) {
     71       # skip these, they contain false positives.
     72       $contains_load_lib = 1;
     73       next;
     74     }
     75     elsif ($line =~ m/=============/) {
     76       next;
     77     }
     78     elsif ($line =~ m/Untracking untracked/) {
     79       next;
     80     }
     81     elsif ($line =~ m/[ ]*([a-z]:\\[a-z]*\\[a-zA-Z_\\0-9\.]*) /) {
     82       my $filename = $1;
     83       if ($filename =~ m/memory_watcher/) {
     84         next;
     85       }
     86       if ($filename =~ m/skmemory_stdlib.cpp/) {
     87         next;
     88       }
     89       if ($filename =~ m/stringimpl.cpp/) {
     90         next;
     91       }
     92       if ($filename =~ m/stringbuffer.h/) {
     93         next;
     94       }
     95       if ($filename =~ m/fastmalloc.h/) {
     96         next;
     97       }
     98       if ($filename =~ m/microsoft visual studio 8/) {
     99         next;
    100       }
    101       if ($filename =~ m/platformsdk_win2008_6_1/) {
    102         next;
    103       }
    104       if ($location_blame eq "") {
    105         # use this to blame the line
    106         $location_blame = $line;
    107   
    108         # use this to blame the file.
    109       #  $location_blame = $filename;
    110 
    111 #print("blaming $location_blame\n");
    112       }
    113     } else {
    114 #      print("junk: " . $line . "\n");
    115       if (! ($line =~ m/GetModuleFileNameA/) ) {
    116         $location_last = $line;
    117       }
    118     }
    119   }
    120 
    121   # now dump our hash table
    122   my $sum = 0;
    123   my @keys = sort { $leaks{$b} <=> $leaks{$a}  }keys %leaks;
    124   for ($i=0; $i<@keys; $i++) {
    125     my $key = @keys[$i];
    126     if (0 == $total_bytes) { $total_bytes = 1; }
    127     printf "%11s\t%3.2f%%\t%s\n", comma_print($leaks{$key}), (100* $leaks{$key} / $total_bytes), $key;
    128     $sum += $leaks{$key};
    129   }
    130   printf("TOTAL: %s\n", comma_print($sum));
    131 }
    132 
    133 # Insert commas into an integer after each three digits for printing.
    134 sub comma_print {
    135     my $num = "$_[0]";
    136     $num =~ s/(\d{1,3}?)(?=(\d{3})+$)/$1,/g;
    137     return $num;
    138 }
    139 
    140 # ----- Main ------------------------------------------------
    141 
    142 # Get the command line argument
    143 my $filename = shift;
    144 
    145 # Process the file.
    146 process_raw($filename);
    147