benchmark_run.py revision d97422aef5709f0a3b16b4efebc397c891940c95
1
2# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Module of benchmark runs."""
7from __future__ import print_function
8
9import datetime
10import threading
11import time
12import traceback
13
14from cros_utils import command_executer
15from cros_utils import timeline
16
17from suite_runner import SuiteRunner
18from results_cache import MockResult
19from results_cache import MockResultsCache
20from results_cache import Result
21from results_cache import ResultsCache
22
23STATUS_FAILED = 'FAILED'
24STATUS_SUCCEEDED = 'SUCCEEDED'
25STATUS_IMAGING = 'IMAGING'
26STATUS_RUNNING = 'RUNNING'
27STATUS_WAITING = 'WAITING'
28STATUS_PENDING = 'PENDING'
29
30
31class BenchmarkRun(threading.Thread):
32  """The benchmarkrun class."""
33  def __init__(self, name, benchmark, label, iteration, cache_conditions,
34               machine_manager, logger_to_use, log_level, share_cache):
35    threading.Thread.__init__(self)
36    self.name = name
37    self._logger = logger_to_use
38    self.log_level = log_level
39    self.benchmark = benchmark
40    self.iteration = iteration
41    self.label = label
42    self.result = None
43    self.terminated = False
44    self.retval = None
45    self.run_completed = False
46    self.machine_manager = machine_manager
47    self.suite_runner = SuiteRunner(self._logger, self.log_level)
48    self.machine = None
49    self.cache_conditions = cache_conditions
50    self.runs_complete = 0
51    self.cache_hit = False
52    self.failure_reason = ''
53    self.test_args = benchmark.test_args
54    self.cache = None
55    self.profiler_args = self._GetExtraAutotestArgs()
56    self._ce = command_executer.GetCommandExecuter(self._logger,
57                                                   log_level=self.log_level)
58    self.timeline = timeline.Timeline()
59    self.timeline.Record(STATUS_PENDING)
60    self.share_cache = share_cache
61    self.cache_has_been_read = False
62
63    # This is used by schedv2.
64    self.owner_thread = None
65
66  def ReadCache(self):
67    # Just use the first machine for running the cached version,
68    # without locking it.
69    self.cache = ResultsCache()
70    self.cache.Init(self.label.chromeos_image, self.label.chromeos_root,
71                    self.benchmark.test_name, self.iteration, self.test_args,
72                    self.profiler_args, self.machine_manager, self.machine,
73                    self.label.board, self.cache_conditions, self._logger,
74                    self.log_level, self.label, self.share_cache,
75                    self.benchmark.suite, self.benchmark.show_all_results,
76                    self.benchmark.run_local)
77
78    self.result = self.cache.ReadResult()
79    self.cache_hit = (self.result is not None)
80    self.cache_has_been_read = True
81
82  def run(self):
83    try:
84      if not self.cache_has_been_read:
85        self.ReadCache()
86
87      if self.result:
88        self._logger.LogOutput('%s: Cache hit.' % self.name)
89        self._logger.LogOutput(self.result.out, print_to_console=False)
90        self._logger.LogError(self.result.err, print_to_console=False)
91
92      elif self.label.cache_only:
93        self._logger.LogOutput('%s: No cache hit.' % self.name)
94        output = '%s: No Cache hit.' % self.name
95        retval = 1
96        err = 'No cache hit.'
97        self.result = Result.CreateFromRun(
98            self._logger, self.log_level, self.label, self.machine, output, err,
99            retval, self.benchmark.show_all_results, self.benchmark.test_name,
100            self.benchmark.suite)
101
102      else:
103        self._logger.LogOutput('%s: No cache hit.' % self.name)
104        self.timeline.Record(STATUS_WAITING)
105        # Try to acquire a machine now.
106        self.machine = self.AcquireMachine()
107        self.cache.machine = self.machine
108        self.result = self.RunTest(self.machine)
109
110        self.cache.remote = self.machine.name
111        self.label.chrome_version = self.machine_manager.GetChromeVersion(
112            self.machine)
113        self.cache.StoreResult(self.result)
114
115      if self.machine and not self.label.chrome_version:
116        self.label.chrome_version = self.machine_manager.GetChromeVersion(
117            self.machine)
118
119      if self.terminated:
120        return
121
122      if not self.result.retval:
123        self.timeline.Record(STATUS_SUCCEEDED)
124      else:
125        if self.timeline.GetLastEvent() != STATUS_FAILED:
126          self.failure_reason = 'Return value of test suite was non-zero.'
127          self.timeline.Record(STATUS_FAILED)
128
129    except Exception, e:
130      self._logger.LogError("Benchmark run: '%s' failed: %s" % (self.name, e))
131      traceback.print_exc()
132      if self.timeline.GetLastEvent() != STATUS_FAILED:
133        self.timeline.Record(STATUS_FAILED)
134        self.failure_reason = str(e)
135    finally:
136      if self.owner_thread is not None:
137        # In schedv2 mode, we do not lock machine locally. So noop here.
138        pass
139      elif self.machine:
140        if not self.machine.IsReachable():
141          self._logger.LogOutput('Machine %s is not reachable, removing it.' %
142                                 self.machine.name)
143          self.machine_manager.RemoveMachine(self.machine.name)
144        self._logger.LogOutput('Releasing machine: %s' % self.machine.name)
145        self.machine_manager.ReleaseMachine(self.machine)
146        self._logger.LogOutput('Released machine: %s' % self.machine.name)
147
148  def Terminate(self):
149    self.terminated = True
150    self.suite_runner.Terminate()
151    if self.timeline.GetLastEvent() != STATUS_FAILED:
152      self.timeline.Record(STATUS_FAILED)
153      self.failure_reason = 'Thread terminated.'
154
155  def AcquireMachine(self):
156    if self.owner_thread is not None:
157      # No need to lock machine locally, DutWorker, which is a thread, is
158      # responsible for running br.
159      return self.owner_thread.dut()
160    while True:
161      machine = None
162      if self.terminated:
163        raise Exception('Thread terminated while trying to acquire machine.')
164
165      machine = self.machine_manager.AcquireMachine(self.label)
166
167      if machine:
168        self._logger.LogOutput('%s: Machine %s acquired at %s' %
169                               (self.name, machine.name,
170                                datetime.datetime.now()))
171        break
172      else:
173        sleep_duration = 10
174        time.sleep(sleep_duration)
175    return machine
176
177  def _GetExtraAutotestArgs(self):
178    if self.benchmark.perf_args and self.benchmark.suite == 'telemetry':
179      self._logger.LogError('Telemetry does not support profiler.')
180      self.benchmark.perf_args = ''
181
182    if self.benchmark.perf_args and self.benchmark.suite == 'test_that':
183      self._logger.LogError('test_that does not support profiler.')
184      self.benchmark.perf_args = ''
185
186    if self.benchmark.perf_args:
187      perf_args_list = self.benchmark.perf_args.split(' ')
188      perf_args_list = [perf_args_list[0]] + ['-a'] + perf_args_list[1:]
189      perf_args = ' '.join(perf_args_list)
190      if not perf_args_list[0] in ['record', 'stat']:
191        raise Exception('perf_args must start with either record or stat')
192      extra_test_args = ['--profiler=custom_perf',
193                         ("--profiler_args='perf_options=\"%s\"'" % perf_args)]
194      return ' '.join(extra_test_args)
195    else:
196      return ''
197
198  def RunTest(self, machine):
199    self.timeline.Record(STATUS_IMAGING)
200    if self.owner_thread is not None:
201      # In schedv2 mode, do not even call ImageMachine. Machine image is
202      # guarenteed.
203      pass
204    else:
205      self.machine_manager.ImageMachine(machine, self.label)
206    self.timeline.Record(STATUS_RUNNING)
207    [retval, out, err] = self.suite_runner.Run(machine.name, self.label,
208                                               self.benchmark, self.test_args,
209                                               self.profiler_args)
210    self.run_completed = True
211    return Result.CreateFromRun(self._logger, self.log_level, self.label,
212                                self.machine, out, err, retval,
213                                self.benchmark.show_all_results,
214                                self.benchmark.test_name, self.benchmark.suite)
215
216  def SetCacheConditions(self, cache_conditions):
217    self.cache_conditions = cache_conditions
218
219  def __str__(self):
220    """For better debugging."""
221
222    return 'BenchmarkRun[name="{}"]'.format(self.name)
223
224
225class MockBenchmarkRun(BenchmarkRun):
226  """Inherited from BenchmarkRun."""
227
228  def ReadCache(self):
229    # Just use the first machine for running the cached version,
230    # without locking it.
231    self.cache = MockResultsCache()
232    self.cache.Init(self.label.chromeos_image, self.label.chromeos_root,
233                    self.benchmark.test_name, self.iteration, self.test_args,
234                    self.profiler_args, self.machine_manager, self.machine,
235                    self.label.board, self.cache_conditions, self._logger,
236                    self.log_level, self.label, self.share_cache,
237                    self.benchmark.suite, self.benchmark.show_all_results,
238                    self.benchmark.run_local)
239
240    self.result = self.cache.ReadResult()
241    self.cache_hit = (self.result is not None)
242
243  def RunTest(self, machine):
244    """Remove Result.CreateFromRun for testing."""
245    self.timeline.Record(STATUS_IMAGING)
246    self.machine_manager.ImageMachine(machine, self.label)
247    self.timeline.Record(STATUS_RUNNING)
248    [retval, out, err] = self.suite_runner.Run(machine.name, self.label,
249                                               self.benchmark, self.test_args,
250                                               self.profiler_args)
251    self.run_completed = True
252    rr = MockResult('logger', self.label, self.log_level, machine)
253    rr.out = out
254    rr.err = err
255    rr.retval = retval
256    return rr
257