blob: 30c44a55dea06a247d035aa655dfd1c88362b64a [file] [log] [blame]
Simran Basi833814b2013-01-29 13:13:43 -08001# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Ryo Hashimoto66ead5c2014-12-11 20:19:35 +09005import json
Simran Basi833814b2013-01-29 13:13:43 -08006import logging
Ryo Hashimoto66ead5c2014-12-11 20:19:35 +09007import math
Simran Basi833814b2013-01-29 13:13:43 -08008import os
Dennis Jeffreyc42fd302013-04-17 11:57:51 -07009import pprint
Simran Basi833814b2013-01-29 13:13:43 -080010import re
11import StringIO
12
Simran Basi833814b2013-01-29 13:13:43 -080013from autotest_lib.client.common_lib import error, utils
14from autotest_lib.client.common_lib.cros import dev_server
Simran Basi5ace6f22016-01-06 17:30:44 -080015from autotest_lib.server import afe_utils
Simran Basi833814b2013-01-29 13:13:43 -080016
17
Dave Tu6a404e62013-11-05 15:54:48 -080018TELEMETRY_RUN_BENCHMARKS_SCRIPT = 'tools/perf/run_benchmark'
Simran Basi1dbfc132013-05-02 10:11:02 -070019TELEMETRY_RUN_CROS_TESTS_SCRIPT = 'chrome/test/telemetry/run_cros_tests'
Ilja Friedelf2473802014-03-28 17:54:34 -070020TELEMETRY_RUN_GPU_TESTS_SCRIPT = 'content/test/gpu/run_gpu_test.py'
Ilja H. Friedel086bc3f2014-02-27 22:17:55 -080021TELEMETRY_RUN_TESTS_SCRIPT = 'tools/telemetry/run_tests'
Achuith Bhandarkar124e4732014-01-21 15:27:54 -080022TELEMETRY_TIMEOUT_MINS = 120
Simran Basi833814b2013-01-29 13:13:43 -080023
24# Result Statuses
25SUCCESS_STATUS = 'SUCCESS'
26WARNING_STATUS = 'WARNING'
27FAILED_STATUS = 'FAILED'
28
Dennis Jeffreyc42fd302013-04-17 11:57:51 -070029# Regex for the RESULT output lines understood by chrome buildbot.
Ryo Hashimoto66ead5c2014-12-11 20:19:35 +090030# Keep in sync with
31# chromium/tools/build/scripts/slave/performance_log_processor.py.
Dennis Jeffreyc42fd302013-04-17 11:57:51 -070032RESULTS_REGEX = re.compile(r'(?P<IMPORTANT>\*)?RESULT '
Ryo Hashimoto66ead5c2014-12-11 20:19:35 +090033 r'(?P<GRAPH>[^:]*): (?P<TRACE>[^=]*)= '
34 r'(?P<VALUE>[\{\[]?[-\d\., ]+[\}\]]?)('
35 r' ?(?P<UNITS>.+))?')
36HISTOGRAM_REGEX = re.compile(r'(?P<IMPORTANT>\*)?HISTOGRAM '
37 r'(?P<GRAPH>[^:]*): (?P<TRACE>[^=]*)= '
38 r'(?P<VALUE_JSON>{.*})(?P<UNITS>.+)?')
Dennis Jeffreyc42fd302013-04-17 11:57:51 -070039
Simran Basi833814b2013-01-29 13:13:43 -080040
41class TelemetryResult(object):
42 """Class to represent the results of a telemetry run.
43
44 This class represents the results of a telemetry run, whether it ran
45 successful, failed or had warnings.
46 """
47
48
49 def __init__(self, exit_code=0, stdout='', stderr=''):
50 """Initializes this TelemetryResultObject instance.
51
52 @param status: Status of the telemtry run.
53 @param stdout: Stdout of the telemetry run.
54 @param stderr: Stderr of the telemetry run.
55 """
56 if exit_code == 0:
57 self.status = SUCCESS_STATUS
58 else:
59 self.status = FAILED_STATUS
60
Fang Denge689e712013-11-13 18:27:06 -080061 # A list of perf values, e.g.
62 # [{'graph': 'graphA', 'trace': 'page_load_time',
63 # 'units': 'secs', 'value':0.5}, ...]
64 self.perf_data = []
Simran Basi833814b2013-01-29 13:13:43 -080065 self._stdout = stdout
66 self._stderr = stderr
67 self.output = '\n'.join([stdout, stderr])
68
69
Dennis Jeffreyc42fd302013-04-17 11:57:51 -070070 def _cleanup_perf_string(self, str):
71 """Clean up a perf-related string by removing illegal characters.
Simran Basi833814b2013-01-29 13:13:43 -080072
Dennis Jeffreyc42fd302013-04-17 11:57:51 -070073 Perf keys stored in the chromeOS database may contain only letters,
74 numbers, underscores, periods, and dashes. Transform an inputted
75 string so that any illegal characters are replaced by underscores.
76
77 @param str: The perf string to clean up.
78
79 @return The cleaned-up perf string.
80 """
81 return re.sub(r'[^\w.-]', '_', str)
82
83
84 def _cleanup_units_string(self, units):
85 """Cleanup a units string.
86
87 Given a string representing units for a perf measurement, clean it up
88 by replacing certain illegal characters with meaningful alternatives.
89 Any other illegal characters should then be replaced with underscores.
Simran Basi833814b2013-01-29 13:13:43 -080090
91 Examples:
Dennis Jeffreyc42fd302013-04-17 11:57:51 -070092 count/time -> count_per_time
93 % -> percent
94 units! --> units_
95 score (bigger is better) -> score__bigger_is_better_
96 score (runs/s) -> score__runs_per_s_
Simran Basi833814b2013-01-29 13:13:43 -080097
Dennis Jeffreyc42fd302013-04-17 11:57:51 -070098 @param units: The units string to clean up.
Simran Basi833814b2013-01-29 13:13:43 -080099
Dennis Jeffreyc42fd302013-04-17 11:57:51 -0700100 @return The cleaned-up units string.
Simran Basi833814b2013-01-29 13:13:43 -0800101 """
Dennis Jeffreyc42fd302013-04-17 11:57:51 -0700102 if '%' in units:
103 units = units.replace('%', 'percent')
Simran Basi833814b2013-01-29 13:13:43 -0800104 if '/' in units:
105 units = units.replace('/','_per_')
Dennis Jeffreyc42fd302013-04-17 11:57:51 -0700106 return self._cleanup_perf_string(units)
Simran Basi833814b2013-01-29 13:13:43 -0800107
108
109 def parse_benchmark_results(self):
110 """Parse the results of a telemetry benchmark run.
111
Dave Tu6a404e62013-11-05 15:54:48 -0800112 Stdout has the output in RESULT block format below.
Simran Basi833814b2013-01-29 13:13:43 -0800113
Dennis Jeffreyc42fd302013-04-17 11:57:51 -0700114 The lines of interest start with the substring "RESULT". These are
115 specially-formatted perf data lines that are interpreted by chrome
116 builbot (when the Telemetry tests run for chrome desktop) and are
117 parsed to extract perf data that can then be displayed on a perf
118 dashboard. This format is documented in the docstring of class
119 GraphingLogProcessor in this file in the chrome tree:
Simran Basi833814b2013-01-29 13:13:43 -0800120
Dennis Jeffreyc42fd302013-04-17 11:57:51 -0700121 chromium/tools/build/scripts/slave/process_log_utils.py
Simran Basi833814b2013-01-29 13:13:43 -0800122
Dennis Jeffreyc42fd302013-04-17 11:57:51 -0700123 Example RESULT output lines:
124 RESULT average_commit_time_by_url: http___www.ebay.com= 8.86528 ms
125 RESULT CodeLoad: CodeLoad= 6343 score (bigger is better)
126 RESULT ai-astar: ai-astar= [614,527,523,471,530,523,577,625,614,538] ms
127
128 Currently for chromeOS, we can only associate a single perf key (string)
129 with a perf value. That string can only contain letters, numbers,
130 dashes, periods, and underscores, as defined by write_keyval() in:
131
132 chromeos/src/third_party/autotest/files/client/common_lib/
133 base_utils.py
134
135 We therefore parse each RESULT line, clean up the strings to remove any
136 illegal characters not accepted by chromeOS, and construct a perf key
137 string based on the parsed components of the RESULT line (with each
138 component separated by a special delimiter). We prefix the perf key
139 with the substring "TELEMETRY" to identify it as a telemetry-formatted
140 perf key.
Simran Basi833814b2013-01-29 13:13:43 -0800141
142 Stderr has the format of Warnings/Tracebacks. There is always a default
Dennis Jeffreyc42fd302013-04-17 11:57:51 -0700143 warning of the display enviornment setting, followed by warnings of
Simran Basi833814b2013-01-29 13:13:43 -0800144 page timeouts or a traceback.
145
146 If there are any other warnings we flag the test as warning. If there
147 is a traceback we consider this test a failure.
Simran Basi833814b2013-01-29 13:13:43 -0800148 """
Simran Basi833814b2013-01-29 13:13:43 -0800149 if not self._stdout:
150 # Nothing in stdout implies a test failure.
151 logging.error('No stdout, test failed.')
152 self.status = FAILED_STATUS
153 return
154
155 stdout_lines = self._stdout.splitlines()
Simran Basi833814b2013-01-29 13:13:43 -0800156 for line in stdout_lines:
Dennis Jeffreyc42fd302013-04-17 11:57:51 -0700157 results_match = RESULTS_REGEX.search(line)
Ryo Hashimoto66ead5c2014-12-11 20:19:35 +0900158 histogram_match = HISTOGRAM_REGEX.search(line)
159 if results_match:
160 self._process_results_line(results_match)
161 elif histogram_match:
162 self._process_histogram_line(histogram_match)
Dennis Jeffreyc42fd302013-04-17 11:57:51 -0700163
164 pp = pprint.PrettyPrinter(indent=2)
Fang Denge689e712013-11-13 18:27:06 -0800165 logging.debug('Perf values: %s', pp.pformat(self.perf_data))
Simran Basi833814b2013-01-29 13:13:43 -0800166
167 if self.status is SUCCESS_STATUS:
168 return
169
170 # Otherwise check if simply a Warning occurred or a Failure,
171 # i.e. a Traceback is listed.
172 self.status = WARNING_STATUS
173 for line in self._stderr.splitlines():
174 if line.startswith('Traceback'):
175 self.status = FAILED_STATUS
176
Ryo Hashimoto66ead5c2014-12-11 20:19:35 +0900177 def _process_results_line(self, line_match):
178 """Processes a line that matches the standard RESULT line format.
179
180 Args:
181 line_match: A MatchObject as returned by re.search.
182 """
183 match_dict = line_match.groupdict()
184 graph_name = self._cleanup_perf_string(match_dict['GRAPH'].strip())
185 trace_name = self._cleanup_perf_string(match_dict['TRACE'].strip())
186 units = self._cleanup_units_string(
187 (match_dict['UNITS'] or 'units').strip())
188 value = match_dict['VALUE'].strip()
189 unused_important = match_dict['IMPORTANT'] or False # Unused now.
190
191 if value.startswith('['):
192 # A list of values, e.g., "[12,15,8,7,16]". Extract just the
193 # numbers, compute the average and use that. In this example,
194 # we'd get 12+15+8+7+16 / 5 --> 11.6.
195 value_list = [float(x) for x in value.strip('[],').split(',')]
196 value = float(sum(value_list)) / len(value_list)
197 elif value.startswith('{'):
198 # A single value along with a standard deviation, e.g.,
199 # "{34.2,2.15}". Extract just the value itself and use that.
200 # In this example, we'd get 34.2.
201 value_list = [float(x) for x in value.strip('{},').split(',')]
202 value = value_list[0] # Position 0 is the value.
203 elif re.search('^\d+$', value):
204 value = int(value)
205 else:
206 value = float(value)
207
208 self.perf_data.append({'graph':graph_name, 'trace': trace_name,
209 'units': units, 'value': value})
210
211 def _process_histogram_line(self, line_match):
212 """Processes a line that matches the HISTOGRAM line format.
213
214 Args:
215 line_match: A MatchObject as returned by re.search.
216 """
217 match_dict = line_match.groupdict()
218 graph_name = self._cleanup_perf_string(match_dict['GRAPH'].strip())
219 trace_name = self._cleanup_perf_string(match_dict['TRACE'].strip())
220 units = self._cleanup_units_string(
221 (match_dict['UNITS'] or 'units').strip())
222 histogram_json = match_dict['VALUE_JSON'].strip()
223 unused_important = match_dict['IMPORTANT'] or False # Unused now.
224 histogram_data = json.loads(histogram_json)
225
226 # Compute geometric mean
227 count = 0
228 sum_of_logs = 0
229 for bucket in histogram_data['buckets']:
230 mean = (bucket['low'] + bucket['high']) / 2.0
231 if mean > 0:
232 sum_of_logs += math.log(mean) * bucket['count']
233 count += bucket['count']
234
235 value = math.exp(sum_of_logs / count) if count > 0 else 0.0
236
237 self.perf_data.append({'graph':graph_name, 'trace': trace_name,
238 'units': units, 'value': value})
Simran Basi833814b2013-01-29 13:13:43 -0800239
240class TelemetryRunner(object):
241 """Class responsible for telemetry for a given build.
242
243 This class will extract and install telemetry on the devserver and is
244 responsible for executing the telemetry benchmarks and returning their
245 output to the caller.
246 """
247
Luis Lozano23ae3192013-11-08 16:22:46 -0800248 def __init__(self, host, local=False):
Simran Basi833814b2013-01-29 13:13:43 -0800249 """Initializes this telemetry runner instance.
250
251 If telemetry is not installed for this build, it will be.
Luis Lozano23ae3192013-11-08 16:22:46 -0800252
253 @param host: Host where the test will be run.
254 @param local: If set, no devserver will be used, test will be run
255 locally.
Simran Basi833814b2013-01-29 13:13:43 -0800256 """
257 self._host = host
Ilja H. Friedelc7bf3102014-05-13 17:31:25 -0700258 self._devserver = None
259 self._telemetry_path = None
Luis Lozano23ae3192013-11-08 16:22:46 -0800260 # TODO (llozano crbug.com/324964). Remove conditional code.
261 # Use a class hierarchy instead.
262 if local:
263 self._setup_local_telemetry()
264 else:
265 self._setup_devserver_telemetry()
266
267 logging.debug('Telemetry Path: %s', self._telemetry_path)
268
269
270 def _setup_devserver_telemetry(self):
271 """Setup Telemetry to use the devserver."""
272 logging.debug('Setting up telemetry for devserver testing')
Simran Basi833814b2013-01-29 13:13:43 -0800273 logging.debug('Grabbing build from AFE.')
274
Simran Basi5ace6f22016-01-06 17:30:44 -0800275 build = afe_utils.get_build(self._host)
Simran Basi833814b2013-01-29 13:13:43 -0800276 if not build:
277 logging.error('Unable to locate build label for host: %s.',
278 self._host.hostname)
279 raise error.AutotestError('Failed to grab build for host %s.' %
280 self._host.hostname)
281
282 logging.debug('Setting up telemetry for build: %s', build)
283
284 self._devserver = dev_server.ImageServer.resolve(build)
Simran Basicf81f682014-12-03 16:31:39 -0800285 self._devserver.stage_artifacts(build, ['autotest_packages'])
Simran Basi833814b2013-01-29 13:13:43 -0800286 self._telemetry_path = self._devserver.setup_telemetry(build=build)
Luis Lozano23ae3192013-11-08 16:22:46 -0800287
288
289 def _setup_local_telemetry(self):
290 """Setup Telemetry to use local path to its sources.
291
292 First look for chrome source root, either externally mounted, or inside
293 the chroot. Prefer chrome-src-internal source tree to chrome-src.
294 """
295 TELEMETRY_DIR = 'src'
296 CHROME_LOCAL_SRC = '/var/cache/chromeos-cache/distfiles/target/'
Josh Triplett05208c92014-07-17 13:21:29 -0700297 CHROME_EXTERNAL_SRC = os.path.expanduser('~/chrome_root/')
Luis Lozano23ae3192013-11-08 16:22:46 -0800298
299 logging.debug('Setting up telemetry for local testing')
300
301 sources_list = ('chrome-src-internal', 'chrome-src')
Josh Triplett05208c92014-07-17 13:21:29 -0700302 dir_list = [CHROME_EXTERNAL_SRC]
Luis Lozano23ae3192013-11-08 16:22:46 -0800303 dir_list.extend(
304 [os.path.join(CHROME_LOCAL_SRC, x) for x in sources_list])
305 if 'CHROME_ROOT' in os.environ:
306 dir_list.insert(0, os.environ['CHROME_ROOT'])
307
308 telemetry_src = ''
309 for dir in dir_list:
310 if os.path.exists(dir):
311 telemetry_src = os.path.join(dir, TELEMETRY_DIR)
312 break
313 else:
314 raise error.TestError('Telemetry source directory not found.')
315
316 self._devserver = None
317 self._telemetry_path = telemetry_src
318
319
Luis Lozano814c7182015-09-08 11:20:47 -0700320 def _get_telemetry_cmd(self, script, test_or_benchmark, *args):
Luis Lozano23ae3192013-11-08 16:22:46 -0800321 """Build command to execute telemetry based on script and benchmark.
322
323 @param script: Telemetry script we want to run. For example:
324 [path_to_telemetry_src]/src/tools/telemetry/run_tests.
325 @param test_or_benchmark: Name of the test or benchmark we want to run,
326 with the page_set (if required) as part of
327 the string.
Luis Lozano814c7182015-09-08 11:20:47 -0700328 @param args: additional list of arguments to pass to the script.
329
Luis Lozano23ae3192013-11-08 16:22:46 -0800330 @returns Full telemetry command to execute the script.
331 """
332 telemetry_cmd = []
333 if self._devserver:
334 devserver_hostname = self._devserver.url().split(
335 'http://')[1].split(':')[0]
336 telemetry_cmd.extend(['ssh', devserver_hostname])
337
338 telemetry_cmd.extend(
339 ['python',
340 script,
Ilja H. Friedel6965bd82014-05-20 18:29:15 -0700341 '--verbose',
Luis Lozano23ae3192013-11-08 16:22:46 -0800342 '--browser=cros-chrome',
Luis Lozano814c7182015-09-08 11:20:47 -0700343 '--remote=%s' % self._host.hostname])
344 telemetry_cmd.extend(args)
345 telemetry_cmd.append(test_or_benchmark)
346
Luis Lozano23ae3192013-11-08 16:22:46 -0800347 return telemetry_cmd
Simran Basi833814b2013-01-29 13:13:43 -0800348
349
Luis Lozano814c7182015-09-08 11:20:47 -0700350 def _run_telemetry(self, script, test_or_benchmark, *args):
Simran Basi833814b2013-01-29 13:13:43 -0800351 """Runs telemetry on a dut.
352
353 @param script: Telemetry script we want to run. For example:
Luis Lozano23ae3192013-11-08 16:22:46 -0800354 [path_to_telemetry_src]/src/tools/telemetry/run_tests.
Simran Basi833814b2013-01-29 13:13:43 -0800355 @param test_or_benchmark: Name of the test or benchmark we want to run,
356 with the page_set (if required) as part of the
357 string.
Luis Lozano814c7182015-09-08 11:20:47 -0700358 @param args: additional list of arguments to pass to the script.
Simran Basi833814b2013-01-29 13:13:43 -0800359
360 @returns A TelemetryResult Instance with the results of this telemetry
361 execution.
362 """
Simran Basi1dbfc132013-05-02 10:11:02 -0700363 # TODO (sbasi crbug.com/239933) add support for incognito mode.
Simran Basi833814b2013-01-29 13:13:43 -0800364
Luis Lozano814c7182015-09-08 11:20:47 -0700365 telemetry_cmd = self._get_telemetry_cmd(script,
366 test_or_benchmark,
367 *args)
Luis Lozano23ae3192013-11-08 16:22:46 -0800368 logging.debug('Running Telemetry: %s', ' '.join(telemetry_cmd))
369
Simran Basi833814b2013-01-29 13:13:43 -0800370 output = StringIO.StringIO()
371 error_output = StringIO.StringIO()
372 exit_code = 0
373 try:
Luis Lozano23ae3192013-11-08 16:22:46 -0800374 result = utils.run(' '.join(telemetry_cmd), stdout_tee=output,
Simran Basi833814b2013-01-29 13:13:43 -0800375 stderr_tee=error_output,
376 timeout=TELEMETRY_TIMEOUT_MINS*60)
377 exit_code = result.exit_status
378 except error.CmdError as e:
379 # Telemetry returned a return code of not 0; for benchmarks this
380 # can be due to a timeout on one of the pages of the page set and
381 # we may still have data on the rest. For a test however this
382 # indicates failure.
383 logging.debug('Error occurred executing telemetry.')
384 exit_code = e.result_obj.exit_status
385
386 stdout = output.getvalue()
387 stderr = error_output.getvalue()
388 logging.debug('Telemetry completed with exit code: %d.\nstdout:%s\n'
389 'stderr:%s', exit_code, stdout, stderr)
390
391 return TelemetryResult(exit_code=exit_code, stdout=stdout,
392 stderr=stderr)
393
394
Luis Lozano814c7182015-09-08 11:20:47 -0700395 def _run_test(self, script, test, *args):
Simran Basi1dbfc132013-05-02 10:11:02 -0700396 """Runs a telemetry test on a dut.
397
398 @param script: Which telemetry test script we want to run. Can be
399 telemetry's base test script or the Chrome OS specific
400 test script.
401 @param test: Telemetry test we want to run.
Luis Lozano814c7182015-09-08 11:20:47 -0700402 @param args: additional list of arguments to pass to the script.
Simran Basi1dbfc132013-05-02 10:11:02 -0700403
404 @returns A TelemetryResult Instance with the results of this telemetry
405 execution.
406 """
407 logging.debug('Running telemetry test: %s', test)
408 telemetry_script = os.path.join(self._telemetry_path, script)
Luis Lozano814c7182015-09-08 11:20:47 -0700409 result = self._run_telemetry(telemetry_script, test, *args)
Simran Basi1dbfc132013-05-02 10:11:02 -0700410 if result.status is FAILED_STATUS:
Ilja H. Friedelc7bf3102014-05-13 17:31:25 -0700411 raise error.TestFail('Telemetry test %s failed.' % test)
Simran Basi1dbfc132013-05-02 10:11:02 -0700412 return result
413
414
Luis Lozano814c7182015-09-08 11:20:47 -0700415 def run_telemetry_test(self, test, *args):
Simran Basi833814b2013-01-29 13:13:43 -0800416 """Runs a telemetry test on a dut.
417
418 @param test: Telemetry test we want to run.
Luis Lozano814c7182015-09-08 11:20:47 -0700419 @param args: additional list of arguments to pass to the telemetry
420 execution script.
Simran Basi833814b2013-01-29 13:13:43 -0800421
422 @returns A TelemetryResult Instance with the results of this telemetry
423 execution.
424 """
Luis Lozano814c7182015-09-08 11:20:47 -0700425 return self._run_test(TELEMETRY_RUN_TESTS_SCRIPT, test, *args)
Simran Basi1dbfc132013-05-02 10:11:02 -0700426
427
Luis Lozano814c7182015-09-08 11:20:47 -0700428 def run_cros_telemetry_test(self, test, *args):
Simran Basi1dbfc132013-05-02 10:11:02 -0700429 """Runs a cros specific telemetry test on a dut.
430
431 @param test: Telemetry test we want to run.
Luis Lozano814c7182015-09-08 11:20:47 -0700432 @param args: additional list of arguments to pass to the telemetry
433 execution script.
Simran Basi1dbfc132013-05-02 10:11:02 -0700434
435 @returns A TelemetryResult instance with the results of this telemetry
436 execution.
437 """
Luis Lozano814c7182015-09-08 11:20:47 -0700438 return self._run_test(TELEMETRY_RUN_CROS_TESTS_SCRIPT, test, *args)
Simran Basi833814b2013-01-29 13:13:43 -0800439
440
Luis Lozano814c7182015-09-08 11:20:47 -0700441 def run_gpu_test(self, test, *args):
Ilja H. Friedel086bc3f2014-02-27 22:17:55 -0800442 """Runs a gpu test on a dut.
443
444 @param test: Gpu test we want to run.
Luis Lozano814c7182015-09-08 11:20:47 -0700445 @param args: additional list of arguments to pass to the telemetry
446 execution script.
Ilja H. Friedel086bc3f2014-02-27 22:17:55 -0800447
448 @returns A TelemetryResult instance with the results of this telemetry
449 execution.
450 """
Luis Lozano814c7182015-09-08 11:20:47 -0700451 return self._run_test(TELEMETRY_RUN_GPU_TESTS_SCRIPT, test, *args)
Ilja H. Friedel086bc3f2014-02-27 22:17:55 -0800452
453
Fang Denge689e712013-11-13 18:27:06 -0800454 @staticmethod
455 def _output_perf_value(perf_value_writer, perf_data):
456 """Output perf values to result dir.
457
458 The perf values will be output to the result dir and
459 be subsequently uploaded to perf dashboard.
460
461 @param perf_value_writer: Should be an instance with the function
462 output_perf_value(), if None, no perf value
463 will be written. Typically this will be the
464 job object from an autotest test.
465 @param perf_data: A list of perf values, each value is
466 a dictionary that looks like
467 {'graph':'GraphA', 'trace':'metric1',
468 'units':'secs', 'value':0.5}
469 """
470 for perf_value in perf_data:
471 perf_value_writer.output_perf_value(
472 description=perf_value['trace'],
473 value=perf_value['value'],
474 units=perf_value['units'],
475 graph=perf_value['graph'])
476
477
Luis Lozano814c7182015-09-08 11:20:47 -0700478 def run_telemetry_benchmark(self, benchmark, perf_value_writer=None,
479 *args):
Simran Basi833814b2013-01-29 13:13:43 -0800480 """Runs a telemetry benchmark on a dut.
481
482 @param benchmark: Benchmark we want to run.
Fang Denge689e712013-11-13 18:27:06 -0800483 @param perf_value_writer: Should be an instance with the function
484 output_perf_value(), if None, no perf value
485 will be written. Typically this will be the
486 job object from an autotest test.
Luis Lozano814c7182015-09-08 11:20:47 -0700487 @param args: additional list of arguments to pass to the telemetry
488 execution script.
Simran Basi833814b2013-01-29 13:13:43 -0800489
490 @returns A TelemetryResult Instance with the results of this telemetry
491 execution.
492 """
Dave Tu6a404e62013-11-05 15:54:48 -0800493 logging.debug('Running telemetry benchmark: %s', benchmark)
Simran Basi833814b2013-01-29 13:13:43 -0800494 telemetry_script = os.path.join(self._telemetry_path,
495 TELEMETRY_RUN_BENCHMARKS_SCRIPT)
Luis Lozano814c7182015-09-08 11:20:47 -0700496 result = self._run_telemetry(telemetry_script, benchmark, *args)
Simran Basi833814b2013-01-29 13:13:43 -0800497 result.parse_benchmark_results()
498
Fang Denge689e712013-11-13 18:27:06 -0800499 if perf_value_writer:
500 self._output_perf_value(perf_value_writer, result.perf_data)
Simran Basi833814b2013-01-29 13:13:43 -0800501
502 if result.status is WARNING_STATUS:
Dave Tu6a404e62013-11-05 15:54:48 -0800503 raise error.TestWarn('Telemetry Benchmark: %s'
504 ' exited with Warnings.' % benchmark)
Simran Basi833814b2013-01-29 13:13:43 -0800505 if result.status is FAILED_STATUS:
Dave Tu6a404e62013-11-05 15:54:48 -0800506 raise error.TestFail('Telemetry Benchmark: %s'
507 ' failed to run.' % benchmark)
Simran Basi833814b2013-01-29 13:13:43 -0800508
509 return result