blob: 5f8ed14f5b38ed464058b8c580af11e1688d9a4f [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
Kuo-Hsin Yang4a006172018-04-25 14:44:55 +08005import json
Simran Basi833814b2013-01-29 13:13:43 -08006import logging
Kuo-Hsin Yang14d002b2019-06-06 13:18:04 +08007import numbers
Simran Basi833814b2013-01-29 13:13:43 -08008import os
Chinglin Yu0adaf112020-01-20 14:48:20 +08009import tempfile
Simran Basi833814b2013-01-29 13:13:43 -080010import StringIO
11
Zhizhou Yang65d37042020-01-08 17:44:12 -080012import numpy
13
Simran Basi833814b2013-01-29 13:13:43 -080014from autotest_lib.client.common_lib import error, utils
15from autotest_lib.client.common_lib.cros import dev_server
16
Dave Tu6a404e62013-11-05 15:54:48 -080017TELEMETRY_RUN_BENCHMARKS_SCRIPT = 'tools/perf/run_benchmark'
Ilja H. Friedel086bc3f2014-02-27 22:17:55 -080018TELEMETRY_RUN_TESTS_SCRIPT = 'tools/telemetry/run_tests'
Gurchetan Singhfaf75e92017-04-17 18:09:44 -070019TELEMETRY_RUN_GPU_TESTS_SCRIPT = 'content/test/gpu/run_gpu_integration_test.py'
Mao Huangc9642d72017-09-28 16:50:02 +080020TELEMETRY_TIMEOUT_MINS = 150
Simran Basi833814b2013-01-29 13:13:43 -080021
Ting-Yuan Huange5b19132016-03-22 13:02:41 +080022DUT_CHROME_ROOT = '/usr/local/telemetry/src'
23
Kuo-Hsin Yang14d002b2019-06-06 13:18:04 +080024CHART_JSON_RESULT = 'results-chart.json'
25HISTOGRAM_SET_RESULT = 'histograms.json'
Zhizhou Yang65d37042020-01-08 17:44:12 -080026PROFILE_ARTIFACTS = 'artifacts'
Kuo-Hsin Yang14d002b2019-06-06 13:18:04 +080027
Simran Basi833814b2013-01-29 13:13:43 -080028# Result Statuses
29SUCCESS_STATUS = 'SUCCESS'
30WARNING_STATUS = 'WARNING'
31FAILED_STATUS = 'FAILED'
32
Kuo-Hsin Yang07da7b62018-08-08 16:56:06 +080033# A list of telemetry tests that cannot run on dut.
Kuo-Hsin Yange0915472018-09-10 10:36:16 +080034ON_DUT_BLACKLIST = [
Zhizhou Yang65d37042020-01-08 17:44:12 -080035 'cros_ui_smoothness', # crbug/976839
36 'loading.desktop', # crbug/882299
37 'rendering.desktop', # crbug/882291
38 'system_health.memory_desktop', # crbug/874386
Kuo-Hsin Yange0915472018-09-10 10:36:16 +080039]
Simran Basi833814b2013-01-29 13:13:43 -080040
Zhizhou Yang65d37042020-01-08 17:44:12 -080041
Simran Basi833814b2013-01-29 13:13:43 -080042class TelemetryResult(object):
43 """Class to represent the results of a telemetry run.
44
45 This class represents the results of a telemetry run, whether it ran
46 successful, failed or had warnings.
47 """
48
Simran Basi833814b2013-01-29 13:13:43 -080049 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
Simran Basi833814b2013-01-29 13:13:43 -080061 self._stdout = stdout
62 self._stderr = stderr
63 self.output = '\n'.join([stdout, stderr])
64
65
Simran Basi833814b2013-01-29 13:13:43 -080066class TelemetryRunner(object):
67 """Class responsible for telemetry for a given build.
68
69 This class will extract and install telemetry on the devserver and is
70 responsible for executing the telemetry benchmarks and returning their
71 output to the caller.
72 """
73
Kuo-Hsin Yang66324262019-12-10 10:36:34 +080074 def __init__(self, host, local=False, telemetry_on_dut=True):
Simran Basi833814b2013-01-29 13:13:43 -080075 """Initializes this telemetry runner instance.
76
77 If telemetry is not installed for this build, it will be.
Luis Lozano23ae3192013-11-08 16:22:46 -080078
Ting-Yuan Huange5b19132016-03-22 13:02:41 +080079 Basically, the following commands on the local pc on which test_that
80 will be executed, depending on the 4 possible combinations of
81 local x telemetry_on_dut:
82
83 local=True, telemetry_on_dut=False:
Kuo-Hsin Yang66324262019-12-10 10:36:34 +080084 python2 run_benchmark --browser=cros-chrome --remote=[dut] [test]
Ting-Yuan Huange5b19132016-03-22 13:02:41 +080085
86 local=True, telemetry_on_dut=True:
Kuo-Hsin Yang66324262019-12-10 10:36:34 +080087 ssh [dut] python2 run_benchmark --browser=system [test]
Ting-Yuan Huange5b19132016-03-22 13:02:41 +080088
89 local=False, telemetry_on_dut=False:
Kuo-Hsin Yang66324262019-12-10 10:36:34 +080090 ssh [devserver] python2 run_benchmark --browser=cros-chrome
91 --remote=[dut] [test]
Ting-Yuan Huange5b19132016-03-22 13:02:41 +080092
93 local=False, telemetry_on_dut=True:
Kuo-Hsin Yang66324262019-12-10 10:36:34 +080094 ssh [devserver] ssh [dut] python2 run_benchmark --browser=system [test]
Ting-Yuan Huange5b19132016-03-22 13:02:41 +080095
Luis Lozano23ae3192013-11-08 16:22:46 -080096 @param host: Host where the test will be run.
97 @param local: If set, no devserver will be used, test will be run
98 locally.
Ting-Yuan Huange5b19132016-03-22 13:02:41 +080099 If not set, "ssh [devserver] " will be appended to test
100 commands.
101 @param telemetry_on_dut: If set, telemetry itself (the test harness)
102 will run on dut.
103 It decides browser=[system|cros-chrome]
Simran Basi833814b2013-01-29 13:13:43 -0800104 """
105 self._host = host
Ilja H. Friedelc7bf3102014-05-13 17:31:25 -0700106 self._devserver = None
107 self._telemetry_path = None
Zhizhou Yang65d37042020-01-08 17:44:12 -0800108 self._perf_value_writer = None
Ting-Yuan Huange5b19132016-03-22 13:02:41 +0800109 self._telemetry_on_dut = telemetry_on_dut
Luis Lozano23ae3192013-11-08 16:22:46 -0800110 # TODO (llozano crbug.com/324964). Remove conditional code.
111 # Use a class hierarchy instead.
112 if local:
113 self._setup_local_telemetry()
114 else:
115 self._setup_devserver_telemetry()
Chinglin Yu0adaf112020-01-20 14:48:20 +0800116 self._benchmark_deps = None
Luis Lozano23ae3192013-11-08 16:22:46 -0800117
118 logging.debug('Telemetry Path: %s', self._telemetry_path)
119
Luis Lozano23ae3192013-11-08 16:22:46 -0800120 def _setup_devserver_telemetry(self):
121 """Setup Telemetry to use the devserver."""
122 logging.debug('Setting up telemetry for devserver testing')
Simran Basi833814b2013-01-29 13:13:43 -0800123 logging.debug('Grabbing build from AFE.')
Prathmesh Prabhucfff58a2017-02-06 10:07:43 -0800124 info = self._host.host_info_store.get()
125 if not info.build:
Simran Basi833814b2013-01-29 13:13:43 -0800126 logging.error('Unable to locate build label for host: %s.',
Dean Liaoe3e75f62017-11-14 10:36:43 +0800127 self._host.host_port)
Zhizhou Yang65d37042020-01-08 17:44:12 -0800128 raise error.AutotestError(
129 'Failed to grab build for host %s.' % self._host.host_port)
Simran Basi833814b2013-01-29 13:13:43 -0800130
Prathmesh Prabhucfff58a2017-02-06 10:07:43 -0800131 logging.debug('Setting up telemetry for build: %s', info.build)
Simran Basi833814b2013-01-29 13:13:43 -0800132
Prathmesh Prabhucfff58a2017-02-06 10:07:43 -0800133 self._devserver = dev_server.ImageServer.resolve(
134 info.build, hostname=self._host.hostname)
135 self._devserver.stage_artifacts(info.build, ['autotest_packages'])
Zhizhou Yang65d37042020-01-08 17:44:12 -0800136 self._telemetry_path = self._devserver.setup_telemetry(
137 build=info.build)
Luis Lozano23ae3192013-11-08 16:22:46 -0800138
139 def _setup_local_telemetry(self):
140 """Setup Telemetry to use local path to its sources.
141
142 First look for chrome source root, either externally mounted, or inside
143 the chroot. Prefer chrome-src-internal source tree to chrome-src.
144 """
145 TELEMETRY_DIR = 'src'
146 CHROME_LOCAL_SRC = '/var/cache/chromeos-cache/distfiles/target/'
Josh Triplett05208c92014-07-17 13:21:29 -0700147 CHROME_EXTERNAL_SRC = os.path.expanduser('~/chrome_root/')
Luis Lozano23ae3192013-11-08 16:22:46 -0800148
149 logging.debug('Setting up telemetry for local testing')
150
151 sources_list = ('chrome-src-internal', 'chrome-src')
Josh Triplett05208c92014-07-17 13:21:29 -0700152 dir_list = [CHROME_EXTERNAL_SRC]
Luis Lozano23ae3192013-11-08 16:22:46 -0800153 dir_list.extend(
154 [os.path.join(CHROME_LOCAL_SRC, x) for x in sources_list])
155 if 'CHROME_ROOT' in os.environ:
156 dir_list.insert(0, os.environ['CHROME_ROOT'])
157
158 telemetry_src = ''
159 for dir in dir_list:
160 if os.path.exists(dir):
161 telemetry_src = os.path.join(dir, TELEMETRY_DIR)
162 break
163 else:
164 raise error.TestError('Telemetry source directory not found.')
165
166 self._devserver = None
167 self._telemetry_path = telemetry_src
168
Kuo-Hsin Yang14d002b2019-06-06 13:18:04 +0800169 def _get_telemetry_cmd(self, script, test_or_benchmark, output_format,
Zhizhou Yangf32fda02019-09-25 14:01:45 -0700170 *args, **kwargs):
Luis Lozano23ae3192013-11-08 16:22:46 -0800171 """Build command to execute telemetry based on script and benchmark.
172
173 @param script: Telemetry script we want to run. For example:
174 [path_to_telemetry_src]/src/tools/telemetry/run_tests.
175 @param test_or_benchmark: Name of the test or benchmark we want to run,
176 with the page_set (if required) as part of
177 the string.
Zhizhou Yang65d37042020-01-08 17:44:12 -0800178 @param output_format: Format of the json result file: histogram or
179 chart-json.
Luis Lozano814c7182015-09-08 11:20:47 -0700180 @param args: additional list of arguments to pass to the script.
Zhizhou Yangf32fda02019-09-25 14:01:45 -0700181 @param kwargs: additional list of keyword arguments to pass to the
182 script.
Luis Lozano814c7182015-09-08 11:20:47 -0700183
Luis Lozano23ae3192013-11-08 16:22:46 -0800184 @returns Full telemetry command to execute the script.
185 """
186 telemetry_cmd = []
187 if self._devserver:
Allen Lia5cfb972016-12-27 17:17:22 -0800188 devserver_hostname = self._devserver.hostname
Luis Lozano23ae3192013-11-08 16:22:46 -0800189 telemetry_cmd.extend(['ssh', devserver_hostname])
190
Zhizhou Yangf32fda02019-09-25 14:01:45 -0700191 no_verbose = kwargs.get('no_verbose', False)
192
Zhizhou Yang65d37042020-01-08 17:44:12 -0800193 output_dir = (DUT_CHROME_ROOT
194 if self._telemetry_on_dut else self._telemetry_path)
195 # Create a temp directory to hold single test run.
196 if self._perf_value_writer:
197 output_dir = os.path.join(
198 output_dir, self._perf_value_writer.tmpdir.strip('/'))
199
Ting-Yuan Huange5b19132016-03-22 13:02:41 +0800200 if self._telemetry_on_dut:
Denis Nikitinfdc2c712019-11-15 15:33:52 -0800201 telemetry_cmd.extend([
Zhizhou Yang65d37042020-01-08 17:44:12 -0800202 self._host.ssh_command(
203 alive_interval=900, connection_attempts=4),
204 'python2',
205 script,
206 '--output-format=%s' % output_format,
207 '--output-dir=%s' % output_dir,
208 '--browser=system',
Denis Nikitinfdc2c712019-11-15 15:33:52 -0800209 ])
Ting-Yuan Huange5b19132016-03-22 13:02:41 +0800210 else:
Denis Nikitinfdc2c712019-11-15 15:33:52 -0800211 telemetry_cmd.extend([
Zhizhou Yang65d37042020-01-08 17:44:12 -0800212 'python2',
213 script,
214 '--browser=cros-chrome',
215 '--output-format=%s' % output_format,
216 '--output-dir=%s' % output_dir,
217 '--remote=%s' % self._host.host_port,
Denis Nikitinfdc2c712019-11-15 15:33:52 -0800218 ])
Zhizhou Yangf32fda02019-09-25 14:01:45 -0700219 if not no_verbose:
220 telemetry_cmd.append('--verbose')
Luis Lozano814c7182015-09-08 11:20:47 -0700221 telemetry_cmd.extend(args)
222 telemetry_cmd.append(test_or_benchmark)
223
Keith Haddow1e5c7012016-03-09 16:05:37 -0800224 return ' '.join(telemetry_cmd)
225
Zhizhou Yang65d37042020-01-08 17:44:12 -0800226 def _scp_telemetry_results_cmd(self, perf_results_dir, output_format,
227 artifacts):
Keith Haddow1e5c7012016-03-09 16:05:37 -0800228 """Build command to copy the telemetry results from the devserver.
229
230 @param perf_results_dir: directory path where test output is to be
231 collected.
Zhizhou Yang65d37042020-01-08 17:44:12 -0800232 @param output_format: Format of the json result file: histogram or
233 chart-json.
234 @param artifacts: Whether we want to copy artifacts directory.
235
236 @returns SCP command to copy the results json to the specified
237 directory.
Keith Haddow1e5c7012016-03-09 16:05:37 -0800238 """
Dean Liaoe3e75f62017-11-14 10:36:43 +0800239 if not perf_results_dir:
240 return ''
241
Kuo-Hsin Yang14d002b2019-06-06 13:18:04 +0800242 output_filename = CHART_JSON_RESULT
243 if output_format == 'histograms':
244 output_filename = HISTOGRAM_SET_RESULT
Zhizhou Yang65d37042020-01-08 17:44:12 -0800245 scp_cmd = []
Dean Liaoe3e75f62017-11-14 10:36:43 +0800246 if self._telemetry_on_dut:
Zhizhou Yang65d37042020-01-08 17:44:12 -0800247 scp_cmd.extend(['scp', '-r'])
248 scp_cmd.append(
249 self._host.make_ssh_options(
250 alive_interval=900, connection_attempts=4))
Dean Liaoe3e75f62017-11-14 10:36:43 +0800251 if not self._host.is_default_port:
252 scp_cmd.append('-P %d' % self._host.port)
Zhizhou Yang65d37042020-01-08 17:44:12 -0800253 src = 'root@%s:%s' % (self._host.hostname, DUT_CHROME_ROOT)
Dean Liaoe3e75f62017-11-14 10:36:43 +0800254 else:
Zhizhou Yang65d37042020-01-08 17:44:12 -0800255 # Use rsync --remove-source-file to move rather than copy from
256 # server. This is because each run will generate certain artifacts
257 # and will not be removed after, making result size getting larger.
258 # We don't do this for results on DUT because 1) rsync doesn't work
259 # 2) DUT will be reflashed frequently and no need to worry about
260 # result size.
261 scp_cmd.extend(['rsync', '-avz', '--remove-source-files'])
Dean Liaoe3e75f62017-11-14 10:36:43 +0800262 devserver_hostname = ''
Ricky Liangd186f3e2016-03-15 16:50:55 +0800263 if self._devserver:
Allen Lia5cfb972016-12-27 17:17:22 -0800264 devserver_hostname = self._devserver.hostname + ':'
Zhizhou Yang65d37042020-01-08 17:44:12 -0800265 src = '%s%s' % (devserver_hostname, self._telemetry_path)
Keith Haddow1e5c7012016-03-09 16:05:37 -0800266
Zhizhou Yang65d37042020-01-08 17:44:12 -0800267 if self._perf_value_writer:
268 src = os.path.join(src, self._perf_value_writer.tmpdir.strip('/'))
269
270 scp_cmd.append(os.path.join(src, output_filename))
271
272 # Copy artifacts back to result directory if needed.
273 if artifacts:
274 scp_cmd.append(os.path.join(src, PROFILE_ARTIFACTS))
275
276 scp_cmd.append(perf_results_dir)
Keith Haddow1e5c7012016-03-09 16:05:37 -0800277 return ' '.join(scp_cmd)
278
Keith Haddow1e5c7012016-03-09 16:05:37 -0800279 def _run_cmd(self, cmd):
280 """Execute an command in a external shell and capture the output.
281
282 @param cmd: String of is a valid shell command.
283
284 @returns The standard out, standard error and the integer exit code of
285 the executed command.
286 """
287 logging.debug('Running: %s', cmd)
288
289 output = StringIO.StringIO()
290 error_output = StringIO.StringIO()
291 exit_code = 0
292 try:
Zhizhou Yang65d37042020-01-08 17:44:12 -0800293 result = utils.run(
294 cmd,
295 stdout_tee=output,
296 stderr_tee=error_output,
297 timeout=TELEMETRY_TIMEOUT_MINS * 60)
Keith Haddow1e5c7012016-03-09 16:05:37 -0800298 exit_code = result.exit_status
299 except error.CmdError as e:
300 logging.debug('Error occurred executing.')
301 exit_code = e.result_obj.exit_status
302
303 stdout = output.getvalue()
304 stderr = error_output.getvalue()
305 logging.debug('Completed with exit code: %d.\nstdout:%s\n'
306 'stderr:%s', exit_code, stdout, stderr)
307 return stdout, stderr, exit_code
Simran Basi833814b2013-01-29 13:13:43 -0800308
Zhizhou Yang65d37042020-01-08 17:44:12 -0800309 def _run_telemetry(self, script, test_or_benchmark, output_format, *args,
310 **kwargs):
Simran Basi833814b2013-01-29 13:13:43 -0800311 """Runs telemetry on a dut.
312
313 @param script: Telemetry script we want to run. For example:
Luis Lozano23ae3192013-11-08 16:22:46 -0800314 [path_to_telemetry_src]/src/tools/telemetry/run_tests.
Simran Basi833814b2013-01-29 13:13:43 -0800315 @param test_or_benchmark: Name of the test or benchmark we want to run,
316 with the page_set (if required) as part of the
317 string.
Luis Lozano814c7182015-09-08 11:20:47 -0700318 @param args: additional list of arguments to pass to the script.
Zhizhou Yangf32fda02019-09-25 14:01:45 -0700319 @param kwargs: additional list of keyword arguments to pass to the
320 script.
Simran Basi833814b2013-01-29 13:13:43 -0800321
322 @returns A TelemetryResult Instance with the results of this telemetry
323 execution.
324 """
Simran Basi1dbfc132013-05-02 10:11:02 -0700325 # TODO (sbasi crbug.com/239933) add support for incognito mode.
Simran Basi833814b2013-01-29 13:13:43 -0800326
Zhizhou Yang65d37042020-01-08 17:44:12 -0800327 telemetry_cmd = self._get_telemetry_cmd(script, test_or_benchmark,
328 output_format, *args, **kwargs)
Zhizhou Yangf32fda02019-09-25 14:01:45 -0700329 logging.info('Running Telemetry: %s', telemetry_cmd)
Luis Lozano23ae3192013-11-08 16:22:46 -0800330
Keith Haddow1e5c7012016-03-09 16:05:37 -0800331 stdout, stderr, exit_code = self._run_cmd(telemetry_cmd)
Simran Basi833814b2013-01-29 13:13:43 -0800332
Zhizhou Yang65d37042020-01-08 17:44:12 -0800333 return TelemetryResult(
334 exit_code=exit_code, stdout=stdout, stderr=stderr)
Simran Basi833814b2013-01-29 13:13:43 -0800335
Zhizhou Yang65d37042020-01-08 17:44:12 -0800336 def _run_scp(self, perf_results_dir, output_format, artifacts=False):
Keith Haddow1e5c7012016-03-09 16:05:37 -0800337 """Runs telemetry on a dut.
338
339 @param perf_results_dir: The local directory that results are being
340 collected.
Zhizhou Yang65d37042020-01-08 17:44:12 -0800341 @param output_format: Format of the json result file.
342 @param artifacts: Whether we want to copy artifacts directory.
Keith Haddow1e5c7012016-03-09 16:05:37 -0800343 """
Kuo-Hsin Yang14d002b2019-06-06 13:18:04 +0800344 scp_cmd = self._scp_telemetry_results_cmd(perf_results_dir,
Zhizhou Yang65d37042020-01-08 17:44:12 -0800345 output_format, artifacts)
Keith Haddow1e5c7012016-03-09 16:05:37 -0800346 logging.debug('Retrieving Results: %s', scp_cmd)
Dean Liaoe4773c72017-11-09 16:15:38 +0800347 _, _, exit_code = self._run_cmd(scp_cmd)
348 if exit_code != 0:
349 raise error.TestFail('Unable to retrieve results.')
Keith Haddow1e5c7012016-03-09 16:05:37 -0800350
Kuo-Hsin Yang14d002b2019-06-06 13:18:04 +0800351 if output_format == 'histograms':
352 # Converts to chart json format.
353 input_filename = os.path.join(perf_results_dir,
354 HISTOGRAM_SET_RESULT)
Zhizhou Yang65d37042020-01-08 17:44:12 -0800355 output_filename = os.path.join(perf_results_dir, CHART_JSON_RESULT)
Kuo-Hsin Yang14d002b2019-06-06 13:18:04 +0800356 histograms = json.loads(open(input_filename).read())
357 chartjson = TelemetryRunner.convert_chart_json(histograms)
358 with open(output_filename, 'w') as fout:
359 fout.write(json.dumps(chartjson, indent=2))
Keith Haddow1e5c7012016-03-09 16:05:37 -0800360
Luis Lozano814c7182015-09-08 11:20:47 -0700361 def _run_test(self, script, test, *args):
Simran Basi1dbfc132013-05-02 10:11:02 -0700362 """Runs a telemetry test on a dut.
363
364 @param script: Which telemetry test script we want to run. Can be
365 telemetry's base test script or the Chrome OS specific
366 test script.
367 @param test: Telemetry test we want to run.
Luis Lozano814c7182015-09-08 11:20:47 -0700368 @param args: additional list of arguments to pass to the script.
Simran Basi1dbfc132013-05-02 10:11:02 -0700369
370 @returns A TelemetryResult Instance with the results of this telemetry
371 execution.
372 """
373 logging.debug('Running telemetry test: %s', test)
374 telemetry_script = os.path.join(self._telemetry_path, script)
Zhizhou Yang65d37042020-01-08 17:44:12 -0800375 result = self._run_telemetry(telemetry_script, test, 'chartjson',
376 *args)
Simran Basi1dbfc132013-05-02 10:11:02 -0700377 if result.status is FAILED_STATUS:
Ilja H. Friedelc7bf3102014-05-13 17:31:25 -0700378 raise error.TestFail('Telemetry test %s failed.' % test)
Simran Basi1dbfc132013-05-02 10:11:02 -0700379 return result
380
Luis Lozano814c7182015-09-08 11:20:47 -0700381 def run_telemetry_test(self, test, *args):
Simran Basi833814b2013-01-29 13:13:43 -0800382 """Runs a telemetry test on a dut.
383
384 @param test: Telemetry test we want to run.
Luis Lozano814c7182015-09-08 11:20:47 -0700385 @param args: additional list of arguments to pass to the telemetry
386 execution script.
Simran Basi833814b2013-01-29 13:13:43 -0800387
388 @returns A TelemetryResult Instance with the results of this telemetry
389 execution.
390 """
Luis Lozano814c7182015-09-08 11:20:47 -0700391 return self._run_test(TELEMETRY_RUN_TESTS_SCRIPT, test, *args)
Simran Basi1dbfc132013-05-02 10:11:02 -0700392
Zhizhou Yangf32fda02019-09-25 14:01:45 -0700393 def run_telemetry_benchmark(self,
394 benchmark,
395 perf_value_writer=None,
396 *args,
397 **kwargs):
Simran Basi833814b2013-01-29 13:13:43 -0800398 """Runs a telemetry benchmark on a dut.
399
400 @param benchmark: Benchmark we want to run.
Fang Denge689e712013-11-13 18:27:06 -0800401 @param perf_value_writer: Should be an instance with the function
402 output_perf_value(), if None, no perf value
403 will be written. Typically this will be the
404 job object from an autotest test.
Luis Lozano814c7182015-09-08 11:20:47 -0700405 @param args: additional list of arguments to pass to the telemetry
406 execution script.
Zhizhou Yangf32fda02019-09-25 14:01:45 -0700407 @param kwargs: additional list of keyword arguments to pass to the
408 telemetry execution script.
Simran Basi833814b2013-01-29 13:13:43 -0800409
410 @returns A TelemetryResult Instance with the results of this telemetry
411 execution.
412 """
Dave Tu6a404e62013-11-05 15:54:48 -0800413 logging.debug('Running telemetry benchmark: %s', benchmark)
Ting-Yuan Huange5b19132016-03-22 13:02:41 +0800414
Zhizhou Yang65d37042020-01-08 17:44:12 -0800415 self._perf_value_writer = perf_value_writer
416
Kuo-Hsin Yang07da7b62018-08-08 16:56:06 +0800417 if benchmark in ON_DUT_BLACKLIST:
Ting-Yuan Huange5b19132016-03-22 13:02:41 +0800418 self._telemetry_on_dut = False
419
Zhizhou Yangf32fda02019-09-25 14:01:45 -0700420 output_format = kwargs.get('ex_output_format', '')
421
422 if not output_format:
Kuo-Hsin Yang72901f32019-10-28 14:24:58 +0800423 output_format = 'histograms'
Kuo-Hsin Yang14d002b2019-06-06 13:18:04 +0800424
Ting-Yuan Huange5b19132016-03-22 13:02:41 +0800425 if self._telemetry_on_dut:
426 telemetry_script = os.path.join(DUT_CHROME_ROOT,
427 TELEMETRY_RUN_BENCHMARKS_SCRIPT)
428 self._ensure_deps(self._host, benchmark)
429 else:
430 telemetry_script = os.path.join(self._telemetry_path,
431 TELEMETRY_RUN_BENCHMARKS_SCRIPT)
432
Kuo-Hsin Yang14d002b2019-06-06 13:18:04 +0800433 result = self._run_telemetry(telemetry_script, benchmark,
Zhizhou Yangf32fda02019-09-25 14:01:45 -0700434 output_format, *args, **kwargs)
Simran Basi833814b2013-01-29 13:13:43 -0800435
436 if result.status is WARNING_STATUS:
Dave Tu6a404e62013-11-05 15:54:48 -0800437 raise error.TestWarn('Telemetry Benchmark: %s'
Zhizhou Yangf32fda02019-09-25 14:01:45 -0700438 ' exited with Warnings.\nOutput:\n%s\n' %
439 (benchmark, result.output))
440 elif result.status is FAILED_STATUS:
Dave Tu6a404e62013-11-05 15:54:48 -0800441 raise error.TestFail('Telemetry Benchmark: %s'
Zhizhou Yangf32fda02019-09-25 14:01:45 -0700442 ' failed to run.\nOutput:\n%s\n' %
443 (benchmark, result.output))
444 elif '[ PASSED ] 0 tests.' in result.output:
445 raise error.TestWarn('Telemetry Benchmark: %s exited successfully,'
446 ' but no test actually passed.\nOutput\n%s\n'
447 % (benchmark, result.output))
Keith Haddow1e5c7012016-03-09 16:05:37 -0800448 if perf_value_writer:
Zhizhou Yang65d37042020-01-08 17:44:12 -0800449 artifacts = kwargs.get('artifacts', False)
450 self._run_scp(perf_value_writer.resultsdir, output_format,
451 artifacts)
Simran Basi833814b2013-01-29 13:13:43 -0800452 return result
Ting-Yuan Huange5b19132016-03-22 13:02:41 +0800453
Gurchetan Singhfaf75e92017-04-17 18:09:44 -0700454 def run_gpu_integration_test(self, test, *args):
455 """Runs a gpu test on a dut.
456
457 @param test: Gpu test we want to run.
458 @param args: additional list of arguments to pass to the telemetry
459 execution script.
460
Drew Davenport84395922018-09-10 10:42:37 -0600461 @returns A TelemetryResult instance with the results of this telemetry
462 execution.
Gurchetan Singhfaf75e92017-04-17 18:09:44 -0700463 """
Zhizhou Yang65d37042020-01-08 17:44:12 -0800464 script = os.path.join(DUT_CHROME_ROOT, TELEMETRY_RUN_GPU_TESTS_SCRIPT)
Gurchetan Singhfaf75e92017-04-17 18:09:44 -0700465 cmd = []
466 if self._devserver:
467 devserver_hostname = self._devserver.hostname
468 cmd.extend(['ssh', devserver_hostname])
469
Zhizhou Yang65d37042020-01-08 17:44:12 -0800470 cmd.extend([
471 self._host.ssh_command(
472 alive_interval=900, connection_attempts=4), 'python2',
473 script
474 ])
Gurchetan Singhfaf75e92017-04-17 18:09:44 -0700475 cmd.extend(args)
476 cmd.append(test)
477 cmd = ' '.join(cmd)
478 stdout, stderr, exit_code = self._run_cmd(cmd)
479
Drew Davenport84395922018-09-10 10:42:37 -0600480 if exit_code:
481 raise error.TestFail('Gpu Integration Test: %s'
482 ' failed to run.' % test)
483
Zhizhou Yang65d37042020-01-08 17:44:12 -0800484 return TelemetryResult(
485 exit_code=exit_code, stdout=stdout, stderr=stderr)
Gurchetan Singhfaf75e92017-04-17 18:09:44 -0700486
Ting-Yuan Huange5b19132016-03-22 13:02:41 +0800487 def _ensure_deps(self, dut, test_name):
488 """
489 Ensure the dependencies are locally available on DUT.
490
491 @param dut: The autotest host object representing DUT.
492 @param test_name: Name of the telemetry test.
493 """
494 # Get DEPs using host's telemetry.
Kuo-Hsin Yang4a006172018-04-25 14:44:55 +0800495 # Example output, fetch_benchmark_deps.py --output-deps=deps octane:
496 # {'octane': ['tools/perf/page_sets/data/octane_002.wprgo']}
Zhizhou Yang65d37042020-01-08 17:44:12 -0800497 fetch_path = os.path.join(self._telemetry_path, 'tools', 'perf',
498 'fetch_benchmark_deps.py')
Chinglin Yu0adaf112020-01-20 14:48:20 +0800499 # Use a temporary file for |deps_path| to avoid race conditions. The
500 # created temporary file is assigned to |self._benchmark_deps| to make
501 # it valid until |self| is destroyed.
502 self._benchmark_deps = tempfile.NamedTemporaryFile(
Zhizhou Yang65d37042020-01-08 17:44:12 -0800503 prefix='fetch_benchmark_deps_result.', suffix='.json')
Chinglin Yu0adaf112020-01-20 14:48:20 +0800504 deps_path = self._benchmark_deps.name
Kuo-Hsin Yang66324262019-12-10 10:36:34 +0800505 format_fetch = ('python2 %s --output-deps=%s %s')
Kuo-Hsin Yang4a006172018-04-25 14:44:55 +0800506 command_fetch = format_fetch % (fetch_path, deps_path, test_name)
507 command_get = 'cat %s' % deps_path
Ting-Yuan Huange5b19132016-03-22 13:02:41 +0800508
509 if self._devserver:
510 devserver_hostname = self._devserver.url().split(
511 'http://')[1].split(':')[0]
Kuo-Hsin Yang4a006172018-04-25 14:44:55 +0800512 command_fetch = 'ssh %s %s' % (devserver_hostname, command_fetch)
513 command_get = 'ssh %s %s' % (devserver_hostname, command_get)
Ting-Yuan Huange5b19132016-03-22 13:02:41 +0800514
Kuo-Hsin Yang4a006172018-04-25 14:44:55 +0800515 logging.info('Getting DEPs: %s', command_fetch)
516 _, _, exit_code = self._run_cmd(command_fetch)
517 if exit_code != 0:
518 raise error.TestFail('Error occurred while fetching DEPs.')
519 stdout, _, exit_code = self._run_cmd(command_get)
520 if exit_code != 0:
Ting-Yuan Huange5b19132016-03-22 13:02:41 +0800521 raise error.TestFail('Error occurred while getting DEPs.')
522
523 # Download DEPs to DUT.
524 # send_file() relies on rsync over ssh. Couldn't be better.
Kuo-Hsin Yang4a006172018-04-25 14:44:55 +0800525 deps = json.loads(stdout)
526 for dep in deps[test_name]:
Ting-Yuan Huange5b19132016-03-22 13:02:41 +0800527 src = os.path.join(self._telemetry_path, dep)
528 dst = os.path.join(DUT_CHROME_ROOT, dep)
Ting-Yuan Huang8a2c7f72016-03-28 22:01:07 +0800529 if self._devserver:
530 logging.info('Copying: %s -> %s', src, dst)
Zhizhou Yang65d37042020-01-08 17:44:12 -0800531 rsync_cmd = utils.sh_escape(
532 'rsync %s %s %s:%s' % (self._host.rsync_options(), src,
533 self._host.hostname, dst))
Chung-yih Wangfd8eb242017-12-09 19:23:04 +0800534 utils.run('ssh %s "%s"' % (devserver_hostname, rsync_cmd))
Ting-Yuan Huang8a2c7f72016-03-28 22:01:07 +0800535 else:
536 if not os.path.isfile(src):
537 raise error.TestFail('Error occurred while saving DEPs.')
538 logging.info('Copying: %s -> %s', src, dst)
539 dut.send_file(src, dst)
Kuo-Hsin Yang14d002b2019-06-06 13:18:04 +0800540
541 @staticmethod
542 def convert_chart_json(histogram_set):
543 """
544 Convert from histogram set to chart json format.
545
546 @param histogram_set: result in histogram set format.
547
548 @returns result in chart json format.
549 """
550 value_map = {}
551
552 # Gets generic set values.
553 for obj in histogram_set:
554 if 'type' in obj and obj['type'] == 'GenericSet':
555 value_map[obj['guid']] = obj['values']
556
Kuo-Hsin Yang14d002b2019-06-06 13:18:04 +0800557 charts = {}
558 benchmark_name = ''
559 benchmark_desc = ''
560
561 # Checks the unit test for how this conversion works.
562 for obj in histogram_set:
563 if 'name' not in obj or 'sampleValues' not in obj:
564 continue
565 metric_name = obj['name']
566 diagnostics = obj['diagnostics']
Kuo-Hsin Yang72901f32019-10-28 14:24:58 +0800567 if diagnostics.has_key('stories'):
568 story_name = value_map[diagnostics['stories']][0]
569 else:
570 story_name = 'default'
Kuo-Hsin Yang14d002b2019-06-06 13:18:04 +0800571 local_benchmark_name = value_map[diagnostics['benchmarks']][0]
Kuo-Hsin Yang99622172019-09-16 20:02:53 +0800572 if benchmark_name == '':
Kuo-Hsin Yang14d002b2019-06-06 13:18:04 +0800573 benchmark_name = local_benchmark_name
Kuo-Hsin Yang72901f32019-10-28 14:24:58 +0800574 if diagnostics.has_key('benchmarkDescriptions'):
575 benchmark_desc = value_map[
Zhizhou Yang65d37042020-01-08 17:44:12 -0800576 diagnostics['benchmarkDescriptions']][0]
Kuo-Hsin Yang72901f32019-10-28 14:24:58 +0800577 if benchmark_name != local_benchmark_name:
Zhizhou Yang65d37042020-01-08 17:44:12 -0800578 logging.warning(
579 'There are more than 1 benchmark names in the'
580 'result. old: %s, new: %s', benchmark_name,
581 local_benchmark_name)
Kuo-Hsin Yang72901f32019-10-28 14:24:58 +0800582 continue
Kuo-Hsin Yang99622172019-09-16 20:02:53 +0800583
584 unit = obj['unit']
585 smaller_postfixes = ('_smallerIsBetter', '-')
586 bigger_postfixes = ('_biggerIsBetter', '+')
587 all_postfixes = smaller_postfixes + bigger_postfixes
588
Kuo-Hsin Yange01ff602019-06-14 11:33:31 +0800589 improvement = 'up'
Kuo-Hsin Yang99622172019-09-16 20:02:53 +0800590 for postfix in smaller_postfixes:
Zhizhou Yang65d37042020-01-08 17:44:12 -0800591 if unit.endswith(postfix):
592 improvement = 'down'
Kuo-Hsin Yang99622172019-09-16 20:02:53 +0800593 for postfix in all_postfixes:
Zhizhou Yang65d37042020-01-08 17:44:12 -0800594 if unit.endswith(postfix):
595 unit = unit[:-len(postfix)]
596 break
Kuo-Hsin Yang99622172019-09-16 20:02:53 +0800597
598 if unit == 'unitless':
Zhizhou Yang65d37042020-01-08 17:44:12 -0800599 unit = 'score'
Kuo-Hsin Yang99622172019-09-16 20:02:53 +0800600
Zhizhou Yang65d37042020-01-08 17:44:12 -0800601 values = [
602 x for x in obj['sampleValues']
603 if isinstance(x, numbers.Number)
604 ]
Kuo-Hsin Yang14d002b2019-06-06 13:18:04 +0800605 if metric_name not in charts:
606 charts[metric_name] = {}
607 charts[metric_name][story_name] = {
Zhizhou Yang65d37042020-01-08 17:44:12 -0800608 'improvement_direction': improvement,
609 'name': metric_name,
610 'std': numpy.std(values),
611 'type': 'list_of_scalar_values',
612 'units': unit,
613 'values': values
Kuo-Hsin Yang14d002b2019-06-06 13:18:04 +0800614 }
615
616 # Adds summaries.
617 for metric_name in charts:
618 values = []
619 metric_content = charts[metric_name]
620 for story_name in metric_content:
621 story_content = metric_content[story_name]
622 values += story_content['values']
623 metric_type = story_content['type']
624 units = story_content['units']
Kuo-Hsin Yange01ff602019-06-14 11:33:31 +0800625 improvement = story_content['improvement_direction']
Kuo-Hsin Yang14d002b2019-06-06 13:18:04 +0800626 values.sort()
627 std = numpy.std(values)
628 metric_content['summary'] = {
Zhizhou Yang65d37042020-01-08 17:44:12 -0800629 'improvement_direction': improvement,
630 'name': metric_name,
631 'std': std,
632 'type': metric_type,
633 'units': units,
634 'values': values
Kuo-Hsin Yang14d002b2019-06-06 13:18:04 +0800635 }
636
637 benchmark_metadata = {
Zhizhou Yang65d37042020-01-08 17:44:12 -0800638 'description': benchmark_desc,
639 'name': benchmark_name,
640 'type': 'telemetry_benchmark'
Kuo-Hsin Yang14d002b2019-06-06 13:18:04 +0800641 }
Kuo-Hsin Yange01ff602019-06-14 11:33:31 +0800642 return {
Zhizhou Yang65d37042020-01-08 17:44:12 -0800643 'benchmark_description': benchmark_desc,
644 'benchmark_metadata': benchmark_metadata,
645 'benchmark_name': benchmark_name,
646 'charts': charts,
647 'format_version': 1.0
Kuo-Hsin Yange01ff602019-06-14 11:33:31 +0800648 }