memanalyze.pl 9.2 KB

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