File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / curl / tests / memanalyze.pl
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 3 10:01:16 2020 UTC (5 years ago) by misho
Branches: curl, MAIN
CVS tags: v7_70_0p4, HEAD
curl

    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>