23 if (!defined($ARGV[0])) {
24 print "No log file specified\n";
25 print "Usage: leak_finder.pl <debug_file> [--option]\n";
26 print " --by-func show leak logs by function name in ascending order.\n";
27 print " --debug print more verbose debugging information.\n";
28 print " --summary implies --by-func, print a summary report by \n";
29 print " the number of total leak bytes of each function \n";
30 print " in ascending order in YAML format.\n";
34 if (defined($ARGV[1]) and $ARGV[1] eq "--debug") {
38 if (defined($ARGV[1]) and $ARGV[1] eq "--summary") {
43 if (defined($ARGV[1]) and ($ARGV[1] eq "--by-func" || $ARGV[1] eq "--by_func")) {
47 open(INFILE, $ARGV[0]);
48 #while ($line = <INFILE>) {
49 # if ($line =~ m/^(.*)\((.*):(\d+):(.*)\(\)\)/) {
50 # @parsed = split(":", $1);
51 # if (substr ($parsed[2], -1, 1) eq "F") {
53 # $cpu{$parsed[2]} = 0;
55 # if (!defined($cpu{$parsed[2]})) {
56 # $cpu{$parsed[2]} = $parsed[3];
62 #foreach $time (values %cpu) {
63 # if ($start_time < $time) {
64 # $start_time = $time;
68 #print "Starting analysis since $start_time\n";
71 while ($line = <INFILE>) {
74 my ($file, $func, $lno, $name, $size, $addr, $type);
75 # message format here needs to match OBD_ALLOC_POST()/OBD_FREE_PRE()
76 # mask:subs:cpu:epoch second.usec:?:pid:?:(filename:line:function_name())
77 # alloc-type 'var_name': size at memory_address.
78 if ($line =~ m/^(.*)\((.*):(\d+):(.*)\(\)\) (k[m]?|v[m]?|slab-|)(alloc(ed)?|free[d]?(_rcu)?) '(.*)': (\d+) at ([\da-f]+)/ ||
79 $line =~ m/^(.*)\((.*):(\d+):(.*)\(\)\) (k[m]?|v[m]?|slab-|)(alloc(ed)?|free[d]?(_rcu)?) '(.*)' of size (\d+) at ([\da-f]+)/) {
87 } elsif ($line =~ m/^(.*)\((.*):(\d+):(.*)\(\)\) (slab-)(alloc(ed)?|free[d]?) '(.*)' at ([\da-f]+)/) {
95 } elsif ($line =~ m/([ -]alloc(ed)? |[ -]free[d]? ).*at [0-9a-f]*/) {
96 # alloc/free line that didn't match regexp, notify user of missed line
97 print STDERR "Couldn't parse line $debug_line, script needs to be fixed:\n$line";
100 # line not related to alloc/free, skip it silently
101 #print STDERR "Couldn't parse $line";
105 # we can't dump the log after portals has exited, so skip "leaks"
106 # from memory freed in the portals module unloading.
107 if ($func =~ 'portals_handle_init') {
115 printf("%8s %6d bytes at %s called %s (%s:%s:%d)\n", $type, $size,
116 $addr, $name, $file, $func, $lno);
119 if (index($type, 'alloc') >= 0) {
120 if (defined($memory->{$addr})) {
121 print STDOUT "*** Two allocs with the same address $addr\n";
122 print STDOUT " first malloc $memory->{$addr}->{size} bytes at $memory->{$addr}->{file}:$memory->{$addr}->{func}:$memory->{$addr}->{lno}, second $size bytes at $file:$func:$lno\n";
123 $memory->{$addr . "_a"} = $memory->{$addr};
126 $memory->{$addr}->{name} = $name;
127 $memory->{$addr}->{size} = $size;
128 $memory->{$addr}->{file} = $file;
129 $memory->{$addr}->{func} = $func;
130 $memory->{$addr}->{lno} = $lno;
131 $memory->{$addr}->{debug_line} = $debug_line;
134 if ($alloced > $max) {
138 if (!defined($memory->{$addr})) {
140 print STDOUT "*** Free without alloc ($size bytes at $addr, $file:$func:$lno)\n";
142 # offset addr to avoid alloc collision, shouldn't be multiple frees
143 $addr = $addr . "_f";
144 $memory->{$addr}->{name} = $name;
145 $memory->{$addr}->{size} = -$size;
146 $memory->{$addr}->{file} = $file;
147 $memory->{$addr}->{func} = $func;
148 $memory->{$addr}->{lno} = $lno;
149 $memory->{$addr}->{debug_line} = $debug_line;
154 my ($oldname, $oldsize, $oldfile, $oldfunc, $oldlno) = $memory->{$addr};
157 $size = $memory->{$addr}->{size};
159 if ($memory->{$addr}->{size} != $size) {
160 print STDOUT "*** Free different size ($memory->{$addr}->{size} alloced, $size freed at $addr).\n";
161 print STDOUT " malloc at $memory->{$addr}->{file}:$memory->{$addr}->{func}:$memory->{$addr}->{lno}, free at $file:$func:$lno\n";
167 delete $memory->{$addr};
177 # Sort leak output by source code position
178 $aa = "$memory->{$a}->{func}:$memory->{$a}->{lno}:$memory->{$a}->{name}:$memory->{$a}->{size}";
179 $bb = "$memory->{$b}->{func}:$memory->{$b}->{lno}:$memory->{$b}->{name}:$memory->{$b}->{size}";
182 # Sort leak output by allocation time
183 $memory->{$a}->{debug_line} <=> $memory->{$b}->{debug_line};
195 foreach $key (@sorted) {
197 $aa = "$memory->{$key}->{func}:$memory->{$key}->{lno}:$memory->{$key}->{name}:$memory->{$key}->{size}";
201 $records[$i]->{func} = $leak_func;
202 $records[$i]->{size} = $leak_size;
203 $records[$i]->{count} = $leak_count;
204 $records[$i]->{total} = $leak_count * $leak_size;;
211 $leak_func = "$memory->{$key}->{func}:$memory->{$key}->{lno}:$memory->{$key}->{name}";
213 print STDOUT "*** Leak: $memory->{$key}->{size} bytes allocated at $key ($memory->{$key}->{file}:$memory->{$key}->{func}:$memory->{$key}->{lno}:$memory->{$key}->{name}, debug file line $memory->{$key}->{debug_line})\n";
216 $leak_size = $memory->{$key}->{size};
217 $leaked += $leak_size;
221 # print a summary report by total leak bytes in ASC order
222 my @sorted_records = sort {
223 $a->{total} <=> $b->{total};
225 foreach $key (@sorted_records) {
226 printf("- { func: \"%-48s\", alloc_bytes: %-6d, leak_count: %-6d, leak_bytes: %-8d }\n",
227 $key->{func}, $key->{size}, $key->{count}, $key->{total});
230 print STDOUT "maximum_used: $max, total_alloc: $alloced, freed: $freed, leaked: $leaked\n";