Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import logging | 5 import logging |
| 6 import os | 6 import os |
| 7 import signal | 7 import signal |
| 8 import subprocess | 8 import subprocess |
| 9 import sys | 9 import sys |
| 10 import time | 10 import time |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 26 if flush: | 26 if flush: |
| 27 sys.stdout.flush() | 27 sys.stdout.flush() |
| 28 | 28 |
| 29 | 29 |
| 30 def RunSubprocessInBackground(proc): | 30 def RunSubprocessInBackground(proc): |
| 31 """Runs a subprocess in the background. Returns a handle to the process.""" | 31 """Runs a subprocess in the background. Returns a handle to the process.""" |
| 32 logging.info("running %s in the background" % " ".join(proc)) | 32 logging.info("running %s in the background" % " ".join(proc)) |
| 33 return subprocess.Popen(proc) | 33 return subprocess.Popen(proc) |
| 34 | 34 |
| 35 | 35 |
| 36 def RunSubprocess(proc, timeout=0, detach=False, background=False): | 36 def RunSubprocessChain(procs, timeout=0, detach=False, background=False): |
| 37 """ Runs a subprocess, until it finishes or |timeout| is exceeded and the | 37 """ Runs a chain of subprocesses, connected by a pipe until it finishes |
| 38 process is killed with taskkill. A |timeout| <= 0 means no timeout. | 38 or |timeout| is exceeded and the processes are killed with taskkill. |
| 39 A |timeout| <= 0 means no timeout. | |
| 39 | 40 |
| 40 Args: | 41 Args: |
| 41 proc: list of process components (exe + args) | 42 procs: list of commands to execute (each is a list of exe + args). |
| 42 timeout: how long to wait before killing, <= 0 means wait forever | 43 timeout: how long to wait before killing, <= 0 means wait forever |
| 43 detach: Whether to pass the DETACHED_PROCESS argument to CreateProcess | 44 detach: Whether to pass the DETACHED_PROCESS argument to CreateProcess |
| 44 on Windows. This is used by Purify subprocesses on buildbot which | 45 on Windows. This is used by Purify subprocesses on buildbot which |
| 45 seem to get confused by the parent console that buildbot sets up. | 46 seem to get confused by the parent console that buildbot sets up. |
| 46 """ | 47 """ |
| 47 | 48 command_str = " | ".join(map(lambda l: " ".join(l), procs)) |
| 48 logging.info("running %s, timeout %d sec" % (" ".join(proc), timeout)) | 49 logging.info("running %s, timeout %d sec" % (command_str, timeout)) |
| 50 processes = [] | |
| 49 if detach: | 51 if detach: |
| 52 if len(procs) > 1: | |
| 53 raise NotImplementedError("Chaining of detachable processes " | |
| 54 "is not supported yet.") | |
| 50 # see MSDN docs for "Process Creation Flags" | 55 # see MSDN docs for "Process Creation Flags" |
| 51 DETACHED_PROCESS = 0x8 | 56 DETACHED_PROCESS = 0x8 |
| 52 p = subprocess.Popen(proc, creationflags=DETACHED_PROCESS) | 57 processes.append(subprocess.Popen(procs[0], |
| 58 creationflags=DETACHED_PROCESS)) | |
| 53 else: | 59 else: |
| 54 # For non-detached processes, manually read and print out stdout and stderr. | 60 # For non-detached processes, manually read and print out stdout and stderr. |
| 55 # By default, the subprocess is supposed to inherit these from its parent, | 61 # By default, the subprocess is supposed to inherit these from its parent, |
| 56 # however when run under buildbot, it seems unable to read data from a | 62 # however when run under buildbot, it seems unable to read data from a |
| 57 # grandchild process, so we have to read the child and print the data as if | 63 # grandchild process, so we have to read the child and print the data as if |
| 58 # it came from us for buildbot to read it. We're not sure why this is | 64 # it came from us for buildbot to read it. We're not sure why this is |
| 59 # necessary. | 65 # necessary. |
| 60 # TODO(erikkay): should we buffer stderr and stdout separately? | 66 # TODO(erikkay): should we buffer stderr and stdout separately? |
| 61 p = subprocess.Popen(proc, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) | 67 if len(procs) == 1: |
| 62 | 68 processes.append(subprocess.Popen(procs[0], |
| 69 stdout=subprocess.PIPE, | |
| 70 stderr=subprocess.STDOUT)) | |
| 71 else: | |
| 72 first = True | |
| 73 for proc in procs: | |
| 74 if first: | |
| 75 processes.append(subprocess.Popen(proc, | |
| 76 stdout=subprocess.PIPE, | |
| 77 stderr=subprocess.STDOUT)) | |
| 78 first = False | |
| 79 else: | |
| 80 processes.append(subprocess.Popen(proc, | |
| 81 stdin=processes[-1].stdout, | |
| 82 stdout=subprocess.PIPE, | |
| 83 stderr=subprocess.STDOUT)) | |
| 63 logging.info("started subprocess") | 84 logging.info("started subprocess") |
| 64 | 85 |
| 65 # How long to wait (in seconds) before printing progress log messages. | 86 # How long to wait (in seconds) before printing progress log messages. |
| 66 progress_delay = 300 | 87 progress_delay = 300 |
| 67 progress_delay_time = time.time() + progress_delay | 88 progress_delay_time = time.time() + progress_delay |
| 68 did_timeout = False | 89 did_timeout = False |
| 69 if timeout > 0: | 90 if timeout > 0: |
| 70 wait_until = time.time() + timeout | 91 wait_until = time.time() + timeout |
| 71 while p.poll() is None and not did_timeout: | 92 while processes[-1].poll() is None and not did_timeout: |
| 72 if not detach: | 93 if not detach: |
| 73 line = p.stdout.readline() | 94 line = processes[-1].stdout.readline() |
| 74 while line and not did_timeout: | 95 while line and not did_timeout: |
| 75 _print_line(line) | 96 _print_line(line) |
| 76 line = p.stdout.readline() | 97 line = processes[-1].stdout.readline() |
| 77 if timeout > 0: | 98 if timeout > 0: |
|
M-A Ruel
2011/09/16 14:57:48
You should seriously consider using subprocess2, i
| |
| 78 did_timeout = time.time() > wait_until | 99 did_timeout = time.time() > wait_until |
| 79 else: | 100 else: |
| 80 # When we detach, blocking on reading stdout doesn't work, so we sleep | 101 # When we detach, blocking on reading stdout doesn't work, so we sleep |
| 81 # a short time and poll. | 102 # a short time and poll. |
| 82 time.sleep(0.5) | 103 time.sleep(0.5) |
| 83 if time.time() >= progress_delay_time: | 104 if time.time() >= progress_delay_time: |
| 84 # Force output on a periodic basis to avoid getting killed off by the | 105 # Force output on a periodic basis to avoid getting killed off by the |
| 85 # buildbot. | 106 # buildbot. |
| 86 # TODO(erikkay): I'd prefer a less obtrusive 'print ".",' with a flush | 107 # TODO(erikkay): I'd prefer a less obtrusive 'print ".",' with a flush |
| 87 # but because of how we're doing subprocesses, this doesn't appear to | 108 # but because of how we're doing subprocesses, this doesn't appear to |
| 88 # work reliably. | 109 # work reliably. |
| 89 logging.info("%s still running..." % os.path.basename(proc[0])) | 110 logging.info("%s still running..." % os.path.basename(procs[-1][0])) |
| 90 progress_delay_time = time.time() + progress_delay | 111 progress_delay_time = time.time() + progress_delay |
| 91 if timeout > 0: | 112 if timeout > 0: |
| 92 did_timeout = time.time() > wait_until | 113 did_timeout = time.time() > wait_until |
| 93 | 114 |
| 94 if did_timeout: | 115 if did_timeout: |
| 95 logging.info("process timed out") | 116 logging.info("process timed out") |
| 96 else: | 117 else: |
| 97 logging.info("process ended, did not time out") | 118 logging.info("process ended, did not time out") |
| 98 | 119 |
| 99 if did_timeout: | 120 if did_timeout: |
| 100 if IsWindows(): | 121 for p, proc in zip(processes, procs): |
| 101 subprocess.call(["taskkill", "/T", "/F", "/PID", str(p.pid)]) | 122 if IsWindows(): |
| 102 else: | 123 subprocess.call(["taskkill", "/T", "/F", "/PID", str(p.pid)]) |
| 103 # Does this kill all children, too? | 124 else: |
| 104 os.kill(p.pid, signal.SIGINT) | 125 # Does this kill all children, too? |
| 105 logging.error("KILLED %d" % p.pid) | 126 os.kill(p.pid, signal.SIGINT) |
| 106 # Give the process a chance to actually die before continuing | 127 logging.error("KILLED %d" % p.pid) |
| 107 # so that cleanup can happen safely. | 128 # Give the process a chance to actually die before continuing |
| 108 time.sleep(1.0) | 129 # so that cleanup can happen safely. |
| 109 logging.error("TIMEOUT waiting for %s" % proc[0]) | 130 time.sleep(1.0) |
| 131 logging.error("TIMEOUT waiting for %s" % proc[0]) | |
| 110 raise TimeoutError(proc[0]) | 132 raise TimeoutError(proc[0]) |
| 111 elif not detach: | 133 elif not detach: |
| 112 for line in p.stdout.readlines(): | 134 for line in processes[-1].stdout.readlines(): |
| 113 _print_line(line, False) | 135 _print_line(line, False) |
| 114 if not IsMac(): # stdout flush fails on Mac | 136 if not IsMac(): # stdout flush fails on Mac |
| 115 logging.info("flushing stdout") | 137 logging.info("flushing stdout") |
| 116 p.stdout.flush() | 138 processes[-1].stdout.flush() |
| 117 | 139 |
| 118 logging.info("collecting result code") | 140 logging.info("collecting result code") |
| 119 result = p.poll() | 141 result = processes[-1].poll() |
| 120 if result: | 142 if result: |
| 121 logging.error("%s exited with non-zero result code %d" % (proc[0], result)) | 143 logging.error("%s exited with non-zero result code %d" |
| 144 % (procs[0][0], result)) | |
| 122 return result | 145 return result |
| 123 | 146 |
| 147 def RunSubprocess(proc, timeout=0, detach=False, background=False): | |
| 148 """ Runs a subprocess, until it finishes or |timeout| is exceeded and the | |
| 149 process is killed with taskkill. A |timeout| <= 0 means no timeout. | |
| 150 | |
| 151 Args: | |
| 152 proc: list of process components (exe + args) | |
| 153 timeout: how long to wait before killing, <= 0 means wait forever | |
| 154 detach: Whether to pass the DETACHED_PROCESS argument to CreateProcess | |
| 155 on Windows. This is used by Purify subprocesses on buildbot which | |
| 156 seem to get confused by the parent console that buildbot sets up. | |
| 157 """ | |
| 158 return RunSubprocessChain([proc], timeout, detach, background) | |
| 159 | |
| 160 | |
| 124 | 161 |
| 125 def IsLinux(): | 162 def IsLinux(): |
| 126 return sys.platform.startswith('linux') | 163 return sys.platform.startswith('linux') |
| 127 | 164 |
| 128 | 165 |
| 129 def IsMac(): | 166 def IsMac(): |
| 130 return sys.platform.startswith('darwin') | 167 return sys.platform.startswith('darwin') |
| 131 | 168 |
| 132 | 169 |
| 133 def IsWindows(): | 170 def IsWindows(): |
| 134 return sys.platform == 'cygwin' or sys.platform.startswith('win') | 171 return sys.platform == 'cygwin' or sys.platform.startswith('win') |
| 135 | 172 |
| 136 | 173 |
| 137 def PlatformNames(): | 174 def PlatformNames(): |
| 138 """Return an array of string to be used in paths for the platform | 175 """Return an array of string to be used in paths for the platform |
| 139 (e.g. suppressions, gtest filters, ignore files etc.) | 176 (e.g. suppressions, gtest filters, ignore files etc.) |
| 140 The first element of the array describes the 'main' platform | 177 The first element of the array describes the 'main' platform |
| 141 """ | 178 """ |
| 142 if IsLinux(): | 179 if IsLinux(): |
| 143 return ['linux'] | 180 return ['linux'] |
| 144 if IsMac(): | 181 if IsMac(): |
| 145 return ['mac'] | 182 return ['mac'] |
| 146 if IsWindows(): | 183 if IsWindows(): |
| 147 return ['win32'] | 184 return ['win32'] |
| 148 raise NotImplementedError('Unknown platform "%s".' % sys.platform) | 185 raise NotImplementedError('Unknown platform "%s".' % sys.platform) |
| OLD | NEW |