Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(157)

Side by Side Diff: Tools/Scripts/webkitpy/layout_tests/port/server_process.py

Issue 23093014: reformat run-webkit-tests --driver-logging output to show ctrl chars. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@eof_crlf
Patch Set: Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | Tools/Scripts/webkitpy/layout_tests/port/server_process_unittest.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (C) 2010 Google Inc. All rights reserved. 1 # Copyright (C) 2010 Google Inc. All rights reserved.
2 # 2 #
3 # Redistribution and use in source and binary forms, with or without 3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are 4 # modification, are permitted provided that the following conditions are
5 # met: 5 # met:
6 # 6 #
7 # * Redistributions of source code must retain the above copyright 7 # * Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer. 8 # notice, this list of conditions and the following disclaimer.
9 # * Redistributions in binary form must reproduce the above 9 # * Redistributions in binary form must reproduce the above
10 # copyright notice, this list of conditions and the following disclaimer 10 # copyright notice, this list of conditions and the following disclaimer
(...skipping 12 matching lines...) Expand all
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 28
29 """Package that implements the ServerProcess wrapper class""" 29 """Package that implements the ServerProcess wrapper class"""
30 30
31 import errno 31 import errno
32 import logging 32 import logging
33 import re
33 import signal 34 import signal
34 import sys 35 import sys
35 import time 36 import time
36 37
37 # Note that although win32 python does provide an implementation of 38 # Note that although win32 python does provide an implementation of
38 # the win32 select API, it only works on sockets, and not on the named pipes 39 # the win32 select API, it only works on sockets, and not on the named pipes
39 # used by subprocess, so we have to use the native APIs directly. 40 # used by subprocess, so we have to use the native APIs directly.
40 _quote_cmd = None 41 _quote_cmd = None
41 42
42 if sys.platform == 'win32': 43 if sys.platform == 'win32':
43 import msvcrt 44 import msvcrt
44 import win32pipe 45 import win32pipe
45 import win32file 46 import win32file
46 import subprocess 47 import subprocess
47 _quote_cmd = subprocess.list2cmdline 48 _quote_cmd = subprocess.list2cmdline
48 else: 49 else:
49 import fcntl 50 import fcntl
50 import os 51 import os
51 import pipes 52 import pipes
52 import select 53 import select
53 _quote_cmd = lambda cmdline: ' '.join(pipes.quote(arg) for arg in cmdline) 54 _quote_cmd = lambda cmdline: ' '.join(pipes.quote(arg) for arg in cmdline)
54 55
55 from webkitpy.common.system.executive import ScriptError 56 from webkitpy.common.system.executive import ScriptError
56 57
57 58
58 _log = logging.getLogger(__name__) 59 _log = logging.getLogger(__name__)
59 60
60 61
62 _trailing_spaces_re = re.compile('(.*[^ ])?( +)$')
63
64
65 def quote_data(data):
66 txt = repr(data).replace('\\n', '\\n\n')[1:-1]
67 lines = []
68 for l in txt.splitlines():
69 m = _trailing_spaces_re.match(l)
70 if m:
71 l = m.group(1) + m.group(2).replace(' ', '\x20')
72 lines.append(l)
73 return lines
74
61 class ServerProcess(object): 75 class ServerProcess(object):
62 """This class provides a wrapper around a subprocess that 76 """This class provides a wrapper around a subprocess that
63 implements a simple request/response usage model. The primary benefit 77 implements a simple request/response usage model. The primary benefit
64 is that reading responses takes a deadline, so that we don't ever block 78 is that reading responses takes a deadline, so that we don't ever block
65 indefinitely. The class also handles transparently restarting processes 79 indefinitely. The class also handles transparently restarting processes
66 as necessary to keep issuing commands.""" 80 as necessary to keep issuing commands."""
67 81
68 def __init__(self, port_obj, name, cmd, env=None, universal_newlines=False, treat_no_data_as_crash=False, 82 def __init__(self, port_obj, name, cmd, env=None, universal_newlines=False, treat_no_data_as_crash=False,
69 logging=False): 83 logging=False):
70 self._port = port_obj 84 self._port = port_obj
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
114 def _start(self): 128 def _start(self):
115 if self._proc: 129 if self._proc:
116 raise ValueError("%s already running" % self._name) 130 raise ValueError("%s already running" % self._name)
117 self._reset() 131 self._reset()
118 # close_fds is a workaround for http://bugs.python.org/issue2320 132 # close_fds is a workaround for http://bugs.python.org/issue2320
119 close_fds = not self._host.platform.is_win() 133 close_fds = not self._host.platform.is_win()
120 if self._logging: 134 if self._logging:
121 env_str = '' 135 env_str = ''
122 if self._env: 136 if self._env:
123 env_str += '\n'.join("%s=%s" % (k, v) for k, v in self._env.item s()) + '\n' 137 env_str += '\n'.join("%s=%s" % (k, v) for k, v in self._env.item s()) + '\n'
124 _log.info('CMD: """\n%s%s\n"""', env_str, _quote_cmd(self._cmd)) 138 _log.info('CMD: \n%s%s\n', env_str, _quote_cmd(self._cmd))
125 self._proc = self._host.executive.popen(self._cmd, stdin=self._host.exec utive.PIPE, 139 self._proc = self._host.executive.popen(self._cmd, stdin=self._host.exec utive.PIPE,
126 stdout=self._host.executive.PIPE, 140 stdout=self._host.executive.PIPE,
127 stderr=self._host.executive.PIPE, 141 stderr=self._host.executive.PIPE,
128 close_fds=close_fds, 142 close_fds=close_fds,
129 env=self._env, 143 env=self._env,
130 universal_newlines=self._universal_newlines) 144 universal_newlines=self._universal_newlines)
131 self._pid = self._proc.pid 145 self._pid = self._proc.pid
132 fd = self._proc.stdout.fileno() 146 fd = self._proc.stdout.fileno()
133 if not self._use_win32_apis: 147 if not self._use_win32_apis:
134 fl = fcntl.fcntl(fd, fcntl.F_GETFL) 148 fl = fcntl.fcntl(fd, fcntl.F_GETFL)
(...skipping 20 matching lines...) Expand all
155 if self._proc: 169 if self._proc:
156 return self._proc.poll() 170 return self._proc.poll()
157 return None 171 return None
158 172
159 def write(self, bytes): 173 def write(self, bytes):
160 """Write a request to the subprocess. The subprocess is (re-)start()'ed 174 """Write a request to the subprocess. The subprocess is (re-)start()'ed
161 if is not already running.""" 175 if is not already running."""
162 if not self._proc: 176 if not self._proc:
163 self._start() 177 self._start()
164 try: 178 try:
165 if self._logging: 179 self._log_data(' IN', bytes)
166 _log.info(' IN: """%s"""', bytes)
167 self._proc.stdin.write(bytes) 180 self._proc.stdin.write(bytes)
168 except IOError, e: 181 except IOError, e:
169 self.stop(0.0) 182 self.stop(0.0)
170 # stop() calls _reset(), so we have to set crashed to True after cal ling stop(). 183 # stop() calls _reset(), so we have to set crashed to True after cal ling stop().
171 self._crashed = True 184 self._crashed = True
172 185
173 def _pop_stdout_line_if_ready(self): 186 def _pop_stdout_line_if_ready(self):
174 index_after_newline = self._output.find('\n') + 1 187 index_after_newline = self._output.find('\n') + 1
175 if index_after_newline > 0: 188 if index_after_newline > 0:
176 return self._pop_output_bytes(index_after_newline) 189 return self._pop_output_bytes(index_after_newline)
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
217 return None 230 return None
218 231
219 return self._read(deadline, retrieve_bytes_from_stdout_buffer) 232 return self._read(deadline, retrieve_bytes_from_stdout_buffer)
220 233
221 def _log(self, message): 234 def _log(self, message):
222 # This is a bit of a hack, but we first log a blank line to avoid 235 # This is a bit of a hack, but we first log a blank line to avoid
223 # messing up the master process's output. 236 # messing up the master process's output.
224 _log.info('') 237 _log.info('')
225 _log.info(message) 238 _log.info(message)
226 239
240 def _log_data(self, prefix, data):
241 if self._logging and data and len(data):
242 for line in quote_data(data):
243 _log.info('%s: %s', prefix, line)
244
227 def _handle_timeout(self): 245 def _handle_timeout(self):
228 self.timed_out = True 246 self.timed_out = True
229 self._port.sample_process(self._name, self._proc.pid) 247 self._port.sample_process(self._name, self._proc.pid)
230 248
231 def _split_string_after_index(self, string, index): 249 def _split_string_after_index(self, string, index):
232 return string[:index], string[index:] 250 return string[:index], string[index:]
233 251
234 def _pop_output_bytes(self, bytes_count): 252 def _pop_output_bytes(self, bytes_count):
235 output, self._output = self._split_string_after_index(self._output, byte s_count) 253 output, self._output = self._split_string_after_index(self._output, byte s_count)
236 return output 254 return output
(...skipping 23 matching lines...) Expand all
260 try: 278 try:
261 # Note that we may get no data during read() even though 279 # Note that we may get no data during read() even though
262 # select says we got something; see the select() man page 280 # select says we got something; see the select() man page
263 # on linux. I don't know if this happens on Mac OS and 281 # on linux. I don't know if this happens on Mac OS and
264 # other Unixen as well, but we don't bother special-casing 282 # other Unixen as well, but we don't bother special-casing
265 # Linux because it's relatively harmless either way. 283 # Linux because it's relatively harmless either way.
266 if out_fd in read_fds: 284 if out_fd in read_fds:
267 data = self._proc.stdout.read() 285 data = self._proc.stdout.read()
268 if not data and not stopping and (self._treat_no_data_as_crash o r self._proc.poll()): 286 if not data and not stopping and (self._treat_no_data_as_crash o r self._proc.poll()):
269 self._crashed = True 287 self._crashed = True
270 if self._logging and len(data): 288 self._log_data('OUT', data)
271 _log.info('OUT: """%s"""', data)
272 self._output += data 289 self._output += data
273 290
274 if err_fd in read_fds: 291 if err_fd in read_fds:
275 data = self._proc.stderr.read() 292 data = self._proc.stderr.read()
276 if not data and not stopping and (self._treat_no_data_as_crash o r self._proc.poll()): 293 if not data and not stopping and (self._treat_no_data_as_crash o r self._proc.poll()):
277 self._crashed = True 294 self._crashed = True
278 if self._logging and len(data): 295 self._log_data('ERR', data)
279 _log.info('ERR: """%s"""', data)
280 self._error += data 296 self._error += data
281 except IOError, e: 297 except IOError, e:
282 # We can ignore the IOErrors because we will detect if the subporces s crashed 298 # We can ignore the IOErrors because we will detect if the subporces s crashed
283 # the next time through the loop in _read() 299 # the next time through the loop in _read()
284 pass 300 pass
285 301
286 def _wait_for_data_and_update_buffers_using_win32_apis(self, deadline): 302 def _wait_for_data_and_update_buffers_using_win32_apis(self, deadline):
287 # See http://code.activestate.com/recipes/440554-module-to-allow-asynchr onous-subprocess-use-on-win/ 303 # See http://code.activestate.com/recipes/440554-module-to-allow-asynchr onous-subprocess-use-on-win/
288 # and http://docs.activestate.com/activepython/2.6/pywin32/modules.html 304 # and http://docs.activestate.com/activepython/2.6/pywin32/modules.html
289 # for documentation on all of these win32-specific modules. 305 # for documentation on all of these win32-specific modules.
290 now = time.time() 306 now = time.time()
291 out_fh = msvcrt.get_osfhandle(self._proc.stdout.fileno()) 307 out_fh = msvcrt.get_osfhandle(self._proc.stdout.fileno())
292 err_fh = msvcrt.get_osfhandle(self._proc.stderr.fileno()) 308 err_fh = msvcrt.get_osfhandle(self._proc.stderr.fileno())
293 while (self._proc.poll() is None) and (now < deadline): 309 while (self._proc.poll() is None) and (now < deadline):
294 output = self._non_blocking_read_win32(out_fh) 310 output = self._non_blocking_read_win32(out_fh)
311 self._log_data('OUT', output)
295 error = self._non_blocking_read_win32(err_fh) 312 error = self._non_blocking_read_win32(err_fh)
313 self._log_data('ERR', error)
296 if output or error: 314 if output or error:
297 if output: 315 if output:
298 self._output += output 316 self._output += output
299 if error: 317 if error:
300 self._error += error 318 self._error += error
301 return 319 return
302 time.sleep(0.01) 320 time.sleep(0.01)
303 now = time.time() 321 now = time.time()
304 return 322 return
305 323
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
345 if not self._proc: 363 if not self._proc:
346 self._start() 364 self._start()
347 365
348 def stop(self, timeout_secs=3.0): 366 def stop(self, timeout_secs=3.0):
349 if not self._proc: 367 if not self._proc:
350 return (None, None) 368 return (None, None)
351 369
352 now = time.time() 370 now = time.time()
353 if self._proc.stdin: 371 if self._proc.stdin:
354 if self._logging: 372 if self._logging:
355 _log.info(' IN: """^D"""') 373 _log.info(' IN: ^D')
356 self._proc.stdin.close() 374 self._proc.stdin.close()
357 self._proc.stdin = None 375 self._proc.stdin = None
358 killed = False 376 killed = False
359 if timeout_secs: 377 if timeout_secs:
360 deadline = now + timeout_secs 378 deadline = now + timeout_secs
361 while self._proc.poll() is None and time.time() < deadline: 379 while self._proc.poll() is None and time.time() < deadline:
362 time.sleep(0.01) 380 time.sleep(0.01)
363 if self._proc.poll() is None: 381 if self._proc.poll() is None:
364 _log.warning('stopping %s(pid %d) timed out, killing it' % (self ._name, self._proc.pid)) 382 _log.warning('stopping %s(pid %d) timed out, killing it' % (self ._name, self._proc.pid))
365 383
(...skipping 21 matching lines...) Expand all
387 self._proc.wait() 405 self._proc.wait()
388 406
389 def replace_outputs(self, stdout, stderr): 407 def replace_outputs(self, stdout, stderr):
390 assert self._proc 408 assert self._proc
391 if stdout: 409 if stdout:
392 self._proc.stdout.close() 410 self._proc.stdout.close()
393 self._proc.stdout = stdout 411 self._proc.stdout = stdout
394 if stderr: 412 if stderr:
395 self._proc.stderr.close() 413 self._proc.stderr.close()
396 self._proc.stderr = stderr 414 self._proc.stderr = stderr
OLDNEW
« no previous file with comments | « no previous file | Tools/Scripts/webkitpy/layout_tests/port/server_process_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698