diff --git a/external/devlib/devlib/collector/ftrace.py b/external/devlib/devlib/collector/ftrace.py index 0aea8eeac0ae592d2b59ff8690b401c2899fe5b4..d060f3603c4ea578323e3c54fd90b030307f9692 100644 --- a/external/devlib/devlib/collector/ftrace.py +++ b/external/devlib/devlib/collector/ftrace.py @@ -200,6 +200,19 @@ class FtraceCollector(CollectorBase): self._selected_events = selected_events + @property + def _buffer_size(self): + top = self.top_buffer_size + nontop = self.buffer_size + if top is None and nontop is None: + return None + elif top is None: + return nontop + elif nontop is None: + return top + else: + return max(top, nontop) + @property def event_string(self): return _build_trace_events(self._selected_events) @@ -254,19 +267,25 @@ class FtraceCollector(CollectorBase): except TargetStableError: kprobe_events = None - self.target.execute('{} reset -B devlib'.format(self.target_binary), + self.target.execute('{} reset'.format(self.target_binary), as_root=True, timeout=TIMEOUT) + + # This code is currently not necessary as we are not using alternate + # instances (not using -B parameter). If we end up using it again, it + # may very well be that trace-cmd at that point takes care of that + # problem itself somehow, so this should be re-evaluated. + # trace-cmd start will not set the top-level buffer size if passed -B # parameter, but unfortunately some events still end up there (e.g. # print event). So we still need to set that size, otherwise the buffer # might be too small and some event lost. - top_buffer_size = self.top_buffer_size if self.top_buffer_size else self.buffer_size - if top_buffer_size: - self.target.write_value( - self.target.path.join(self.tracing_path, 'buffer_size_kb'), - top_buffer_size, verify=False - ) + # top_buffer_size = self.top_buffer_size or self.buffer_size + # if top_buffer_size: + # self.target.write_value( + # self.target.path.join(self.tracing_path, 'buffer_size_kb'), + # top_buffer_size, verify=False + # ) if self.functions: self.target.write_value(self.function_profile_file, 0, verify=False) @@ -315,11 +334,11 @@ class FtraceCollector(CollectorBase): with contextlib.suppress(TargetStableError): self.target.write_value('/proc/sys/kernel/kptr_restrict', 0) - params = '-B devlib {buffer_size} {cmdlines_size} {clock} {events} {tracer} {functions}'.format( + params = '{buffer_size} {cmdlines_size} {clock} {events} {tracer} {functions}'.format( events=self.event_string, tracer=tracer_string, functions=tracecmd_functions, - buffer_size='-b {}'.format(self.buffer_size) if self.buffer_size is not None else '', + buffer_size='-b {}'.format(self._buffer_size) if self._buffer_size is not None else '', clock='-C {}'.format(self.trace_clock) if self.trace_clock else '', cmdlines_size='--cmdlines-size {}'.format(self.saved_cmdlines_nr) if self.saved_cmdlines_nr is not None else '', ) @@ -381,7 +400,7 @@ class FtraceCollector(CollectorBase): bg_cmd.communicate() bg_cmd.__exit__(None, None, None) elif mode == 'write-to-memory': - self.target.execute('{} stop -B devlib'.format(self.target_binary), + self.target.execute('{} stop'.format(self.target_binary), timeout=TIMEOUT, as_root=True) else: raise ValueError(f'Unknown mode {mode}') @@ -404,7 +423,7 @@ class FtraceCollector(CollectorBase): # Interrupting trace-cmd record will make it create the file pass elif mode == 'write-to-memory': - cmd = f'{self.target_binary} extract -B devlib -o {self.target_output_file} && {busybox} chmod 666 {self.target_output_file}' + cmd = f'{self.target_binary} extract -o {self.target_output_file} && {busybox} chmod 666 {self.target_output_file}' self.target.execute(cmd, timeout=TIMEOUT, as_root=True) else: raise ValueError(f'Unknown mode {mode}') diff --git a/external/devlib/devlib/utils/asyn.py b/external/devlib/devlib/utils/asyn.py index dd6d42d598df00d689621b1719a456e12474bccd..c4df021dc4d2200719935bd98b9c303e98af41f2 100644 --- a/external/devlib/devlib/utils/asyn.py +++ b/external/devlib/devlib/utils/asyn.py @@ -123,6 +123,10 @@ class AsyncManager: except BaseException: for task in tasks: task.cancel() + try: + await task + except asyncio.CancelledError: + pass raise finally: diff --git a/external/devlib/tests/test_asyn.py b/external/devlib/tests/test_asyn.py index 8fe9a22128d6bbcd65f0063b192b121fd055adf5..cc11beaefdde2a01407526b0431a38b72cdeac0d 100644 --- a/external/devlib/tests/test_asyn.py +++ b/external/devlib/tests/test_asyn.py @@ -23,7 +23,7 @@ from contextlib import contextmanager from pytest import skip, raises -from devlib.utils.asyn import run, asynccontextmanager +from devlib.utils.asyn import run, asynccontextmanager, AsyncManager class AsynTestExcep(Exception): @@ -517,6 +517,53 @@ def _do_test_run(top_run): test_async_gen1() + def test_async_map_concurrently(): + async def agen_f(): + manager = AsyncManager() + + async def f1(): + return 1 + + async def f2(): + return 2 + + return await manager.concurrently([f1(), f2()]) + + agen = agen_f() + assert top_run(agen) == [1, 2] + + test_async_map_concurrently() + + def test_async_map_concurrently_cancel(): + class MyException(Exception): + pass + + async def agen_f(): + manager = AsyncManager() + cancelled1 = False + + async def f1(): + nonlocal cancelled1 + + try: + # Await on a future that will never be available. We should + # get canceled at some point, so it does not matter. + await asyncio.Future() + except asyncio.CancelledError: + cancelled1 = True + + async def f2(): + raise MyException('from f2') + + with raises(MyException): + await manager.concurrently([f1(), f2()]) + + assert cancelled1 + + top_run(agen_f()) + + test_async_map_concurrently_cancel() + def _test_in_thread(setup, test): def f(): diff --git a/external/workload-automation/wa/instruments/poller/__init__.py b/external/workload-automation/wa/instruments/poller/__init__.py index 7c670683520d9c0c628c75cfb8bf239f13a08e51..3205f1acb0d5cb3d770ccd879f921ab95645681c 100644 --- a/external/workload-automation/wa/instruments/poller/__init__.py +++ b/external/workload-automation/wa/instruments/poller/__init__.py @@ -59,6 +59,12 @@ class FilePoller(Instrument): Whether or not the poller will be run as root. This should be used when the file you need to poll can only be accessed by root. """), + Parameter('reopen', kind=bool, default=False, + description=""" + When enabled files will be re-opened with each read. This is + useful for some sysfs/debugfs entries that only generate a + value when opened. + """), ] def validate(self): @@ -91,13 +97,17 @@ class FilePoller(Instrument): if self.align_with_ftrace: marker_option = '-m' signal.connect(self._adjust_timestamps, signal.AFTER_JOB_OUTPUT_PROCESSED) - self.command = '{} -t {} {} -l {} {} > {} 2>{}'.format(target_poller, - self.sample_interval * 1000, - marker_option, - ','.join(self.labels), - ' '.join(self.files), - self.target_output_path, - self.target_log_path) + reopen_option = '' + if self.reopen: + reopen_option = '-r' + self.command = '{} {} -t {} {} -l {} {} > {} 2>{}'.format(target_poller, + reopen_option, + self.sample_interval * 1000, + marker_option, + ','.join(self.labels), + ' '.join(self.files), + self.target_output_path, + self.target_log_path) def start(self, context): self.target.kick_off(self.command, as_root=self.as_root) diff --git a/external/workload-automation/wa/instruments/poller/bin/arm64/poller b/external/workload-automation/wa/instruments/poller/bin/arm64/poller index d64ddc41fd1c0aa6534f9cbc35ca4b52307b1127..2d63ce03e9d00a847cafe67ba43c42e6e4899efb 100755 Binary files a/external/workload-automation/wa/instruments/poller/bin/arm64/poller and b/external/workload-automation/wa/instruments/poller/bin/arm64/poller differ diff --git a/external/workload-automation/wa/instruments/poller/bin/armeabi/poller b/external/workload-automation/wa/instruments/poller/bin/armeabi/poller index 8a9e0c4d2ccfae41b5a4a90f01699ce4ca0c3786..27485a8985e5a7fcdedd09e169dc7ca2decaa159 100755 Binary files a/external/workload-automation/wa/instruments/poller/bin/armeabi/poller and b/external/workload-automation/wa/instruments/poller/bin/armeabi/poller differ diff --git a/external/workload-automation/wa/instruments/poller/poller.c b/external/workload-automation/wa/instruments/poller/poller.c index 85df4c6f69b1eb9ab55b75655a83611b3d5d1103..db845197758cb9a0740c6925bc7a11027545b778 100644 --- a/external/workload-automation/wa/instruments/poller/poller.c +++ b/external/workload-automation/wa/instruments/poller/poller.c @@ -77,9 +77,10 @@ int main(int argc, char ** argv) { char *labels; int labelCount = 0; int should_write_marker = 0; + int reopen_files = 0; int ret; - static char usage[] = "usage: %s [-h] [-m] [-t INTERVAL] FILE [FILE ...]\n" + static char usage[] = "usage: %s [-h] [-m] [-r] [-t INTERVAL] FILE [FILE ...]\n" "polls FILE(s) every INTERVAL microseconds and outputs\n" "the results in CSV format including a timestamp to STDOUT\n" "\n" @@ -87,6 +88,7 @@ int main(int argc, char ** argv) { " -m Insert a marker into ftrace at the time of the first\n" " sample. This marker may be used to align the timestamps\n" " produced by the poller with those of ftrace events.\n" + " -r Reopen files on each read (needed for some sysfs/debugfs files)\n" " -t The polling sample interval in microseconds\n" " Defaults to 1000000 (1 second)\n" " -l Comma separated list of labels to use in the CSV\n" @@ -94,7 +96,7 @@ int main(int argc, char ** argv) { //Handling command line arguments - while ((c = getopt(argc, argv, "hmt:l:")) != -1) + while ((c = getopt(argc, argv, "hmrt:l:")) != -1) { switch(c) { case 'h': @@ -104,7 +106,10 @@ int main(int argc, char ** argv) { break; case 'm': should_write_marker = 1; - break; + break; + case 'r': + reopen_files = 1; + break; case 't': interval = (useconds_t)atoi(optarg); break; @@ -184,7 +189,20 @@ int main(int argc, char ** argv) { time_float += ((double)current_time.tv_nsec)/1000/1000/1000; printf("%f", time_float); for (i = 0; i < num_files; i++) { - lseek(files_to_poll[i].fd, 0, SEEK_SET); + if (reopen_files) { + // Close and reopen the file to get fresh data + close(files_to_poll[i].fd); + files_to_poll[i].fd = open(files_to_poll[i].path, O_RDONLY); + if (files_to_poll[i].fd == -1) { + fprintf(stderr, "WARNING: Could not reopen \"%s\", got: %s\n", + files_to_poll[i].path, strerror(errno)); + printf(","); + continue; + } + } else { + lseek(files_to_poll[i].fd, 0, SEEK_SET); + } + bytes_read = read(files_to_poll[i].fd, buf, 1024); if (bytes_read < 0) { diff --git a/external/workload-automation/wa/workloads/geekbench/__init__.py b/external/workload-automation/wa/workloads/geekbench/__init__.py index 0aa7b342eab3dd0c4aa5dc92f2c60ade2d3b14ca..9d239bd484ebc5ffe96bc4c5517f9e2609cfbc4e 100644 --- a/external/workload-automation/wa/workloads/geekbench/__init__.py +++ b/external/workload-automation/wa/workloads/geekbench/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2013-2018 ARM Limited +# Copyright 2013-2025 ARM Limited # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,10 +20,11 @@ import tempfile import json from collections import defaultdict -from wa import ApkUiautoWorkload, Parameter +from wa import Workload, ApkUiautoWorkload, Parameter from wa.framework.exception import ConfigError, WorkloadError from wa.utils.misc import capitalize -from wa.utils.types import version_tuple +from wa.utils.types import version_tuple, list_or_integer +from wa.utils.exec_control import once class Geekbench(ApkUiautoWorkload): @@ -370,3 +371,233 @@ class GeekbenchCorproate(Geekbench): # pylint: disable=too-many-ancestors def namemify(basename, i): return basename + (' {}'.format(i) if i else '') + + +class GeekbenchCmdline(Workload): + + name = "geekbench_cli" + description = "Workload for running command line version Geekbench" + + gb6_workloads = { + # Single-Core and Multi-Core + 101: 'File Compression', + 102: 'Navigation', + 103: 'HTML5 Browser', + 104: 'PDF Renderer', + 105: 'Photo Library', + 201: 'Clang', + 202: 'Text Processing', + 203: 'Asset Compression', + 301: 'Object Detection', + 402: 'Object Remover', + 403: 'HDR', + 404: 'Photo Filter', + 501: 'Ray Tracer', + 502: 'Structure from Motion', + # OpenCL and Vulkan + 303: 'Face Detection', + 406: 'Edge Detection', + 407: 'Gaussian Blur', + 503: 'Feature Matching', + 504: 'Stereo Matching', + 601: 'Particle Physics', + # Single-Core, Multi-Core, OpenCL, and Vulkan + 302: 'Background Blur', + 401: 'Horizon Detection', + } + + gb5_workloads = { + # Single-Core and Multi-Core + 101: 'AES-XTS', + 201: 'Text Compression', + 202: 'Image Compression', + 203: 'Navigation', + 204: 'HTML5', + 205: 'SQLite', + 206: 'PDF Rendering', + 207: 'Text Rendering', + 208: 'Clang', + 209: 'Camera', + 301: 'N-Body Physics', + 302: 'Rigid Body Physics', + 307: 'Image Inpainting', + 308: 'HDR', + 309: 'Ray Tracing', + 310: 'Structure from Motion', + 312: 'Speech Recognition', + 313: 'Machine Learning', + # OpenCL and Vulkan + 220: 'Sobel', + 221: 'Canny', + 222: 'Stereo Matching', + 230: 'Histogram Equalization', + 304: 'Depth of Field', + 311: 'Feature Matching', + 320: 'Particle Physics', + 321: 'SFFT', + # Single-Core, Multi-Core, OpenCL, and Vulkan + 303: 'Gaussian Blur', + 305: 'Face Detection', + 306: 'Horizon Detection', + } + + binary_name = 'geekbench_aarch64' + + allowed_extensions = ['json', 'csv', 'xml', 'html', 'text'] + + parameters = [ + Parameter('cpumask', kind=str, default='', + description='CPU mask used by taskset.'), + Parameter('section', kind=int, default=1, allowed_values=[1, 4, 9], + description="""Run the specified sections. It should be 1 for CPU benchmarks, + 4 for OpenCL benchmarks and 9 for Vulkan benchmarks."""), + Parameter('upload', kind=bool, default=False, + description='Upload results to Geekbench Browser'), + Parameter('is_single_core', kind=bool, default=True, + description='Run workload in single-core or multi-core mode.'), + Parameter('workload', kind=list_or_integer, default=301, + description='Specify workload to run'), + Parameter('iterations', kind=int, default=5, + description='Number of iterations'), + Parameter('workload_gap', kind=int, default=2000, + description='N milliseconds gap between workloads'), + Parameter('output_file', kind=str, default='gb_cli.json', + description=f"""Specify the name of the output results file. + If it is not specified, the output file will be generated as a JSON file. + It can be {', '.join(allowed_extensions)} files."""), + Parameter('timeout', kind=int, default=2000, + description='The test timeout in ms. It should be long for 1000 iterations.'), + Parameter('version', kind=str, default='6.3.0', + description='Specifies which version of the Geekbench should run.'), + ] + + def __init__(self, target, **kwargs): + super(GeekbenchCmdline, self).__init__(target, **kwargs) + self.target_result_json = None + self.host_result_json = None + self.workloads = self.gb6_workloads + self.params = '' + self.output = '' + self.target_exec_directory = '' + self.tar_file_src = '' + self.tar_file_dst = '' + self.file_exists = False + + def init_resources(self, context): + """ + Retrieves necessary files to run the benchmark in TAR format. + WA will look for `gb_cli_artifacts_.tar` file to deploy them to the + working directory. If there is no specified version, it will look for version + 6.3.0 by default. + """ + self.deployable_assets = [''.join(['gb_cli_artifacts', '_', self.version, '.tar'])] + + # Create an executables directory + self.target_exec_directory = self.target.path.join(self.target.executables_directory, f'gb_cli-{self.version}') + self.target.execute("mkdir -p {}".format(self.target_exec_directory)) + + # Source and Destination paths for the artifacts tar file + self.tar_file_src = self.target.path.join(self.target.working_directory, self.deployable_assets[0]) + self.tar_file_dst = self.target.path.join(self.target_exec_directory, self.deployable_assets[0]) + # Check the tar file if it already exists + if self.target.file_exists(self.tar_file_dst): + self.file_exists = True + else: + # Get the assets file + super(GeekbenchCmdline, self).init_resources(context) + + @once + def initialize(self, context): + if self.version[0] == '5': + self.workloads = self.gb5_workloads + # If the tar file does not exist in the target, deploy the assets + if not self.file_exists: + super(GeekbenchCmdline, self).initialize(context) + # Move the tar file to the executables directory + self.target.execute( + '{} mv {} {}'.format( + self.target.busybox, self.tar_file_src, self.tar_file_dst)) + # Extract the tar file + self.target.execute( + '{} tar -xf {} -C {}'.format( + self.target.busybox, self.tar_file_dst, self.target_exec_directory)) + + def setup(self, context): + super(GeekbenchCmdline, self).setup(context) + + self.params = '' + + self.params += '--section {} '.format(self.section) + if self.section == 1: + self.params += '--single-core ' if self.is_single_core else '--multi-core ' + + self.params += '--upload ' if self.upload else '--no-upload ' + + known_workloads = '\n'.join("{}: {}".format(k, v) for k, v in self.workloads.items()) + if any([t not in self.workloads.keys() for t in self.workload]): + msg = 'Unknown workload(s) specified. Known workloads: {}' + raise ValueError(msg.format(known_workloads)) + + self.params += '--workload {} '.format(''.join("{},".format(i) for i in self.workload)) + + if self.iterations: + self.params += '--iterations {} '.format(self.iterations) + + if self.workload_gap: + self.params += '--workload-gap {} '.format(self.workload_gap) + + extension = os.path.splitext(self.output_file)[1][1:] + if self.output_file and extension not in self.allowed_extensions: + msg = f"No allowed extension specified. Allowed extensions: {', '.join(self.allowed_extensions)}" + raise ValueError(msg) + elif self.output_file: + # Output results file with the given name and extension + self.target_result_json = os.path.join(self.target_exec_directory, self.output_file) + self.params += '--export-{} {}'.format(extension, self.target_result_json) + self.host_result_json = os.path.join(context.output_directory, self.output_file) + else: + # The output file is not specified + self.target_result_json = os.path.join(self.target_exec_directory, self.output_file) + self.params += '--save {}'.format(self.target_result_json) + self.host_result_json = os.path.join(context.output_directory, self.output_file) + + def run(self, context): + super(GeekbenchCmdline, self).run(context) + taskset = f"taskset {self.cpumask}" if self.cpumask else "" + binary = self.target.path.join(self.target_exec_directory, self.binary_name) + cmd = '{} {} {}'.format(taskset, binary, self.params) + + try: + self.output = self.target.execute(cmd, timeout=self.timeout, as_root=True) + except KeyboardInterrupt: + self.target.killall(self.binary_name) + raise + + def update_output(self, context): + super(GeekbenchCmdline, self).update_output(context) + if not self.output: + return + for workload in self.workload: + scores = [] + matches = re.findall(self.workloads[workload] + '(.+\d)', self.output) + for match in matches: + scores.append(int(re.search(r'\d+', match).group(0))) + if self.section == 4: + context.add_metric("OpenCL Score " + self.workloads[workload], scores[0]) + elif self.section == 9: + context.add_metric("Vulkan Score " + self.workloads[workload], scores[0]) + else: + context.add_metric("Single-Core Score " + self.workloads[workload], scores[0]) + if not self.is_single_core: + context.add_metric("Multi-Core Score " + self.workloads[workload], scores[1]) + + def extract_results(self, context): + # Extract results on the target + super(GeekbenchCmdline, self).extract_results(context) + self.target.pull(self.target_result_json, self.host_result_json) + context.add_artifact('GeekbenchCmdline_results', self.host_result_json, kind='raw') + + @once + def finalize(self, context): + if self.cleanup_assets: + self.target.remove(self.target_exec_directory) diff --git a/external/workload-automation/wa/workloads/speedometer/LICENSE b/external/workload-automation/wa/workloads/speedometer/LICENSE index dcf7fb523c8a392f254551b4945ba27c49c71b59..b34ad01b34d6f4cb8dd43bdb3a41440b39a2070c 100644 --- a/external/workload-automation/wa/workloads/speedometer/LICENSE +++ b/external/workload-automation/wa/workloads/speedometer/LICENSE @@ -1,7 +1,10 @@ The speedometer_archive.tgz file is a tarball containing the following archives from WebKit: the PerformanceTests/Speedometer directory state taken from https://github.com/WebKit/webkit as of: + 2.0: commit 5f402692d5f3406527dc107b5d20cc47dac929e8 Tue Jul 14 14:06:17 2020 +0000 + 3.0: + commit 734c49b3d075dcc33f56becf3bde8aca5245b719 Mon Feb 24 09:00:53 2025 -0800 WebKit is open source software with portions licensed under the LGPL and BSD licenses available at https://webkit.org/licensing-webkit/ diff --git a/external/workload-automation/wa/workloads/speedometer/__init__.py b/external/workload-automation/wa/workloads/speedometer/__init__.py index 45c1bdd4ba582f15b503a4602f4a10e0a1422fa4..ba13a61b5f39fce350267dfff53fd420d2681ae4 100755 --- a/external/workload-automation/wa/workloads/speedometer/__init__.py +++ b/external/workload-automation/wa/workloads/speedometer/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2014-2018 ARM Limited +# Copyright 2014-2025 ARM Limited # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,10 +14,10 @@ # from collections import defaultdict from http.server import SimpleHTTPRequestHandler, HTTPServer -import logging +from urllib.parse import urlencode +import lzma import os import re -import subprocess import tarfile import tempfile import threading @@ -36,7 +36,7 @@ class Speedometer(Workload): name = "speedometer" description = """ - A workload to execute the speedometer 2.0 web based benchmark. Requires device to be rooted. + A workload to execute the speedometer web based benchmark. Requires device to be rooted. This workload will only with Android 9+ devices if connected via TCP, or Android 5+ if connected via USB. @@ -54,14 +54,15 @@ class Speedometer(Workload): 1. Run 'git clone https://github.com/WebKit/webkit' - 2. Copy PerformanceTests/Speedometer to a directory called document_root, renaming Speedometer to Speedometer2.0 + 2. Copy PerformanceTests/Speedometer to a directory called document_root, renaming Speedometer + to Speedometer. For example, Speedometer2.0. - 3. Modify document_root/Speedometer2.0/index.html: + 3. Modify document_root/Speedometer/index.html: - 3a. Remove the 'defer' attribute from the ' to the very end of the section. - 4. Modify document_root/Speedometer2.0/resources/main.js: + 4. Modify document_root/Speedometer/resources/main.js (it's main.mjs for 3.0): 4a. Add the listed code after this line: @@ -81,7 +82,7 @@ class Speedometer(Workload): } } - 5. Run 'tar -cpzf speedometer_archive.tgz document_root' + 5. Run 'tar -cpzf speedometer_archive-.tar document_root; xz --format=lzma -9 -e speedometer_archive-.tar' 6. Copy the tarball into the workloads/speedometer directory @@ -120,6 +121,15 @@ class Speedometer(Workload): The app package for the browser that will be launched. """, ), + Parameter( + "version", + allowed_values=["2.0", "3.0"], + kind=str, + default="2.0", + description=""" + Speedometer version to run. Currently supports 2.0 and 3.0. + """, + ), ] def __init__(self, target, **kwargs): @@ -154,13 +164,16 @@ class Speedometer(Workload): Speedometer.document_root = os.path.join(self.temp_dir.name, "document_root") # Host a copy of Speedometer locally - tarball = context.get_resource(File(self, "speedometer_archive.tgz")) - with tarfile.open(name=tarball) as handle: - safe_extract(handle, self.temp_dir.name) + tarball = context.get_resource(File(self, f"speedometer_archive-{self.version}.tar.lzma")) + with lzma.open(tarball) as lzma_handle: + with tarfile.open(fileobj=lzma_handle) as handle: + safe_extract(handle, self.temp_dir.name) + self.archive_server.start(self.document_root) - Speedometer.speedometer_url = "http://localhost:{}/Speedometer2.0/index.html".format( - self.archive_server.get_port() + Speedometer.speedometer_url = "http://localhost:{}/Speedometer{}/index.html".format( + self.archive_server.get_port(), + self.version, ) def setup(self, context): @@ -240,10 +253,14 @@ class Speedometer(Workload): # Generate a UUID to search for in the browser's local storage to find out # when the workload has ended. report_end_id = uuid.uuid4().hex - url_with_unique_id = "{}?reportEndId={}".format( - self.speedometer_url, report_end_id - ) + query_params = {"reportEndId": report_end_id} + # Speedometer 3.0 does not start the test automatically, so we need to + # pass the "startAutomatically=true" parameter. + if self.version == "3.0": + query_params["startAutomatically"] = "true" + + url_with_unique_id = f"{self.speedometer_url}?{urlencode(query_params)}" browser_launch_cmd = "am start -a android.intent.action.VIEW -d '{}' {}".format( url_with_unique_id, self.chrome_package ) @@ -275,6 +292,7 @@ class Speedometer(Workload): benchmark_complete = False while not benchmark_complete: if self.target_file_was_created(local_storage): + candidate_files = [] if ( iterations % (find_period_s // sleep_period_s) == 0 or not local_storage_seen @@ -308,12 +326,12 @@ class Speedometer(Workload): iterations += 1 if iterations > ((timeout_period_m * 60) // sleep_period_s): - # We've been waiting 15 minutes for Speedometer to finish running - give up. + # We've been waiting minutes for Speedometer to finish running - give up. if not local_storage_seen: raise WorkloadError( - "Speedometer did not complete within 15m - Local Storage wasn't found" + f"Speedometer did not complete within {timeout_period_m} minutes - Local Storage wasn't found" ) - raise WorkloadError("Speedometer did not complete within 15 minutes.") + raise WorkloadError(f"Speedometer did not complete within {timeout_period_m} minutes.") time.sleep(sleep_period_s) diff --git a/external/workload-automation/wa/workloads/speedometer/speedometer_archive-2.0.tar.lzma b/external/workload-automation/wa/workloads/speedometer/speedometer_archive-2.0.tar.lzma new file mode 100644 index 0000000000000000000000000000000000000000..73fdc7cbf3e22eea9bd177fb2bc3d761aaa73363 Binary files /dev/null and b/external/workload-automation/wa/workloads/speedometer/speedometer_archive-2.0.tar.lzma differ diff --git a/external/workload-automation/wa/workloads/speedometer/speedometer_archive-3.0.tar.lzma b/external/workload-automation/wa/workloads/speedometer/speedometer_archive-3.0.tar.lzma new file mode 100644 index 0000000000000000000000000000000000000000..7a028a6bdc64dad9c9ff3ebb00416b257155b59c Binary files /dev/null and b/external/workload-automation/wa/workloads/speedometer/speedometer_archive-3.0.tar.lzma differ diff --git a/external/workload-automation/wa/workloads/speedometer/speedometer_archive.tgz b/external/workload-automation/wa/workloads/speedometer/speedometer_archive.tgz deleted file mode 100644 index cea62d3fbf14cd4303d23cdeb00a3b8aba937bec..0000000000000000000000000000000000000000 Binary files a/external/workload-automation/wa/workloads/speedometer/speedometer_archive.tgz and /dev/null differ