bloatdiff.pl 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. #!/usr/bin/perl -w
  2. # This Source Code Form is subject to the terms of the Mozilla Public
  3. # License, v. 2.0. If a copy of the MPL was not distributed with this
  4. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
  5. ################################################################################
  6. sub usage() {
  7. print <<EOUSAGE;
  8. # bloatdiff.pl - munges the output from
  9. # XPCOM_MEM_BLOAT_LOG=1
  10. # firefox-bin -P default resource:///res/bloatcycle.html
  11. # so that it does some summary and stats stuff.
  12. #
  13. # To show leak test results for a set of changes, do something like this:
  14. #
  15. # XPCOM_MEM_BLOAT_LOG=1
  16. # firefox-bin -P default resource:///res/bloatcycle.html > a.out
  17. # **make change**
  18. # firefox-bin -P default resource:///res/bloatcycle.html > b.out
  19. # bloatdiff.pl a.out b.out
  20. EOUSAGE
  21. }
  22. $OLDFILE = $ARGV[0];
  23. $NEWFILE = $ARGV[1];
  24. #$LABEL = $ARGV[2];
  25. if (!$OLDFILE or
  26. ! -e $OLDFILE or
  27. -z $OLDFILE) {
  28. print "\nError: Previous log file not specified, does not exist, or is empty.\n\n";
  29. &usage();
  30. exit 1;
  31. }
  32. if (!$NEWFILE or
  33. ! -e $NEWFILE or
  34. -z $NEWFILE) {
  35. print "\nError: Current log file not specified, does not exist, or is empty.\n\n";
  36. &usage();
  37. exit 1;
  38. }
  39. sub processFile {
  40. my ($filename, $map, $prevMap) = @_;
  41. open(FH, $filename);
  42. while (<FH>) {
  43. if (m{
  44. ^\s*(\d+)\s # Line number
  45. ([\w:]+)\s+ # Name
  46. (-?\d+)\s+ # Size
  47. (-?\d+)\s+ # Leaked
  48. (-?\d+)\s+ # Objects Total
  49. (-?\d+)\s+ # Objects Rem
  50. \(\s*(-?[\d.]+)\s+ # Objects Mean
  51. \+/-\s+
  52. ([\w.]+)\)\s+ # Objects StdDev
  53. (-?\d+)\s+ # Reference Total
  54. (-?\d+)\s+ # Reference Rem
  55. \(\s*(-?[\d.]+)\s+ # Reference Mean
  56. \+/-\s+
  57. ([\w\.]+)\) # Reference StdDev
  58. }x) {
  59. $$map{$2} = { name => $2,
  60. size => $3,
  61. leaked => $4,
  62. objTotal => $5,
  63. objRem => $6,
  64. objMean => $7,
  65. objStdDev => $8,
  66. refTotal => $9,
  67. refRem => $10,
  68. refMean => $11,
  69. refStdDev => $12,
  70. bloat => $3 * $5 # size * objTotal
  71. };
  72. } else {
  73. # print "failed to parse: $_\n";
  74. }
  75. }
  76. close(FH);
  77. }
  78. %oldMap = ();
  79. processFile($OLDFILE, \%oldMap);
  80. %newMap = ();
  81. processFile($NEWFILE, \%newMap);
  82. ################################################################################
  83. $inf = 9999999.99;
  84. sub getLeaksDelta {
  85. my ($key) = @_;
  86. my $oldLeaks = $oldMap{$key}{leaked} || 0;
  87. my $newLeaks = $newMap{$key}{leaked};
  88. my $percentLeaks = 0;
  89. if ($oldLeaks == 0) {
  90. if ($newLeaks != 0) {
  91. # there weren't any leaks before, but now there are!
  92. $percentLeaks = $inf;
  93. }
  94. }
  95. else {
  96. $percentLeaks = ($newLeaks - $oldLeaks) / $oldLeaks * 100;
  97. }
  98. # else we had no record of this class before
  99. return ($newLeaks - $oldLeaks, $percentLeaks);
  100. }
  101. ################################################################################
  102. sub getBloatDelta {
  103. my ($key) = @_;
  104. my $newBloat = $newMap{$key}{bloat};
  105. my $percentBloat = 0;
  106. my $oldSize = $oldMap{$key}{size} || 0;
  107. my $oldTotal = $oldMap{$key}{objTotal} || 0;
  108. my $oldBloat = $oldTotal * $oldSize;
  109. if ($oldBloat == 0) {
  110. if ($newBloat != 0) {
  111. # this class wasn't used before, but now it is
  112. $percentBloat = $inf;
  113. }
  114. }
  115. else {
  116. $percentBloat = ($newBloat - $oldBloat) / $oldBloat * 100;
  117. }
  118. # else we had no record of this class before
  119. return ($newBloat - $oldBloat, $percentBloat);
  120. }
  121. ################################################################################
  122. foreach $key (keys %newMap) {
  123. my ($newLeaks, $percentLeaks) = getLeaksDelta($key);
  124. my ($newBloat, $percentBloat) = getBloatDelta($key);
  125. $newMap{$key}{leakDelta} = $newLeaks;
  126. $newMap{$key}{leakPercent} = $percentLeaks;
  127. $newMap{$key}{bloatDelta} = $newBloat;
  128. $newMap{$key}{bloatPercent} = $percentBloat;
  129. }
  130. ################################################################################
  131. # Print a value of bytes out in a reasonable
  132. # KB, MB, or GB form. Copied from build-seamonkey-util.pl, sorry. -mcafee
  133. sub PrintSize($) {
  134. # print a number with 3 significant figures
  135. sub PrintNum($) {
  136. my ($num) = @_;
  137. my $rv;
  138. if ($num < 1) {
  139. $rv = sprintf "%.3f", ($num);
  140. } elsif ($num < 10) {
  141. $rv = sprintf "%.2f", ($num);
  142. } elsif ($num < 100) {
  143. $rv = sprintf "%.1f", ($num);
  144. } else {
  145. $rv = sprintf "%d", ($num);
  146. }
  147. }
  148. my ($size) = @_;
  149. my $rv;
  150. if ($size > 1000000000) {
  151. $rv = PrintNum($size / 1000000000.0) . "G";
  152. } elsif ($size > 1000000) {
  153. $rv = PrintNum($size / 1000000.0) . "M";
  154. } elsif ($size > 1000) {
  155. $rv = PrintNum($size / 1000.0) . "K";
  156. } else {
  157. $rv = PrintNum($size);
  158. }
  159. }
  160. print "Bloat/Leak Delta Report\n";
  161. print "--------------------------------------------------------------------------------------\n";
  162. print "Current file: $NEWFILE\n";
  163. print "Previous file: $OLDFILE\n";
  164. print "----------------------------------------------leaks------leaks%------bloat------bloat%\n";
  165. if (! $newMap{"TOTAL"} or
  166. ! $newMap{"TOTAL"}{bloat}) {
  167. # It's OK if leaked or leakPercent are 0 (in fact, that would be good).
  168. # If bloatPercent is zero, it is also OK, because we may have just had
  169. # two runs exactly the same or with no new bloat.
  170. print "\nError: unable to calculate bloat/leak data.\n";
  171. print "There is no data present.\n\n";
  172. print "HINT - Did your test run complete successfully?\n";
  173. print "HINT - Are you pointing at the right log files?\n\n";
  174. &usage();
  175. exit 1;
  176. }
  177. printf "%-40s %10s %10.2f%% %10s %10.2f%%\n",
  178. ("TOTAL",
  179. $newMap{"TOTAL"}{leaked}, $newMap{"TOTAL"}{leakPercent},
  180. $newMap{"TOTAL"}{bloat}, $newMap{"TOTAL"}{bloatPercent});
  181. ################################################################################
  182. sub percentStr {
  183. my ($p) = @_;
  184. if ($p == $inf) {
  185. return "-";
  186. }
  187. else {
  188. return sprintf "%10.2f%%", $p;
  189. }
  190. }
  191. # NEW LEAKS
  192. @keys = sort { $newMap{$b}{leakPercent} <=> $newMap{$a}{leakPercent} } keys %newMap;
  193. my $needsHeading = 1;
  194. my $total = 0;
  195. foreach $key (@keys) {
  196. my $percentLeaks = $newMap{$key}{leakPercent};
  197. my $leaks = $newMap{$key}{leaked};
  198. if ($percentLeaks > 0 && $key !~ /TOTAL/) {
  199. if ($needsHeading) {
  200. printf "--NEW-LEAKS-----------------------------------leaks------leaks%%-----------------------\n";
  201. $needsHeading = 0;
  202. }
  203. printf "%-40s %10s %10s\n", ($key, $leaks, percentStr($percentLeaks));
  204. $total += $leaks;
  205. }
  206. }
  207. if (!$needsHeading) {
  208. printf "%-40s %10s\n", ("TOTAL", $total);
  209. }
  210. # FIXED LEAKS
  211. @keys = sort { $newMap{$b}{leakPercent} <=> $newMap{$a}{leakPercent} } keys %newMap;
  212. $needsHeading = 1;
  213. $total = 0;
  214. foreach $key (@keys) {
  215. my $percentLeaks = $newMap{$key}{leakPercent};
  216. my $leaks = $newMap{$key}{leaked};
  217. if ($percentLeaks < 0 && $key !~ /TOTAL/) {
  218. if ($needsHeading) {
  219. printf "--FIXED-LEAKS---------------------------------leaks------leaks%%-----------------------\n";
  220. $needsHeading = 0;
  221. }
  222. printf "%-40s %10s %10s\n", ($key, $leaks, percentStr($percentLeaks));
  223. $total += $leaks;
  224. }
  225. }
  226. if (!$needsHeading) {
  227. printf "%-40s %10s\n", ("TOTAL", $total);
  228. }
  229. # NEW BLOAT
  230. @keys = sort { $newMap{$b}{bloatPercent} <=> $newMap{$a}{bloatPercent} } keys %newMap;
  231. $needsHeading = 1;
  232. $total = 0;
  233. foreach $key (@keys) {
  234. my $percentBloat = $newMap{$key}{bloatPercent};
  235. my $bloat = $newMap{$key}{bloat};
  236. if ($percentBloat > 0 && $key !~ /TOTAL/) {
  237. if ($needsHeading) {
  238. printf "--NEW-BLOAT-----------------------------------bloat------bloat%%-----------------------\n";
  239. $needsHeading = 0;
  240. }
  241. printf "%-40s %10s %10s\n", ($key, $bloat, percentStr($percentBloat));
  242. $total += $bloat;
  243. }
  244. }
  245. if (!$needsHeading) {
  246. printf "%-40s %10s\n", ("TOTAL", $total);
  247. }
  248. # ALL LEAKS
  249. @keys = sort { $newMap{$b}{leaked} <=> $newMap{$a}{leaked} } keys %newMap;
  250. $needsHeading = 1;
  251. $total = 0;
  252. foreach $key (@keys) {
  253. my $leaks = $newMap{$key}{leaked};
  254. my $percentLeaks = $newMap{$key}{leakPercent};
  255. if ($leaks > 0) {
  256. if ($needsHeading) {
  257. printf "--ALL-LEAKS-----------------------------------leaks------leaks%%-----------------------\n";
  258. $needsHeading = 0;
  259. }
  260. printf "%-40s %10s %10s\n", ($key, $leaks, percentStr($percentLeaks));
  261. if ($key !~ /TOTAL/) {
  262. $total += $leaks;
  263. }
  264. }
  265. }
  266. if (!$needsHeading) {
  267. # printf "%-40s %10s\n", ("TOTAL", $total);
  268. }
  269. # ALL BLOAT
  270. @keys = sort { $newMap{$b}{bloat} <=> $newMap{$a}{bloat} } keys %newMap;
  271. $needsHeading = 1;
  272. $total = 0;
  273. foreach $key (@keys) {
  274. my $bloat = $newMap{$key}{bloat};
  275. my $percentBloat = $newMap{$key}{bloatPercent};
  276. if ($bloat > 0) {
  277. if ($needsHeading) {
  278. printf "--ALL-BLOAT-----------------------------------bloat------bloat%%-----------------------\n";
  279. $needsHeading = 0;
  280. }
  281. printf "%-40s %10s %10s\n", ($key, $bloat, percentStr($percentBloat));
  282. if ($key !~ /TOTAL/) {
  283. $total += $bloat;
  284. }
  285. }
  286. }
  287. if (!$needsHeading) {
  288. # printf "%-40s %10s\n", ("TOTAL", $total);
  289. }
  290. # NEW CLASSES
  291. @keys = sort { $newMap{$b}{bloatDelta} <=> $newMap{$a}{bloatDelta} } keys %newMap;
  292. $needsHeading = 1;
  293. my $ltotal = 0;
  294. my $btotal = 0;
  295. foreach $key (@keys) {
  296. my $leaks = $newMap{$key}{leaked};
  297. my $bloat = $newMap{$key}{bloat};
  298. my $percentBloat = $newMap{$key}{bloatPercent};
  299. if ($percentBloat == $inf && $key !~ /TOTAL/) {
  300. if ($needsHeading) {
  301. printf "--CLASSES-NOT-REPORTED-LAST-TIME--------------leaks------bloat------------------------\n";
  302. $needsHeading = 0;
  303. }
  304. printf "%-40s %10s %10s\n", ($key, $leaks, $bloat);
  305. if ($key !~ /TOTAL/) {
  306. $ltotal += $leaks;
  307. $btotal += $bloat;
  308. }
  309. }
  310. }
  311. if (!$needsHeading) {
  312. printf "%-40s %10s %10s\n", ("TOTAL", $ltotal, $btotal);
  313. }
  314. # OLD CLASSES
  315. @keys = sort { ($oldMap{$b}{bloat} || 0) <=> ($oldMap{$a}{bloat} || 0) } keys %oldMap;
  316. $needsHeading = 1;
  317. $ltotal = 0;
  318. $btotal = 0;
  319. foreach $key (@keys) {
  320. if (!defined($newMap{$key})) {
  321. my $leaks = $oldMap{$key}{leaked};
  322. my $bloat = $oldMap{$key}{bloat};
  323. if ($needsHeading) {
  324. printf "--CLASSES-THAT-WENT-AWAY----------------------leaks------bloat------------------------\n";
  325. $needsHeading = 0;
  326. }
  327. printf "%-40s %10s %10s\n", ($key, $leaks, $bloat);
  328. if ($key !~ /TOTAL/) {
  329. $ltotal += $leaks;
  330. $btotal += $bloat;
  331. }
  332. }
  333. }
  334. if (!$needsHeading) {
  335. printf "%-40s %10s %10s\n", ("TOTAL", $ltotal, $btotal);
  336. }
  337. print "--------------------------------------------------------------------------------------\n";