• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (c) 2013~2015 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"""SuiteRunner defines the interface from crosperf to test script."""
5
6from __future__ import print_function
7
8import os
9import time
10import shlex
11
12from cros_utils import command_executer
13import test_flag
14
15TEST_THAT_PATH = '/usr/bin/test_that'
16AUTOTEST_DIR = '~/trunk/src/third_party/autotest/files'
17CHROME_MOUNT_DIR = '/tmp/chrome_root'
18
19
20def GetProfilerArgs(profiler_args):
21  # Remove "--" from in front of profiler args.
22  args_list = shlex.split(profiler_args)
23  new_list = []
24  for arg in args_list:
25    if arg[0:2] == '--':
26      arg = arg[2:]
27    new_list.append(arg)
28  args_list = new_list
29
30  # Remove "perf_options=" from middle of profiler args.
31  new_list = []
32  for arg in args_list:
33    idx = arg.find('perf_options=')
34    if idx != -1:
35      prefix = arg[0:idx]
36      suffix = arg[idx + len('perf_options=') + 1:-1]
37      new_arg = prefix + "'" + suffix + "'"
38      new_list.append(new_arg)
39    else:
40      new_list.append(arg)
41  args_list = new_list
42
43  return ' '.join(args_list)
44
45
46class SuiteRunner(object):
47  """This defines the interface from crosperf to test script."""
48
49  def __init__(self,
50               logger_to_use=None,
51               log_level='verbose',
52               cmd_exec=None,
53               cmd_term=None):
54    self.logger = logger_to_use
55    self.log_level = log_level
56    self._ce = cmd_exec or command_executer.GetCommandExecuter(
57        self.logger, log_level=self.log_level)
58    self._ct = cmd_term or command_executer.CommandTerminator()
59
60  def Run(self, machine, label, benchmark, test_args, profiler_args):
61    for i in range(0, benchmark.retries + 1):
62      self.PinGovernorExecutionFrequencies(machine, label.chromeos_root)
63      if benchmark.suite == 'telemetry':
64        self.DecreaseWaitTime(machine, label.chromeos_root)
65        ret_tup = self.Telemetry_Run(machine, label, benchmark, profiler_args)
66      elif benchmark.suite == 'telemetry_Crosperf':
67        self.DecreaseWaitTime(machine, label.chromeos_root)
68        ret_tup = self.Telemetry_Crosperf_Run(machine, label, benchmark,
69                                              test_args, profiler_args)
70      else:
71        ret_tup = self.Test_That_Run(machine, label, benchmark, test_args,
72                                     profiler_args)
73      if ret_tup[0] != 0:
74        self.logger.LogOutput('benchmark %s failed. Retries left: %s' %
75                              (benchmark.name, benchmark.retries - i))
76      elif i > 0:
77        self.logger.LogOutput('benchmark %s succeded after %s retries' %
78                              (benchmark.name, i))
79        break
80      else:
81        self.logger.LogOutput('benchmark %s succeded on first try' %
82                              benchmark.name)
83        break
84    return ret_tup
85
86  def PinGovernorExecutionFrequencies(self, machine_name, chromeos_root):
87    """Set min and max frequencies to max static frequency."""
88    # pyformat: disable
89    set_cpu_freq = (
90        'set -e && '
91        'for f in /sys/devices/system/cpu/cpu*/cpufreq; do '
92        'cd $f; '
93        'val=0; '
94        'if [[ -e scaling_available_frequencies ]]; then '
95        # pylint: disable=line-too-long
96        '  val=`cat scaling_available_frequencies | tr " " "\\n" | sort -n -b -r`; '
97        'else '
98        '  val=`cat scaling_max_freq | tr " " "\\n" | sort -n -b -r`; fi ; '
99        'set -- $val; '
100        'highest=$1; '
101        'if [[ $# -gt 1 ]]; then '
102        '  case $highest in *1000) highest=$2;; esac; '
103        'fi ;'
104        'echo $highest > scaling_max_freq; '
105        'echo $highest > scaling_min_freq; '
106        'echo performance > scaling_governor; '
107        'done'
108    )
109    # pyformat: enable
110    if self.log_level == 'average':
111      self.logger.LogOutput('Pinning governor execution frequencies for %s' %
112                            machine_name)
113    ret = self._ce.CrosRunCommand(
114        set_cpu_freq, machine=machine_name, chromeos_root=chromeos_root)
115    self.logger.LogFatalIf(ret, 'Could not pin frequencies on machine: %s' %
116                           machine_name)
117
118  def DecreaseWaitTime(self, machine_name, chromeos_root):
119    """Change the ten seconds wait time for pagecycler to two seconds."""
120    FILE = '/usr/local/telemetry/src/tools/perf/page_sets/page_cycler_story.py'
121    ret = self._ce.CrosRunCommand(
122        'ls ' + FILE, machine=machine_name, chromeos_root=chromeos_root)
123    self.logger.LogFatalIf(ret, 'Could not find {} on machine: {}'.format(
124        FILE, machine_name))
125
126    if not ret:
127      sed_command = 'sed -i "s/_TTI_WAIT_TIME = 10/_TTI_WAIT_TIME = 2/g" '
128      ret = self._ce.CrosRunCommand(
129          sed_command + FILE, machine=machine_name, chromeos_root=chromeos_root)
130      self.logger.LogFatalIf(ret, 'Could not modify {} on machine: {}'.format(
131          FILE, machine_name))
132
133  def RebootMachine(self, machine_name, chromeos_root):
134    command = 'reboot && exit'
135    self._ce.CrosRunCommand(
136        command, machine=machine_name, chromeos_root=chromeos_root)
137    time.sleep(60)
138    # Whenever we reboot the machine, we need to restore the governor settings.
139    self.PinGovernorExecutionFrequencies(machine_name, chromeos_root)
140
141  def Test_That_Run(self, machine, label, benchmark, test_args, profiler_args):
142    """Run the test_that test.."""
143    options = ''
144    if label.board:
145      options += ' --board=%s' % label.board
146    if test_args:
147      options += ' %s' % test_args
148    if profiler_args:
149      self.logger.LogFatal('test_that does not support profiler.')
150    command = 'rm -rf /usr/local/autotest/results/*'
151    self._ce.CrosRunCommand(
152        command, machine=machine, chromeos_root=label.chromeos_root)
153
154    # We do this because some tests leave the machine in weird states.
155    # Rebooting between iterations has proven to help with this.
156    self.RebootMachine(machine, label.chromeos_root)
157
158    autotest_dir = AUTOTEST_DIR
159    if label.autotest_path != '':
160      autotest_dir = label.autotest_path
161
162    autotest_dir_arg = '--autotest_dir %s' % autotest_dir
163    # For non-telemetry tests, specify an autotest directory only if the
164    # specified directory is different from default (crosbug.com/679001).
165    if autotest_dir == AUTOTEST_DIR:
166      autotest_dir_arg = ''
167
168    command = (('%s %s --fast '
169                '%s %s %s') % (TEST_THAT_PATH, autotest_dir_arg, options,
170                               machine, benchmark.test_name))
171    if self.log_level != 'verbose':
172      self.logger.LogOutput('Running test.')
173      self.logger.LogOutput('CMD: %s' % command)
174    # Use --no-ns-pid so that cros_sdk does not create a different
175    # process namespace and we can kill process created easily by
176    # their process group.
177    return self._ce.ChrootRunCommandWOutput(
178        label.chromeos_root,
179        command,
180        command_terminator=self._ct,
181        cros_sdk_options='--no-ns-pid')
182
183  def RemoveTelemetryTempFile(self, machine, chromeos_root):
184    filename = 'telemetry@%s' % machine
185    fullname = os.path.join(chromeos_root, 'chroot', 'tmp', filename)
186    if os.path.exists(fullname):
187      os.remove(fullname)
188
189  def Telemetry_Crosperf_Run(self, machine, label, benchmark, test_args,
190                             profiler_args):
191    if not os.path.isdir(label.chrome_src):
192      self.logger.LogFatal('Cannot find chrome src dir to'
193                           ' run telemetry: %s' % label.chrome_src)
194
195    # Check for and remove temporary file that may have been left by
196    # previous telemetry runs (and which might prevent this run from
197    # working).
198    self.RemoveTelemetryTempFile(machine, label.chromeos_root)
199
200    # For telemetry runs, we can use the autotest copy from the source
201    # location. No need to have one under /build/<board>.
202    autotest_dir_arg = '--autotest_dir %s' % AUTOTEST_DIR
203    if label.autotest_path != '':
204      autotest_dir_arg = '--autotest_dir %s' % label.autotest_path
205
206    profiler_args = GetProfilerArgs(profiler_args)
207    fast_arg = ''
208    if not profiler_args:
209      # --fast works unless we are doing profiling (autotest limitation).
210      # --fast avoids unnecessary copies of syslogs.
211      fast_arg = '--fast'
212    args_string = ''
213    if test_args:
214      # Strip double quotes off args (so we can wrap them in single
215      # quotes, to pass through to Telemetry).
216      if test_args[0] == '"' and test_args[-1] == '"':
217        test_args = test_args[1:-1]
218      args_string = "test_args='%s'" % test_args
219
220    cmd = ('{} {} {} --board={} --args="{} run_local={} test={} '
221           '{}" {} telemetry_Crosperf'.format(TEST_THAT_PATH, autotest_dir_arg,
222                                              fast_arg, label.board,
223                                              args_string, benchmark.run_local,
224                                              benchmark.test_name,
225                                              profiler_args, machine))
226
227    # Use --no-ns-pid so that cros_sdk does not create a different
228    # process namespace and we can kill process created easily by their
229    # process group.
230    chrome_root_options = ('--no-ns-pid '
231                           '--chrome_root={} --chrome_root_mount={} '
232                           "FEATURES=\"-usersandbox\" "
233                           'CHROME_ROOT={}'.format(label.chrome_src,
234                                                   CHROME_MOUNT_DIR,
235                                                   CHROME_MOUNT_DIR))
236    if self.log_level != 'verbose':
237      self.logger.LogOutput('Running test.')
238      self.logger.LogOutput('CMD: %s' % cmd)
239    return self._ce.ChrootRunCommandWOutput(
240        label.chromeos_root,
241        cmd,
242        command_terminator=self._ct,
243        cros_sdk_options=chrome_root_options)
244
245  def Telemetry_Run(self, machine, label, benchmark, profiler_args):
246    telemetry_run_path = ''
247    if not os.path.isdir(label.chrome_src):
248      self.logger.LogFatal('Cannot find chrome src dir to' ' run telemetry.')
249    else:
250      telemetry_run_path = os.path.join(label.chrome_src, 'src/tools/perf')
251      if not os.path.exists(telemetry_run_path):
252        self.logger.LogFatal('Cannot find %s directory.' % telemetry_run_path)
253
254    if profiler_args:
255      self.logger.LogFatal('Telemetry does not support the perf profiler.')
256
257    # Check for and remove temporary file that may have been left by
258    # previous telemetry runs (and which might prevent this run from
259    # working).
260    if not test_flag.GetTestMode():
261      self.RemoveTelemetryTempFile(machine, label.chromeos_root)
262
263    rsa_key = os.path.join(
264        label.chromeos_root,
265        'src/scripts/mod_for_test_scripts/ssh_keys/testing_rsa')
266
267    cmd = ('cd {0} && '
268           './run_measurement '
269           '--browser=cros-chrome '
270           '--output-format=csv '
271           '--remote={1} '
272           '--identity {2} '
273           '{3} {4}'.format(telemetry_run_path, machine, rsa_key,
274                            benchmark.test_name, benchmark.test_args))
275    if self.log_level != 'verbose':
276      self.logger.LogOutput('Running test.')
277      self.logger.LogOutput('CMD: %s' % cmd)
278    return self._ce.RunCommandWOutput(cmd, print_to_console=False)
279
280  def CommandTerminator(self):
281    return self._ct
282
283  def Terminate(self):
284    self._ct.Terminate()
285
286
287class MockSuiteRunner(object):
288  """Mock suite runner for test."""
289
290  def __init__(self):
291    self._true = True
292
293  def Run(self, *_args):
294    if self._true:
295      return [0, '', '']
296    else:
297      return [0, '', '']
298