diff options
author | Markus Lehtonen <markus.lehtonen@linux.intel.com> | 2016-09-29 17:28:02 +0300 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2016-10-01 21:45:55 +0100 |
commit | 01a2b5823b628581548701e0e420b8584c2c1ca6 (patch) | |
tree | cb87f4e06d5acbdbcde9b4da24f6e4e11ea74a66 | |
parent | e05309194a962f817b043c8f1014886cfc324470 (diff) | |
download | poky-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>
-rwxr-xr-x | scripts/buildstats-diff | 140 |
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 | 51 | taskdiff_fields = ('pkg', 'pkg_op', 'task', 'task_op', 'value1', 'value2', | |
52 | taskdiff_fields = ('pkg', 'pkg_op', 'task', 'task_op', 'cputime1', 'cputime2', | ||
53 | 'absdiff', 'reldiff') | 52 | 'absdiff', 'reldiff') |
54 | TaskDiff = namedtuple('TaskDiff', ' '.join(taskdiff_fields)) | 53 | TaskDiff = 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 | 274 | def print_task_diff(bs1, bs2, val_type, min_val=0, min_absdiff=0, sort_by=('absdiff',)): | |
276 | def 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 | |
349 | def 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 | ||
377 | def parse_args(argv): | 390 | def 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 | ||
403 | def main(argv=None): | 431 | def 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 | ||