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 # Given a memwatcher logfile, group memory allocations by callstack. 7 # 8 # Usage: 9 # 10 # memprof.pl <logfile> 11 # 12 # logfile -- The memwatcher.logXXXX file to summarize. 13 # 14 # 15 # 16 # Sample output: 17 # 18 # 54,061,617 100.00% AllocationStack::AllocationStack 19 # 41,975,368 77.64% malloc 20 # 11,886,592 21.99% VirtualAlloc 21 # 7,168,000 13.26% v8::internal::OS::Allocate 22 # 7,168,000 13.26% v8::internal::MemoryAllocator::AllocateRawMemory 23 # 5,976,184 11.05% WebCore::V8Bridge::evaluate 24 # 5,767,168 10.67% v8::internal::MemoryAllocator::AllocatePages 25 # 5,451,776 10.08% WebCore::V8Proxy::initContextIfNeeded 26 # .... 27 # 28 # 29 # 30 # ******** 31 # Note: The output is currently sorted by decreasing size. 32 # ******** 33 # 34 35 sub process_raw($$) { 36 my $file = shift; 37 my $filter = shift; 38 39 my %leaks = (); 40 my %stackframes = (); 41 42 my $blamed = 0; 43 my $bytes = 0; 44 my $hits = 0; 45 open (LOGFILE, "$file") or die("could not open $file"); 46 while(<LOGFILE>) { 47 my $line = $_; 48 #print "$line"; 49 chomp($line); 50 if ($line =~ m/([0-9]*) bytes, ([0-9]*) allocs/) { 51 52 # If we didn't find any frames to account this to, log that. 53 if ($blamed == 0) { 54 $leaks{"UNACCOUNTED"} += $bytes; 55 } 56 57 #print "START\n"; 58 #print("stackframe " . $1 . ", " . $2 . "\n"); 59 $hits = $2; 60 $bytes = $1; 61 %stackframes = (); # we have a new frame, clear the list. 62 $blamed = 0; # we haven't blamed anyone yet 63 } 64 elsif ($line =~ m/Total Bytes:[ ]*([0-9]*)/) { 65 $total_bytes += $1; 66 } 67 elsif ($line =~ m/=============/) { 68 next; 69 } 70 elsif ($line =~ m/[ ]*([\-a-zA-Z_\\0-9\.]*) \(([0-9]*)\):[ ]*([<>_a-zA-Z_0-9:]*)/) { 71 # print("junk: " . $line . "\n"); 72 # print("file: $1\n"); 73 # print("line: $2\n"); 74 # print("function: $3\n"); 75 # 76 77 # blame the function 78 my $pig = $3; 79 # my $pig = $1; 80 81 # only add the memory if this function is not yet on our callstack 82 if (!exists $stackframes{$pig}) { 83 $leaks{$pig} += $bytes; 84 } 85 86 $stackframes{$pig}++; 87 $blamed++; 88 } 89 } 90 91 # now dump our hash table 92 my $sum = 0; 93 my @keys = sort { $leaks{$b} <=> $leaks{$a} }keys %leaks; 94 for ($i=0; $i<@keys; $i++) { 95 my $key = @keys[$i]; 96 printf "%11s\t%3.2f%%\t%s\n", comma_print($leaks{$key}), (100* $leaks{$key} / $total_bytes), $key; 97 $sum += $leaks{$key}; 98 } 99 printf("TOTAL: %s\n", comma_print($sum)); 100 } 101 102 # Insert commas into an integer after each three digits for printing. 103 sub comma_print { 104 my $num = "$_[0]"; 105 $num =~ s/(\d{1,3}?)(?=(\d{3})+$)/$1,/g; 106 return $num; 107 } 108 109 # ----- Main ------------------------------------------------ 110 111 # Get the command line argument 112 my $filename = shift; 113 my $filter = shift; 114 115 # Process the file. 116 process_raw($filename, $filter); 117