quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

memanalyze.pl (12245B)


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