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