Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

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