Annotation of embedaddon/curl/tests/memanalyze.pl, revision 1.1.1.1
1.1 misho 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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>