memanalyze.pl 9.0 KB

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