Imported Upstream version 7.59.0
[platform/upstream/curl.git] / tests / memanalyze.pl
1 #!/usr/bin/env perl
2 #***************************************************************************
3 #                                  _   _ ____  _
4 #  Project                     ___| | | |  _ \| |
5 #                             / __| | | | |_) | |
6 #                            | (__| |_| |  _ <| |___
7 #                             \___|\___/|_| \_\_____|
8 #
9 # Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
10 #
11 # This software is licensed as described in the file COPYING, which
12 # you should have received as part of this distribution. The terms
13 # are also available at https://curl.haxx.se/docs/copyright.html.
14 #
15 # You may opt to use, copy, modify, merge, publish, distribute and/or sell
16 # copies of the Software, and permit persons to whom the Software is
17 # furnished to do so, under the terms of the COPYING file.
18 #
19 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 # KIND, either express or implied.
21 #
22 ###########################################################################
23 #
24 # Example input:
25 #
26 # MEM mprintf.c:1094 malloc(32) = e5718
27 # MEM mprintf.c:1103 realloc(e5718, 64) = e6118
28 # MEM sendf.c:232 free(f6520)
29
30 my $mallocs=0;
31 my $callocs=0;
32 my $reallocs=0;
33 my $strdups=0;
34 my $wcsdups=0;
35 my $showlimit;
36 my $sends=0;
37 my $recvs=0;
38 my $sockets=0;
39
40 while(1) {
41     if($ARGV[0] eq "-v") {
42         $verbose=1;
43         shift @ARGV;
44     }
45     elsif($ARGV[0] eq "-t") {
46         $trace=1;
47         shift @ARGV;
48     }
49     elsif($ARGV[0] eq "-l") {
50         # only show what alloc that caused a memlimit failure
51         $showlimit=1;
52         shift @ARGV;
53     }
54     else {
55         last;
56     }
57 }
58
59 my $maxmem;
60
61 sub newtotal {
62     my ($newtot)=@_;
63     # count a max here
64
65     if($newtot > $maxmem) {
66         $maxmem= $newtot;
67     }
68 }
69
70 my $file = $ARGV[0];
71
72 if(! -f $file) {
73     print "Usage: memanalyze.pl [options] <dump file>\n",
74     "Options:\n",
75     " -l  memlimit failure displayed\n",
76     " -v  Verbose\n",
77     " -t  Trace\n";
78     exit;
79 }
80
81 open(FILE, "<$file");
82
83 if($showlimit) {
84     while(<FILE>) {
85         if(/^LIMIT.*memlimit$/) {
86             print $_;
87             last;
88         }
89     }
90     close(FILE);
91     exit;
92 }
93
94
95 my $lnum=0;
96 while(<FILE>) {
97     chomp $_;
98     $line = $_;
99     $lnum++;
100     if($line =~ /^LIMIT ([^ ]*):(\d*) (.*)/) {
101         # new memory limit test prefix
102         my $i = $3;
103         my ($source, $linenum) = ($1, $2);
104         if($trace && ($i =~ /([^ ]*) reached memlimit/)) {
105             print "LIMIT: $1 returned error at $source:$linenum\n";
106         }
107     }
108     elsif($line =~ /^MEM ([^ ]*):(\d*) (.*)/) {
109         # generic match for the filename+linenumber
110         $source = $1;
111         $linenum = $2;
112         $function = $3;
113
114         if($function =~ /free\((\(nil\)|0x([0-9a-f]*))/) {
115             $addr = $2;
116             if($1 eq "(nil)") {
117                 ; # do nothing when free(NULL)
118             }
119             elsif(!exists $sizeataddr{$addr}) {
120                 print "FREE ERROR: No memory allocated: $line\n";
121             }
122             elsif(-1 == $sizeataddr{$addr}) {
123                 print "FREE ERROR: Memory freed twice: $line\n";
124                 print "FREE ERROR: Previously freed at: ".$getmem{$addr}."\n";
125             }
126             else {
127                 $totalmem -= $sizeataddr{$addr};
128                 if($trace) {
129                     print "FREE: malloc at ".$getmem{$addr}." is freed again at $source:$linenum\n";
130                     printf("FREE: %d bytes freed, left allocated: $totalmem bytes\n", $sizeataddr{$addr});
131                 }
132
133                 newtotal($totalmem);
134                 $frees++;
135
136                 $sizeataddr{$addr}=-1; # set -1 to mark as freed
137                 $getmem{$addr}="$source:$linenum";
138
139             }
140         }
141         elsif($function =~ /malloc\((\d*)\) = 0x([0-9a-f]*)/) {
142             $size = $1;
143             $addr = $2;
144
145             if($sizeataddr{$addr}>0) {
146                 # this means weeeeeirdo
147                 print "Mixed debug compile ($source:$linenum at line $lnum), rebuild curl now\n";
148                 print "We think $sizeataddr{$addr} bytes are already allocated at that memory address: $addr!\n";
149             }
150
151             $sizeataddr{$addr}=$size;
152             $totalmem += $size;
153
154             if($trace) {
155                 print "MALLOC: malloc($size) at $source:$linenum",
156                 " makes totally $totalmem bytes\n";
157             }
158
159             newtotal($totalmem);
160             $mallocs++;
161
162             $getmem{$addr}="$source:$linenum";
163         }
164         elsif($function =~ /calloc\((\d*),(\d*)\) = 0x([0-9a-f]*)/) {
165             $size = $1*$2;
166             $addr = $3;
167
168             $arg1 = $1;
169             $arg2 = $2;
170
171             if($sizeataddr{$addr}>0) {
172                 # this means weeeeeirdo
173                 print "Mixed debug compile, rebuild curl now\n";
174             }
175
176             $sizeataddr{$addr}=$size;
177             $totalmem += $size;
178
179             if($trace) {
180                 print "CALLOC: calloc($arg1,$arg2) at $source:$linenum",
181                 " makes totally $totalmem bytes\n";
182             }
183
184             newtotal($totalmem);
185             $callocs++;
186
187             $getmem{$addr}="$source:$linenum";
188         }
189         elsif($function =~ /realloc\((\(nil\)|0x([0-9a-f]*)), (\d*)\) = 0x([0-9a-f]*)/) {
190             my ($oldaddr, $newsize, $newaddr) = ($2, $3, $4);
191
192             $totalmem -= $sizeataddr{$oldaddr};
193             if($trace) {
194                 printf("REALLOC: %d less bytes and ", $sizeataddr{$oldaddr});
195             }
196             $sizeataddr{$oldaddr}=0;
197
198             $totalmem += $newsize;
199             $sizeataddr{$newaddr}=$newsize;
200
201             if($trace) {
202                 printf("%d more bytes ($source:$linenum)\n", $newsize);
203             }
204
205             newtotal($totalmem);
206             $reallocs++;
207
208             $getmem{$oldaddr}="";
209             $getmem{$newaddr}="$source:$linenum";
210         }
211         elsif($function =~ /strdup\(0x([0-9a-f]*)\) \((\d*)\) = 0x([0-9a-f]*)/) {
212             # strdup(a5b50) (8) = df7c0
213
214             $dup = $1;
215             $size = $2;
216             $addr = $3;
217             $getmem{$addr}="$source:$linenum";
218             $sizeataddr{$addr}=$size;
219
220             $totalmem += $size;
221
222             if($trace) {
223                 printf("STRDUP: $size bytes at %s, makes totally: %d bytes\n",
224                        $getmem{$addr}, $totalmem);
225             }
226
227             newtotal($totalmem);
228             $strdups++;
229         }
230         elsif($function =~ /wcsdup\(0x([0-9a-f]*)\) \((\d*)\) = 0x([0-9a-f]*)/) {
231             # wcsdup(a5b50) (8) = df7c0
232
233             $dup = $1;
234             $size = $2;
235             $addr = $3;
236             $getmem{$addr}="$source:$linenum";
237             $sizeataddr{$addr}=$size;
238
239             $totalmem += $size;
240
241             if($trace) {
242                 printf("WCSDUP: $size bytes at %s, makes totally: %d bytes\n",
243                        $getmem{$addr}, $totalmem);
244             }
245
246             newtotal($totalmem);
247             $wcsdups++;
248         }
249         else {
250             print "Not recognized input line: $function\n";
251         }
252     }
253     # FD url.c:1282 socket() = 5
254     elsif($_ =~ /^FD ([^ ]*):(\d*) (.*)/) {
255         # generic match for the filename+linenumber
256         $source = $1;
257         $linenum = $2;
258         $function = $3;
259
260         if($function =~ /socket\(\) = (\d*)/) {
261             $filedes{$1}=1;
262             $getfile{$1}="$source:$linenum";
263             $openfile++;
264             $sockets++; # number of socket() calls
265         }
266         elsif($function =~ /socketpair\(\) = (\d*) (\d*)/) {
267             $filedes{$1}=1;
268             $getfile{$1}="$source:$linenum";
269             $openfile++;
270             $filedes{$2}=1;
271             $getfile{$2}="$source:$linenum";
272             $openfile++;
273         }
274         elsif($function =~ /accept\(\) = (\d*)/) {
275             $filedes{$1}=1;
276             $getfile{$1}="$source:$linenum";
277             $openfile++;
278         }
279         elsif($function =~ /sclose\((\d*)\)/) {
280             if($filedes{$1} != 1) {
281                 print "Close without open: $line\n";
282             }
283             else {
284                 $filedes{$1}=0; # closed now
285                 $openfile--;
286             }
287         }
288     }
289     # FILE url.c:1282 fopen("blabla") = 0x5ddd
290     elsif($_ =~ /^FILE ([^ ]*):(\d*) (.*)/) {
291         # generic match for the filename+linenumber
292         $source = $1;
293         $linenum = $2;
294         $function = $3;
295
296         if($function =~ /f[d]*open\(\"(.*)\",\"([^\"]*)\"\) = (\(nil\)|0x([0-9a-f]*))/) {
297             if($3 eq "(nil)") {
298                 ;
299             }
300             else {
301                 $fopen{$4}=1;
302                 $fopenfile{$4}="$source:$linenum";
303                 $fopens++;
304             }
305         }
306         # fclose(0x1026c8)
307         elsif($function =~ /fclose\(0x([0-9a-f]*)\)/) {
308             if(!$fopen{$1}) {
309                 print "fclose() without fopen(): $line\n";
310             }
311             else {
312                 $fopen{$1}=0;
313                 $fopens--;
314             }
315         }
316     }
317     # GETNAME url.c:1901 getnameinfo()
318     elsif($_ =~ /^GETNAME ([^ ]*):(\d*) (.*)/) {
319         # not much to do
320     }
321     # SEND url.c:1901 send(83) = 83
322     elsif($_ =~ /^SEND ([^ ]*):(\d*) (.*)/) {
323         $sends++;
324     }
325     # RECV url.c:1901 recv(102400) = 256
326     elsif($_ =~ /^RECV ([^ ]*):(\d*) (.*)/) {
327         $recvs++;
328     }
329
330     # ADDR url.c:1282 getaddrinfo() = 0x5ddd
331     elsif($_ =~ /^ADDR ([^ ]*):(\d*) (.*)/) {
332         # generic match for the filename+linenumber
333         $source = $1;
334         $linenum = $2;
335         $function = $3;
336
337         if($function =~ /getaddrinfo\(\) = (\(nil\)|0x([0-9a-f]*))/) {
338             my $add = $2;
339             if($add eq "(nil)") {
340                 ;
341             }
342             else {
343                 $addrinfo{$add}=1;
344                 $addrinfofile{$add}="$source:$linenum";
345                 $addrinfos++;
346             }
347             if($trace) {
348                 printf("GETADDRINFO ($source:$linenum)\n");
349             }
350         }
351         # fclose(0x1026c8)
352         elsif($function =~ /freeaddrinfo\(0x([0-9a-f]*)\)/) {
353             if(!$addrinfo{$1}) {
354                 print "freeaddrinfo() without getaddrinfo(): $line\n";
355             }
356             else {
357                 $addrinfo{$1}=0;
358                 $addrinfos--;
359             }
360             if($trace) {
361                 printf("FREEADDRINFO ($source:$linenum)\n");
362             }
363         }
364
365     }
366     else {
367         print "Not recognized prefix line: $line\n";
368     }
369 }
370 close(FILE);
371
372 if($totalmem) {
373     print "Leak detected: memory still allocated: $totalmem bytes\n";
374
375     for(keys %sizeataddr) {
376         $addr = $_;
377         $size = $sizeataddr{$addr};
378         if($size > 0) {
379             print "At $addr, there's $size bytes.\n";
380             print " allocated by ".$getmem{$addr}."\n";
381         }
382     }
383 }
384
385 if($openfile) {
386     for(keys %filedes) {
387         if($filedes{$_} == 1) {
388             print "Open file descriptor created at ".$getfile{$_}."\n";
389         }
390     }
391 }
392
393 if($fopens) {
394     print "Open FILE handles left at:\n";
395     for(keys %fopen) {
396         if($fopen{$_} == 1) {
397             print "fopen() called at ".$fopenfile{$_}."\n";
398         }
399     }
400 }
401
402 if($addrinfos) {
403     print "IPv6-style name resolve data left at:\n";
404     for(keys %addrinfofile) {
405         if($addrinfo{$_} == 1) {
406             print "getaddrinfo() called at ".$addrinfofile{$_}."\n";
407         }
408     }
409 }
410
411 if($verbose) {
412     print "Mallocs: $mallocs\n",
413         "Reallocs: $reallocs\n",
414         "Callocs: $callocs\n",
415         "Strdups:  $strdups\n",
416         "Wcsdups:  $wcsdups\n",
417         "Frees: $frees\n",
418         "Sends: $sends\n",
419         "Recvs: $recvs\n",
420         "Sockets: $sockets\n",
421         "Allocations: ".($mallocs + $callocs + $reallocs + $strdups + $wcsdups)."\n",
422         "Operations: ".($mallocs + $callocs + $reallocs + $strdups + $wcsdups + $sends + $recvs + $sockets)."\n";
423
424     print "Maximum allocated: $maxmem\n";
425 }