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