mirror of
				https://github.com/ARM-software/workload-automation.git
				synced 2025-10-31 15:12:25 +00:00 
			
		
		
		
	Adding telemetry workload.
This workload executes tests from Google's Telemetry browser test framework. Currently, only ChromeOS devices are supported.
This commit is contained in:
		
							
								
								
									
										222
									
								
								wlauto/workloads/telemetry/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								wlauto/workloads/telemetry/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,222 @@ | ||||
| # pylint: disable=attribute-defined-outside-init | ||||
| import os | ||||
| import re | ||||
| import csv | ||||
| import math | ||||
| from collections import defaultdict | ||||
|  | ||||
| from wlauto import Workload, Parameter | ||||
| from wlauto.exceptions import WorkloadError | ||||
| from wlauto.utils.misc import check_output, get_null, get_meansd | ||||
| from wlauto.utils.types import numeric, identifier | ||||
|  | ||||
|  | ||||
| RESULT_REGEX = re.compile(r'RESULT (\w+): ([^=]+)\s*=\s*\[([^\]]+)\]\s*(\S+)') | ||||
|  | ||||
|  | ||||
| class Telemetry(Workload): | ||||
|  | ||||
|     name = 'telemetry' | ||||
|     description = """ | ||||
|     Executes Google's Telemetery benchmarking framework (must be installed). | ||||
|  | ||||
|     Url: https://www.chromium.org/developers/telemetry | ||||
|  | ||||
|     From the web site: | ||||
|  | ||||
|     Telemetry is Chrome's performance testing framework. It allows you to | ||||
|     perform arbitrary actions on a set of web pages and report metrics about | ||||
|     it. The framework abstracts: | ||||
|  | ||||
|       - Launching a browser with arbitrary flags on any platform. | ||||
|       - Opening a tab and navigating to the page under test. | ||||
|       - Fetching data via the Inspector timeline and traces. | ||||
|       - Using Web Page Replay to cache real-world websites so they don't | ||||
|         change when used in benchmarks. | ||||
|  | ||||
|     Design Principles | ||||
|  | ||||
|       - Write one performance test that runs on all platforms - Windows, Mac, | ||||
|         Linux, Chrome OS, and Android for both Chrome and ContentShell. | ||||
|       - Runs on browser binaries, without a full Chromium checkout, and without | ||||
|         having to build the browser yourself. | ||||
|       - Use WebPageReplay to get repeatable test results. | ||||
|       - Clean architecture for writing benchmarks that keeps measurements and | ||||
|         use cases separate. | ||||
|       - Run on non-Chrome browsers for comparative studies. | ||||
|  | ||||
|     This instrument runs  telemetry via its ``run_benchmarks`` script (which | ||||
|     must be in PATH or specified using ``run_benchmarks_path`` parameter) and | ||||
|     parses metrics from the resulting output. | ||||
|  | ||||
|     **device setup** | ||||
|  | ||||
|     The device setup will depend on whether you're running a test image (in | ||||
|     which case little or no setup should be necessary) | ||||
|  | ||||
|  | ||||
|     """ | ||||
|  | ||||
|     parameters = [ | ||||
|         Parameter('run_benchmark_path', default='run_benchmark', | ||||
|                   description=""" | ||||
|                   This is the path to run_benchmark script which runs a | ||||
|                   Telemetry benchmark. If not specified, the assumption will be | ||||
|                   that it is in path (i.e. with be invoked as ``run_benchmark``). | ||||
|                   """), | ||||
|         Parameter('test', default='page_cycler.top_10_mobile', | ||||
|                   description=""" | ||||
|                   Specifies with of the the telemetry tests is to be run. | ||||
|                   """), | ||||
|         Parameter('run_benchmark_params', default='', | ||||
|                   description=""" | ||||
|                   Additional paramters to be passed to ``run_benchmarks``. | ||||
|                   """), | ||||
|         Parameter('run_timeout', kind=int, default=900, | ||||
|                   description=""" | ||||
|                   Timeout for execution of the test. | ||||
|                   """), | ||||
|     ] | ||||
|  | ||||
|     summary_metrics = ['cold_times', | ||||
|                        'commit_charge', | ||||
|                        'cpu_utilization', | ||||
|                        'processes', | ||||
|                        'resident_set_size_peak_size_browser', | ||||
|                        'resident_set_size_peak_size_gpu', | ||||
|                        'vm_final_size_browser', | ||||
|                        'vm_final_size_gpu', | ||||
|                        'vm_final_size_renderer', | ||||
|                        'vm_final_size_total', | ||||
|                        'vm_peak_size_browser', | ||||
|                        'vm_peak_size_gpu', | ||||
|                        'vm_private_dirty_final_browser', | ||||
|                        'vm_private_dirty_final_gpu', | ||||
|                        'vm_private_dirty_final_renderer', | ||||
|                        'vm_private_dirty_final_total', | ||||
|                        'vm_resident_set_size_final_size_browser', | ||||
|                        'vm_resident_set_size_final_size_gpu', | ||||
|                        'vm_resident_set_size_final_size_renderer', | ||||
|                        'vm_resident_set_size_final_size_total', | ||||
|                        'warm_times'] | ||||
|  | ||||
|     def validate(self): | ||||
|         ret = os.system('{} > {} 2>&1'.format(self.run_benchmark_path, get_null())) | ||||
|         if ret == 0xff00:  # is it supposed to be 0xff? | ||||
|             pass  # telemetry found and appears to be installed properly. | ||||
|         elif ret == 127: | ||||
|             raise WorkloadError('run_benchmarks not found (did you specify correct run_benchmarks_path?)') | ||||
|         else: | ||||
|             raise WorkloadError('Unexected error from run_benchmarks: {}'.format(ret)) | ||||
|  | ||||
|     def setup(self, context): | ||||
|         self.raw_output = None | ||||
|         self.command = self.build_command() | ||||
|  | ||||
|     def run(self, context): | ||||
|         self.logger.debug(self.command) | ||||
|         self.raw_output, _ = check_output(self.command, shell=True, timeout=self.run_timeout) | ||||
|  | ||||
|     def update_result(self, context): | ||||
|         if not self.raw_output: | ||||
|             self.logger.warning('Did not get run_benchmark output.') | ||||
|             return | ||||
|         raw_outfile = os.path.join(context.output_directory, 'telemetry_raw.out') | ||||
|         with open(raw_outfile, 'w') as wfh: | ||||
|             wfh.write(self.raw_output) | ||||
|         context.add_artifact('telemetry-raw', raw_outfile, kind='raw') | ||||
|  | ||||
|         results = parse_telemetry_results(raw_outfile) | ||||
|         csv_outfile = os.path.join(context.output_directory, 'telemetry.csv') | ||||
|         averages = defaultdict(list) | ||||
|         with open(csv_outfile, 'wb') as wfh: | ||||
|             writer = csv.writer(wfh) | ||||
|             writer.writerow(['kind', 'url', 'iteration', 'value', 'units']) | ||||
|             for result in results: | ||||
|                 name_template = identifier('{}_{}_{{}}'.format(result.url, result.kind)) | ||||
|                 averages[result.kind].append(result.average) | ||||
|                 context.result.add_metric(name_template.format('avg'), result.average, | ||||
|                                           result.units, lower_is_better=True) | ||||
|                 context.result.add_metric(name_template.format('sd'), result.std, | ||||
|                                           result.units, lower_is_better=True) | ||||
|                 writer.writerows(result.rows) | ||||
|             context.add_artifact('telemetry', csv_outfile, kind='data') | ||||
|  | ||||
|         for kind, values in averages.iteritems(): | ||||
|             context.result.add_metric(kind, special_average(values), lower_is_better=True) | ||||
|  | ||||
|     def teardown(self, context): | ||||
|         pass | ||||
|  | ||||
|     def build_command(self): | ||||
|         if self.device.platform == 'chromeos': | ||||
|             device_opts = '--remote={} --browser=cros-chrome'.format(self.device.host) | ||||
|         else: | ||||
|             raise WorkloadError('Currently, telemetry workload supports only ChromeOS devices.') | ||||
|         return '{} {} {} {}'.format(self.run_benchmark_path, | ||||
|                                     self.test, | ||||
|                                     device_opts, | ||||
|                                     self.run_benchmark_params) | ||||
|  | ||||
|  | ||||
| class TelemetryResult(object): | ||||
|  | ||||
|     @property | ||||
|     def average(self): | ||||
|         return get_meansd(self.values)[0] | ||||
|  | ||||
|     @property | ||||
|     def std(self): | ||||
|         return get_meansd(self.values)[1] | ||||
|  | ||||
|     @property | ||||
|     def rows(self): | ||||
|         for i, v in enumerate(self.values): | ||||
|             yield [self.kind, self.url, i, v, self.units] | ||||
|  | ||||
|     def __init__(self, kind=None, url=None, values=None, units=None): | ||||
|         self.kind = kind | ||||
|         self.url = url | ||||
|         self.values = values or [] | ||||
|         self.units = units | ||||
|  | ||||
|     def __str__(self): | ||||
|         return 'TR({kind},{url},{values},{units})'.format(**self.__dict__) | ||||
|  | ||||
|     __repr__ = __str__ | ||||
|  | ||||
|  | ||||
| def parse_telemetry_results(filepath): | ||||
|     results = [] | ||||
|     with open(filepath) as fh: | ||||
|         for line in fh: | ||||
|             match = RESULT_REGEX.search(line) | ||||
|             if match: | ||||
|                 result = TelemetryResult() | ||||
|                 result.kind = match.group(1) | ||||
|                 result.url = match.group(2) | ||||
|                 result.values = map(numeric, match.group(3).split(',')) | ||||
|                 result.units = match.group(4) | ||||
|                 results.append(result) | ||||
|     return results | ||||
|  | ||||
|  | ||||
| def special_average(values): | ||||
|     """Like Geometric mean but for negative numbers!""" | ||||
|     negs = [v < 0 for v in values] | ||||
|     abs_logs = [math.log(av, 10) for av in map(abs, values)] | ||||
|     signed_logs = [] | ||||
|     for lv, n in zip(abs_logs, negs): | ||||
|         if n: | ||||
|             signed_logs.append(-lv) | ||||
|         else: | ||||
|             signed_logs.append(lv) | ||||
|     return get_meansd(signed_logs)[0] | ||||
|  | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     import sys | ||||
|     from pprint import pprint | ||||
|     path = sys.argv[1] | ||||
|     pprint(parse_telemetry_results(path)) | ||||
|  | ||||
		Reference in New Issue
	
	Block a user