diff options
author | Markus Lehtonen <markus.lehtonen@linux.intel.com> | 2016-05-11 13:39:22 +0300 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2016-07-01 16:22:46 +0100 |
commit | 6e27b2ae0ebab02efefc39014fd59817f53a5853 (patch) | |
tree | 242a9fe37c4741e4dd27d56bae820f5b8a2f4ab1 /meta/lib | |
parent | 1b10ded015d4375602c6be0b98493002b984d0e5 (diff) | |
download | poky-6e27b2ae0ebab02efefc39014fd59817f53a5853.tar.gz |
oeqa.buildperf: method for measuring system resource usage
Extend BuildPerfTest class with a new method for measuring the system
resource usage of a shell command to BuildPerfTest class. For now,
easurement of the elapsed time is done with the Gnu time utility,
similarly to the build-perf-test.sh shell script. And, it currently only
records the elapsed (wall clock).
The measured values (currently, only the elapsed time) is actually a
dictionary, making it possible to extend it with additional resource
values, e.g. cpu time or i/o usage, in the future. In addition to the
actual values of the measurement each record contains a 'name' and
'legend' where name is supposed to function as a common key or id over
test runs, making comparison and trending easier, for example. Legend is
supposed to be a short human readable description.
(From OE-Core rev: ced156bfea4a6649d201f41275641a633f218322)
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 'meta/lib')
-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'""" |