Fix leak-checking for iOS Simulators
[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, target_host=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._target_host = target_host or port_obj.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 pid(self):
81         return self._pid
82
83     def _reset(self):
84         if getattr(self, '_proc', None):
85             if self._proc.stdin:
86                 self._proc.stdin.close()
87                 self._proc.stdin = None
88             if self._proc.stdout:
89                 self._proc.stdout.close()
90                 self._proc.stdout = None
91             if self._proc.stderr:
92                 self._proc.stderr.close()
93                 self._proc.stderr = None
94
95         self._proc = None
96         self._output = str()  # bytesarray() once we require Python 2.6
97         self._error = str()  # bytesarray() once we require Python 2.6
98         self._crashed = False
99         self.timed_out = False
100
101     def process_name(self):
102         return self._name
103
104     @staticmethod
105     def _set_file_nonblocking(file):
106         flags = fcntl.fcntl(file.fileno(), fcntl.F_GETFL)
107         fcntl.fcntl(file.fileno(), fcntl.F_SETFL, flags | os.O_NONBLOCK)
108
109     def _start(self):
110         if self._proc:
111             raise ValueError("%s already running" % self._name)
112         self._reset()
113         # close_fds is a workaround for http://bugs.python.org/issue2320
114         # In Python 2.7.10, close_fds is also supported on Windows.
115         close_fds = True
116         self._proc = self._target_host.executive.popen(self._cmd, stdin=self._target_host.executive.PIPE,
117             stdout=self._target_host.executive.PIPE,
118             stderr=self._target_host.executive.PIPE,
119             close_fds=close_fds,
120             env=self._env,
121             universal_newlines=self._universal_newlines)
122         self._pid = self._proc.pid
123         self._port.find_system_pid(self.process_name(), self._pid)
124         if not self._use_win32_apis:
125             self._set_file_nonblocking(self._proc.stdout)
126             self._set_file_nonblocking(self._proc.stderr)
127
128     def _handle_possible_interrupt(self):
129         """This routine checks to see if the process crashed or exited
130         because of a keyboard interrupt and raises KeyboardInterrupt
131         accordingly."""
132         # FIXME: Linux and Mac set the returncode to -signal.SIGINT if a
133         # subprocess is killed with a ctrl^C.  Previous comments in this
134         # routine said that supposedly Windows returns 0xc000001d, but that's not what
135         # -1073741510 evaluates to. Figure out what the right value is
136         # for win32 and cygwin here ...
137         if self._proc.returncode in (-1073741510, -signal.SIGINT):
138             raise KeyboardInterrupt
139
140     def poll(self):
141         """Check to see if the underlying process is running; returns None
142         if it still is (wrapper around subprocess.poll)."""
143         if self._proc:
144             return self._proc.poll()
145         return None
146
147     def write(self, bytes, ignore_crash=False):
148         """Write a request to the subprocess. The subprocess is (re-)start()'ed
149         if is not already running."""
150         if not self._proc:
151             self._start()
152         try:
153             self._proc.stdin.write(bytes)
154         except IOError, e:
155             self.stop(0.0)
156             # stop() calls _reset(), so we have to set crashed to True after calling stop()
157             # unless we already know that this is a timeout.
158             if not ignore_crash:
159                 _log.debug('This test marked as a crash because of a broken pipe when writing to stdin of the server process.')
160                 self._crashed = True
161
162     def _pop_stdout_line_if_ready(self):
163         index_after_newline = self._output.find('\n') + 1
164         if index_after_newline > 0:
165             return self._pop_output_bytes(index_after_newline)
166         return None
167
168     def _pop_stderr_line_if_ready(self):
169         index_after_newline = self._error.find('\n') + 1
170         if index_after_newline > 0:
171             return self._pop_error_bytes(index_after_newline)
172         return None
173
174     def pop_all_buffered_stderr(self):
175         return self._pop_error_bytes(len(self._error))
176
177     def read_stdout_line(self, deadline):
178         return self._read(deadline, self._pop_stdout_line_if_ready)
179
180     def read_stderr_line(self, deadline):
181         return self._read(deadline, self._pop_stderr_line_if_ready)
182
183     def read_either_stdout_or_stderr_line(self, deadline):
184         def retrieve_bytes_from_buffers():
185             stdout_line = self._pop_stdout_line_if_ready()
186             if stdout_line:
187                 return stdout_line, None
188             stderr_line = self._pop_stderr_line_if_ready()
189             if stderr_line:
190                 return None, stderr_line
191             return None  # Instructs the caller to keep waiting.
192
193         return_value = self._read(deadline, retrieve_bytes_from_buffers)
194         # 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.
195         if return_value is None:
196             return None, None
197         return return_value
198
199     def read_stdout(self, deadline, size):
200         if size <= 0:
201             raise ValueError('ServerProcess.read() called with a non-positive size: %d ' % size)
202
203         def retrieve_bytes_from_stdout_buffer():
204             if len(self._output) >= size:
205                 return self._pop_output_bytes(size)
206             return None
207
208         return self._read(deadline, retrieve_bytes_from_stdout_buffer)
209
210     def _log(self, message):
211         # This is a bit of a hack, but we first log a blank line to avoid
212         # messing up the master process's output.
213         _log.info('')
214         _log.info(message)
215
216     def _handle_timeout(self):
217         self.timed_out = True
218         if self._port.get_option("sample_on_timeout"):
219             self._port.sample_process(self._name, self._proc.pid, self._target_host)
220
221     def _split_string_after_index(self, string, index):
222         return string[:index], string[index:]
223
224     def _pop_output_bytes(self, bytes_count):
225         output, self._output = self._split_string_after_index(self._output, bytes_count)
226         return output
227
228     def _pop_error_bytes(self, bytes_count):
229         output, self._error = self._split_string_after_index(self._error, bytes_count)
230         return output
231
232     def _wait_for_data_and_update_buffers_using_select(self, deadline, stopping=False):
233         if self._proc.stdout.closed or self._proc.stderr.closed:
234             # If the process crashed and is using FIFOs, like Chromium Android, the
235             # stdout and stderr pipes will be closed.
236             return
237
238         out_fd = self._proc.stdout.fileno()
239         err_fd = self._proc.stderr.fileno()
240         select_fds = (out_fd, err_fd)
241         try:
242             read_fds, _, _ = select.select(select_fds, [], select_fds, max(deadline - time.time(), 0))
243         except select.error, e:
244             # We can ignore EINVAL since it's likely the process just crashed and we'll
245             # figure that out the next time through the loop in _read().
246             # We also ignore EINTR as we can resume reading the next time
247             # through the loop in _read().
248             if e.args[0] in [errno.EINVAL, errno.EINTR]:
249                 return
250             raise
251
252         try:
253             # Note that we may get no data during read() even though
254             # select says we got something; see the select() man page
255             # on linux. I don't know if this happens on Mac OS and
256             # other Unixen as well, but we don't bother special-casing
257             # Linux because it's relatively harmless either way.
258             if out_fd in read_fds:
259                 data = self._proc.stdout.read()
260                 if not data and not stopping and (self._treat_no_data_as_crash or self._proc.poll()):
261                     _log.debug('This test marked as a crash because of no data while reading stdout for the server process.')
262                     self._crashed = True
263                 self._output += data
264
265             if err_fd in read_fds:
266                 data = self._proc.stderr.read()
267                 if not data and not stopping and (self._treat_no_data_as_crash or self._proc.poll()):
268                     _log.debug('This test marked as a crash because of no data while reading stdout for the server process.')
269                     self._crashed = True
270                 self._error += data
271         except IOError, e:
272             # We can ignore the IOErrors because we will detect if the subporcess crashed
273             # the next time through the loop in _read()
274             pass
275
276     def _wait_for_data_and_update_buffers_using_win32_apis(self, deadline):
277         # See http://code.activestate.com/recipes/440554-module-to-allow-asynchronous-subprocess-use-on-win/
278         # and http://docs.activestate.com/activepython/2.6/pywin32/modules.html
279         # for documentation on all of these win32-specific modules.
280         now = time.time()
281         out_fh = msvcrt.get_osfhandle(self._proc.stdout.fileno())
282         err_fh = msvcrt.get_osfhandle(self._proc.stderr.fileno())
283         while (self._proc.poll() is None) and (now < deadline):
284             output = self._non_blocking_read_win32(out_fh)
285             error = self._non_blocking_read_win32(err_fh)
286             if output or error:
287                 if output:
288                     self._output += output
289                 if error:
290                     self._error += error
291                 return
292             time.sleep(0.01)
293             now = time.time()
294         return
295
296     def _non_blocking_read_win32(self, handle):
297         try:
298             _, avail, _ = win32pipe.PeekNamedPipe(handle, 0)
299             if avail > 0:
300                 _, buf = win32file.ReadFile(handle, avail, None)
301                 return buf
302         except Exception, e:
303             if e[0] not in (109, errno.ESHUTDOWN):  # 109 == win32 ERROR_BROKEN_PIPE
304                 raise
305         return None
306
307     def has_crashed(self):
308         if not self._crashed and self.poll():
309             _log.debug('This test marked as a crash because of failure to poll the server process.')
310             self._crashed = True
311             self._handle_possible_interrupt()
312         return self._crashed
313
314     # This read function is a bit oddly-designed, as it polls both stdout and stderr, yet
315     # only reads/returns from one of them (buffering both in local self._output/self._error).
316     # It might be cleaner to pass in the file descriptor to poll instead.
317     def _read(self, deadline, fetch_bytes_from_buffers_callback):
318         while True:
319             # Polling does not need to occur before bytes are fetched from the buffer.
320             if self._crashed:
321                 return None
322
323             if time.time() > deadline:
324                 self._handle_timeout()
325                 return None
326
327             bytes = fetch_bytes_from_buffers_callback()
328             if bytes is not None:
329                 return bytes
330
331             if self.has_crashed():
332                 return None
333
334             if self._use_win32_apis:
335                 self._wait_for_data_and_update_buffers_using_win32_apis(deadline)
336             else:
337                 self._wait_for_data_and_update_buffers_using_select(deadline)
338
339     def start(self):
340         if not self._proc:
341             self._start()
342
343     def stop(self, timeout_secs=3.0):
344         if not self._proc:
345             return (None, None)
346
347         # Only bother to check for leaks or stderr if the process is still running.
348         if self.poll() is None:
349             self._port.check_for_leaks(self.process_name(), self.pid())
350
351         if self._proc.stdin:
352             self._proc.stdin.close()
353             self._proc.stdin = None
354
355         return self._wait_for_stop(timeout_secs)
356
357     def _wait_for_stop(self, timeout_secs=3.0):
358         now = time.time()
359         killed = False
360         if timeout_secs:
361             deadline = now + timeout_secs
362             while self._proc and self._proc.poll() is None and time.time() < deadline:
363                 time.sleep(0.01)
364             if self._proc and self._proc.poll() is None:
365                 _log.warning('stopping %s(pid %d) timed out, killing it' % (self._name, self._proc.pid))
366
367         if self._proc and self._proc.poll() is None:
368             self._kill()
369             killed = True
370             _log.debug('killed pid %d' % self._proc.pid)
371
372         # read any remaining data on the pipes and return it.
373         if self._proc and not killed:
374             if self._use_win32_apis:
375                 self._wait_for_data_and_update_buffers_using_win32_apis(now)
376             else:
377                 self._wait_for_data_and_update_buffers_using_select(now, stopping=True)
378         out, err = self._output, self._error
379         self._reset()
380         return (out, err)
381
382     def kill(self):
383         self.stop(0.0)
384
385     def _kill(self):
386         self._target_host.executive.kill_process(self._proc.pid)
387         if self._proc.poll() is None:
388             self._proc.wait()
389
390     def replace_outputs(self, stdout, stderr):
391         assert self._proc
392         if stdout:
393             self._proc.stdout.close()
394             self._proc.stdout = stdout
395         if stderr:
396             self._proc.stderr.close()
397             self._proc.stderr = stderr