summaryrefslogtreecommitdiffstats
path: root/scripts/buildstats-diff
diff options
context:
space:
mode:
authorMarkus Lehtonen <markus.lehtonen@linux.intel.com>2016-09-29 17:28:02 +0300
committerRichard Purdie <richard.purdie@linuxfoundation.org>2016-10-01 21:45:55 +0100
commit01a2b5823b628581548701e0e420b8584c2c1ca6 (patch)
treecb87f4e06d5acbdbcde9b4da24f6e4e11ea74a66 /scripts/buildstats-diff
parente05309194a962f817b043c8f1014886cfc324470 (diff)
downloadpoky-01a2b5823b628581548701e0e420b8584c2c1ca6.tar.gz
scripts/buildstats-diff: introduce --diff-attr
A new command line option for choosing which "attribute" of the buildstats to compare. At first, the already supported 'cputime' is the only available option. But, refactoring done in this patch should make it easy to add new attribute types. (From OE-Core rev: 0782825138731b3f1e6a8e05d723c1d5cd60c90c) Signed-off-by: Markus Lehtonen <markus.lehtonen@linux.intel.com> Signed-off-by: Ross Burton <ross.burton@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'scripts/buildstats-diff')
-rwxr-xr-xscripts/buildstats-diff140
1 files changed, 84 insertions, 56 deletions
diff --git a/scripts/buildstats-diff b/scripts/buildstats-diff
index 56ac840d51..05bf74c64d 100755
--- a/scripts/buildstats-diff
+++ b/scripts/buildstats-diff
@@ -48,8 +48,7 @@ TIMEZONES = {'UTC': TimeZone(0, 'UTC'),
48 'EET': TimeZone(7200, 'EET'), 48 'EET': TimeZone(7200, 'EET'),
49 'EEST': TimeZone(10800, 'EEST')} 49 'EEST': TimeZone(10800, 'EEST')}
50 50
51 51taskdiff_fields = ('pkg', 'pkg_op', 'task', 'task_op', 'value1', 'value2',
52taskdiff_fields = ('pkg', 'pkg_op', 'task', 'task_op', 'cputime1', 'cputime2',
53 'absdiff', 'reldiff') 52 'absdiff', 'reldiff')
54TaskDiff = namedtuple('TaskDiff', ' '.join(taskdiff_fields)) 53TaskDiff = namedtuple('TaskDiff', ' '.join(taskdiff_fields))
55 54
@@ -272,16 +271,46 @@ def print_ver_diff(bs1, bs2):
272 print(fmt_str.format(pkg, field1, field2, maxlen=maxlen)) 271 print(fmt_str.format(pkg, field1, field2, maxlen=maxlen))
273 272
274 273
275 274def print_task_diff(bs1, bs2, val_type, min_val=0, min_absdiff=0, sort_by=('absdiff',)):
276def print_task_diff(bs1, bs2, min_val=0, min_absdiff=0, sort_by=('absdiff',)):
277 """Diff task execution times""" 275 """Diff task execution times"""
276 def val_to_str(val, human_readable=False):
277 """Convert raw value to printable string"""
278 def hms_time(secs):
279 """Get time in human-readable HH:MM:SS format"""
280 h = int(secs / 3600)
281 m = int((secs % 3600) / 60)
282 s = secs % 60
283 if h == 0:
284 return "{:02d}:{:04.1f}".format(m, s)
285 else:
286 return "{:d}:{:02d}:{:04.1f}".format(h, m, s)
287
288 if val_type == 'cputime':
289 if human_readable:
290 return hms_time(val)
291 else:
292 return "{:.1f}s".format(val)
293 else:
294 return str(val)
295
296 def sum_vals(buildstats):
297 """Get cumulative sum of all tasks"""
298 total = 0.0
299 for recipe_data in buildstats.values():
300 for bs_task in recipe_data['tasks'].values():
301 total += getattr(bs_task, val_type)
302 return total
303
278 tasks_diff = [] 304 tasks_diff = []
279 305
280 if min_val: 306 if min_val:
281 print("Ignoring tasks shorter than {}s".format(min_val)) 307 print("Ignoring tasks less than {} ({})".format(
308 val_to_str(min_val, True), val_to_str(min_val)))
282 if min_absdiff: 309 if min_absdiff:
283 print("Ignoring time differences shorter than {}s".format(min_absdiff)) 310 print("Ignoring differences less than {} ({})".format(
311 val_to_str(min_absdiff, True), val_to_str(min_absdiff)))
284 312
313 # Prepare the data
285 pkgs = set(bs1.keys()).union(set(bs2.keys())) 314 pkgs = set(bs1.keys()).union(set(bs2.keys()))
286 for pkg in pkgs: 315 for pkg in pkgs:
287 tasks1 = bs1[pkg]['tasks'] if pkg in bs1 else {} 316 tasks1 = bs1[pkg]['tasks'] if pkg in bs1 else {}
@@ -294,25 +323,27 @@ def print_task_diff(bs1, bs2, min_val=0, min_absdiff=0, sort_by=('absdiff',)):
294 pkg_op = ' ' 323 pkg_op = ' '
295 324
296 for task in set(tasks1.keys()).union(set(tasks2.keys())): 325 for task in set(tasks1.keys()).union(set(tasks2.keys())):
297 t1 = bs1[pkg]['tasks'][task].cputime if task in tasks1 else 0 326 val1 = getattr(bs1[pkg]['tasks'][task], val_type) if task in tasks1 else 0
298 t2 = bs2[pkg]['tasks'][task].cputime if task in tasks2 else 0 327 val2 = getattr(bs2[pkg]['tasks'][task], val_type) if task in tasks2 else 0
299 task_op = ' ' 328 task_op = ' '
300 if t1 == 0: 329 if val1 == 0:
301 reldiff = float('inf') 330 reldiff = float('inf')
302 task_op = '+ ' 331 task_op = '+ '
303 else: 332 else:
304 reldiff = 100 * (t2 - t1) / t1 333 reldiff = 100 * (val2 - val1) / val1
305 if t2 == 0: 334 if val2 == 0:
306 task_op = '- ' 335 task_op = '- '
307 336
308 cputime = max(t1, t2) 337 if max(val1, val2) < min_val:
309 if cputime < min_val: 338 log.debug("Filtering out %s:%s (%s)", pkg, task,
310 log.debug("Filtering out %s:%s (%0.1fs)", pkg, task, cputime) 339 val_to_str(max(val1, val2)))
311 continue 340 continue
312 if abs(t2-t1) < min_absdiff: 341 if abs(val2 - val1) < min_absdiff:
313 log.debug("Filtering out %s:%s (difference of %0.1fs)", pkg, task, t2-t1) 342 log.debug("Filtering out %s:%s (difference of %s)", pkg, task,
343 val_to_str(val2-val1))
314 continue 344 continue
315 tasks_diff.append(TaskDiff(pkg, pkg_op, task, task_op, t1, t2, t2-t1, reldiff)) 345 tasks_diff.append(TaskDiff(pkg, pkg_op, task, task_op, val1, val2,
346 val2-val1, reldiff))
316 347
317 # Sort our list 348 # Sort our list
318 for field in reversed(sort_by): 349 for field in reversed(sort_by):
@@ -323,17 +354,18 @@ def print_task_diff(bs1, bs2, min_val=0, min_absdiff=0, sort_by=('absdiff',)):
323 reverse = False 354 reverse = False
324 tasks_diff = sorted(tasks_diff, key=attrgetter(field), reverse=reverse) 355 tasks_diff = sorted(tasks_diff, key=attrgetter(field), reverse=reverse)
325 356
326 linedata = [(' ', 'PKG', ' ', 'TASK', 'ABSDIFF', 'RELDIFF', 'CPUTIME1', 'CPUTIME2')] 357 linedata = [(' ', 'PKG', ' ', 'TASK', 'ABSDIFF', 'RELDIFF',
358 val_type.upper() + '1', val_type.upper() + '2')]
327 field_lens = dict([('len_{}'.format(i), len(f)) for i, f in enumerate(linedata[0])]) 359 field_lens = dict([('len_{}'.format(i), len(f)) for i, f in enumerate(linedata[0])])
328 360
329 # Prepare fields in string format and measure field lengths 361 # Prepare fields in string format and measure field lengths
330 for diff in tasks_diff: 362 for diff in tasks_diff:
331 task_prefix = diff.task_op if diff.pkg_op == ' ' else ' ' 363 task_prefix = diff.task_op if diff.pkg_op == ' ' else ' '
332 linedata.append((diff.pkg_op, diff.pkg, task_prefix, diff.task, 364 linedata.append((diff.pkg_op, diff.pkg, task_prefix, diff.task,
333 '{:+.1f}'.format(diff.absdiff), 365 val_to_str(diff.absdiff),
334 '{:+.1f}%'.format(diff.reldiff), 366 '{:+.1f}%'.format(diff.reldiff),
335 '{:.1f}s'.format(diff.cputime1), 367 val_to_str(diff.value1),
336 '{:.1f}s'.format(diff.cputime2))) 368 val_to_str(diff.value2)))
337 for i, field in enumerate(linedata[-1]): 369 for i, field in enumerate(linedata[-1]):
338 key = 'len_{}'.format(i) 370 key = 'len_{}'.format(i)
339 if len(field) > field_lens[key]: 371 if len(field) > field_lens[key]:
@@ -345,33 +377,14 @@ def print_task_diff(bs1, bs2, min_val=0, min_absdiff=0, sort_by=('absdiff',)):
345 print("{:{len_0}}{:{len_1}} {:{len_2}}{:{len_3}} {:>{len_4}} {:>{len_5}} {:>{len_6}} -> {:{len_7}}".format( 377 print("{:{len_0}}{:{len_1}} {:{len_2}}{:{len_3}} {:>{len_4}} {:>{len_5}} {:>{len_6}} -> {:{len_7}}".format(
346 *fields, **field_lens)) 378 *fields, **field_lens))
347 379
348 380 # Print summary of the diffs
349def print_timediff_summary(bs1, bs2): 381 total1 = sum_vals(bs1)
350 """Print summary of the timediffs""" 382 total2 = sum_vals(bs2)
351 def total_cputime(buildstats): 383 print("\nCumulative {}:".format(val_type))
352 sum = 0.0 384 print (" {} {:+.1f}% {} ({}) -> {} ({})".format(
353 for recipe_data in buildstats.values(): 385 val_to_str(total2 - total1), 100 * (total2-total1) / total1,
354 for bs_task in recipe_data['tasks'].values(): 386 val_to_str(total1, True), val_to_str(total1),
355 sum += bs_task.cputime 387 val_to_str(total2, True), val_to_str(total2)))
356 return sum
357
358 def hms_time(secs):
359 """Get time in human-readable HH:MM:SS format"""
360 h = int(secs / 3600)
361 m = int((secs % 3600) / 60)
362 s = secs % 60
363 if h == 0:
364 return "{:02d}:{:04.1f}".format(m, s)
365 else:
366 return "{:d}:{:02d}:{:04.1f}".format(h, m, s)
367
368 total1 = total_cputime(bs1)
369 total2 = total_cputime(bs2)
370 print("\nCumulative CPU Time:")
371 print (" {:+.1f}s {:+.1f}% {} ({:.1f}s) -> {} ({:.1f}s)".format(
372 total2 - total1, 100 * (total2-total1) / total1,
373 hms_time(total1), total1, hms_time(total2), total2))
374
375 388
376 389
377def parse_args(argv): 390def parse_args(argv):
@@ -382,15 +395,21 @@ Script for comparing buildstats of two separate builds."""
382 formatter_class=argparse.ArgumentDefaultsHelpFormatter, 395 formatter_class=argparse.ArgumentDefaultsHelpFormatter,
383 description=description) 396 description=description)
384 397
398 min_val_defaults = {'cputime': 3.0}
399 min_absdiff_defaults = {'cputime': 1.0}
400
385 parser.add_argument('--debug', '-d', action='store_true', 401 parser.add_argument('--debug', '-d', action='store_true',
386 help="Verbose logging") 402 help="Verbose logging")
387 parser.add_argument('--ver-diff', action='store_true', 403 parser.add_argument('--ver-diff', action='store_true',
388 help="Show package version differences and exit") 404 help="Show package version differences and exit")
389 parser.add_argument('--min-val', default=3.0, type=float, 405 parser.add_argument('--diff-attr', default='cputime', choices=('cputime',),
390 help="Filter out tasks shorter than MIN_VAL seconds") 406 help="Buildstat attribute which to compare")
391 parser.add_argument('--min-absdiff', default=1.0, type=float, 407 parser.add_argument('--min-val', default=min_val_defaults, type=float,
392 help="Filter out tasks whose difference in cputime is " 408 help="Filter out tasks less than MIN_VAL. "
393 "less that MIN_ABSDIFF seconds") 409 "Default depends on --diff-attr.")
410 parser.add_argument('--min-absdiff', default=min_absdiff_defaults, type=float,
411 help="Filter out tasks whose difference is less than "
412 "MIN_ABSDIFF, Default depends on --diff-attr.")
394 parser.add_argument('--sort-by', default='absdiff', 413 parser.add_argument('--sort-by', default='absdiff',
395 help="Comma-separated list of field sort order. " 414 help="Comma-separated list of field sort order. "
396 "Prepend the field name with '-' for reversed sort. " 415 "Prepend the field name with '-' for reversed sort. "
@@ -398,7 +417,16 @@ Script for comparing buildstats of two separate builds."""
398 parser.add_argument('buildstats1', metavar='BUILDSTATS1', help="'Left' buildstat") 417 parser.add_argument('buildstats1', metavar='BUILDSTATS1', help="'Left' buildstat")
399 parser.add_argument('buildstats2', metavar='BUILDSTATS2', help="'Right' buildstat") 418 parser.add_argument('buildstats2', metavar='BUILDSTATS2', help="'Right' buildstat")
400 419
401 return parser.parse_args(argv) 420 args = parser.parse_args(argv)
421
422 # Handle defaults for the filter arguments
423 if args.min_val is min_val_defaults:
424 args.min_val = min_val_defaults[args.diff_attr]
425 if args.min_absdiff is min_absdiff_defaults:
426 args.min_absdiff = min_absdiff_defaults[args.diff_attr]
427
428 return args
429
402 430
403def main(argv=None): 431def main(argv=None):
404 """Script entry point""" 432 """Script entry point"""
@@ -422,8 +450,8 @@ def main(argv=None):
422 if args.ver_diff: 450 if args.ver_diff:
423 print_ver_diff(bs1, bs2) 451 print_ver_diff(bs1, bs2)
424 else: 452 else:
425 print_task_diff(bs1, bs2, args.min_val, args.min_absdiff, sort_by) 453 print_task_diff(bs1, bs2, args.diff_attr, args.min_val,
426 print_timediff_summary(bs1, bs2) 454 args.min_absdiff, sort_by)
427 455
428 return 0 456 return 0
429 457