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