Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(86)

Side by Side Diff: chrome/test/vr/perf/latency/webvr_latency_test.py

Issue 2823883003: Make VR latency results compatible with perf dashboard, refactor into classes (Closed)
Patch Set: Move BUILD.gn, merge logging Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright 2017 The Chromium 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
5 import motopho_thread as mt
6 import robot_arm as ra
7
8 import json
9 import glob
10 import logging
11 import numpy
12 import os
13 import re
14 import subprocess
15 import sys
16 import time
17
Nico 2017/04/20 17:51:21 ditto
bsheedy 2017/04/20 20:05:00 Done.
18 MOTOPHO_THREAD_TIMEOUT = 30
19
Nico 2017/04/20 17:51:21 ditto
bsheedy 2017/04/20 20:05:00 Done.
20 def GetTtyDevices(tty_pattern, vendor_ids):
21 """Finds all devices connected to tty that match a pattern and device id.
22
23 If a serial device is connected to the computer via USB, this function
24 will check all tty devices that match tty_pattern, and return the ones
25 that have vendor identification number in the list vendor_ids.
26
27 Args:
28 tty_pattern: The search pattern, such as r'ttyACM\d+'.
29 vendor_ids: The list of 16-bit USB vendor ids, such as [0x2a03].
30
31 Returns:
32 A list of strings of tty devices, for example ['ttyACM0'].
33 """
34 product_string = 'PRODUCT='
35 sys_class_dir = '/sys/class/tty/'
36
37 tty_devices = glob.glob(sys_class_dir + '*')
38
39 matcher = re.compile('.*' + tty_pattern)
40 tty_matches = [x for x in tty_devices if matcher.search(x)]
41 tty_matches = [x[len(sys_class_dir):] for x in tty_matches]
42
43 found_devices = []
44 for match in tty_matches:
45 class_filename = sys_class_dir + match + '/device/uevent'
46 with open(class_filename, 'r') as uevent_file:
47 # Look for the desired product id in the uevent text.
48 for line in uevent_file:
49 if product_string in line:
50 ids = line[len(product_string):].split('/')
51 ids = [int(x, 16) for x in ids]
52
53 for desired_id in vendor_ids:
54 if desired_id in ids:
55 found_devices.append(match)
56
57 return found_devices
58
Nico 2017/04/20 17:51:21 ditto
bsheedy 2017/04/20 20:05:00 Done.
59 class WebVrLatencyTest(object):
60 """Base class for all WebVR latency tests.
61
62 This is meant to be subclassed for each platform the test is run on. While
63 the latency test itself is cross-platform, the setup and teardown for
64 tests is platform-dependent.
65 """
66 def __init__(self, args):
67 self.args = args
68 self._num_samples = args.num_samples
69 self._flicker_app_url = args.url
70 assert (self._num_samples > 0),'Number of samples must be greater than 0'
71 self._device_name = 'generic_device'
72
73 # Connect to the Arduino that drives the servos
74 devices = GetTtyDevices(r'ttyACM\d+', [0x2a03, 0x2341])
75 assert (len(devices) == 1),'Found %d devices, expected 1' % len(devices)
76 self.robot_arm = ra.RobotArm(devices[0])
77
78 def RunTest(self):
79 """Runs the steps to start Chrome, measure/save latency, and clean up."""
80 self._Setup()
81 self._Run()
82 self._Teardown()
83
84 def _Setup(self):
85 """Perform any platform-specific setup."""
86 raise NotImplementedError(
87 'Platform-specific setup must be implemented in subclass')
88
89 def _Run(self):
90 """Run the latency test.
91
92 Handles the actual latency measurement, which is identical across
93 different platforms, as well as result saving.
94 """
95 # Motopho scripts use relative paths, so switch to the Motopho directory
96 os.chdir(self.args.motopho_path)
97
98 # Set up the thread that runs the Motopho script
99 motopho_thread = mt.MotophoThread(self._num_samples)
100 motopho_thread.start()
101
102 # Run multiple times so we can get an average and standard deviation
103 for _ in xrange(self._num_samples):
104 self.robot_arm.ResetPosition()
105 # Start the Motopho script
106 motopho_thread.StartIteration()
107 # Let the Motopho be stationary so the script can calculate the bias
108 time.sleep(3)
109 motopho_thread.BlockNextIteration()
110 # Move so we can measure latency
111 self.robot_arm.StartMotophoMovement()
112 if not motopho_thread.WaitForIterationEnd(MOTOPHO_THREAD_TIMEOUT):
113 # TODO(bsheedy): Look into ways to prevent Motopho from not sending any
114 # data until unplugged and replugged into the machine after a reboot.
115 logging.error('Motopho thread timeout, '
116 'Motopho may need to be replugged.')
117 self.robot_arm.StopAllMovement()
118 time.sleep(1)
119 self._SaveResults(motopho_thread.latencies, motopho_thread.correlations)
120
121 def _Teardown(self):
122 """Performs any platform-specific teardown."""
123 raise NotImplementedError(
124 'Platform-specific setup must be implemented in subclass')
125
126 def _RunCommand(self, cmd):
127 """Runs the given cmd list and returns its output.
128
129 Prints the command's output and exits if any error occurs.
130
131 Returns:
132 A string containing the stdout and stderr of the command.
133 """
134 try:
135 return subprocess.check_output(cmd, stderr=subprocess.STDOUT)
136 except subprocess.CalledProcessError as e:
137 logging.error('Failed command output: %s', e.output)
138 raise e
139
140 def _SetChromeCommandLineFlags(self, flags):
141 raise NotImplementedError(
142 'Command-line flag setting must be implemented in subclass')
143
144 def _SaveResults(self, latencies, correlations):
145 """Saves the results to a JSON file.
146
147 Saved JSON object is compatible with Chrome perf dashboard if
148 put in as the 'chart_data' value. Also logs the raw data and its
149 average/standard deviation.
150 """
151 avg_latency = sum(latencies) / len(latencies)
152 std_latency = numpy.std(latencies)
153 avg_correlation = sum(correlations) / len(correlations)
154 std_correlation = numpy.std(correlations)
155 logging.info('Raw latencies: %s\nRaw correlations: %s\n'
156 'Avg latency: %f +/- %f\nAvg correlation: %f +/- %f',
157 str(latencies), str(correlations), avg_latency, std_latency,
158 avg_correlation, std_correlation)
159
160 if not (self.args.output_dir and os.path.isdir(self.args.output_dir)):
161 logging.warning('No output directory set, not saving results to file')
162 return
163
164 results = {
165 'format_version': '1.0',
166 'benchmark_name': 'webvr_latency',
167 'benchmark_description': 'Measures the motion-to-photon latency of WebVR',
168 'charts': {
169 'correlation': {
170 'summary': {
171 'improvement_direction': 'up',
172 'name': 'correlation',
173 'std': std_correlation,
174 'type': 'list_of_scalar_values',
175 'units': '',
176 'values': correlations,
177 },
178 },
179 'latency': {
180 'summary': {
181 'improvement_direction': 'down',
182 'name': 'latency',
183 'std': std_latency,
184 'type': 'list_of_scalar_values',
185 'units': 'ms',
186 'values': latencies,
187 },
188 }
189 }
190 }
191
192 with file(os.path.join(self.args.output_dir,
193 self.args.results_file), 'w') as outfile:
194 json.dump(results, outfile)
195
Nico 2017/04/20 17:51:21 nit: no trailing blank line
bsheedy 2017/04/20 20:05:01 Done.
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698