diff options
author | Markus Lehtonen <markus.lehtonen@linux.intel.com> | 2016-08-29 22:48:26 +0300 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2016-09-03 09:58:42 +0100 |
commit | 0b332039ea445ff03b172bc19c38457b7535df0e (patch) | |
tree | 3f6bda9ac665d760a3628046dc0db0192d7b5760 /meta/lib/oeqa/buildperf | |
parent | 33a38bc18a9cd3f04c573dab004965d00a1ed81f (diff) | |
download | poky-0b332039ea445ff03b172bc19c38457b7535df0e.tar.gz |
oeqa.buildperf: don't use Gnu time
Use Python standard library functionality instead of the time utility
for measuring elapsed (wall clock) time of commands. The time.* log
files are also ditched. However, the same detailed resource usage data,
previously found in time.* logs is now provided in results.json file.
This data is collected through the resource module of Python.
(From OE-Core rev: d5ad818dd501b18379aa3540bffa9b920d7c3bab)
Signed-off-by: Markus Lehtonen <markus.lehtonen@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/lib/oeqa/buildperf')
-rw-r--r-- | meta/lib/oeqa/buildperf/base.py | 72 |
1 files changed, 39 insertions, 33 deletions
diff --git a/meta/lib/oeqa/buildperf/base.py b/meta/lib/oeqa/buildperf/base.py index de22850d4d..5987bfbe4a 100644 --- a/meta/lib/oeqa/buildperf/base.py +++ b/meta/lib/oeqa/buildperf/base.py | |||
@@ -15,14 +15,16 @@ import json | |||
15 | import logging | 15 | import logging |
16 | import os | 16 | import os |
17 | import re | 17 | import re |
18 | import resource | ||
18 | import shutil | 19 | import shutil |
19 | import socket | 20 | import socket |
20 | import tempfile | ||
21 | import time | 21 | import time |
22 | import traceback | 22 | import traceback |
23 | import unittest | 23 | import unittest |
24 | from datetime import datetime, timedelta | 24 | from datetime import datetime, timedelta |
25 | from functools import partial | 25 | from functools import partial |
26 | from multiprocessing import Process | ||
27 | from multiprocessing import SimpleQueue | ||
26 | 28 | ||
27 | import oe.path | 29 | import oe.path |
28 | from oeqa.utils.commands import CommandError, runCmd, get_bb_vars | 30 | from oeqa.utils.commands import CommandError, runCmd, get_bb_vars |
@@ -69,16 +71,9 @@ class KernelDropCaches(object): | |||
69 | runCmd2(cmd, data=input_data) | 71 | runCmd2(cmd, data=input_data) |
70 | 72 | ||
71 | 73 | ||
72 | def time_cmd(cmd, **kwargs): | 74 | def str_to_fn(string): |
73 | """TIme a command""" | 75 | """Convert string to a sanitized filename""" |
74 | with tempfile.NamedTemporaryFile(mode='w+') as tmpf: | 76 | return re.sub(r'(\W+)', '-', string, flags=re.LOCALE) |
75 | timecmd = ['/usr/bin/time', '-v', '-o', tmpf.name] | ||
76 | if isinstance(cmd, str): | ||
77 | timecmd = ' '.join(timecmd) + ' ' | ||
78 | timecmd += cmd | ||
79 | ret = runCmd2(timecmd, **kwargs) | ||
80 | timedata = tmpf.file.read() | ||
81 | return ret, timedata | ||
82 | 77 | ||
83 | 78 | ||
84 | class ResultsJsonEncoder(json.JSONEncoder): | 79 | class ResultsJsonEncoder(json.JSONEncoder): |
@@ -338,45 +333,56 @@ class BuildPerfTestCase(unittest.TestCase): | |||
338 | 333 | ||
339 | def measure_cmd_resources(self, cmd, name, legend): | 334 | def measure_cmd_resources(self, cmd, name, legend): |
340 | """Measure system resource usage of a command""" | 335 | """Measure system resource usage of a command""" |
341 | def str_time_to_timedelta(strtime): | 336 | def _worker(data_q, cmd, **kwargs): |
342 | """Convert time strig from the time utility to timedelta""" | 337 | """Worker process for measuring resources""" |
343 | split = strtime.split(':') | ||
344 | hours = int(split[0]) if len(split) > 2 else 0 | ||
345 | mins = int(split[-2]) | ||
346 | try: | 338 | try: |
347 | secs, frac = split[-1].split('.') | 339 | start_time = datetime.now() |
348 | except: | 340 | ret = runCmd2(cmd, **kwargs) |
349 | secs = split[-1] | 341 | etime = datetime.now() - start_time |
350 | frac = '0' | 342 | rusage_struct = resource.getrusage(resource.RUSAGE_CHILDREN) |
351 | secs = int(secs) | 343 | rusage = {} |
352 | microsecs = int(float('0.' + frac) * pow(10, 6)) | 344 | # Skip unused fields, (i.e. 'ru_ixrss', 'ru_idrss', 'ru_isrss', |
353 | return timedelta(0, hours*3600 + mins*60 + secs, microsecs) | 345 | # 'ru_nswap', 'ru_msgsnd', 'ru_msgrcv' and 'ru_nsignals') |
346 | for key in ['ru_utime', 'ru_stime', 'ru_maxrss', 'ru_minflt', | ||
347 | 'ru_majflt', 'ru_inblock', 'ru_oublock', | ||
348 | 'ru_nvcsw', 'ru_nivcsw']: | ||
349 | rusage[key] = getattr(rusage_struct, key) | ||
350 | data_q.put({'ret': ret, | ||
351 | 'start_time': start_time, | ||
352 | 'elapsed_time': etime, | ||
353 | 'rusage': rusage}) | ||
354 | except Exception as err: | ||
355 | data_q.put(err) | ||
354 | 356 | ||
355 | cmd_str = cmd if isinstance(cmd, str) else ' '.join(cmd) | 357 | cmd_str = cmd if isinstance(cmd, str) else ' '.join(cmd) |
356 | log.info("Timing command: %s", cmd_str) | 358 | log.info("Timing command: %s", cmd_str) |
359 | data_q = SimpleQueue() | ||
357 | cmd_log = os.path.join(self.out_dir, 'commands.log') | 360 | cmd_log = os.path.join(self.out_dir, 'commands.log') |
358 | try: | 361 | try: |
359 | with open(cmd_log, 'a') as fobj: | 362 | with open(cmd_log, 'a') as fobj: |
360 | ret, timedata = time_cmd(cmd, stdout=fobj) | 363 | proc = Process(target=_worker, args=(data_q, cmd,), |
364 | kwargs={'stdout': fobj}) | ||
365 | proc.start() | ||
366 | data = data_q.get() | ||
367 | proc.join() | ||
368 | if isinstance(data, Exception): | ||
369 | raise data | ||
361 | except CommandError: | 370 | except CommandError: |
362 | log.error("Command '%s' failed, see %s for more details", cmd_str, | 371 | log.error("Command '%s' failed, see %s for more details", cmd_str, |
363 | cmd_log) | 372 | cmd_log) |
364 | raise | 373 | raise |
365 | match = re.search(r'.*wall clock.*: (?P<etime>.*)\n', timedata) | 374 | etime = data['elapsed_time'] |
366 | etime = str_time_to_timedelta(match.group('etime')) | ||
367 | 375 | ||
368 | measurement = {'type': self.SYSRES, | 376 | measurement = {'type': self.SYSRES, |
369 | 'name': name, | 377 | 'name': name, |
370 | 'legend': legend} | 378 | 'legend': legend} |
371 | measurement['values'] = {'elapsed_time': etime} | 379 | measurement['values'] = {'start_time': data['start_time'], |
380 | 'elapsed_time': etime, | ||
381 | 'rusage': data['rusage']} | ||
372 | self.measurements.append(measurement) | 382 | self.measurements.append(measurement) |
373 | e_sec = etime.total_seconds() | 383 | |
374 | nlogs = len(glob.glob(self.out_dir + '/results.log*')) | ||
375 | results_log = os.path.join(self.out_dir, | ||
376 | 'results.log.{}'.format(nlogs + 1)) | ||
377 | with open(results_log, 'w') as fobj: | ||
378 | fobj.write(timedata) | ||
379 | # Append to 'times' array for globalres log | 384 | # Append to 'times' array for globalres log |
385 | e_sec = etime.total_seconds() | ||
380 | self.times.append('{:d}:{:02d}:{:.2f}'.format(int(e_sec / 3600), | 386 | self.times.append('{:d}:{:02d}:{:.2f}'.format(int(e_sec / 3600), |
381 | int((e_sec % 3600) / 60), | 387 | int((e_sec % 3600) / 60), |
382 | e_sec % 60)) | 388 | e_sec % 60)) |