Annotation of embedaddon/curl/tests/memanalyze.pl, revision 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>