blob: 15eeb6df558663e4519c38956e11b258cb9acc01 [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
15
16
Dave Tu6a404e62013-11-05 15:54:48 -080017TELEMETRY_RUN_BENCHMARKS_SCRIPT = 'tools/perf/run_benchmark'
Simran Basi1dbfc132013-05-02 10:11:02 -070018TELEMETRY_RUN_CROS_TESTS_SCRIPT = 'chrome/test/telemetry/run_cros_tests'
Ilja Friedelf2473802014-03-28 17:54:34 -070019TELEMETRY_RUN_GPU_TESTS_SCRIPT = 'content/test/gpu/run_gpu_test.py'
Ilja H. Friedel086bc3f2014-02-27 22:17:55 -080020TELEMETRY_RUN_TESTS_SCRIPT = 'tools/telemetry/run_tests'
Achuith Bhandarkar124e4732014-01-21 15:27:54 -080021TELEMETRY_TIMEOUT_MINS = 120
Simran Basi833814b2013-01-29 13:13:43 -080022
23# Result Statuses
24SUCCESS_STATUS = 'SUCCESS'
25WARNING_STATUS = 'WARNING'
26FAILED_STATUS = 'FAILED'
27
Dennis Jeffreyc42fd302013-04-17 11:57:51 -070028# Regex for the RESULT output lines understood by chrome buildbot.
Ryo Hashimoto66ead5c2014-12-11 20:19:35 +090029# Keep in sync with
30# chromium/tools/build/scripts/slave/performance_log_processor.py.
Dennis Jeffreyc42fd302013-04-17 11:57:51 -070031RESULTS_REGEX = re.compile(r'(?P<IMPORTANT>\*)?RESULT '
Ryo Hashimoto66ead5c2014-12-11 20:19:35 +090032 r'(?P<GRAPH>[^:]*): (?P<TRACE>[^=]*)= '
33 r'(?P<VALUE>[\{\[]?[-\d\., ]+[\}\]]?)('
34 r' ?(?P<UNITS>.+))?')
35HISTOGRAM_REGEX = re.compile(r'(?P<IMPORTANT>\*)?HISTOGRAM '
36 r'(?P<GRAPH>[^:]*): (?P<TRACE>[^=]*)= '
37 r'(?P<VALUE_JSON>{.*})(?P<UNITS>.+)?')
Dennis Jeffreyc42fd302013-04-17 11:57:51 -070038
Simran Basi833814b2013-01-29 13:13:43 -080039
40class TelemetryResult(object):
41 """Class to represent the results of a telemetry run.
42
43 This class represents the results of a telemetry run, whether it ran
44 successful, failed or had warnings.
45 """
46
47
48 def __init__(self, exit_code=0, stdout='', stderr=''):
49 """Initializes this TelemetryResultObject instance.
50
51 @param status: Status of the telemtry run.
52 @param stdout: Stdout of the telemetry run.
53 @param stderr: Stderr of the telemetry run.
54 """
55 if exit_code == 0:
56 self.status = SUCCESS_STATUS
57 else:
58 self.status = FAILED_STATUS
59
Fang Denge689e712013-11-13 18:27:06 -080060 # A list of perf values, e.g.
61 # [{'graph': 'graphA', 'trace': 'page_load_time',
62 # 'units': 'secs', 'value':0.5}, ...]
63 self.perf_data = []
Simran Basi833814b2013-01-29 13:13:43 -080064 self._stdout = stdout
65 self._stderr = stderr
66 self.output = '\n'.join([stdout, stderr])
67
68
Dennis Jeffreyc42fd302013-04-17 11:57:51 -070069 def _cleanup_perf_string(self, str):
70 """Clean up a perf-related string by removing illegal characters.
Simran Basi833814b2013-01-29 13:13:43 -080071
Dennis Jeffreyc42fd302013-04-17 11:57:51 -070072 Perf keys stored in the chromeOS database may contain only letters,
73 numbers, underscores, periods, and dashes. Transform an inputted
74 string so that any illegal characters are replaced by underscores.
75
76 @param str: The perf string to clean up.
77
78 @return The cleaned-up perf string.
79 """
80 return re.sub(r'[^\w.-]', '_', str)
81
82
83 def _cleanup_units_string(self, units):
84 """Cleanup a units string.
85
86 Given a string representing units for a perf measurement, clean it up
87 by replacing certain illegal characters with meaningful alternatives.
88 Any other illegal characters should then be replaced with underscores.
Simran Basi833814b2013-01-29 13:13:43 -080089
90 Examples:
Dennis Jeffreyc42fd302013-04-17 11:57:51 -070091 count/time -> count_per_time
92 % -> percent
93 units! --> units_
94 score (bigger is better) -> score__bigger_is_better_
95 score (runs/s) -> score__runs_per_s_
Simran Basi833814b2013-01-29 13:13:43 -080096
Dennis Jeffreyc42fd302013-04-17 11:57:51 -070097 @param units: The units string to clean up.
Simran Basi833814b2013-01-29 13:13:43 -080098
Dennis Jeffreyc42fd302013-04-17 11:57:51 -070099 @return The cleaned-up units string.
Simran Basi833814b2013-01-29 13:13:43 -0800100 """
Dennis Jeffreyc42fd302013-04-17 11:57:51 -0700101 if '%' in units:
102 units = units.replace('%', 'percent')
Simran Basi833814b2013-01-29 13:13:43 -0800103 if '/' in units:
104 units = units.replace('/','_per_')
Dennis Jeffreyc42fd302013-04-17 11:57:51 -0700105 return self._cleanup_perf_string(units)
Simran Basi833814b2013-01-29 13:13:43 -0800106
107
108 def parse_benchmark_results(self):
109 """Parse the results of a telemetry benchmark run.
110
Dave Tu6a404e62013-11-05 15:54:48 -0800111 Stdout has the output in RESULT block format below.
Simran Basi833814b2013-01-29 13:13:43 -0800112
Dennis Jeffreyc42fd302013-04-17 11:57:51 -0700113 The lines of interest start with the substring "RESULT". These are
114 specially-formatted perf data lines that are interpreted by chrome
115 builbot (when the Telemetry tests run for chrome desktop) and are
116 parsed to extract perf data that can then be displayed on a perf
117 dashboard. This format is documented in the docstring of class
118 GraphingLogProcessor in this file in the chrome tree:
Simran Basi833814b2013-01-29 13:13:43 -0800119
Dennis Jeffreyc42fd302013-04-17 11:57:51 -0700120 chromium/tools/build/scripts/slave/process_log_utils.py
Simran Basi833814b2013-01-29 13:13:43 -0800121
Dennis Jeffreyc42fd302013-04-17 11:57:51 -0700122 Example RESULT output lines:
123 RESULT average_commit_time_by_url: http___www.ebay.com= 8.86528 ms
124 RESULT CodeLoad: CodeLoad= 6343 score (bigger is better)
125 RESULT ai-astar: ai-astar= [614,527,523,471,530,523,577,625,614,538] ms
126
127 Currently for chromeOS, we can only associate a single perf key (string)
128 with a perf value. That string can only contain letters, numbers,
129 dashes, periods, and underscores, as defined by write_keyval() in:
130
131 chromeos/src/third_party/autotest/files/client/common_lib/
132 base_utils.py
133
134 We therefore parse each RESULT line, clean up the strings to remove any
135 illegal characters not accepted by chromeOS, and construct a perf key
136 string based on the parsed components of the RESULT line (with each
137 component separated by a special delimiter). We prefix the perf key
138 with the substring "TELEMETRY" to identify it as a telemetry-formatted
139 perf key.
Simran Basi833814b2013-01-29 13:13:43 -0800140
141 Stderr has the format of Warnings/Tracebacks. There is always a default
Dennis Jeffreyc42fd302013-04-17 11:57:51 -0700142 warning of the display enviornment setting, followed by warnings of
Simran Basi833814b2013-01-29 13:13:43 -0800143 page timeouts or a traceback.
144
145 If there are any other warnings we flag the test as warning. If there
146 is a traceback we consider this test a failure.
Simran Basi833814b2013-01-29 13:13:43 -0800147 """
Simran Basi833814b2013-01-29 13:13:43 -0800148 if not self._stdout:
149 # Nothing in stdout implies a test failure.
150 logging.error('No stdout, test failed.')
151 self.status = FAILED_STATUS
152 return
153
154 stdout_lines = self._stdout.splitlines()
Simran Basi833814b2013-01-29 13:13:43 -0800155 for line in stdout_lines:
Dennis Jeffreyc42fd302013-04-17 11:57:51 -0700156 results_match = RESULTS_REGEX.search(line)
Ryo Hashimoto66ead5c2014-12-11 20:19:35 +0900157 histogram_match = HISTOGRAM_REGEX.search(line)
158 if results_match:
159 self._process_results_line(results_match)
160 elif histogram_match:
161 self._process_histogram_line(histogram_match)
Dennis Jeffreyc42fd302013-04-17 11:57:51 -0700162
163 pp = pprint.PrettyPrinter(indent=2)
Fang Denge689e712013-11-13 18:27:06 -0800164 logging.debug('Perf values: %s', pp.pformat(self.perf_data))
Simran Basi833814b2013-01-29 13:13:43 -0800165
166 if self.status is SUCCESS_STATUS:
167 return
168
169 # Otherwise check if simply a Warning occurred or a Failure,
170 # i.e. a Traceback is listed.
171 self.status = WARNING_STATUS
172 for line in self._stderr.splitlines():
173 if line.startswith('Traceback'):
174 self.status = FAILED_STATUS
175
Ryo Hashimoto66ead5c2014-12-11 20:19:35 +0900176 def _process_results_line(self, line_match):
177 """Processes a line that matches the standard RESULT line format.
178
179 Args:
180 line_match: A MatchObject as returned by re.search.
181 """
182 match_dict = line_match.groupdict()
183 graph_name = self._cleanup_perf_string(match_dict['GRAPH'].strip())
184 trace_name = self._cleanup_perf_string(match_dict['TRACE'].strip())
185 units = self._cleanup_units_string(
186 (match_dict['UNITS'] or 'units').strip())
187 value = match_dict['VALUE'].strip()
188 unused_important = match_dict['IMPORTANT'] or False # Unused now.
189
190 if value.startswith('['):
191 # A list of values, e.g., "[12,15,8,7,16]". Extract just the
192 # numbers, compute the average and use that. In this example,
193 # we'd get 12+15+8+7+16 / 5 --> 11.6.
194 value_list = [float(x) for x in value.strip('[],').split(',')]
195 value = float(sum(value_list)) / len(value_list)
196 elif value.startswith('{'):
197 # A single value along with a standard deviation, e.g.,
198 # "{34.2,2.15}". Extract just the value itself and use that.
199 # In this example, we'd get 34.2.
200 value_list = [float(x) for x in value.strip('{},').split(',')]
201 value = value_list[0] # Position 0 is the value.
202 elif re.search('^\d+$', value):
203 value = int(value)
204 else:
205 value = float(value)
206
207 self.perf_data.append({'graph':graph_name, 'trace': trace_name,
208 'units': units, 'value': value})
209
210 def _process_histogram_line(self, line_match):
211 """Processes a line that matches the HISTOGRAM line format.
212
213 Args:
214 line_match: A MatchObject as returned by re.search.
215 """
216 match_dict = line_match.groupdict()
217 graph_name = self._cleanup_perf_string(match_dict['GRAPH'].strip())
218 trace_name = self._cleanup_perf_string(match_dict['TRACE'].strip())
219 units = self._cleanup_units_string(
220 (match_dict['UNITS'] or 'units').strip())
221 histogram_json = match_dict['VALUE_JSON'].strip()
222 unused_important = match_dict['IMPORTANT'] or False # Unused now.
223 histogram_data = json.loads(histogram_json)
224
225 # Compute geometric mean
226 count = 0
227 sum_of_logs = 0
228 for bucket in histogram_data['buckets']:
229 mean = (bucket['low'] + bucket['high']) / 2.0
230 if mean > 0:
231 sum_of_logs += math.log(mean) * bucket['count']
232 count += bucket['count']
233
234 value = math.exp(sum_of_logs / count) if count > 0 else 0.0
235
236 self.perf_data.append({'graph':graph_name, 'trace': trace_name,
237 'units': units, 'value': value})
Simran Basi833814b2013-01-29 13:13:43 -0800238
239class TelemetryRunner(object):
240 """Class responsible for telemetry for a given build.
241
242 This class will extract and install telemetry on the devserver and is
243 responsible for executing the telemetry benchmarks and returning their
244 output to the caller.
245 """
246
Luis Lozano23ae3192013-11-08 16:22:46 -0800247 def __init__(self, host, local=False):
Simran Basi833814b2013-01-29 13:13:43 -0800248 """Initializes this telemetry runner instance.
249
250 If telemetry is not installed for this build, it will be.
Luis Lozano23ae3192013-11-08 16:22:46 -0800251
252 @param host: Host where the test will be run.
253 @param local: If set, no devserver will be used, test will be run
254 locally.
Simran Basi833814b2013-01-29 13:13:43 -0800255 """
256 self._host = host
Ilja H. Friedelc7bf3102014-05-13 17:31:25 -0700257 self._devserver = None
258 self._telemetry_path = None
Luis Lozano23ae3192013-11-08 16:22:46 -0800259 # TODO (llozano crbug.com/324964). Remove conditional code.
260 # Use a class hierarchy instead.
261 if local:
262 self._setup_local_telemetry()
263 else:
264 self._setup_devserver_telemetry()
265
266 logging.debug('Telemetry Path: %s', self._telemetry_path)
267
268
269 def _setup_devserver_telemetry(self):
270 """Setup Telemetry to use the devserver."""
271 logging.debug('Setting up telemetry for devserver testing')
Simran Basi833814b2013-01-29 13:13:43 -0800272 logging.debug('Grabbing build from AFE.')
273
Luis Lozano23ae3192013-11-08 16:22:46 -0800274 build = self._host.get_build()
Simran Basi833814b2013-01-29 13:13:43 -0800275 if not build:
276 logging.error('Unable to locate build label for host: %s.',
277 self._host.hostname)
278 raise error.AutotestError('Failed to grab build for host %s.' %
279 self._host.hostname)
280
281 logging.debug('Setting up telemetry for build: %s', build)
282
283 self._devserver = dev_server.ImageServer.resolve(build)
Simran Basicf81f682014-12-03 16:31:39 -0800284 self._devserver.stage_artifacts(build, ['autotest_packages'])
Simran Basi833814b2013-01-29 13:13:43 -0800285 self._telemetry_path = self._devserver.setup_telemetry(build=build)
Luis Lozano23ae3192013-11-08 16:22:46 -0800286
287
288 def _setup_local_telemetry(self):
289 """Setup Telemetry to use local path to its sources.
290
291 First look for chrome source root, either externally mounted, or inside
292 the chroot. Prefer chrome-src-internal source tree to chrome-src.
293 """
294 TELEMETRY_DIR = 'src'
295 CHROME_LOCAL_SRC = '/var/cache/chromeos-cache/distfiles/target/'
Josh Triplett05208c92014-07-17 13:21:29 -0700296 CHROME_EXTERNAL_SRC = os.path.expanduser('~/chrome_root/')
Luis Lozano23ae3192013-11-08 16:22:46 -0800297
298 logging.debug('Setting up telemetry for local testing')
299
300 sources_list = ('chrome-src-internal', 'chrome-src')
Josh Triplett05208c92014-07-17 13:21:29 -0700301 dir_list = [CHROME_EXTERNAL_SRC]
Luis Lozano23ae3192013-11-08 16:22:46 -0800302 dir_list.extend(
303 [os.path.join(CHROME_LOCAL_SRC, x) for x in sources_list])
304 if 'CHROME_ROOT' in os.environ:
305 dir_list.insert(0, os.environ['CHROME_ROOT'])
306
307 telemetry_src = ''
308 for dir in dir_list:
309 if os.path.exists(dir):
310 telemetry_src = os.path.join(dir, TELEMETRY_DIR)
311 break
312 else:
313 raise error.TestError('Telemetry source directory not found.')
314
315 self._devserver = None
316 self._telemetry_path = telemetry_src
317
318
Luis Lozano814c7182015-09-08 11:20:47 -0700319 def _get_telemetry_cmd(self, script, test_or_benchmark, *args):
Luis Lozano23ae3192013-11-08 16:22:46 -0800320 """Build command to execute telemetry based on script and benchmark.
321
322 @param script: Telemetry script we want to run. For example:
323 [path_to_telemetry_src]/src/tools/telemetry/run_tests.
324 @param test_or_benchmark: Name of the test or benchmark we want to run,
325 with the page_set (if required) as part of
326 the string.
Luis Lozano814c7182015-09-08 11:20:47 -0700327 @param args: additional list of arguments to pass to the script.
328
Luis Lozano23ae3192013-11-08 16:22:46 -0800329 @returns Full telemetry command to execute the script.
330 """
331 telemetry_cmd = []
332 if self._devserver:
333 devserver_hostname = self._devserver.url().split(
334 'http://')[1].split(':')[0]
335 telemetry_cmd.extend(['ssh', devserver_hostname])
336
337 telemetry_cmd.extend(
338 ['python',
339 script,
Ilja H. Friedel6965bd82014-05-20 18:29:15 -0700340 '--verbose',
Luis Lozano23ae3192013-11-08 16:22:46 -0800341 '--browser=cros-chrome',
Luis Lozano814c7182015-09-08 11:20:47 -0700342 '--remote=%s' % self._host.hostname])
343 telemetry_cmd.extend(args)
344 telemetry_cmd.append(test_or_benchmark)
345
Luis Lozano23ae3192013-11-08 16:22:46 -0800346 return telemetry_cmd
Simran Basi833814b2013-01-29 13:13:43 -0800347
348
Luis Lozano814c7182015-09-08 11:20:47 -0700349 def _run_telemetry(self, script, test_or_benchmark, *args):
Simran Basi833814b2013-01-29 13:13:43 -0800350 """Runs telemetry on a dut.
351
352 @param script: Telemetry script we want to run. For example:
Luis Lozano23ae3192013-11-08 16:22:46 -0800353 [path_to_telemetry_src]/src/tools/telemetry/run_tests.
Simran Basi833814b2013-01-29 13:13:43 -0800354 @param test_or_benchmark: Name of the test or benchmark we want to run,
355 with the page_set (if required) as part of the
356 string.
Luis Lozano814c7182015-09-08 11:20:47 -0700357 @param args: additional list of arguments to pass to the script.
Simran Basi833814b2013-01-29 13:13:43 -0800358
359 @returns A TelemetryResult Instance with the results of this telemetry
360 execution.
361 """
Simran Basi1dbfc132013-05-02 10:11:02 -0700362 # TODO (sbasi crbug.com/239933) add support for incognito mode.
Simran Basi833814b2013-01-29 13:13:43 -0800363
Luis Lozano814c7182015-09-08 11:20:47 -0700364 telemetry_cmd = self._get_telemetry_cmd(script,
365 test_or_benchmark,
366 *args)
Luis Lozano23ae3192013-11-08 16:22:46 -0800367 logging.debug('Running Telemetry: %s', ' '.join(telemetry_cmd))
368
Simran Basi833814b2013-01-29 13:13:43 -0800369 output = StringIO.StringIO()
370 error_output = StringIO.StringIO()
371 exit_code = 0
372 try:
Luis Lozano23ae3192013-11-08 16:22:46 -0800373 result = utils.run(' '.join(telemetry_cmd), stdout_tee=output,
Simran Basi833814b2013-01-29 13:13:43 -0800374 stderr_tee=error_output,
375 timeout=TELEMETRY_TIMEOUT_MINS*60)
376 exit_code = result.exit_status
377 except error.CmdError as e:
378 # Telemetry returned a return code of not 0; for benchmarks this
379 # can be due to a timeout on one of the pages of the page set and
380 # we may still have data on the rest. For a test however this
381 # indicates failure.
382 logging.debug('Error occurred executing telemetry.')
383 exit_code = e.result_obj.exit_status
384
385 stdout = output.getvalue()
386 stderr = error_output.getvalue()
387 logging.debug('Telemetry completed with exit code: %d.\nstdout:%s\n'
388 'stderr:%s', exit_code, stdout, stderr)
389
390 return TelemetryResult(exit_code=exit_code, stdout=stdout,
391 stderr=stderr)
392
393
Luis Lozano814c7182015-09-08 11:20:47 -0700394 def _run_test(self, script, test, *args):
Simran Basi1dbfc132013-05-02 10:11:02 -0700395 """Runs a telemetry test on a dut.
396
397 @param script: Which telemetry test script we want to run. Can be
398 telemetry's base test script or the Chrome OS specific
399 test script.
400 @param test: Telemetry test we want to run.
Luis Lozano814c7182015-09-08 11:20:47 -0700401 @param args: additional list of arguments to pass to the script.
Simran Basi1dbfc132013-05-02 10:11:02 -0700402
403 @returns A TelemetryResult Instance with the results of this telemetry
404 execution.
405 """
406 logging.debug('Running telemetry test: %s', test)
407 telemetry_script = os.path.join(self._telemetry_path, script)
Luis Lozano814c7182015-09-08 11:20:47 -0700408 result = self._run_telemetry(telemetry_script, test, *args)
Simran Basi1dbfc132013-05-02 10:11:02 -0700409 if result.status is FAILED_STATUS:
Ilja H. Friedelc7bf3102014-05-13 17:31:25 -0700410 raise error.TestFail('Telemetry test %s failed.' % test)
Simran Basi1dbfc132013-05-02 10:11:02 -0700411 return result
412
413
Luis Lozano814c7182015-09-08 11:20:47 -0700414 def run_telemetry_test(self, test, *args):
Simran Basi833814b2013-01-29 13:13:43 -0800415 """Runs a telemetry test on a dut.
416
417 @param test: Telemetry test we want to run.
Luis Lozano814c7182015-09-08 11:20:47 -0700418 @param args: additional list of arguments to pass to the telemetry
419 execution script.
Simran Basi833814b2013-01-29 13:13:43 -0800420
421 @returns A TelemetryResult Instance with the results of this telemetry
422 execution.
423 """
Luis Lozano814c7182015-09-08 11:20:47 -0700424 return self._run_test(TELEMETRY_RUN_TESTS_SCRIPT, test, *args)
Simran Basi1dbfc132013-05-02 10:11:02 -0700425
426
Luis Lozano814c7182015-09-08 11:20:47 -0700427 def run_cros_telemetry_test(self, test, *args):
Simran Basi1dbfc132013-05-02 10:11:02 -0700428 """Runs a cros specific telemetry test on a dut.
429
430 @param test: Telemetry test we want to run.
Luis Lozano814c7182015-09-08 11:20:47 -0700431 @param args: additional list of arguments to pass to the telemetry
432 execution script.
Simran Basi1dbfc132013-05-02 10:11:02 -0700433
434 @returns A TelemetryResult instance with the results of this telemetry
435 execution.
436 """
Luis Lozano814c7182015-09-08 11:20:47 -0700437 return self._run_test(TELEMETRY_RUN_CROS_TESTS_SCRIPT, test, *args)
Simran Basi833814b2013-01-29 13:13:43 -0800438
439
Luis Lozano814c7182015-09-08 11:20:47 -0700440 def run_gpu_test(self, test, *args):
Ilja H. Friedel086bc3f2014-02-27 22:17:55 -0800441 """Runs a gpu test on a dut.
442
443 @param test: Gpu test we want to run.
Luis Lozano814c7182015-09-08 11:20:47 -0700444 @param args: additional list of arguments to pass to the telemetry
445 execution script.
Ilja H. Friedel086bc3f2014-02-27 22:17:55 -0800446
447 @returns A TelemetryResult instance with the results of this telemetry
448 execution.
449 """
Luis Lozano814c7182015-09-08 11:20:47 -0700450 return self._run_test(TELEMETRY_RUN_GPU_TESTS_SCRIPT, test, *args)
Ilja H. Friedel086bc3f2014-02-27 22:17:55 -0800451
452
Fang Denge689e712013-11-13 18:27:06 -0800453 @staticmethod
454 def _output_perf_value(perf_value_writer, perf_data):
455 """Output perf values to result dir.
456
457 The perf values will be output to the result dir and
458 be subsequently uploaded to perf dashboard.
459
460 @param perf_value_writer: Should be an instance with the function
461 output_perf_value(), if None, no perf value
462 will be written. Typically this will be the
463 job object from an autotest test.
464 @param perf_data: A list of perf values, each value is
465 a dictionary that looks like
466 {'graph':'GraphA', 'trace':'metric1',
467 'units':'secs', 'value':0.5}
468 """
469 for perf_value in perf_data:
470 perf_value_writer.output_perf_value(
471 description=perf_value['trace'],
472 value=perf_value['value'],
473 units=perf_value['units'],
474 graph=perf_value['graph'])
475
476
Luis Lozano814c7182015-09-08 11:20:47 -0700477 def run_telemetry_benchmark(self, benchmark, perf_value_writer=None,
478 *args):
Simran Basi833814b2013-01-29 13:13:43 -0800479 """Runs a telemetry benchmark on a dut.
480
481 @param benchmark: Benchmark we want to run.
Fang Denge689e712013-11-13 18:27:06 -0800482 @param perf_value_writer: Should be an instance with the function
483 output_perf_value(), if None, no perf value
484 will be written. Typically this will be the
485 job object from an autotest test.
Luis Lozano814c7182015-09-08 11:20:47 -0700486 @param args: additional list of arguments to pass to the telemetry
487 execution script.
Simran Basi833814b2013-01-29 13:13:43 -0800488
489 @returns A TelemetryResult Instance with the results of this telemetry
490 execution.
491 """
Dave Tu6a404e62013-11-05 15:54:48 -0800492 logging.debug('Running telemetry benchmark: %s', benchmark)
Simran Basi833814b2013-01-29 13:13:43 -0800493 telemetry_script = os.path.join(self._telemetry_path,
494 TELEMETRY_RUN_BENCHMARKS_SCRIPT)
Luis Lozano814c7182015-09-08 11:20:47 -0700495 result = self._run_telemetry(telemetry_script, benchmark, *args)
Simran Basi833814b2013-01-29 13:13:43 -0800496 result.parse_benchmark_results()
497
Fang Denge689e712013-11-13 18:27:06 -0800498 if perf_value_writer:
499 self._output_perf_value(perf_value_writer, result.perf_data)
Simran Basi833814b2013-01-29 13:13:43 -0800500
501 if result.status is WARNING_STATUS:
Dave Tu6a404e62013-11-05 15:54:48 -0800502 raise error.TestWarn('Telemetry Benchmark: %s'
503 ' exited with Warnings.' % benchmark)
Simran Basi833814b2013-01-29 13:13:43 -0800504 if result.status is FAILED_STATUS:
Dave Tu6a404e62013-11-05 15:54:48 -0800505 raise error.TestFail('Telemetry Benchmark: %s'
506 ' failed to run.' % benchmark)
Simran Basi833814b2013-01-29 13:13:43 -0800507
508 return result