Whamcloud - gitweb
LU-14736 utils: update leak-finder.pl for new format
[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 $total = 0;
12 my $max = 0;
13
14 my @parsed;
15 my %cpu;
16 my $start_time = 0;
17
18 if (!defined($ARGV[0])) {
19     print "No log file specified\n";
20     print "Usage: leak_finder.pl <debug_file> [--option]\n";
21     print "    --by_func show leak logs by function name in ascending order.\n";
22     print "    --summary implies --by_func, print a summary report by \n";
23     print "              the number of total leak bytes of each function \n";
24     print "              in ascending order in YAML format.\n";
25     exit
26 }
27
28 open(INFILE, $ARGV[0]);
29 while ($line = <INFILE>) {
30     if ($line =~ m/^(.*)\((.*):(\d+):(.*)\(\)\)/) {
31         @parsed = split(":", $1);
32         if (substr ($parsed[2], -1, 1) eq "F") {
33             chop $parsed[2];
34             $cpu{$parsed[2]} = 0;
35         } else {
36             if (!defined($cpu{$parsed[2]})) {
37                 $cpu{$parsed[2]} = $parsed[3];
38             }
39         }
40     }
41 }
42
43 foreach $time (values %cpu) {
44     if ($start_time < $time) {
45         $start_time = $time;
46     }
47 }
48
49 print "Starting analysis since $start_time\n";
50
51 seek(INFILE, 0, 0);
52 while ($line = <INFILE>) {
53     $debug_line++;
54     my ($file, $func, $lno, $name, $size, $addr, $type);
55     # message format here needs to match OBD_ALLOC_POST()/OBD_FREE_PRE()
56     # mask:subs:cpu:epoch second.usec:?:pid:?:(filename:line:function_name())
57     #    alloc-type 'var_name': size at memory_address.
58     if ($line =~ m/^(.*)\((.*):(\d+):(.*)\(\)\) (k[m]?|v[m]?|slab-|)(alloc(ed)?|free[d]?(_rcu)?) '(.*)': (\d+) at ([\da-f]+)/) {
59         @parsed = split(":", $1);
60         if ($parsed[3] <= $start_time) {
61                 next;
62         }
63
64         $file = $2;
65         $lno  = $3;
66         $func = $4;
67         $type = $6;
68         $name = $9;
69         $size = $10;
70         $addr = $11;
71
72         # we can't dump the log after portals has exited, so skip "leaks"
73         # from memory freed in the portals module unloading.
74         if ($func eq 'portals_handle_init') {
75             next;
76         }
77         printf("%8s %6d bytes at %s called %s (%s:%s:%d)\n", $type, $size,
78                $addr, $name, $file, $func, $lno);
79     } elsif ($line =~ m/(alloc(ed)?|free[d]?).*at [0-9a-f]*/) {
80         # alloc/free line that didn't match regexp, notify user of missed line
81         print STDERR "Couldn't parse line $debug_line, script needs to be fixed:\n$line";
82         next;
83     } else {
84         # line not related to alloc/free, skip it silently
85         #print STDERR "Couldn't parse $line";
86         next;
87     }
88
89     if (index($type, 'alloc') >= 0) {
90         if (defined($memory->{$addr})) {
91             print STDOUT "*** Two allocs with the same address ($size bytes at $addr, $file:$func:$lno)\n";
92             print STDOUT "    first malloc at $memory->{$addr}->{file}:$memory->{$addr}->{func}:$memory->{$addr}->{lno}, second at $file:$func:$lno\n";
93             next;
94         }
95
96         $memory->{$addr}->{name} = $name;
97         $memory->{$addr}->{size} = $size;
98         $memory->{$addr}->{file} = $file;
99         $memory->{$addr}->{func} = $func;
100         $memory->{$addr}->{lno} = $lno;
101         $memory->{$addr}->{debug_line} = $debug_line;
102
103         $total += $size;
104         if ($total > $max) {
105             $max = $total;
106         }
107     } else {
108         if (!defined($memory->{$addr})) {
109             print STDOUT "*** Free without malloc ($size bytes at $addr, $file:$func:$lno)\n";
110             next;
111         }
112         my ($oldname, $oldsize, $oldfile, $oldfunc, $oldlno) = $memory->{$addr};
113
114         if ($memory->{$addr}->{size} != $size) {
115             print STDOUT "*** Free different size ($memory->{$addr}->{size} alloced, $size freed).\n";
116             print STDOUT "    malloc at $memory->{$addr}->{file}:$memory->{$addr}->{func}:$memory->{$addr}->{lno}, free at $file:$func:$lno\n";
117             next;
118         }
119
120         delete $memory->{$addr};
121         $total -= $size;
122     }
123 }
124 close(INFILE);
125
126 my $aa;
127 my $bb;
128 my @sorted = sort {
129     if (defined($ARGV[1])) {
130         if ($ARGV[1] eq "--by_func" or $ARGV[1] eq "--summary") {
131             # Sort leak output by source code position
132             $aa = "$memory->{$a}->{func}:$memory->{$a}->{lno}:$memory->{$a}->{name}:$memory->{$a}->{size}";
133             $bb = "$memory->{$b}->{func}:$memory->{$b}->{lno}:$memory->{$b}->{name}:$memory->{$b}->{size}";
134             $aa cmp $bb;
135         }
136     } else {
137         # Sort leak output by allocation time
138         $memory->{$a}->{debug_line} <=> $memory->{$b}->{debug_line};
139     }
140 } keys(%{$memory});
141
142 $aa = "";
143 $bb = "";
144 my $key;
145 my $leak_count = 1;
146 my @records;
147 my $leak_size = 0;
148 my $leak_func;
149 my $i = 0;
150 foreach $key (@sorted) {
151     if (defined($ARGV[1]) and $ARGV[1] eq "--summary") {
152         $aa = "$memory->{$key}->{func}:$memory->{$key}->{lno}:$memory->{$key}->{name}:$memory->{$key}->{size}";
153         if ($bb eq $aa) {
154             $leak_count++;
155         } elsif ($bb ne ""){
156             $records[$i]->{func} = $leak_func;
157             $records[$i]->{size} = $leak_size;
158             $records[$i]->{count} = $leak_count;
159             $records[$i]->{total} = $leak_count * $leak_size;;
160             $bb = $aa;
161             $i++;
162             $leak_count = 1;
163         } else {
164             $bb = $aa;
165         }
166         $leak_func = "$memory->{$key}->{func}:$memory->{$key}->{lno}:$memory->{$key}->{name}";
167         $leak_size = $memory->{$key}->{size};
168     } else {
169         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";
170     }
171 }
172
173 if (defined($ARGV[1]) and $ARGV[1] eq "--summary") {
174     # print a summary report by total leak bytes in ASC order
175     my @sorted_records = sort {
176         $a->{total} <=> $b->{total};
177     } @records;
178     foreach $key (@sorted_records) {
179         printf("- { func: %-58s, alloc_bytes: %-8d, leak_count: %-6d, leak_bytes: %-8d },\n",
180                $key->{func}, $key->{size}, $key->{count}, $key->{total});
181     }
182 }
183 print STDOUT "maximum used: $max, amount leaked: $total\n";