moved memanalyze.pl into the tests dir
[platform/upstream/curl.git] / tests / memanalyze.pl
1 #!/usr/bin/perl
2 #
3 # Example input:
4 #
5 # MEM mprintf.c:1094 malloc(32) = e5718
6 # MEM mprintf.c:1103 realloc(e5718, 64) = e6118
7 # MEM sendf.c:232 free(f6520)
8
9 do {
10     if($ARGV[0] eq "-v") {
11         $verbose=1;
12     }
13     elsif($ARGV[0] eq "-t") {
14         $trace=1;
15     }
16 } while (shift @ARGV);
17
18 my $maxmem;
19
20 sub newtotal {
21     my ($newtot)=@_;
22     # count a max here
23
24     if($newtot > $maxmem) {
25         $maxmem= $newtot;
26     }
27 }
28
29 while(<STDIN>) {
30     chomp $_;
31     $line = $_;
32
33     if($line =~ /^MEM ([^:]*):(\d*) (.*)/) {
34         # generic match for the filename+linenumber
35         $source = $1;
36         $linenum = $2;
37         $function = $3;
38
39         if($function =~ /free\(0x([0-9a-f]*)/) {
40             $addr = $1;
41             if($sizeataddr{$addr} == 0) {
42                 print "FREE ERROR: No memory allocated: $line\n";
43             }
44             elsif(-1 == $sizeataddr{$addr}) {
45                 print "FREE ERROR: Memory freed twice: $line\n";
46                 print "FREE ERROR: Previously freed at: ".$getmem{$addr}."\n";
47             }
48             else {
49                 $totalmem -= $sizeataddr{$addr};
50                 if($trace) {
51                     print "FREE: malloc at ".$getmem{$addr}." is freed again at $source:$linenum\n";
52                     printf("FREE: %d bytes freed, left allocated: $totalmem bytes\n", $sizeataddr{$addr});
53                 }
54
55                 newtotal($totalmem);
56                 $frees++;
57
58                 $sizeataddr{$addr}=-1; # set -1 to mark as freed
59                 $getmem{$addr}="$source:$linenum";
60
61             }
62         }
63         elsif($function =~ /malloc\((\d*)\) = 0x([0-9a-f]*)/) {
64             $size = $1;
65             $addr = $2;
66
67             if($sizeataddr{$addr}>0) {
68                 # this means weeeeeirdo
69                 print "Fucked up debug compile, rebuild curl now\n";
70             }
71
72             $sizeataddr{$addr}=$size;
73             $totalmem += $size;
74
75             if($trace) {
76                 print "MALLOC: malloc($size) at $source:$linenum",
77                 " makes totally $totalmem bytes\n";
78             }
79
80             newtotal($totalmem);
81             $mallocs++;
82
83             $getmem{$addr}="$source:$linenum";
84         }
85         elsif($function =~ /realloc\(0x([0-9a-f]*), (\d*)\) = 0x([0-9a-f]*)/) {
86             $oldaddr = $1;
87             $newsize = $2;
88             $newaddr = $3;
89
90             $totalmem -= $sizeataddr{$oldaddr};
91             if($trace) {
92                 printf("REALLOC: %d less bytes and ", $sizeataddr{$oldaddr});
93             }
94             $sizeataddr{$oldaddr}=0;
95
96             $totalmem += $newsize;
97             $sizeataddr{$newaddr}=$newsize;
98
99             if($trace) {
100                 printf("%d more bytes ($source:$linenum)\n", $newsize);
101             }
102
103             newtotal($totalmem);
104             $reallocs++;
105             
106             $getmem{$oldaddr}="";
107             $getmem{$newaddr}="$source:$linenum";
108         }
109         elsif($function =~ /strdup\(0x([0-9a-f]*)\) \((\d*)\) = 0x([0-9a-f]*)/) {
110             # strdup(a5b50) (8) = df7c0
111
112             $dup = $1;
113             $size = $2;
114             $addr = $3;
115             $getmem{$addr}="$source:$linenum";
116             $sizeataddr{$addr}=$size;
117
118             $totalmem += $size;
119
120             if($trace) {
121                 printf("STRDUP: $size bytes at %s, makes totally: %d bytes\n", 
122                        $getmem{$addr}, $totalmem);
123             }
124
125             newtotal($totalmem);
126             $strdups++;
127         }
128         else {
129             print "Not recognized input line: $function\n";
130         }        
131     }
132     # FD url.c:1282 socket() = 5
133     elsif($_ =~ /^FD ([^:]*):(\d*) (.*)/) {
134         # generic match for the filename+linenumber
135         $source = $1;
136         $linenum = $2;
137         $function = $3;
138
139         if($function =~ /socket\(\) = (\d*)/) {
140             $filedes{$1}=1;
141             $getfile{$1}="$source:$linenum";
142             $openfile++;
143         }
144         elsif($function =~ /accept\(\) = (\d*)/) {
145             $filedes{$1}=1;
146             $getfile{$1}="$source:$linenum";
147             $openfile++;
148         }
149         elsif($function =~ /sclose\((\d*)\)/) {
150             if($filedes{$1} != 1) {
151                 print "Close without open: $line\n";
152             }
153             else {
154                 $filedes{$1}=0; # closed now
155                 $openfile--;
156             }
157         }
158     }
159     # FILE url.c:1282 fopen("blabla") = 0x5ddd
160     elsif($_ =~ /^FILE ([^:]*):(\d*) (.*)/) {
161         # generic match for the filename+linenumber
162         $source = $1;
163         $linenum = $2;
164         $function = $3;
165
166         if($function =~ /fopen\(\"([^\"]*)\"\) = (\(nil\)|0x([0-9a-f]*))/) {
167             if($2 eq "(nil)") {
168                 ;
169             }
170             else {
171                 $fopen{$3}=1;
172                 $fopenfile{$3}="$source:$linenum";
173                 $fopens++;
174             }
175         }
176         # fclose(0x1026c8)
177         elsif($function =~ /fclose\(0x([0-9a-f]*)\)/) {
178             if(!$fopen{$1}) {
179                 print "fclose() without fopen(): $line\n";
180             }
181             else {
182                 $fopen{$1}=0;
183                 $fopens--;
184             }
185         }
186     }
187     # ADDR url.c:1282 getaddrinfo() = 0x5ddd
188     elsif($_ =~ /^ADDR ([^:]*):(\d*) (.*)/) {
189         # generic match for the filename+linenumber
190         $source = $1;
191         $linenum = $2;
192         $function = $3;
193
194         if($function =~ /getaddrinfo\(\) = (\(nil\)|0x([0-9a-f]*))/) {
195             my $add = $2;
196             if($add eq "(nil)") {
197                 ;
198             }
199             else {
200                 $addrinfo{$add}=1;
201                 $addrinfofile{$add}="$source:$linenum";
202                 $addrinfos++;
203             }
204         }
205         # fclose(0x1026c8)
206         elsif($function =~ /freeaddrinfo\(0x([0-9a-f]*)\)/) {
207             if(!$addrinfo{$1}) {
208                 print "freeaddrinfo() without getaddrinfo(): $line\n";
209             }
210             else {
211                 $addrinfo{$1}=0;
212                 $addrinfos--;
213             }
214         }
215
216         
217     }
218     else {
219         print "Not recognized prefix line: $line\n";
220     }
221 }
222
223 if($totalmem) {
224     print "Leak detected: memory still allocated: $totalmem bytes\n";
225
226     for(keys %sizeataddr) {
227         $addr = $_;
228         $size = $sizeataddr{$addr};
229         if($size > 0) {
230             print "At $addr, there's $size bytes.\n";
231             print " allocated by ".$getmem{$addr}."\n";
232         }
233     }
234 }
235
236 if($openfile) {
237     for(keys %filedes) {
238         if($filedes{$_} == 1) {
239             print "Open file descriptor created at ".$getfile{$_}."\n";
240         }
241     }
242 }
243
244 if($fopens) {
245     print "Open FILE handles left at:\n";
246     for(keys %fopen) {
247         if($fopen{$_} == 1) {
248             print "fopen() called at ".$fopenfile{$_}."\n";
249         }
250     }
251 }
252
253 if($addrinfos) {
254     print "IPv6-style name resolve data left at:\n";
255     for(keys %addrinfofile) {
256         if($addrinfo{$_} == 1) {
257             print "getaddrinfo() called at ".$addrinfofile{$_}."\n";
258         }
259     }
260 }
261
262 if($verbose) {
263     print "Mallocs: $mallocs\n",
264     "Reallocs: $reallocs\n",
265     "Strdups:  $strdups\n",
266     "Frees: $frees\n";
267
268     print "Maximum allocated: $maxmem\n";
269 }