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>