[Win] Pass close_fds = True in Python popen call.
[WebKit-https.git] / Tools / Scripts / webkitpy / port / server_process.py
1 # Copyright (C) 2017 Apple Inc. All rights reserved.
2 # Copyright (C) 2010 Google Inc. All rights reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
6 # met:
7 #
8 #     * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
10 #     * Redistributions in binary form must reproduce the above
11 # copyright notice, this list of conditions and the following disclaimer
12 # in the documentation and/or other materials provided with the
13 # distribution.
14 #     * Neither the Google name nor the names of its
15 # contributors may be used to endorse or promote products derived from
16 # this software without specific prior written permission.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 """Package that implements the ServerProcess wrapper class"""
31
32 import errno
33 import logging
34 import signal
35 import sys
36 import time
37
38 # Note that although win32 python does provide an implementation of
39 # the win32 select API, it only works on sockets, and not on the named pipes
40 # used by subprocess, so we have to use the native APIs directly.
41 if sys.platform.startswith('win'):
42     import msvcrt
43     import win32pipe
44     import win32file
45 else:
46     import fcntl
47     import os
48     import select
49
50 from webkitpy.common.system.executive import ScriptError
51
52
53 _log = logging.getLogger(__name__)
54
55
56 class ServerProcess(object):
57     """This class provides a wrapper around a subprocess that
58     implements a simple request/response usage model. The primary benefit
59     is that reading responses takes a deadline, so that we don't ever block
60     indefinitely. The class also handles transparently restarting processes
61     as necessary to keep issuing commands."""
62
63     def __init__(self, port_obj, name, cmd, env=None, universal_newlines=False, treat_no_data_as_crash=False, worker_number=None):
64         self._port = port_obj
65         self._name = name  # Should be the command name (e.g. DumpRenderTree, ImageDiff)
66         self._cmd = cmd
67         self._env = env
68         # Set if the process outputs non-standard newlines like '\r\n' or '\r'.
69         # Don't set if there will be binary data or the data must be ASCII encoded.
70         self._universal_newlines = universal_newlines
71         self._treat_no_data_as_crash = treat_no_data_as_crash
72         self._host = self._port.host
73         self._pid = None
74         self._reset()
75
76         # See comment in imports for why we need the win32 APIs and can't just use select.
77         # FIXME: there should be a way to get win32 vs. cygwin from platforminfo.
78         self._use_win32_apis = sys.platform.startswith('win')
79
80     def name(self):
81         return self._name
82
83     def pid(self):
84         return self._pid
85
86     def _reset(self):
87         if getattr(self, '_proc', None):
88             if self._proc.stdin:
89                 self._proc.stdin.close()
90                 self._proc.stdin = None
91             if self._proc.stdout:
92                 self._proc.stdout.close()
93                 self._proc.stdout = None
94             if self._proc.stderr:
95                 self._proc.stderr.close()
96                 self._proc.stderr = None
97
98         self._proc = None
99         self._output = str()  # bytesarray() once we require Python 2.6
100         self._error = str()  # bytesarray() once we require Python 2.6
101         self._crashed = False
102         self.timed_out = False
103
104     def process_name(self):
105         return self._name
106
107     @staticmethod
108     def _set_file_nonblocking(file):
109         flags = fcntl.fcntl(file.fileno(), fcntl.F_GETFL)
110         fcntl.fcntl(file.fileno(), fcntl.F_SETFL, flags | os.O_NONBLOCK)
111
112     def _start(self):
113         if self._proc:
114             raise ValueError("%s already running" % self._name)
115         self._reset()
116         # close_fds is a workaround for http://bugs.python.org/issue2320
117         # In Python 2.7.10, close_fds is also supported on Windows.
118         close_fds = True
119         self._proc = self._host.executive.popen(self._cmd, stdin=self._host.executive.PIPE,
120             stdout=self._host.executive.PIPE,
121             stderr=self._host.executive.PIPE,
122             close_fds=close_fds,
123             env=self._env,
124             universal_newlines=self._universal_newlines)
125         self._pid = self._proc.pid
126         self._port.find_system_pid(self.name(), self._pid)
127         if not self._use_win32_apis:
128             self._set_file_nonblocking(self._proc.stdout)
129             self._set_file_nonblocking(self._proc.stderr)
130
131     def _handle_possible_interrupt(self):
132         """This routine checks to see if the process crashed or exited
133         because of a keyboard interrupt and raises KeyboardInterrupt
134         accordingly."""
135         # FIXME: Linux and Mac set the returncode to -signal.SIGINT if a
136         # subprocess is killed with a ctrl^C.  Previous comments in this
137         # routine said that supposedly Windows returns 0xc000001d, but that's not what
138         # -1073741510 evaluates to. Figure out what the right value is
139         # for win32 and cygwin here ...
140         if self._proc.returncode in (-1073741510, -signal.SIGINT):
141             raise KeyboardInterrupt
142
143     def poll(self):
144         """Check to see if the underlying process is running; returns None
145         if it still is (wrapper around subprocess.poll)."""
146         if self._proc:
147             return self._proc.poll()
148         return None
149
150     def write(self, bytes, ignore_crash=False):
151         """Write a request to the subprocess. The subprocess is (re-)start()'ed
152         if is not already running."""
153         if not self._proc:
154             self._start()
155         try:
156             self._proc.stdin.write(bytes)
157         except IOError, e:
158             self.stop(0.0)
159             # stop() calls _reset(), so we have to set crashed to True after calling stop()
160             # unless we already know that this is a timeout.
161             if not ignore_crash:
162                 _log.debug('This test marked as a crash because of a broken pipe when writing to stdin of the server process.')
163                 self._crashed = True
164
165     def _pop_stdout_line_if_ready(self):
166         index_after_newline = self._output.find('\n') + 1
167         if index_after_newline > 0:
168             return self._pop_output_bytes(index_after_newline)
169         return None
170
171     def _pop_stderr_line_if_ready(self):
172         index_after_newline = self._error.find('\n') + 1
173         if index_after_newline > 0:
174             return self._pop_error_bytes(index_after_newline)
175         return None
176
177     def pop_all_buffered_stderr(self):
178         return self._pop_error_bytes(len(self._error))
179
180     def read_stdout_line(self, deadline):
181         return self._read(deadline, self._pop_stdout_line_if_ready)
182
183     def read_stderr_line(self, deadline):
184         return self._read(deadline, self._pop_stderr_line_if_ready)
185
186     def read_either_stdout_or_stderr_line(self, deadline):
187         def retrieve_bytes_from_buffers():
188             stdout_line = self._pop_stdout_line_if_ready()
189             if stdout_line:
190                 return stdout_line, None
191             stderr_line = self._pop_stderr_line_if_ready()
192             if stderr_line:
193                 return None, stderr_line
194             return None  # Instructs the caller to keep waiting.
195
196         return_value = self._read(deadline, retrieve_bytes_from_buffers)
197         # FIXME: This is a bit of a hack around the fact that _read normally only returns one value, but this caller wants it to return two.
198         if return_value is None:
199             return None, None
200         return return_value
201
202     def read_stdout(self, deadline, size):
203         if size <= 0:
204             raise ValueError('ServerProcess.read() called with a non-positive size: %d ' % size)
205
206         def retrieve_bytes_from_stdout_buffer():
207             if len(self._output) >= size:
208                 return self._pop_output_bytes(size)
209             return None
210
211         return self._read(deadline, retrieve_bytes_from_stdout_buffer)
212
213     def _log(self, message):
214         # This is a bit of a hack, but we first log a blank line to avoid
215         # messing up the master process's output.
216         _log.info('')
217         _log.info(message)
218
219     def _handle_timeout(self):
220         self.timed_out = True
221         if self._port.get_option("sample_on_timeout"):
222             self._port.sample_process(self._name, self._proc.pid)
223
224     def _split_string_after_index(self, string, index):
225         return string[:index], string[index:]
226
227     def _pop_output_bytes(self, bytes_count):
228         output, self._output = self._split_string_after_index(self._output, bytes_count)
229         return output
230
231     def _pop_error_bytes(self, bytes_count):
232         output, self._error = self._split_string_after_index(self._error, bytes_count)
233         return output
234
235     def _wait_for_data_and_update_buffers_using_select(self, deadline, stopping=False):
236         if self._proc.stdout.closed or self._proc.stderr.closed:
237             # If the process crashed and is using FIFOs, like Chromium Android, the
238             # stdout and stderr pipes will be closed.
239             return
240
241         out_fd = self._proc.stdout.fileno()
242         err_fd = self._proc.stderr.fileno()
243         select_fds = (out_fd, err_fd)
244         try:
245             read_fds, _, _ = select.select(select_fds, [], select_fds, max(deadline - time.time(), 0))
246         except select.error, e:
247             # We can ignore EINVAL since it's likely the process just crashed and we'll
248             # figure that out the next time through the loop in _read().
249             # We also ignore EINTR as we can resume reading the next time
250             # through the loop in _read().
251             if e.args[0] in [errno.EINVAL, errno.EINTR]:
252                 return
253             raise
254
255         try:
256             # Note that we may get no data during read() even though
257             # select says we got something; see the select() man page
258             # on linux. I don't know if this happens on Mac OS and
259             # other Unixen as well, but we don't bother special-casing
260             # Linux because it's relatively harmless either way.
261             if out_fd in read_fds:
262                 data = self._proc.stdout.read()
263                 if not data and not stopping and (self._treat_no_data_as_crash or self._proc.poll()):
264                     _log.debug('This test marked as a crash because of no data while reading stdout for the server process.')
265                     self._crashed = True
266                 self._output += data
267
268             if err_fd in read_fds:
269                 data = self._proc.stderr.read()
270                 if not data and not stopping and (self._treat_no_data_as_crash or self._proc.poll()):
271                     _log.debug('This test marked as a crash because of no data while reading stdout for the server process.')
272                     self._crashed = True
273                 self._error += data
274         except IOError, e:
275             # We can ignore the IOErrors because we will detect if the subporcess crashed
276             # the next time through the loop in _read()
277             pass
278
279     def _wait_for_data_and_update_buffers_using_win32_apis(self, deadline):
280         # See http://code.activestate.com/recipes/440554-module-to-allow-asynchronous-subprocess-use-on-win/
281         # and http://docs.activestate.com/activepython/2.6/pywin32/modules.html
282         # for documentation on all of these win32-specific modules.
283         now = time.time()
284         out_fh = msvcrt.get_osfhandle(self._proc.stdout.fileno())
285         err_fh = msvcrt.get_osfhandle(self._proc.stderr.fileno())
286         while (self._proc.poll() is None) and (now < deadline):
287             output = self._non_blocking_read_win32(out_fh)
288             error = self._non_blocking_read_win32(err_fh)
289             if output or error:
290                 if output:
291                     self._output += output
292                 if error:
293                     self._error += error
294                 return
295             time.sleep(0.01)
296             now = time.time()
297         return
298
299     def _non_blocking_read_win32(self, handle):
300         try:
301             _, avail, _ = win32pipe.PeekNamedPipe(handle, 0)
302             if avail > 0:
303                 _, buf = win32file.ReadFile(handle, avail, None)
304                 return buf
305         except Exception, e:
306             if e[0] not in (109, errno.ESHUTDOWN):  # 109 == win32 ERROR_BROKEN_PIPE
307                 raise
308         return None
309
310     def has_crashed(self):
311         if not self._crashed and self.poll():
312             _log.debug('This test marked as a crash because of failure to poll the server process.')
313             self._crashed = True
314             self._handle_possible_interrupt()
315         return self._crashed
316
317     # This read function is a bit oddly-designed, as it polls both stdout and stderr, yet
318     # only reads/returns from one of them (buffering both in local self._output/self._error).
319     # It might be cleaner to pass in the file descriptor to poll instead.
320     def _read(self, deadline, fetch_bytes_from_buffers_callback):
321         while True:
322             if self.has_crashed():
323                 return None
324
325             if time.time() > deadline:
326                 self._handle_timeout()
327                 return None
328
329             bytes = fetch_bytes_from_buffers_callback()
330             if bytes is not None:
331                 return bytes
332
333             if self._use_win32_apis:
334                 self._wait_for_data_and_update_buffers_using_win32_apis(deadline)
335             else:
336                 self._wait_for_data_and_update_buffers_using_select(deadline)
337
338     def start(self):
339         if not self._proc:
340             self._start()
341
342     def stop(self, timeout_secs=3.0):
343         if not self._proc:
344             return (None, None)
345
346         # Only bother to check for leaks or stderr if the process is still running.
347         if self.poll() is None:
348             self._port.check_for_leaks(self.name(), self.pid())
349
350         now = time.time()
351         if self._proc.stdin:
352             self._proc.stdin.close()
353             self._proc.stdin = None
354         killed = False
355         if timeout_secs:
356             deadline = now + timeout_secs
357             while self._proc.poll() is None and time.time() < deadline:
358                 time.sleep(0.01)
359             if self._proc.poll() is None:
360                 _log.warning('stopping %s(pid %d) timed out, killing it' % (self._name, self._proc.pid))
361
362         if self._proc.poll() is None:
363             self._kill()
364             killed = True
365             _log.debug('killed pid %d' % self._proc.pid)
366
367         # read any remaining data on the pipes and return it.
368         if not killed:
369             if self._use_win32_apis:
370                 self._wait_for_data_and_update_buffers_using_win32_apis(now)
371             else:
372                 self._wait_for_data_and_update_buffers_using_select(now, stopping=True)
373         out, err = self._output, self._error
374         self._reset()
375         return (out, err)
376
377     def kill(self):
378         self.stop(0.0)
379
380     def _kill(self):
381         self._host.executive.kill_process(self._proc.pid)
382         if self._proc.poll() is not None:
383             self._proc.wait()
384
385     def replace_outputs(self, stdout, stderr):
386         assert self._proc
387         if stdout:
388             self._proc.stdout.close()
389             self._proc.stdout = stdout
390         if stderr:
391             self._proc.stderr.close()
392             self._proc.stderr = stderr