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
35sub 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.
103sub 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
112my $filename = shift;
113my $filter = shift;
114
115# Process the file.
116process_raw($filename, $filter);
117