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

Side by Side Diff: tools/testrunner/local/execution.py

Issue 275093002: Introduce a dynamic process pool for the local test driver (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Review Created 6 years, 7 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 | Annotate | Revision Log
« no previous file with comments | « tools/testrunner/local/commands.py ('k') | tools/testrunner/local/pool.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2012 the V8 project authors. All rights reserved. 1 # Copyright 2012 the V8 project authors. All rights reserved.
2 # Redistribution and use in source and binary forms, with or without 2 # Redistribution and use in source and binary forms, with or without
3 # modification, are permitted provided that the following conditions are 3 # modification, are permitted provided that the following conditions are
4 # met: 4 # met:
5 # 5 #
6 # * Redistributions of source code must retain the above copyright 6 # * Redistributions of source code must retain the above copyright
7 # notice, this list of conditions and the following disclaimer. 7 # notice, this list of conditions and the following disclaimer.
8 # * Redistributions in binary form must reproduce the above 8 # * Redistributions in binary form must reproduce the above
9 # copyright notice, this list of conditions and the following 9 # copyright notice, this list of conditions and the following
10 # disclaimer in the documentation and/or other materials provided 10 # disclaimer in the documentation and/or other materials provided
11 # with the distribution. 11 # with the distribution.
12 # * Neither the name of Google Inc. nor the names of its 12 # * Neither the name of Google Inc. nor the names of its
13 # contributors may be used to endorse or promote products derived 13 # contributors may be used to endorse or promote products derived
14 # from this software without specific prior written permission. 14 # from this software without specific prior written permission.
15 # 15 #
16 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 27
28 28
29 import multiprocessing
30 import os 29 import os
31 import threading
32 import time 30 import time
33 31
32 from pool import Pool
34 from . import commands 33 from . import commands
35 from . import utils 34 from . import utils
36 35
37 36
38 BREAK_NOW = -1
39 EXCEPTION = -2
40
41
42 class Job(object): 37 class Job(object):
43 def __init__(self, command, dep_command, test_id, timeout, verbose): 38 def __init__(self, command, dep_command, test_id, timeout, verbose):
44 self.command = command 39 self.command = command
45 self.dep_command = dep_command 40 self.dep_command = dep_command
46 self.id = test_id 41 self.id = test_id
47 self.timeout = timeout 42 self.timeout = timeout
48 self.verbose = verbose 43 self.verbose = verbose
49 44
50 45
51 def RunTest(job): 46 def RunTest(job):
52 try: 47 start_time = time.time()
53 start_time = time.time() 48 if job.dep_command is not None:
54 if job.dep_command is not None: 49 dep_output = commands.Execute(job.dep_command, job.verbose, job.timeout)
55 dep_output = commands.Execute(job.dep_command, job.verbose, job.timeout) 50 # TODO(jkummerow): We approximate the test suite specific function
56 # TODO(jkummerow): We approximate the test suite specific function 51 # IsFailureOutput() by just checking the exit code here. Currently
57 # IsFailureOutput() by just checking the exit code here. Currently 52 # only cctests define dependencies, for which this simplification is
58 # only cctests define dependencies, for which this simplification is 53 # correct.
59 # correct. 54 if dep_output.exit_code != 0:
60 if dep_output.exit_code != 0: 55 return (job.id, dep_output, time.time() - start_time)
61 return (job.id, dep_output, time.time() - start_time) 56 output = commands.Execute(job.command, job.verbose, job.timeout)
62 output = commands.Execute(job.command, job.verbose, job.timeout) 57 return (job.id, output, time.time() - start_time)
63 return (job.id, output, time.time() - start_time)
64 except KeyboardInterrupt:
65 return (-1, BREAK_NOW, 0)
66 except Exception, e:
67 print(">>> EXCEPTION: %s" % e)
68 return (-1, EXCEPTION, 0)
69
70 58
71 class Runner(object): 59 class Runner(object):
72 60
73 def __init__(self, suites, progress_indicator, context): 61 def __init__(self, suites, progress_indicator, context):
74 self.tests = [ t for s in suites for t in s.tests ] 62 self.tests = [ t for s in suites for t in s.tests ]
75 self._CommonInit(len(self.tests), progress_indicator, context) 63 self._CommonInit(len(self.tests), progress_indicator, context)
76 64
77 def _CommonInit(self, num_tests, progress_indicator, context): 65 def _CommonInit(self, num_tests, progress_indicator, context):
78 self.indicator = progress_indicator 66 self.indicator = progress_indicator
79 progress_indicator.runner = self 67 progress_indicator.runner = self
80 self.context = context 68 self.context = context
81 self.succeeded = 0 69 self.succeeded = 0
82 self.total = num_tests 70 self.total = num_tests
83 self.remaining = num_tests 71 self.remaining = num_tests
84 self.failed = [] 72 self.failed = []
85 self.crashed = 0 73 self.crashed = 0
86 self.terminate = False
87 self.lock = threading.Lock()
88 74
89 def Run(self, jobs): 75 def Run(self, jobs):
90 self.indicator.Starting() 76 self.indicator.Starting()
91 self._RunInternal(jobs) 77 self._RunInternal(jobs)
92 self.indicator.Done() 78 self.indicator.Done()
93 if self.failed or self.remaining: 79 if self.failed or self.remaining:
94 return 1 80 return 1
95 return 0 81 return 0
96 82
97 def _RunInternal(self, jobs): 83 def _RunInternal(self, jobs):
98 pool = multiprocessing.Pool(processes=jobs) 84 pool = Pool(jobs)
99 test_map = {} 85 test_map = {}
86 # TODO(machenbach): Instead of filling the queue completely before
87 # pool.imap_unordered, make this a generator that already starts testing
88 # while the queue is filled.
100 queue = [] 89 queue = []
101 queued_exception = None 90 queued_exception = None
102 for test in self.tests: 91 for test in self.tests:
103 assert test.id >= 0 92 assert test.id >= 0
104 test_map[test.id] = test 93 test_map[test.id] = test
105 try: 94 try:
106 command = self.GetCommand(test) 95 command = self.GetCommand(test)
107 except Exception, e: 96 except Exception, e:
108 # If this failed, save the exception and re-raise it later (after 97 # If this failed, save the exception and re-raise it later (after
109 # all other tests have had a chance to run). 98 # all other tests have had a chance to run).
110 queued_exception = e 99 queued_exception = e
111 continue 100 continue
112 timeout = self.context.timeout 101 timeout = self.context.timeout
113 if ("--stress-opt" in test.flags or 102 if ("--stress-opt" in test.flags or
114 "--stress-opt" in self.context.mode_flags or 103 "--stress-opt" in self.context.mode_flags or
115 "--stress-opt" in self.context.extra_flags): 104 "--stress-opt" in self.context.extra_flags):
116 timeout *= 4 105 timeout *= 4
117 if test.dependency is not None: 106 if test.dependency is not None:
118 dep_command = [ c.replace(test.path, test.dependency) for c in command ] 107 dep_command = [ c.replace(test.path, test.dependency) for c in command ]
119 else: 108 else:
120 dep_command = None 109 dep_command = None
121 job = Job(command, dep_command, test.id, timeout, self.context.verbose) 110 job = Job(command, dep_command, test.id, timeout, self.context.verbose)
122 queue.append(job) 111 queue.append([job])
123 try: 112 try:
124 kChunkSize = 1 113 it = pool.imap_unordered(RunTest, queue)
125 it = pool.imap_unordered(RunTest, queue, kChunkSize)
126 for result in it: 114 for result in it:
127 test_id = result[0] 115 test = test_map[result[0]]
128 if test_id < 0:
129 if result[1] == BREAK_NOW:
130 self.terminate = True
131 else:
132 continue
133 if self.terminate:
134 pool.terminate()
135 pool.join()
136 raise BreakNowException("User pressed Ctrl+C or IO went wrong")
137 test = test_map[test_id]
138 self.indicator.AboutToRun(test) 116 self.indicator.AboutToRun(test)
139 test.output = result[1] 117 test.output = result[1]
140 test.duration = result[2] 118 test.duration = result[2]
141 has_unexpected_output = test.suite.HasUnexpectedOutput(test) 119 has_unexpected_output = test.suite.HasUnexpectedOutput(test)
142 if has_unexpected_output: 120 if has_unexpected_output:
143 self.failed.append(test) 121 self.failed.append(test)
144 if test.output.HasCrashed(): 122 if test.output.HasCrashed():
145 self.crashed += 1 123 self.crashed += 1
146 else: 124 else:
147 self.succeeded += 1 125 self.succeeded += 1
148 self.remaining -= 1 126 self.remaining -= 1
149 self.indicator.HasRun(test, has_unexpected_output) 127 self.indicator.HasRun(test, has_unexpected_output)
150 except KeyboardInterrupt: 128 finally:
151 pool.terminate() 129 pool.terminate()
152 pool.join()
153 raise
154 except Exception, e:
155 print("Exception: %s" % e)
156 pool.terminate()
157 pool.join()
158 raise
159 if queued_exception: 130 if queued_exception:
160 raise queued_exception 131 raise queued_exception
161 return
162 132
163 133
164 def GetCommand(self, test): 134 def GetCommand(self, test):
165 d8testflag = [] 135 d8testflag = []
166 shell = test.suite.shell() 136 shell = test.suite.shell()
167 if shell == "d8": 137 if shell == "d8":
168 d8testflag = ["--test"] 138 d8testflag = ["--test"]
169 if utils.IsWindows(): 139 if utils.IsWindows():
170 shell += ".exe" 140 shell += ".exe"
171 cmd = (self.context.command_prefix + 141 cmd = (self.context.command_prefix +
172 [os.path.abspath(os.path.join(self.context.shell_dir, shell))] + 142 [os.path.abspath(os.path.join(self.context.shell_dir, shell))] +
173 d8testflag + 143 d8testflag +
174 ["--random-seed=%s" % self.context.random_seed] + 144 ["--random-seed=%s" % self.context.random_seed] +
175 test.suite.GetFlagsForTestCase(test, self.context) + 145 test.suite.GetFlagsForTestCase(test, self.context) +
176 self.context.extra_flags) 146 self.context.extra_flags)
177 return cmd 147 return cmd
178 148
179 149
180 class BreakNowException(Exception): 150 class BreakNowException(Exception):
181 def __init__(self, value): 151 def __init__(self, value):
182 self.value = value 152 self.value = value
183 def __str__(self): 153 def __str__(self):
184 return repr(self.value) 154 return repr(self.value)
OLDNEW
« no previous file with comments | « tools/testrunner/local/commands.py ('k') | tools/testrunner/local/pool.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698