IndexedDB: Introduce putWithIndexKeys and calculate them in the renderer
[WebKit-https.git] / Tools / Scripts / webkitpy / layout_tests / port / server_process.py
1 #!/usr/bin/env python
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 subprocess
36 import sys
37 import time
38
39 # Note that although win32 python does provide an implementation of
40 # the win32 select API, it only works on sockets, and not on the named pipes
41 # used by subprocess, so we have to use the native APIs directly.
42 if sys.platform == 'win32':
43     import msvcrt
44     import win32pipe
45     import win32file
46 else:
47     import fcntl
48     import os
49     import select
50
51 from webkitpy.common.system.executive import ScriptError
52
53
54 _log = logging.getLogger(__name__)
55
56
57 class ServerProcess(object):
58     """This class provides a wrapper around a subprocess that
59     implements a simple request/response usage model. The primary benefit
60     is that reading responses takes a deadline, so that we don't ever block
61     indefinitely. The class also handles transparently restarting processes
62     as necessary to keep issuing commands."""
63
64     def __init__(self, port_obj, name, cmd, env=None):
65         self._port = port_obj
66         self._name = name  # Should be the command name (e.g. DumpRenderTree, ImageDiff)
67         self._cmd = cmd
68         self._env = env
69         self._host = self._port.host
70         self._pid = None
71         self._reset()
72
73         # See comment in imports for why we need the win32 APIs and can't just use select.
74         # FIXME: there should be a way to get win32 vs. cygwin from platforminfo.
75         self._use_win32_apis = sys.platform == 'win32'
76
77     def name(self):
78         return self._name
79
80     def pid(self):
81         return self._pid
82
83     def _reset(self):
84         self._proc = None
85         self._output = str()  # bytesarray() once we require Python 2.6
86         self._error = str()  # bytesarray() once we require Python 2.6
87         self._crashed = False
88         self.timed_out = False
89
90     def process_name(self):
91         return self._name
92
93     def _start(self):
94         if self._proc:
95             raise ValueError("%s already running" % self._name)
96         self._reset()
97         # close_fds is a workaround for http://bugs.python.org/issue2320
98         close_fds = not self._host.platform.is_win()
99         self._proc = subprocess.Popen(self._cmd, stdin=subprocess.PIPE,
100                                       stdout=subprocess.PIPE,
101                                       stderr=subprocess.PIPE,
102                                       close_fds=close_fds,
103                                       env=self._env)
104         self._pid = self._proc.pid
105         fd = self._proc.stdout.fileno()
106         if not self._use_win32_apis:
107             fl = fcntl.fcntl(fd, fcntl.F_GETFL)
108             fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
109             fd = self._proc.stderr.fileno()
110             fl = fcntl.fcntl(fd, fcntl.F_GETFL)
111             fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
112
113     def _handle_possible_interrupt(self):
114         """This routine checks to see if the process crashed or exited
115         because of a keyboard interrupt and raises KeyboardInterrupt
116         accordingly."""
117         # FIXME: Linux and Mac set the returncode to -signal.SIGINT if a
118         # subprocess is killed with a ctrl^C.  Previous comments in this
119         # routine said that supposedly Windows returns 0xc000001d, but that's not what
120         # -1073741510 evaluates to. Figure out what the right value is
121         # for win32 and cygwin here ...
122         if self._proc.returncode in (-1073741510, -signal.SIGINT):
123             raise KeyboardInterrupt
124
125     def poll(self):
126         """Check to see if the underlying process is running; returns None
127         if it still is (wrapper around subprocess.poll)."""
128         if self._proc:
129             return self._proc.poll()
130         return None
131
132     def write(self, bytes):
133         """Write a request to the subprocess. The subprocess is (re-)start()'ed
134         if is not already running."""
135         if not self._proc:
136             self._start()
137         try:
138             self._proc.stdin.write(bytes)
139         except IOError, e:
140             self.stop()
141             # stop() calls _reset(), so we have to set crashed to True after calling stop().
142             self._crashed = True
143
144     def _pop_stdout_line_if_ready(self):
145         index_after_newline = self._output.find('\n') + 1
146         if index_after_newline > 0:
147             return self._pop_output_bytes(index_after_newline)
148         return None
149
150     def _pop_stderr_line_if_ready(self):
151         index_after_newline = self._error.find('\n') + 1
152         if index_after_newline > 0:
153             return self._pop_error_bytes(index_after_newline)
154         return None
155
156     def pop_all_buffered_stderr(self):
157         return self._pop_error_bytes(len(self._error))
158
159     def read_stdout_line(self, deadline):
160         return self._read(deadline, self._pop_stdout_line_if_ready)
161
162     def read_stderr_line(self, deadline):
163         return self._read(deadline, self._pop_stderr_line_if_ready)
164
165     def read_either_stdout_or_stderr_line(self, deadline):
166         def retrieve_bytes_from_buffers():
167             stdout_line = self._pop_stdout_line_if_ready()
168             if stdout_line:
169                 return stdout_line, None
170             stderr_line = self._pop_stderr_line_if_ready()
171             if stderr_line:
172                 return None, stderr_line
173             return None  # Instructs the caller to keep waiting.
174
175         return_value = self._read(deadline, retrieve_bytes_from_buffers)
176         # 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.
177         if return_value is None:
178             return None, None
179         return return_value
180
181     def read_stdout(self, deadline, size):
182         if size <= 0:
183             raise ValueError('ServerProcess.read() called with a non-positive size: %d ' % size)
184
185         def retrieve_bytes_from_stdout_buffer():
186             if len(self._output) >= size:
187                 return self._pop_output_bytes(size)
188             return None
189
190         return self._read(deadline, retrieve_bytes_from_stdout_buffer)
191
192     def _log(self, message):
193         # This is a bit of a hack, but we first log a blank line to avoid
194         # messing up the master process's output.
195         _log.info('')
196         _log.info(message)
197
198     def _handle_timeout(self):
199         self.timed_out = True
200         self._port.sample_process(self._name, self._proc.pid)
201
202     def _split_string_after_index(self, string, index):
203         return string[:index], string[index:]
204
205     def _pop_output_bytes(self, bytes_count):
206         output, self._output = self._split_string_after_index(self._output, bytes_count)
207         return output
208
209     def _pop_error_bytes(self, bytes_count):
210         output, self._error = self._split_string_after_index(self._error, bytes_count)
211         return output
212
213     def _wait_for_data_and_update_buffers_using_select(self, deadline):
214         out_fd = self._proc.stdout.fileno()
215         err_fd = self._proc.stderr.fileno()
216         select_fds = (out_fd, err_fd)
217         try:
218             read_fds, _, _ = select.select(select_fds, [], select_fds, deadline - time.time())
219         except select.error, e:
220             # We can ignore EINVAL since it's likely the process just crashed and we'll
221             # figure that out the next time through the loop in _read().
222             if e.args[0] == errno.EINVAL:
223                 return
224             raise
225
226         try:
227             if out_fd in read_fds:
228                 self._output += self._proc.stdout.read()
229             if err_fd in read_fds:
230                 self._error += self._proc.stderr.read()
231         except IOError, e:
232             # We can ignore the IOErrors because we will detect if the subporcess crashed
233             # the next time through the loop in _read()
234             pass
235
236     def _wait_for_data_and_update_buffers_using_win32_apis(self, deadline):
237         # See http://code.activestate.com/recipes/440554-module-to-allow-asynchronous-subprocess-use-on-win/
238         # and http://docs.activestate.com/activepython/2.6/pywin32/modules.html
239         # for documentation on all of these win32-specific modules.
240         now = time.time()
241         out_fh = msvcrt.get_osfhandle(self._proc.stdout.fileno())
242         err_fh = msvcrt.get_osfhandle(self._proc.stderr.fileno())
243         while (self._proc.poll() is None) and (now < deadline):
244             output = self._non_blocking_read_win32(out_fh)
245             error = self._non_blocking_read_win32(err_fh)
246             if output or error:
247                 if output:
248                     self._output += output
249                 if error:
250                     self._error += error
251                 return
252             time.sleep(0.01)
253             now = time.time()
254         return
255
256     def _non_blocking_read_win32(self, handle):
257         try:
258             _, avail, _ = win32pipe.PeekNamedPipe(handle, 0)
259             if avail > 0:
260                 _, buf = win32file.ReadFile(handle, avail, None)
261                 return buf
262         except Exception, e:
263             if e[0] not in (109, errno.ESHUTDOWN):  # 109 == win32 ERROR_BROKEN_PIPE
264                 raise
265         return None
266
267     def has_crashed(self):
268         if not self._crashed and self.poll():
269             self._crashed = True
270             self._handle_possible_interrupt()
271         return self._crashed
272
273     # This read function is a bit oddly-designed, as it polls both stdout and stderr, yet
274     # only reads/returns from one of them (buffering both in local self._output/self._error).
275     # It might be cleaner to pass in the file descriptor to poll instead.
276     def _read(self, deadline, fetch_bytes_from_buffers_callback):
277         while True:
278             if self.has_crashed():
279                 return None
280
281             if time.time() > deadline:
282                 self._handle_timeout()
283                 return None
284
285             bytes = fetch_bytes_from_buffers_callback()
286             if bytes is not None:
287                 return bytes
288
289             if self._use_win32_apis:
290                 self._wait_for_data_and_update_buffers_using_win32_apis(deadline)
291             else:
292                 self._wait_for_data_and_update_buffers_using_select(deadline)
293
294     def start(self):
295         if not self._proc:
296             self._start()
297
298     def stop(self):
299         if not self._proc:
300             return
301
302         # Only bother to check for leaks if the process is still running.
303         if self.poll() is None:
304             self._port.check_for_leaks(self.name(), self.pid())
305
306         self._proc.stdin.close()
307         self._proc.stdout.close()
308         if self._proc.stderr:
309             self._proc.stderr.close()
310         if not self._host.platform.is_win():
311             # Closing stdin/stdout/stderr hangs sometimes on OS X,
312             # (see restart(), above), and anyway we don't want to hang
313             # the harness if DumpRenderTree is buggy, so we wait a couple
314             # seconds to give DumpRenderTree a chance to clean up, but then
315             # force-kill the process if necessary.
316             KILL_TIMEOUT = 3.0
317             timeout = time.time() + KILL_TIMEOUT
318             while self._proc.poll() is None and time.time() < timeout:
319                 time.sleep(0.01)
320             if self._proc.poll() is None:
321                 _log.warning('stopping %s timed out, killing it' % self._name)
322                 self.kill()
323                 _log.warning('killed')
324         self._reset()
325
326     def kill(self):
327         if self._proc:
328             self._host.executive.kill_process(self._proc.pid)
329             if self._proc.poll() is not None:
330                 self._proc.wait()
331             self._reset()