blob: 18d697caa2a53bd946925a08a9d84acedc8b8e15 [file] [log] [blame]
Johnny Chene8d9dc62011-10-31 19:04:07 +00001#!/usr/bin/env python
2
3"""
4Run the test suite using a separate process for each test file.
Vince Harronede59652015-01-08 02:11:26 +00005
Siva Chandra2d7832e2015-05-08 23:08:53 +00006Each test will run with a time limit of 10 minutes by default.
Vince Harronede59652015-01-08 02:11:26 +00007
Siva Chandra2d7832e2015-05-08 23:08:53 +00008Override the default time limit of 10 minutes by setting
Vince Harronede59652015-01-08 02:11:26 +00009the environment variable LLDB_TEST_TIMEOUT.
10
11E.g., export LLDB_TEST_TIMEOUT=10m
12
13Override the time limit for individual tests by setting
14the environment variable LLDB_[TEST NAME]_TIMEOUT.
15
16E.g., export LLDB_TESTCONCURRENTEVENTS_TIMEOUT=2m
17
18Set to "0" to run without time limit.
19
20E.g., export LLDB_TEST_TIMEOUT=0
21or export LLDB_TESTCONCURRENTEVENTS_TIMEOUT=0
Vince Harrondcc2b9f2015-05-27 04:40:36 +000022
Chaoren Linb6325d02015-08-12 18:02:54 +000023To collect core files for timed out tests,
24do the following before running dosep.py
Vince Harrondcc2b9f2015-05-27 04:40:36 +000025
26OSX
27ulimit -c unlimited
28sudo sysctl -w kern.corefile=core.%P
29
30Linux:
31ulimit -c unlimited
32echo core.%p | sudo tee /proc/sys/kernel/core_pattern
Johnny Chene8d9dc62011-10-31 19:04:07 +000033"""
34
Todd Fiala68615ce2015-09-15 21:38:04 +000035import asyncore
Vince Harrondcc2b9f2015-05-27 04:40:36 +000036import fnmatch
Todd Fiala8cbeed32015-09-08 22:22:33 +000037import multiprocessing
38import multiprocessing.pool
39import os
Todd Fiala3f0a3602014-07-08 06:42:37 +000040import platform
Todd Fiala8cbeed32015-09-08 22:22:33 +000041import Queue
Vince Harron06381732015-05-12 23:10:36 +000042import re
Todd Fiala8cbeed32015-09-08 22:22:33 +000043import signal
Vince Harron17f429f2014-12-13 00:08:19 +000044import subprocess
Todd Fiala3f0a3602014-07-08 06:42:37 +000045import sys
Todd Fiala8cbeed32015-09-08 22:22:33 +000046import threading
47
Todd Fiala68615ce2015-09-15 21:38:04 +000048import dotest_channels
Todd Fiala8cbeed32015-09-08 22:22:33 +000049import dotest_args
Steve Puccibefe2b12014-03-07 00:01:11 +000050
Chaoren Linb6325d02015-08-12 18:02:54 +000051
Vince Harron17f429f2014-12-13 00:08:19 +000052def get_timeout_command():
Vince Harronede59652015-01-08 02:11:26 +000053 """Search for a suitable timeout command."""
Ying Chen93190c42015-07-20 20:04:22 +000054 if not sys.platform.startswith("win32"):
55 try:
56 subprocess.call("timeout", stderr=subprocess.PIPE)
57 return "timeout"
58 except OSError:
59 pass
Vince Harron17f429f2014-12-13 00:08:19 +000060 try:
Chaoren Lin45c17ff2015-05-28 23:00:10 +000061 subprocess.call("gtimeout", stderr=subprocess.PIPE)
Vince Harron17f429f2014-12-13 00:08:19 +000062 return "gtimeout"
63 except OSError:
64 pass
65 return None
66
67timeout_command = get_timeout_command()
68
Vince Harron17f429f2014-12-13 00:08:19 +000069# Status codes for running command with timeout.
70eTimedOut, ePassed, eFailed = 124, 0, 1
71
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000072output_lock = None
73test_counter = None
74total_tests = None
Chaoren Linffc63b02015-08-12 18:02:49 +000075test_name_len = None
Pavel Labath05ab2372015-07-06 15:57:52 +000076dotest_options = None
Zachary Turner38e64172015-08-10 17:46:11 +000077output_on_success = False
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000078
Todd Fiala68615ce2015-09-15 21:38:04 +000079RESULTS_FORMATTER = None
80RUNNER_PROCESS_ASYNC_MAP = None
81RESULTS_LISTENER_CHANNEL = None
Chaoren Linb6325d02015-08-12 18:02:54 +000082
Todd Fiala33896a92015-09-18 21:01:13 +000083"""Contains an optional function pointer that can return the worker index
84 for the given thread/process calling it. Returns a 0-based index."""
85GET_WORKER_INDEX = None
86
87def setup_global_variables(
88 lock, counter, total, name_len, options, worker_index_map):
Chaoren Linffc63b02015-08-12 18:02:49 +000089 global output_lock, test_counter, total_tests, test_name_len
90 global dotest_options
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000091 output_lock = lock
92 test_counter = counter
93 total_tests = total
Chaoren Linffc63b02015-08-12 18:02:49 +000094 test_name_len = name_len
Pavel Labath05ab2372015-07-06 15:57:52 +000095 dotest_options = options
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000096
Todd Fiala33896a92015-09-18 21:01:13 +000097 if worker_index_map is not None:
98 # We'll use the output lock for this to avoid sharing another lock.
99 # This won't be used much.
100 index_lock = lock
101
102 def get_worker_index_use_pid():
103 """Returns a 0-based, process-unique index for the worker."""
104 pid = os.getpid()
105 with index_lock:
106 if pid not in worker_index_map:
107 worker_index_map[pid] = len(worker_index_map)
108 return worker_index_map[pid]
109
110 global GET_WORKER_INDEX
111 GET_WORKER_INDEX = get_worker_index_use_pid
112
Chaoren Linb6325d02015-08-12 18:02:54 +0000113
Zachary Turner38e64172015-08-10 17:46:11 +0000114def report_test_failure(name, command, output):
115 global output_lock
116 with output_lock:
Chaoren Linffc63b02015-08-12 18:02:49 +0000117 print >> sys.stderr
Zachary Turner38e64172015-08-10 17:46:11 +0000118 print >> sys.stderr, output
Chaoren Linffc63b02015-08-12 18:02:49 +0000119 print >> sys.stderr, "[%s FAILED]" % name
Zachary Turner38e64172015-08-10 17:46:11 +0000120 print >> sys.stderr, "Command invoked: %s" % ' '.join(command)
Chaoren Linffc63b02015-08-12 18:02:49 +0000121 update_progress(name)
Zachary Turner38e64172015-08-10 17:46:11 +0000122
Chaoren Linb6325d02015-08-12 18:02:54 +0000123
Zachary Turner38e64172015-08-10 17:46:11 +0000124def report_test_pass(name, output):
125 global output_lock, output_on_success
126 with output_lock:
127 if output_on_success:
Chaoren Linffc63b02015-08-12 18:02:49 +0000128 print >> sys.stderr
Zachary Turner38e64172015-08-10 17:46:11 +0000129 print >> sys.stderr, output
Chaoren Linffc63b02015-08-12 18:02:49 +0000130 print >> sys.stderr, "[%s PASSED]" % name
131 update_progress(name)
Zachary Turner38e64172015-08-10 17:46:11 +0000132
Chaoren Linb6325d02015-08-12 18:02:54 +0000133
Chaoren Linffc63b02015-08-12 18:02:49 +0000134def update_progress(test_name=""):
135 global output_lock, test_counter, total_tests, test_name_len
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000136 with output_lock:
Chaoren Linffc63b02015-08-12 18:02:49 +0000137 counter_len = len(str(total_tests))
138 sys.stderr.write(
139 "\r%*d out of %d test suites processed - %-*s" %
140 (counter_len, test_counter.value, total_tests,
141 test_name_len.value, test_name))
142 if len(test_name) > test_name_len.value:
143 test_name_len.value = len(test_name)
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000144 test_counter.value += 1
Zachary Turner38e64172015-08-10 17:46:11 +0000145 sys.stdout.flush()
146 sys.stderr.flush()
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000147
Chaoren Linb6325d02015-08-12 18:02:54 +0000148
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000149def parse_test_results(output):
150 passes = 0
151 failures = 0
Zachary Turner4cceca72015-08-14 16:45:32 +0000152 unexpected_successes = 0
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000153 for result in output:
Chaoren Linb6325d02015-08-12 18:02:54 +0000154 pass_count = re.search("^RESULT:.*([0-9]+) passes",
155 result, re.MULTILINE)
156 fail_count = re.search("^RESULT:.*([0-9]+) failures",
157 result, re.MULTILINE)
158 error_count = re.search("^RESULT:.*([0-9]+) errors",
159 result, re.MULTILINE)
Zachary Turner4cceca72015-08-14 16:45:32 +0000160 unexpected_success_count = re.search("^RESULT:.*([0-9]+) unexpected successes",
161 result, re.MULTILINE)
Chaoren Linb6325d02015-08-12 18:02:54 +0000162 if pass_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000163 passes = passes + int(pass_count.group(1))
Chaoren Linb6325d02015-08-12 18:02:54 +0000164 if fail_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000165 failures = failures + int(fail_count.group(1))
Zachary Turner4cceca72015-08-14 16:45:32 +0000166 if unexpected_success_count is not None:
167 unexpected_successes = unexpected_successes + int(unexpected_success_count.group(1))
Chaoren Linb6325d02015-08-12 18:02:54 +0000168 if error_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000169 failures = failures + int(error_count.group(1))
170 pass
Zachary Turner4cceca72015-08-14 16:45:32 +0000171 return passes, failures, unexpected_successes
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000172
Chaoren Linb6325d02015-08-12 18:02:54 +0000173
Todd Fiala8cbeed32015-09-08 22:22:33 +0000174def call_with_timeout(command, timeout, name, inferior_pid_events):
Todd Fiala68615ce2015-09-15 21:38:04 +0000175 """Run command with a timeout if possible.
176 -s QUIT will create a coredump if they are enabled on your system
177 """
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000178 process = None
179 if timeout_command and timeout != "0":
180 command = [timeout_command, '-s', 'QUIT', timeout] + command
Todd Fiala68615ce2015-09-15 21:38:04 +0000181
Todd Fiala33896a92015-09-18 21:01:13 +0000182 if GET_WORKER_INDEX is not None:
183 worker_index = GET_WORKER_INDEX()
184 command.extend([
185 "--event-add-entries", "worker_index={}".format(worker_index)])
Chaoren Linb6325d02015-08-12 18:02:54 +0000186 # Specifying a value for close_fds is unsupported on Windows when using
187 # subprocess.PIPE
Zachary Turnerdc494d52015-02-07 00:14:55 +0000188 if os.name != "nt":
Chaoren Linb6325d02015-08-12 18:02:54 +0000189 process = subprocess.Popen(command,
190 stdin=subprocess.PIPE,
191 stdout=subprocess.PIPE,
192 stderr=subprocess.PIPE,
193 close_fds=True)
Zachary Turnerdc494d52015-02-07 00:14:55 +0000194 else:
Chaoren Linb6325d02015-08-12 18:02:54 +0000195 process = subprocess.Popen(command,
196 stdin=subprocess.PIPE,
197 stdout=subprocess.PIPE,
198 stderr=subprocess.PIPE)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000199 inferior_pid = process.pid
200 if inferior_pid_events:
201 inferior_pid_events.put_nowait(('created', inferior_pid))
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000202 output = process.communicate()
Todd Fiala68615ce2015-09-15 21:38:04 +0000203
204 # The inferior should now be entirely wrapped up.
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000205 exit_status = process.returncode
Todd Fiala68615ce2015-09-15 21:38:04 +0000206 if exit_status is None:
207 raise Exception(
208 "no exit status available after the inferior dotest.py "
209 "should have completed")
210
Todd Fiala8cbeed32015-09-08 22:22:33 +0000211 if inferior_pid_events:
212 inferior_pid_events.put_nowait(('destroyed', inferior_pid))
213
Zachary Turner4cceca72015-08-14 16:45:32 +0000214 passes, failures, unexpected_successes = parse_test_results(output)
Zachary Turner38e64172015-08-10 17:46:11 +0000215 if exit_status == 0:
Chaoren Linb6325d02015-08-12 18:02:54 +0000216 # stdout does not have any useful information from 'dotest.py',
217 # only stderr does.
Zachary Turner38e64172015-08-10 17:46:11 +0000218 report_test_pass(name, output[1])
219 else:
Todd Fiala68615ce2015-09-15 21:38:04 +0000220 # TODO need to differentiate a failing test from a run that
221 # was broken out of by a SIGTERM/SIGKILL, reporting those as
222 # an error. If a signal-based completion, need to call that
223 # an error.
Zachary Turner38e64172015-08-10 17:46:11 +0000224 report_test_failure(name, command, output[1])
Zachary Turner4cceca72015-08-14 16:45:32 +0000225 return name, exit_status, passes, failures, unexpected_successes
Johnny Chene8d9dc62011-10-31 19:04:07 +0000226
Chaoren Linb6325d02015-08-12 18:02:54 +0000227
Todd Fiala8cbeed32015-09-08 22:22:33 +0000228def process_dir(root, files, test_root, dotest_argv, inferior_pid_events):
Steve Puccibefe2b12014-03-07 00:01:11 +0000229 """Examine a directory for tests, and invoke any found within it."""
Chaoren Line80372a2015-08-12 18:02:53 +0000230 results = []
Steve Puccibefe2b12014-03-07 00:01:11 +0000231 for name in files:
Zachary Turnerf6896b02015-01-05 19:37:03 +0000232 script_file = os.path.join(test_root, "dotest.py")
Zachary Turnerf6896b02015-01-05 19:37:03 +0000233 command = ([sys.executable, script_file] +
Vince Harron41657cc2015-05-21 18:15:09 +0000234 dotest_argv +
Todd Fialafed95662015-09-03 18:58:44 +0000235 ["--inferior", "-p", name, root])
Vince Harron17f429f2014-12-13 00:08:19 +0000236
237 timeout_name = os.path.basename(os.path.splitext(name)[0]).upper()
238
Chaoren Linb6325d02015-08-12 18:02:54 +0000239 timeout = (os.getenv("LLDB_%s_TIMEOUT" % timeout_name) or
240 getDefaultTimeout(dotest_options.lldb_platform_name))
Vince Harron17f429f2014-12-13 00:08:19 +0000241
Todd Fiala8cbeed32015-09-08 22:22:33 +0000242 results.append(call_with_timeout(
243 command, timeout, name, inferior_pid_events))
Vince Harron17f429f2014-12-13 00:08:19 +0000244
Zachary Turner4cceca72015-08-14 16:45:32 +0000245 # result = (name, status, passes, failures, unexpected_successes)
246 timed_out = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000247 if status == eTimedOut]
Zachary Turner4cceca72015-08-14 16:45:32 +0000248 passed = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000249 if status == ePassed]
Zachary Turner4cceca72015-08-14 16:45:32 +0000250 failed = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000251 if status != ePassed]
Zachary Turner4cceca72015-08-14 16:45:32 +0000252 unexpected_passes = [name for name, _, _, _, unexpected_successes in results
253 if unexpected_successes > 0]
Todd Fialafed95662015-09-03 18:58:44 +0000254
Chaoren Line80372a2015-08-12 18:02:53 +0000255 pass_count = sum([result[2] for result in results])
256 fail_count = sum([result[3] for result in results])
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000257
Zachary Turner4cceca72015-08-14 16:45:32 +0000258 return (timed_out, passed, failed, unexpected_passes, pass_count, fail_count)
Steve Puccibefe2b12014-03-07 00:01:11 +0000259
260in_q = None
261out_q = None
262
Chaoren Linb6325d02015-08-12 18:02:54 +0000263
Todd Fiala8cbeed32015-09-08 22:22:33 +0000264def process_dir_worker_multiprocessing(
265 a_output_lock, a_test_counter, a_total_tests, a_test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000266 a_dotest_options, job_queue, result_queue, inferior_pid_events,
267 worker_index_map):
Todd Fiala8cbeed32015-09-08 22:22:33 +0000268 """Worker thread main loop when in multiprocessing mode.
Steve Puccibefe2b12014-03-07 00:01:11 +0000269 Takes one directory specification at a time and works on it."""
Todd Fiala8cbeed32015-09-08 22:22:33 +0000270
271 # Shut off interrupt handling in the child process.
272 signal.signal(signal.SIGINT, signal.SIG_IGN)
273
274 # Setup the global state for the worker process.
275 setup_global_variables(
276 a_output_lock, a_test_counter, a_total_tests, a_test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000277 a_dotest_options, worker_index_map)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000278
279 # Keep grabbing entries from the queue until done.
280 while not job_queue.empty():
281 try:
282 job = job_queue.get(block=False)
283 result = process_dir(job[0], job[1], job[2], job[3],
284 inferior_pid_events)
285 result_queue.put(result)
286 except Queue.Empty:
287 # Fine, we're done.
288 pass
Steve Puccibefe2b12014-03-07 00:01:11 +0000289
Chaoren Linb6325d02015-08-12 18:02:54 +0000290
Todd Fiala8cbeed32015-09-08 22:22:33 +0000291def process_dir_worker_multiprocessing_pool(args):
292 return process_dir(*args)
293
294
Todd Fiala68615ce2015-09-15 21:38:04 +0000295def process_dir_worker_threading(job_queue, result_queue, inferior_pid_events):
Todd Fiala8cbeed32015-09-08 22:22:33 +0000296 """Worker thread main loop when in threading mode.
297
298 This one supports the hand-rolled pooling support.
299
300 Takes one directory specification at a time and works on it."""
301
302 # Keep grabbing entries from the queue until done.
303 while not job_queue.empty():
304 try:
305 job = job_queue.get(block=False)
306 result = process_dir(job[0], job[1], job[2], job[3],
307 inferior_pid_events)
308 result_queue.put(result)
309 except Queue.Empty:
310 # Fine, we're done.
311 pass
312
313
314def process_dir_worker_threading_pool(args):
315 return process_dir(*args)
316
317
318def process_dir_mapper_inprocess(args):
319 """Map adapter for running the subprocess-based, non-threaded test runner.
320
321 @param args the process work item tuple
322 @return the test result tuple
323 """
324 return process_dir(*args)
325
326
327def collect_active_pids_from_pid_events(event_queue):
328 """
329 Returns the set of what should be active inferior pids based on
330 the event stream.
331
332 @param event_queue a multiprocessing.Queue containing events of the
333 form:
334 ('created', pid)
335 ('destroyed', pid)
336
337 @return set of inferior dotest.py pids activated but never completed.
338 """
339 active_pid_set = set()
340 while not event_queue.empty():
341 pid_event = event_queue.get_nowait()
342 if pid_event[0] == 'created':
343 active_pid_set.add(pid_event[1])
344 elif pid_event[0] == 'destroyed':
345 active_pid_set.remove(pid_event[1])
346 return active_pid_set
347
348
349def kill_all_worker_processes(workers, inferior_pid_events):
350 """
351 Kills all specified worker processes and their process tree.
352
353 @param workers a list of multiprocess.Process worker objects.
354 @param inferior_pid_events a multiprocess.Queue that contains
355 all inferior create and destroy events. Used to construct
356 the list of child pids still outstanding that need to be killed.
357 """
358 for worker in workers:
359 worker.terminate()
360 worker.join()
361
362 # Add all the child test pids created.
363 active_pid_set = collect_active_pids_from_pid_events(
364 inferior_pid_events)
365 for inferior_pid in active_pid_set:
366 print "killing inferior pid {}".format(inferior_pid)
367 os.kill(inferior_pid, signal.SIGKILL)
368
369
370def kill_all_worker_threads(workers, inferior_pid_events):
371 """
372 Kills all specified worker threads and their process tree.
373
374 @param workers a list of multiprocess.Process worker objects.
375 @param inferior_pid_events a multiprocess.Queue that contains
376 all inferior create and destroy events. Used to construct
377 the list of child pids still outstanding that need to be killed.
378 """
379
380 # Add all the child test pids created.
381 active_pid_set = collect_active_pids_from_pid_events(
382 inferior_pid_events)
383 for inferior_pid in active_pid_set:
384 print "killing inferior pid {}".format(inferior_pid)
385 os.kill(inferior_pid, signal.SIGKILL)
386
387 # We don't have a way to nuke the threads. However, since we killed
388 # all the inferiors, and we drained the job queue, this will be
389 # good enough. Wait cleanly for each worker thread to wrap up.
390 for worker in workers:
391 worker.join()
392
393
394def find_test_files_in_dir_tree(dir_root, found_func):
395 """Calls found_func for all the test files in the given dir hierarchy.
396
397 @param dir_root the path to the directory to start scanning
398 for test files. All files in this directory and all its children
399 directory trees will be searched.
400
401 @param found_func a callable object that will be passed
402 the parent directory (relative to dir_root) and the list of
403 test files from within that directory.
404 """
405 for root, _, files in os.walk(dir_root, topdown=False):
406 def is_test_filename(test_dir, base_filename):
407 """Returns True if the given filename matches the test name format.
408
409 @param test_dir the directory to check. Should be absolute or
410 relative to current working directory.
411
412 @param base_filename the base name of the filename to check for a
413 dherence to the python test case filename format.
414
415 @return True if name matches the python test case filename format.
416 """
417 # Not interested in symbolically linked files.
418 if os.path.islink(os.path.join(test_dir, base_filename)):
419 return False
420 # Only interested in test files with the "Test*.py" naming pattern.
421 return (base_filename.startswith("Test") and
422 base_filename.endswith(".py"))
423
424 tests = [filename for filename in files
425 if is_test_filename(root, filename)]
426 if tests:
427 found_func(root, tests)
428
429
430def initialize_global_vars_common(num_threads, test_work_items):
431 global total_tests, test_counter, test_name_len
432 total_tests = sum([len(item[1]) for item in test_work_items])
433 test_counter = multiprocessing.Value('i', 0)
434 test_name_len = multiprocessing.Value('i', 0)
435 print >> sys.stderr, "Testing: %d test suites, %d thread%s" % (
436 total_tests, num_threads, (num_threads > 1) * "s")
437 update_progress()
438
439
440def initialize_global_vars_multiprocessing(num_threads, test_work_items):
441 # Initialize the global state we'll use to communicate with the
442 # rest of the flat module.
443 global output_lock
444 output_lock = multiprocessing.RLock()
Todd Fiala33896a92015-09-18 21:01:13 +0000445
Todd Fiala8cbeed32015-09-08 22:22:33 +0000446 initialize_global_vars_common(num_threads, test_work_items)
447
448
449def initialize_global_vars_threading(num_threads, test_work_items):
Todd Fiala33896a92015-09-18 21:01:13 +0000450 """Initializes global variables used in threading mode.
451 @param num_threads specifies the number of workers used.
452 @param test_work_items specifies all the work items
453 that will be processed.
454 """
Todd Fiala8cbeed32015-09-08 22:22:33 +0000455 # Initialize the global state we'll use to communicate with the
456 # rest of the flat module.
457 global output_lock
458 output_lock = threading.RLock()
Todd Fiala33896a92015-09-18 21:01:13 +0000459
460 index_lock = threading.RLock()
461 index_map = {}
462
463 def get_worker_index_threading():
464 """Returns a 0-based, thread-unique index for the worker thread."""
465 thread_id = threading.current_thread().ident
466 with index_lock:
467 if thread_id not in index_map:
468 index_map[thread_id] = len(index_map)
469 return index_map[thread_id]
470
471
472 global GET_WORKER_INDEX
473 GET_WORKER_INDEX = get_worker_index_threading
474
Todd Fiala8cbeed32015-09-08 22:22:33 +0000475 initialize_global_vars_common(num_threads, test_work_items)
476
477
Todd Fiala68615ce2015-09-15 21:38:04 +0000478def ctrl_c_loop(main_op_func, done_func, ctrl_c_handler):
479 """Provides a main loop that is Ctrl-C protected.
480
481 The main loop calls the main_op_func() repeatedly until done_func()
482 returns true. The ctrl_c_handler() method is called with a single
483 int parameter that contains the number of times the ctrl_c has been
484 hit (starting with 1). The ctrl_c_handler() should mutate whatever
485 it needs to have the done_func() return True as soon as it is desired
486 to exit the loop.
487 """
488 done = False
489 ctrl_c_count = 0
490
491 while not done:
492 try:
493 # See if we're done. Start with done check since it is
494 # the first thing executed after a Ctrl-C handler in the
495 # following loop.
496 done = done_func()
497 if not done:
498 # Run the main op once.
499 main_op_func()
500
501 except KeyboardInterrupt:
502 ctrl_c_count += 1
503 ctrl_c_handler(ctrl_c_count)
504
505
506def pump_workers_and_asyncore_map(workers, asyncore_map):
507 """Prunes out completed workers and maintains the asyncore loop.
508
509 The asyncore loop contains the optional socket listener
510 and handlers. When all workers are complete, this method
511 takes care of stopping the listener. It also runs the
512 asyncore loop for the given async map for 10 iterations.
513
514 @param workers the list of worker Thread/Process instances.
515
516 @param asyncore_map the asyncore threading-aware map that
517 indicates which channels are in use and still alive.
518 """
519
520 # Check on all the workers, removing them from the workers
521 # list as they complete.
522 dead_workers = []
523 for worker in workers:
524 # This non-blocking join call is what allows us
525 # to still receive keyboard interrupts.
526 worker.join(0.01)
527 if not worker.is_alive():
528 dead_workers.append(worker)
529 # Clear out the completed workers
530 for dead_worker in dead_workers:
531 workers.remove(dead_worker)
532
533 # If there are no more workers and there is a listener,
534 # close the listener.
535 global RESULTS_LISTENER_CHANNEL
536 if len(workers) == 0 and RESULTS_LISTENER_CHANNEL is not None:
537 RESULTS_LISTENER_CHANNEL.close()
538 RESULTS_LISTENER_CHANNEL = None
539
540 # Pump the asyncore map if it isn't empty.
541 if len(asyncore_map) > 0:
542 asyncore.loop(0.1, False, asyncore_map, 10)
543
544
545def handle_ctrl_c(ctrl_c_count, job_queue, workers, inferior_pid_events,
546 stop_all_inferiors_func):
547 """Performs the appropriate ctrl-c action for non-pool parallel test runners
548
549 @param ctrl_c_count starting with 1, indicates the number of times ctrl-c
550 has been intercepted. The value is 1 on the first intercept, 2 on the
551 second, etc.
552
553 @param job_queue a Queue object that contains the work still outstanding
554 (i.e. hasn't been assigned to a worker yet).
555
556 @param workers list of Thread or Process workers.
557
558 @param inferior_pid_events specifies a Queue of inferior process
559 construction and destruction events. Used to build the list of inferior
560 processes that should be killed if we get that far.
561
562 @param stop_all_inferiors_func a callable object that takes the
563 workers and inferior_pid_events parameters (in that order) if a hard
564 stop is to be used on the workers.
565 """
566
567 # Print out which Ctrl-C we're handling.
568 key_name = [
569 "first",
570 "second",
571 "third",
572 "many"]
573
574 if ctrl_c_count < len(key_name):
575 name_index = ctrl_c_count - 1
576 else:
577 name_index = len(key_name) - 1
578 message = "\nHandling {} KeyboardInterrupt".format(key_name[name_index])
579 with output_lock:
580 print message
581
582 if ctrl_c_count == 1:
583 # Remove all outstanding items from the work queue so we stop
584 # doing any more new work.
585 while not job_queue.empty():
586 try:
587 # Just drain it to stop more work from being started.
588 job_queue.get_nowait()
589 except Queue.Empty:
590 pass
591 with output_lock:
592 print "Stopped more work from being started."
593 elif ctrl_c_count == 2:
594 # Try to stop all inferiors, even the ones currently doing work.
595 stop_all_inferiors_func(workers, inferior_pid_events)
596 else:
597 with output_lock:
598 print "All teardown activities kicked off, should finish soon."
599
600
601def workers_and_async_done(workers, async_map):
602 """Returns True if the workers list and asyncore channels are all done.
603
604 @param workers list of workers (threads/processes). These must adhere
605 to the threading Thread or multiprocessing.Process interface.
606
607 @param async_map the threading-aware asyncore channel map to check
608 for live channels.
609
610 @return False if the workers list exists and has any entries in it, or
611 if the async_map exists and has any entries left in it; otherwise, True.
612 """
613 if workers is not None and len(workers) > 0:
614 # We're not done if we still have workers left.
615 return False
616 if async_map is not None and len(async_map) > 0:
617 return False
618 # We're done.
619 return True
620
621
Todd Fiala8cbeed32015-09-08 22:22:33 +0000622def multiprocessing_test_runner(num_threads, test_work_items):
623 """Provides hand-wrapped pooling test runner adapter with Ctrl-C support.
624
625 This concurrent test runner is based on the multiprocessing
626 library, and rolls its own worker pooling strategy so it
627 can handle Ctrl-C properly.
628
629 This test runner is known to have an issue running on
630 Windows platforms.
631
632 @param num_threads the number of worker processes to use.
633
634 @param test_work_items the iterable of test work item tuples
635 to run.
636 """
637
638 # Initialize our global state.
639 initialize_global_vars_multiprocessing(num_threads, test_work_items)
640
641 # Create jobs.
642 job_queue = multiprocessing.Queue(len(test_work_items))
643 for test_work_item in test_work_items:
644 job_queue.put(test_work_item)
645
646 result_queue = multiprocessing.Queue(len(test_work_items))
647
648 # Create queues for started child pids. Terminating
649 # the multiprocess processes does not terminate the
650 # child processes they spawn. We can remove this tracking
651 # if/when we move to having the multiprocess process directly
652 # perform the test logic. The Queue size needs to be able to
653 # hold 2 * (num inferior dotest.py processes started) entries.
654 inferior_pid_events = multiprocessing.Queue(4096)
655
Todd Fiala33896a92015-09-18 21:01:13 +0000656 # Worker dictionary allows each worker to figure out its worker index.
657 manager = multiprocessing.Manager()
658 worker_index_map = manager.dict()
659
Todd Fiala8cbeed32015-09-08 22:22:33 +0000660 # Create workers. We don't use multiprocessing.Pool due to
661 # challenges with handling ^C keyboard interrupts.
662 workers = []
663 for _ in range(num_threads):
664 worker = multiprocessing.Process(
665 target=process_dir_worker_multiprocessing,
666 args=(output_lock,
667 test_counter,
668 total_tests,
669 test_name_len,
670 dotest_options,
671 job_queue,
672 result_queue,
Todd Fiala33896a92015-09-18 21:01:13 +0000673 inferior_pid_events,
674 worker_index_map))
Todd Fiala8cbeed32015-09-08 22:22:33 +0000675 worker.start()
676 workers.append(worker)
677
Todd Fiala68615ce2015-09-15 21:38:04 +0000678 # Main loop: wait for all workers to finish and wait for
679 # the socket handlers to wrap up.
680 ctrl_c_loop(
681 # Main operation of loop
682 lambda: pump_workers_and_asyncore_map(
683 workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000684
Todd Fiala68615ce2015-09-15 21:38:04 +0000685 # Return True when we're done with the main loop.
686 lambda: workers_and_async_done(workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000687
Todd Fiala68615ce2015-09-15 21:38:04 +0000688 # Indicate what we do when we receive one or more Ctrl-Cs.
689 lambda ctrl_c_count: handle_ctrl_c(
690 ctrl_c_count, job_queue, workers, inferior_pid_events,
691 kill_all_worker_processes))
692
693 # Reap the test results.
Todd Fiala8cbeed32015-09-08 22:22:33 +0000694 test_results = []
695 while not result_queue.empty():
696 test_results.append(result_queue.get(block=False))
697 return test_results
698
699
Todd Fiala68615ce2015-09-15 21:38:04 +0000700def map_async_run_loop(future, channel_map, listener_channel):
701 """Blocks until the Pool.map_async completes and the channel completes.
702
703 @param future an AsyncResult instance from a Pool.map_async() call.
704
705 @param channel_map the asyncore dispatch channel map that should be pumped.
706 Optional: may be None.
707
708 @param listener_channel the channel representing a listener that should be
709 closed once the map_async results are available.
710
711 @return the results from the async_result instance.
712 """
713 map_results = None
714
715 done = False
716 while not done:
717 # Check if we need to reap the map results.
718 if map_results is None:
719 if future.ready():
720 # Get the results.
721 map_results = future.get()
722
723 # Close the runner process listener channel if we have
724 # one: no more connections will be incoming.
725 if listener_channel is not None:
726 listener_channel.close()
727
728 # Pump the asyncore loop if we have a listener socket.
729 if channel_map is not None:
730 asyncore.loop(0.01, False, channel_map, 10)
731
732 # Figure out if we're done running.
733 done = map_results is not None
734 if channel_map is not None:
735 # We have a runner process async map. Check if it
736 # is complete.
737 if len(channel_map) > 0:
738 # We still have an asyncore channel running. Not done yet.
739 done = False
740
741 return map_results
742
743
Todd Fiala8cbeed32015-09-08 22:22:33 +0000744def multiprocessing_test_runner_pool(num_threads, test_work_items):
745 # Initialize our global state.
746 initialize_global_vars_multiprocessing(num_threads, test_work_items)
747
Todd Fiala33896a92015-09-18 21:01:13 +0000748 manager = multiprocessing.Manager()
749 worker_index_map = manager.dict()
750
Todd Fiala8cbeed32015-09-08 22:22:33 +0000751 pool = multiprocessing.Pool(
752 num_threads,
753 initializer=setup_global_variables,
754 initargs=(output_lock, test_counter, total_tests, test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000755 dotest_options, worker_index_map))
Todd Fiala68615ce2015-09-15 21:38:04 +0000756
757 # Start the map operation (async mode).
758 map_future = pool.map_async(
759 process_dir_worker_multiprocessing_pool, test_work_items)
760 return map_async_run_loop(
761 map_future, RUNNER_PROCESS_ASYNC_MAP, RESULTS_LISTENER_CHANNEL)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000762
763
764def threading_test_runner(num_threads, test_work_items):
765 """Provides hand-wrapped pooling threading-based test runner adapter
766 with Ctrl-C support.
767
768 This concurrent test runner is based on the threading
769 library, and rolls its own worker pooling strategy so it
770 can handle Ctrl-C properly.
771
772 @param num_threads the number of worker processes to use.
773
774 @param test_work_items the iterable of test work item tuples
775 to run.
776 """
777
778 # Initialize our global state.
779 initialize_global_vars_threading(num_threads, test_work_items)
780
781 # Create jobs.
782 job_queue = Queue.Queue()
783 for test_work_item in test_work_items:
784 job_queue.put(test_work_item)
785
786 result_queue = Queue.Queue()
787
788 # Create queues for started child pids. Terminating
789 # the threading threads does not terminate the
790 # child processes they spawn.
791 inferior_pid_events = Queue.Queue()
792
793 # Create workers. We don't use multiprocessing.pool.ThreadedPool
794 # due to challenges with handling ^C keyboard interrupts.
795 workers = []
796 for _ in range(num_threads):
797 worker = threading.Thread(
798 target=process_dir_worker_threading,
Todd Fiala68615ce2015-09-15 21:38:04 +0000799 args=(job_queue,
Todd Fiala8cbeed32015-09-08 22:22:33 +0000800 result_queue,
801 inferior_pid_events))
802 worker.start()
803 workers.append(worker)
804
Todd Fiala68615ce2015-09-15 21:38:04 +0000805 # Main loop: wait for all workers to finish and wait for
806 # the socket handlers to wrap up.
807 ctrl_c_loop(
808 # Main operation of loop
809 lambda: pump_workers_and_asyncore_map(
810 workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000811
Todd Fiala68615ce2015-09-15 21:38:04 +0000812 # Return True when we're done with the main loop.
813 lambda: workers_and_async_done(workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000814
Todd Fiala68615ce2015-09-15 21:38:04 +0000815 # Indicate what we do when we receive one or more Ctrl-Cs.
816 lambda ctrl_c_count: handle_ctrl_c(
817 ctrl_c_count, job_queue, workers, inferior_pid_events,
818 kill_all_worker_threads))
Todd Fiala8cbeed32015-09-08 22:22:33 +0000819
Todd Fiala68615ce2015-09-15 21:38:04 +0000820 # Reap the test results.
Todd Fiala8cbeed32015-09-08 22:22:33 +0000821 test_results = []
822 while not result_queue.empty():
823 test_results.append(result_queue.get(block=False))
824 return test_results
825
826
827def threading_test_runner_pool(num_threads, test_work_items):
828 # Initialize our global state.
829 initialize_global_vars_threading(num_threads, test_work_items)
830
Todd Fiala68615ce2015-09-15 21:38:04 +0000831 pool = multiprocessing.pool.ThreadPool(num_threads)
832 map_future = pool.map_async(
833 process_dir_worker_threading_pool, test_work_items)
834
835 return map_async_run_loop(
836 map_future, RUNNER_PROCESS_ASYNC_MAP, RESULTS_LISTENER_CHANNEL)
837
838
839def asyncore_run_loop(channel_map):
840 try:
841 asyncore.loop(None, False, channel_map)
842 except:
843 # Swallow it, we're seeing:
844 # error: (9, 'Bad file descriptor')
845 # when the listener channel is closed. Shouldn't be the case.
846 pass
Todd Fiala8cbeed32015-09-08 22:22:33 +0000847
848
849def inprocess_exec_test_runner(test_work_items):
850 # Initialize our global state.
851 initialize_global_vars_multiprocessing(1, test_work_items)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000852
Todd Fiala33896a92015-09-18 21:01:13 +0000853 # We're always worker index 0
854 global GET_WORKER_INDEX
855 GET_WORKER_INDEX = lambda: 0
856
Todd Fiala68615ce2015-09-15 21:38:04 +0000857 # Run the listener and related channel maps in a separate thread.
858 # global RUNNER_PROCESS_ASYNC_MAP
859 global RESULTS_LISTENER_CHANNEL
860 if RESULTS_LISTENER_CHANNEL is not None:
861 socket_thread = threading.Thread(
862 target=lambda: asyncore_run_loop(RUNNER_PROCESS_ASYNC_MAP))
863 socket_thread.start()
864
865 # Do the work.
866 test_results = map(process_dir_mapper_inprocess, test_work_items)
867
868 # If we have a listener channel, shut it down here.
869 if RESULTS_LISTENER_CHANNEL is not None:
870 # Close down the channel.
871 RESULTS_LISTENER_CHANNEL.close()
872 RESULTS_LISTENER_CHANNEL = None
873
874 # Wait for the listener and handlers to complete.
875 socket_thread.join()
876
877 return test_results
Todd Fiala8cbeed32015-09-08 22:22:33 +0000878
879def walk_and_invoke(test_directory, test_subdir, dotest_argv,
880 test_runner_func):
Steve Puccibefe2b12014-03-07 00:01:11 +0000881 """Look for matched files and invoke test driver on each one.
882 In single-threaded mode, each test driver is invoked directly.
883 In multi-threaded mode, submit each test driver to a worker
Vince Harrone06a7a82015-05-12 23:12:19 +0000884 queue, and then wait for all to complete.
885
886 test_directory - lldb/test/ directory
Chaoren Linb6325d02015-08-12 18:02:54 +0000887 test_subdir - lldb/test/ or a subfolder with the tests we're interested in
888 running
Vince Harrone06a7a82015-05-12 23:12:19 +0000889 """
Todd Fiala68615ce2015-09-15 21:38:04 +0000890 # The async_map is important to keep all thread-related asyncore
891 # channels distinct when we call asyncore.loop() later on.
892 global RESULTS_LISTENER_CHANNEL, RUNNER_PROCESS_ASYNC_MAP
893 RUNNER_PROCESS_ASYNC_MAP = {}
894
895 # If we're outputting side-channel test results, create the socket
896 # listener channel and tell the inferior to send results to the
897 # port on which we'll be listening.
898 if RESULTS_FORMATTER is not None:
Todd Fiala33896a92015-09-18 21:01:13 +0000899 forwarding_func = RESULTS_FORMATTER.process_event
Todd Fiala68615ce2015-09-15 21:38:04 +0000900 RESULTS_LISTENER_CHANNEL = (
901 dotest_channels.UnpicklingForwardingListenerChannel(
902 RUNNER_PROCESS_ASYNC_MAP, "localhost", 0, forwarding_func))
903 dotest_argv.append("--results-port")
904 dotest_argv.append(str(RESULTS_LISTENER_CHANNEL.address[1]))
Todd Fiala3f0a3602014-07-08 06:42:37 +0000905
906 # Collect the test files that we'll run.
907 test_work_items = []
Todd Fiala8cbeed32015-09-08 22:22:33 +0000908 find_test_files_in_dir_tree(
909 test_subdir, lambda testdir, test_files: test_work_items.append([
910 test_subdir, test_files, test_directory, dotest_argv, None]))
Todd Fiala3f0a3602014-07-08 06:42:37 +0000911
Todd Fiala8cbeed32015-09-08 22:22:33 +0000912 # Convert test work items into test results using whatever
913 # was provided as the test run function.
914 test_results = test_runner_func(test_work_items)
Chaoren Linffc63b02015-08-12 18:02:49 +0000915
Todd Fiala8cbeed32015-09-08 22:22:33 +0000916 # Summarize the results and return to caller.
Chaoren Line80372a2015-08-12 18:02:53 +0000917 timed_out = sum([result[0] for result in test_results], [])
918 passed = sum([result[1] for result in test_results], [])
919 failed = sum([result[2] for result in test_results], [])
Zachary Turner4cceca72015-08-14 16:45:32 +0000920 unexpected_successes = sum([result[3] for result in test_results], [])
921 pass_count = sum([result[4] for result in test_results])
922 fail_count = sum([result[5] for result in test_results])
Todd Fiala3f0a3602014-07-08 06:42:37 +0000923
Todd Fiala8cbeed32015-09-08 22:22:33 +0000924 return (timed_out, passed, failed, unexpected_successes, pass_count,
925 fail_count)
Johnny Chene8d9dc62011-10-31 19:04:07 +0000926
Chaoren Linb6325d02015-08-12 18:02:54 +0000927
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000928def getExpectedTimeouts(platform_name):
Vince Harron06381732015-05-12 23:10:36 +0000929 # returns a set of test filenames that might timeout
930 # are we running against a remote target?
Chaoren Linfebef1b2015-08-19 17:22:12 +0000931 host = sys.platform
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000932 if platform_name is None:
Vince Harron06381732015-05-12 23:10:36 +0000933 target = sys.platform
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000934 else:
Todd Fiala68615ce2015-09-15 21:38:04 +0000935 m = re.search(r'remote-(\w+)', platform_name)
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000936 target = m.group(1)
Vince Harron06381732015-05-12 23:10:36 +0000937
938 expected_timeout = set()
939
940 if target.startswith("linux"):
941 expected_timeout |= {
Chaoren Lin0b8bb3d2015-07-22 20:52:17 +0000942 "TestProcessAttach.py",
Vince Harron06381732015-05-12 23:10:36 +0000943 "TestConnectRemote.py",
944 "TestCreateAfterAttach.py",
Tamas Berghammer0d0ec9f2015-05-19 10:49:40 +0000945 "TestEvents.py",
Vince Harron06381732015-05-12 23:10:36 +0000946 "TestExitDuringStep.py",
Chaoren Linb6325d02015-08-12 18:02:54 +0000947
948 # Times out in ~10% of the times on the build bot
949 "TestHelloWorld.py",
Oleksiy Vyalov18f4c9f2015-06-10 01:34:25 +0000950 "TestMultithreaded.py",
Chaoren Linb6325d02015-08-12 18:02:54 +0000951 "TestRegisters.py", # ~12/600 dosep runs (build 3120-3122)
Vince Harron06381732015-05-12 23:10:36 +0000952 "TestThreadStepOut.py",
953 }
954 elif target.startswith("android"):
955 expected_timeout |= {
956 "TestExitDuringStep.py",
957 "TestHelloWorld.py",
958 }
Chaoren Linfebef1b2015-08-19 17:22:12 +0000959 if host.startswith("win32"):
960 expected_timeout |= {
961 "TestEvents.py",
962 "TestThreadStates.py",
963 }
Ed Maste4dd8fba2015-05-14 16:25:52 +0000964 elif target.startswith("freebsd"):
965 expected_timeout |= {
966 "TestBreakpointConditions.py",
Ed Maste08948132015-05-28 18:45:30 +0000967 "TestChangeProcessGroup.py",
Ed Mastebfd05632015-05-27 19:11:29 +0000968 "TestValueObjectRecursion.py",
Ed Maste4dd8fba2015-05-14 16:25:52 +0000969 "TestWatchpointConditionAPI.py",
970 }
Vince Harron0f173ac2015-05-18 19:36:33 +0000971 elif target.startswith("darwin"):
972 expected_timeout |= {
Chaoren Linb6325d02015-08-12 18:02:54 +0000973 # times out on MBP Retina, Mid 2012
974 "TestThreadSpecificBreakpoint.py",
Chaoren Lind9043712015-08-19 17:13:02 +0000975 "TestExitDuringStep.py",
Chaoren Lin99f25be2015-08-20 01:26:57 +0000976 "TestIntegerTypesExpr.py",
Vince Harron0f173ac2015-05-18 19:36:33 +0000977 }
Vince Harron06381732015-05-12 23:10:36 +0000978 return expected_timeout
979
Chaoren Linb6325d02015-08-12 18:02:54 +0000980
Pavel Labathfad30cf2015-06-29 14:16:51 +0000981def getDefaultTimeout(platform_name):
982 if os.getenv("LLDB_TEST_TIMEOUT"):
983 return os.getenv("LLDB_TEST_TIMEOUT")
984
985 if platform_name is None:
986 platform_name = sys.platform
987
988 if platform_name.startswith("remote-"):
989 return "10m"
990 else:
991 return "4m"
992
Chaoren Linb6325d02015-08-12 18:02:54 +0000993
Vince Harron0b9dbb52015-05-21 18:18:52 +0000994def touch(fname, times=None):
Greg Clayton8c3f9c92015-08-11 21:01:32 +0000995 if os.path.exists(fname):
Vince Harron0b9dbb52015-05-21 18:18:52 +0000996 os.utime(fname, times)
997
Chaoren Linb6325d02015-08-12 18:02:54 +0000998
Vince Harrondcc2b9f2015-05-27 04:40:36 +0000999def find(pattern, path):
1000 result = []
1001 for root, dirs, files in os.walk(path):
1002 for name in files:
1003 if fnmatch.fnmatch(name, pattern):
1004 result.append(os.path.join(root, name))
1005 return result
1006
Chaoren Linb6325d02015-08-12 18:02:54 +00001007
Todd Fiala8cbeed32015-09-08 22:22:33 +00001008def get_test_runner_strategies(num_threads):
1009 """Returns the test runner strategies by name in a dictionary.
1010
1011 @param num_threads specifies the number of threads/processes
1012 that will be used for concurrent test runners.
1013
1014 @return dictionary with key as test runner strategy name and
1015 value set to a callable object that takes the test work item
1016 and returns a test result tuple.
1017 """
1018 return {
1019 # multiprocessing supports ctrl-c and does not use
1020 # multiprocessing.Pool.
1021 "multiprocessing":
1022 (lambda work_items: multiprocessing_test_runner(
1023 num_threads, work_items)),
1024
1025 # multiprocessing-pool uses multiprocessing.Pool but
1026 # does not support Ctrl-C.
1027 "multiprocessing-pool":
1028 (lambda work_items: multiprocessing_test_runner_pool(
1029 num_threads, work_items)),
1030
1031 # threading uses a hand-rolled worker pool much
1032 # like multiprocessing, but instead uses in-process
1033 # worker threads. This one supports Ctrl-C.
1034 "threading":
1035 (lambda work_items: threading_test_runner(num_threads, work_items)),
1036
1037 # threading-pool uses threading for the workers (in-process)
1038 # and uses the multiprocessing.pool thread-enabled pool.
Todd Fiala68615ce2015-09-15 21:38:04 +00001039 # This does not properly support Ctrl-C.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001040 "threading-pool":
1041 (lambda work_items: threading_test_runner_pool(
1042 num_threads, work_items)),
1043
1044 # serial uses the subprocess-based, single process
1045 # test runner. This provides process isolation but
Todd Fiala68615ce2015-09-15 21:38:04 +00001046 # no concurrent test execution.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001047 "serial":
1048 inprocess_exec_test_runner
1049 }
1050
1051
Todd Fiala68615ce2015-09-15 21:38:04 +00001052def _remove_option(args, option_name, removal_count):
1053 """Removes option and related option arguments from args array.
1054 @param args the array of command line arguments (in/out)
1055 @param option_name the full command line representation of the
1056 option that will be removed (including '--' or '-').
1057 @param the count of elements to remove. A value of 1 will remove
1058 just the found option, while 2 will remove the option and its first
1059 argument.
1060 """
1061 try:
1062 index = args.index(option_name)
1063 # Handle the exact match case.
1064 del args[index:index+removal_count]
1065 return
1066 except ValueError:
1067 # Thanks to argparse not handling options with known arguments
1068 # like other options parsing libraries (see
1069 # https://bugs.python.org/issue9334), we need to support the
1070 # --results-formatter-options={second-level-arguments} (note
1071 # the equal sign to fool the first-level arguments parser into
1072 # not treating the second-level arguments as first-level
1073 # options). We're certainly at risk of getting this wrong
1074 # since now we're forced into the business of trying to figure
1075 # out what is an argument (although I think this
1076 # implementation will suffice).
1077 regex_string = "^" + option_name + "="
1078 regex = re.compile(regex_string)
1079 for index in range(len(args)):
1080 match = regex.match(args[index])
1081 if match:
1082 print "found matching option= at index {}".format(index)
1083 del args[index]
1084 return
1085 print "failed to find regex '{}'".format(regex_string)
1086
1087 # We didn't find the option but we should have.
1088 raise Exception("failed to find option '{}' in args '{}'".format(
1089 option_name, args))
1090
1091
1092def adjust_inferior_options(dotest_argv):
1093 """Adjusts the commandline args array for inferiors.
1094
1095 This method adjusts the inferior dotest commandline options based
1096 on the parallel test runner's options. Some of the inferior options
1097 will need to change to properly handle aggregation functionality.
1098 """
1099 global dotest_options
1100
1101 # If we don't have a session directory, create one.
1102 if not dotest_options.s:
1103 # no session log directory, we need to add this to prevent
1104 # every dotest invocation from creating its own directory
1105 import datetime
1106 # The windows platforms don't like ':' in the pathname.
1107 timestamp_started = datetime.datetime.now().strftime("%F-%H_%M_%S")
1108 dotest_argv.append('-s')
1109 dotest_argv.append(timestamp_started)
1110 dotest_options.s = timestamp_started
1111
1112 # Adjust inferior results formatter options - if the parallel
1113 # test runner is collecting into the user-specified test results,
1114 # we'll have inferiors spawn with the --results-port option and
1115 # strip the original test runner options.
1116 if dotest_options.results_file is not None:
1117 _remove_option(dotest_argv, "--results-file", 2)
1118 if dotest_options.results_port is not None:
1119 _remove_option(dotest_argv, "--results-port", 2)
1120 if dotest_options.results_formatter is not None:
1121 _remove_option(dotest_argv, "--results-formatter", 2)
1122 if dotest_options.results_formatter_options is not None:
1123 _remove_option(dotest_argv, "--results-formatter-options", 2)
1124
Todd Fiala33896a92015-09-18 21:01:13 +00001125 # Remove test runner name if present.
1126 if dotest_options.test_runner_name is not None:
1127 _remove_option(dotest_argv, "--test-runner-name", 2)
1128
1129
Todd Fiala8cbeed32015-09-08 22:22:33 +00001130def main(print_details_on_success, num_threads, test_subdir,
Todd Fiala68615ce2015-09-15 21:38:04 +00001131 test_runner_name, results_formatter):
Todd Fialafed95662015-09-03 18:58:44 +00001132 """Run dotest.py in inferior mode in parallel.
1133
1134 @param print_details_on_success the parsed value of the output-on-success
1135 command line argument. When True, details of a successful dotest inferior
1136 are printed even when everything succeeds. The normal behavior is to
1137 not print any details when all the inferior tests pass.
1138
1139 @param num_threads the parsed value of the num-threads command line
1140 argument.
1141
1142 @param test_subdir optionally specifies a subdir to limit testing
1143 within. May be None if the entire test tree is to be used. This subdir
1144 is assumed to be relative to the lldb/test root of the test hierarchy.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001145
1146 @param test_runner_name if specified, contains the test runner
1147 name which selects the strategy used to run the isolated and
1148 optionally concurrent test runner. Specify None to allow the
1149 system to choose the most appropriate test runner given desired
1150 thread count and OS type.
1151
Todd Fiala68615ce2015-09-15 21:38:04 +00001152 @param results_formatter if specified, provides the TestResultsFormatter
1153 instance that will format and output test result data from the
1154 side-channel test results. When specified, inferior dotest calls
1155 will send test results side-channel data over a socket to the parallel
1156 test runner, which will forward them on to results_formatter.
Todd Fialafed95662015-09-03 18:58:44 +00001157 """
1158
1159 dotest_argv = sys.argv[1:]
1160
Todd Fiala68615ce2015-09-15 21:38:04 +00001161 global output_on_success, RESULTS_FORMATTER
Todd Fialafed95662015-09-03 18:58:44 +00001162 output_on_success = print_details_on_success
Todd Fiala68615ce2015-09-15 21:38:04 +00001163 RESULTS_FORMATTER = results_formatter
Todd Fialafed95662015-09-03 18:58:44 +00001164
Vince Harrond5fa1022015-05-10 15:24:12 +00001165 # We can't use sys.path[0] to determine the script directory
1166 # because it doesn't work under a debugger
Vince Harron8994fed2015-05-22 19:49:23 +00001167 parser = dotest_args.create_parser()
Pavel Labath05ab2372015-07-06 15:57:52 +00001168 global dotest_options
Vince Harron8994fed2015-05-22 19:49:23 +00001169 dotest_options = dotest_args.parse_args(parser, dotest_argv)
1170
Todd Fiala68615ce2015-09-15 21:38:04 +00001171 adjust_inferior_options(dotest_argv)
Vince Harron0b9dbb52015-05-21 18:18:52 +00001172
1173 session_dir = os.path.join(os.getcwd(), dotest_options.s)
Ed Mastecec2a5b2014-11-21 02:41:25 +00001174
Vince Harrone06a7a82015-05-12 23:12:19 +00001175 # The root directory was specified on the command line
Todd Fiala68615ce2015-09-15 21:38:04 +00001176 test_directory = os.path.dirname(os.path.realpath(__file__))
Todd Fialafed95662015-09-03 18:58:44 +00001177 if test_subdir and len(test_subdir) > 0:
1178 test_subdir = os.path.join(test_directory, test_subdir)
Vince Harrone06a7a82015-05-12 23:12:19 +00001179 else:
Todd Fialafed95662015-09-03 18:58:44 +00001180 test_subdir = test_directory
Vince Harrone06a7a82015-05-12 23:12:19 +00001181
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001182 # clean core files in test tree from previous runs (Linux)
1183 cores = find('core.*', test_subdir)
1184 for core in cores:
1185 os.unlink(core)
1186
Todd Fialafed95662015-09-03 18:58:44 +00001187 if not num_threads:
Greg Clayton2256d0d2014-03-24 23:01:57 +00001188 num_threads_str = os.environ.get("LLDB_TEST_THREADS")
1189 if num_threads_str:
1190 num_threads = int(num_threads_str)
Greg Clayton2256d0d2014-03-24 23:01:57 +00001191 else:
Ed Mastecec2a5b2014-11-21 02:41:25 +00001192 num_threads = multiprocessing.cpu_count()
1193 if num_threads < 1:
1194 num_threads = 1
Johnny Chene8d9dc62011-10-31 19:04:07 +00001195
Daniel Maleab42556f2013-04-19 18:32:53 +00001196 system_info = " ".join(platform.uname())
Todd Fiala8cbeed32015-09-08 22:22:33 +00001197
1198 # Figure out which testrunner strategy we'll use.
1199 runner_strategies_by_name = get_test_runner_strategies(num_threads)
1200
1201 # If the user didn't specify a test runner strategy, determine
1202 # the default now based on number of threads and OS type.
1203 if not test_runner_name:
1204 if num_threads == 1:
1205 # Use the serial runner.
1206 test_runner_name = "serial"
1207 elif os.name == "nt":
1208 # Currently the multiprocessing test runner with ctrl-c
1209 # support isn't running correctly on nt. Use the pool
1210 # support without ctrl-c.
1211 test_runner_name = "multiprocessing-pool"
1212 else:
1213 # For everyone else, use the ctrl-c-enabled
1214 # multiprocessing support.
1215 test_runner_name = "multiprocessing"
1216
1217 if test_runner_name not in runner_strategies_by_name:
1218 raise Exception("specified testrunner name '{}' unknown. "
1219 "Valid choices: {}".format(
1220 test_runner_name,
1221 runner_strategies_by_name.keys()))
1222 test_runner_func = runner_strategies_by_name[test_runner_name]
1223
1224 summary_results = walk_and_invoke(
1225 test_directory, test_subdir, dotest_argv, test_runner_func)
1226
1227 (timed_out, passed, failed, unexpected_successes, pass_count,
1228 fail_count) = summary_results
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +00001229
Vince Harron17f429f2014-12-13 00:08:19 +00001230 timed_out = set(timed_out)
Chaoren Line80372a2015-08-12 18:02:53 +00001231 num_test_files = len(passed) + len(failed)
1232 num_test_cases = pass_count + fail_count
Daniel Maleab42556f2013-04-19 18:32:53 +00001233
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001234 # move core files into session dir
1235 cores = find('core.*', test_subdir)
1236 for core in cores:
1237 dst = core.replace(test_directory, "")[1:]
1238 dst = dst.replace(os.path.sep, "-")
1239 os.rename(core, os.path.join(session_dir, dst))
1240
Vince Harron06381732015-05-12 23:10:36 +00001241 # remove expected timeouts from failures
Vince Harronf8b9a1d2015-05-18 19:40:54 +00001242 expected_timeout = getExpectedTimeouts(dotest_options.lldb_platform_name)
Vince Harron06381732015-05-12 23:10:36 +00001243 for xtime in expected_timeout:
1244 if xtime in timed_out:
1245 timed_out.remove(xtime)
1246 failed.remove(xtime)
Vince Harron0b9dbb52015-05-21 18:18:52 +00001247 result = "ExpectedTimeout"
1248 elif xtime in passed:
1249 result = "UnexpectedCompletion"
1250 else:
1251 result = None # failed
1252
1253 if result:
1254 test_name = os.path.splitext(xtime)[0]
1255 touch(os.path.join(session_dir, "{}-{}".format(result, test_name)))
Vince Harron06381732015-05-12 23:10:36 +00001256
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +00001257 print
Chaoren Lin5a59e462015-08-12 18:02:51 +00001258 sys.stdout.write("Ran %d test suites" % num_test_files)
1259 if num_test_files > 0:
1260 sys.stdout.write(" (%d failed) (%f%%)" % (
1261 len(failed), 100.0 * len(failed) / num_test_files))
1262 print
Chaoren Line80372a2015-08-12 18:02:53 +00001263 sys.stdout.write("Ran %d test cases" % num_test_cases)
1264 if num_test_cases > 0:
Chaoren Lin5a59e462015-08-12 18:02:51 +00001265 sys.stdout.write(" (%d failed) (%f%%)" % (
Chaoren Line80372a2015-08-12 18:02:53 +00001266 fail_count, 100.0 * fail_count / num_test_cases))
Chaoren Lin5a59e462015-08-12 18:02:51 +00001267 print
Zachary Turner4cceca72015-08-14 16:45:32 +00001268 exit_code = 0
1269
Daniel Maleacbaef262013-02-15 21:31:37 +00001270 if len(failed) > 0:
Shawn Best13491c42014-10-22 19:29:00 +00001271 failed.sort()
Ying Chen10ed1a92015-05-28 23:51:49 +00001272 print "Failing Tests (%d)" % len(failed)
Daniel Maleacbaef262013-02-15 21:31:37 +00001273 for f in failed:
Vince Harron17f429f2014-12-13 00:08:19 +00001274 print "%s: LLDB (suite) :: %s (%s)" % (
1275 "TIMEOUT" if f in timed_out else "FAIL", f, system_info
1276 )
Zachary Turner4cceca72015-08-14 16:45:32 +00001277 exit_code = 1
1278
1279 if len(unexpected_successes) > 0:
1280 unexpected_successes.sort()
1281 print "\nUnexpected Successes (%d)" % len(unexpected_successes)
1282 for u in unexpected_successes:
1283 print "UNEXPECTED SUCCESS: LLDB (suite) :: %s (%s)" % (u, system_info)
1284
1285 sys.exit(exit_code)
Johnny Chene8d9dc62011-10-31 19:04:07 +00001286
1287if __name__ == '__main__':
Todd Fialafed95662015-09-03 18:58:44 +00001288 sys.stderr.write(
1289 "error: dosep.py no longer supports being called directly. "
1290 "Please call dotest.py directly. The dosep.py-specific arguments "
1291 "have been added under the Parallel processing arguments.\n")
1292 sys.exit(128)