Whamcloud - gitweb
LU-17744 ldiskfs: mballoc stats fixes
[fs/lustre-release.git] / lustre / tests / leak_finder.pl
1 #!/usr/bin/perl -w
2
3 use IO::Handle;
4
5 STDOUT->autoflush(1);
6 STDERR->autoflush(1);
7
8 my ($line, $memory);
9 my $debug_line = 0;
10
11 my $alloced = 0;
12 my $leaked = 0;
13 my $freed = 0;
14 my $max = 0;
15 my $debug = 0;
16 my $summary = 0;
17 my $by_func = 0;
18
19 my @parsed;
20 my %cpu;
21 my $start_time = 0;
22
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";
31     exit
32 }
33
34 if (defined($ARGV[1]) and $ARGV[1] eq "--debug") {
35     $debug = 1;
36 }
37
38 if (defined($ARGV[1]) and $ARGV[1] eq "--summary") {
39     $summary = 1;
40     $by_func = 1;
41 }
42
43 if (defined($ARGV[1]) and ($ARGV[1] eq "--by-func" || $ARGV[1] eq "--by_func")) {
44     $by_func = 1;
45 }
46
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") {
52 #            chop $parsed[2];
53 #            $cpu{$parsed[2]} = 0;
54 #        } else {
55 #            if (!defined($cpu{$parsed[2]})) {
56 #                $cpu{$parsed[2]} = $parsed[3];
57 #            }
58 #        }
59 #    }
60 #}
61 #
62 #foreach $time (values %cpu) {
63 #    if ($start_time < $time) {
64 #        $start_time = $time;
65 #    }
66 #}
67 #
68 #print "Starting analysis since $start_time\n";
69
70 seek(INFILE, 0, 0);
71 while ($line = <INFILE>) {
72     $debug_line++;
73
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]+)/) {
80         $file = $2;
81         $lno  = $3;
82         $func = $4;
83         $type = $6;
84         $name = $9;
85         $size = $10;
86         $addr = $11;
87     } elsif ($line =~ m/^(.*)\((.*):(\d+):(.*)\(\)\) (slab-)(alloc(ed)?|free[d]?) '(.*)' at ([\da-f]+)/) {
88         $file = $2;
89         $lno  = $3;
90         $func = $4;
91         $type = $6;
92         $name = $8;
93         $size = 0;
94         $addr = $9;
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";
98         next;
99     } else {
100         # line not related to alloc/free, skip it silently
101         #print STDERR "Couldn't parse $line";
102         next;
103     }
104
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') {
108         next;
109     }
110
111     if ($debug) {
112         print $line;
113     }
114     if ($summary == 0) {
115         printf("%8s %6d bytes at %s called %s (%s:%s:%d)\n", $type, $size,
116                $addr, $name, $file, $func, $lno);
117     }
118
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};
124         }
125
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;
132
133         $alloced += $size;
134         if ($alloced > $max) {
135             $max = $alloced;
136         }
137     } else {
138         if (!defined($memory->{$addr})) {
139             if ($summary == 0) {
140                 print STDOUT "*** Free without alloc ($size bytes at $addr, $file:$func:$lno)\n";
141             }
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;
150
151             $freed -= $size;
152             next;
153         }
154         my ($oldname, $oldsize, $oldfile, $oldfunc, $oldlno) = $memory->{$addr};
155
156         if ($size == 0) {
157             $size = $memory->{$addr}->{size};
158         }
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";
162
163             $freed -= $size;
164             next;
165         }
166
167         delete $memory->{$addr};
168         $alloced -= $size;
169     }
170 }
171 close(INFILE);
172
173 my $aa;
174 my $bb;
175 my @sorted = sort {
176     if ($by_func) {
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}";
180         $aa cmp $bb;
181     } else {
182         # Sort leak output by allocation time
183         $memory->{$a}->{debug_line} <=> $memory->{$b}->{debug_line};
184     }
185 } keys(%{$memory});
186
187 $aa = "";
188 $bb = "";
189 my $key;
190 my $leak_count = 1;
191 my @records;
192 my $leak_size = 0;
193 my $leak_func;
194 my $i = 0;
195 foreach $key (@sorted) {
196     if ($summary) {
197         $aa = "$memory->{$key}->{func}:$memory->{$key}->{lno}:$memory->{$key}->{name}:$memory->{$key}->{size}";
198         if ($bb eq $aa) {
199             $leak_count++;
200         } elsif ($bb ne ""){
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;;
205             $bb = $aa;
206             $i++;
207             $leak_count = 1;
208         } else {
209             $bb = $aa;
210         }
211         $leak_func = "$memory->{$key}->{func}:$memory->{$key}->{lno}:$memory->{$key}->{name}";
212     } else {
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";
214     }
215     
216     $leak_size = $memory->{$key}->{size};
217     $leaked += $leak_size;
218 }
219
220 if ($summary) {
221     # print a summary report by total leak bytes in ASC order
222     my @sorted_records = sort {
223         $a->{total} <=> $b->{total};
224     } @records;
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});
228     }
229 }
230 print STDOUT "maximum_used: $max, total_alloc: $alloced, freed: $freed, leaked: $leaked\n";