diff options
| -rw-r--r-- | meta/lib/oeqa/buildperf/base.py | 64 |
1 files changed, 62 insertions, 2 deletions
diff --git a/meta/lib/oeqa/buildperf/base.py b/meta/lib/oeqa/buildperf/base.py index 3ef038494f..d608061ec0 100644 --- a/meta/lib/oeqa/buildperf/base.py +++ b/meta/lib/oeqa/buildperf/base.py | |||
| @@ -10,11 +10,14 @@ | |||
| 10 | # more details. | 10 | # more details. |
| 11 | # | 11 | # |
| 12 | """Build performance test base classes and functionality""" | 12 | """Build performance test base classes and functionality""" |
| 13 | import glob | ||
| 13 | import logging | 14 | import logging |
| 14 | import os | 15 | import os |
| 16 | import re | ||
| 15 | import shutil | 17 | import shutil |
| 18 | import tempfile | ||
| 16 | import time | 19 | import time |
| 17 | from datetime import datetime | 20 | from datetime import datetime, timedelta |
| 18 | 21 | ||
| 19 | from oeqa.utils.commands import runCmd, get_bb_vars | 22 | from oeqa.utils.commands import runCmd, get_bb_vars |
| 20 | 23 | ||
| @@ -55,8 +58,24 @@ class KernelDropCaches(object): | |||
| 55 | runCmd(cmd, data=input_data) | 58 | runCmd(cmd, data=input_data) |
| 56 | 59 | ||
| 57 | 60 | ||
| 61 | def time_cmd(cmd, **kwargs): | ||
| 62 | """TIme a command""" | ||
| 63 | with tempfile.NamedTemporaryFile(mode='w+') as tmpf: | ||
| 64 | timecmd = ['/usr/bin/time', '-v', '-o', tmpf.name] | ||
| 65 | if isinstance(cmd, str): | ||
| 66 | timecmd = ' '.join(timecmd) + ' ' | ||
| 67 | timecmd += cmd | ||
| 68 | # TODO: 'ignore_status' could/should be removed when globalres.log is | ||
| 69 | # deprecated. The function would just raise an exception, instead | ||
| 70 | ret = runCmd(timecmd, ignore_status=True, **kwargs) | ||
| 71 | timedata = tmpf.file.read() | ||
| 72 | return ret, timedata | ||
| 73 | |||
| 74 | |||
| 58 | class BuildPerfTest(object): | 75 | class BuildPerfTest(object): |
| 59 | """Base class for build performance tests""" | 76 | """Base class for build performance tests""" |
| 77 | SYSRES = 'sysres' | ||
| 78 | |||
| 60 | name = None | 79 | name = None |
| 61 | description = None | 80 | description = None |
| 62 | 81 | ||
| @@ -73,6 +92,10 @@ class BuildPerfTest(object): | |||
| 73 | if not self.name: | 92 | if not self.name: |
| 74 | self.name = self.__class__.__name__ | 93 | self.name = self.__class__.__name__ |
| 75 | self.bb_vars = get_bb_vars() | 94 | self.bb_vars = get_bb_vars() |
| 95 | # TODO: remove the _failed flag when globalres.log is ditched as all | ||
| 96 | # failures should raise an exception | ||
| 97 | self._failed = False | ||
| 98 | self.cmd_log = os.path.join(self.out_dir, 'commands.log') | ||
| 76 | 99 | ||
| 77 | def run(self): | 100 | def run(self): |
| 78 | """Run test""" | 101 | """Run test""" |
| @@ -82,12 +105,49 @@ class BuildPerfTest(object): | |||
| 82 | self.results['elapsed_time'] = (datetime.now() - | 105 | self.results['elapsed_time'] = (datetime.now() - |
| 83 | self.results['start_time']) | 106 | self.results['start_time']) |
| 84 | # Test is regarded as completed if it doesn't raise an exception | 107 | # Test is regarded as completed if it doesn't raise an exception |
| 85 | self.results['status'] = 'COMPLETED' | 108 | if not self._failed: |
| 109 | self.results['status'] = 'COMPLETED' | ||
| 86 | 110 | ||
| 87 | def _run(self): | 111 | def _run(self): |
| 88 | """Actual test payload""" | 112 | """Actual test payload""" |
| 89 | raise NotImplementedError | 113 | raise NotImplementedError |
| 90 | 114 | ||
| 115 | def measure_cmd_resources(self, cmd, name, legend): | ||
| 116 | """Measure system resource usage of a command""" | ||
| 117 | def str_time_to_timedelta(strtime): | ||
| 118 | """Convert time strig from the time utility to timedelta""" | ||
| 119 | split = strtime.split(':') | ||
| 120 | hours = int(split[0]) if len(split) > 2 else 0 | ||
| 121 | mins = int(split[-2]) | ||
| 122 | secs, frac = split[-1].split('.') | ||
| 123 | secs = int(secs) | ||
| 124 | microsecs = int(float('0.' + frac) * pow(10, 6)) | ||
| 125 | return timedelta(0, hours*3600 + mins*60 + secs, microsecs) | ||
| 126 | |||
| 127 | cmd_str = cmd if isinstance(cmd, str) else ' '.join(cmd) | ||
| 128 | log.info("Timing command: %s", cmd_str) | ||
| 129 | with open(self.cmd_log, 'a') as fobj: | ||
| 130 | ret, timedata = time_cmd(cmd, stdout=fobj) | ||
| 131 | if ret.status: | ||
| 132 | log.error("Time will be reported as 0. Command failed: %s", | ||
| 133 | ret.status) | ||
| 134 | etime = timedelta(0) | ||
| 135 | self._failed = True | ||
| 136 | else: | ||
| 137 | match = re.search(r'.*wall clock.*: (?P<etime>.*)\n', timedata) | ||
| 138 | etime = str_time_to_timedelta(match.group('etime')) | ||
| 139 | |||
| 140 | measurement = {'type': self.SYSRES, | ||
| 141 | 'name': name, | ||
| 142 | 'legend': legend} | ||
| 143 | measurement['values'] = {'elapsed_time': etime} | ||
| 144 | self.results['measurements'].append(measurement) | ||
| 145 | nlogs = len(glob.glob(self.out_dir + '/results.log*')) | ||
| 146 | results_log = os.path.join(self.out_dir, | ||
| 147 | 'results.log.{}'.format(nlogs + 1)) | ||
| 148 | with open(results_log, 'w') as fobj: | ||
| 149 | fobj.write(timedata) | ||
| 150 | |||
| 91 | @staticmethod | 151 | @staticmethod |
| 92 | def force_rm(path): | 152 | def force_rm(path): |
| 93 | """Equivalent of 'rm -rf'""" | 153 | """Equivalent of 'rm -rf'""" |
