1 # Copyright 2012 the V8 project authors. All rights reserved. 2 # Redistribution and use in source and binary forms, with or without 3 # modification, are permitted provided that the following conditions are 4 # met: 5 # 6 # * Redistributions of source code must retain the above copyright 7 # notice, this list of conditions and the following disclaimer. 8 # * Redistributions in binary form must reproduce the above 9 # copyright notice, this list of conditions and the following 10 # disclaimer in the documentation and/or other materials provided 11 # with the distribution. 12 # * Neither the name of Google Inc. nor the names of its 13 # contributors may be used to endorse or promote products derived 14 # from this software without specific prior written permission. 15 # 16 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 29 import os 30 import signal 31 import subprocess 32 import sys 33 import tempfile 34 import time 35 36 from ..local import utils 37 from ..objects import output 38 39 40 def KillProcessWithID(pid): 41 if utils.IsWindows(): 42 os.popen('taskkill /T /F /PID %d' % pid) 43 else: 44 os.kill(pid, signal.SIGTERM) 45 46 47 MAX_SLEEP_TIME = 0.1 48 INITIAL_SLEEP_TIME = 0.0001 49 SLEEP_TIME_FACTOR = 1.25 50 51 SEM_INVALID_VALUE = -1 52 SEM_NOGPFAULTERRORBOX = 0x0002 # Microsoft Platform SDK WinBase.h 53 54 55 def Win32SetErrorMode(mode): 56 prev_error_mode = SEM_INVALID_VALUE 57 try: 58 import ctypes 59 prev_error_mode = \ 60 ctypes.windll.kernel32.SetErrorMode(mode) #@UndefinedVariable 61 except ImportError: 62 pass 63 return prev_error_mode 64 65 66 def RunProcess(verbose, timeout, args, **rest): 67 if verbose: print "#", " ".join(args) 68 popen_args = args 69 prev_error_mode = SEM_INVALID_VALUE 70 if utils.IsWindows(): 71 popen_args = subprocess.list2cmdline(args) 72 # Try to change the error mode to avoid dialogs on fatal errors. Don't 73 # touch any existing error mode flags by merging the existing error mode. 74 # See http://blogs.msdn.com/oldnewthing/archive/2004/07/27/198410.aspx. 75 error_mode = SEM_NOGPFAULTERRORBOX 76 prev_error_mode = Win32SetErrorMode(error_mode) 77 Win32SetErrorMode(error_mode | prev_error_mode) 78 process = subprocess.Popen( 79 shell=utils.IsWindows(), 80 args=popen_args, 81 **rest 82 ) 83 if (utils.IsWindows() and prev_error_mode != SEM_INVALID_VALUE): 84 Win32SetErrorMode(prev_error_mode) 85 # Compute the end time - if the process crosses this limit we 86 # consider it timed out. 87 if timeout is None: end_time = None 88 else: end_time = time.time() + timeout 89 timed_out = False 90 # Repeatedly check the exit code from the process in a 91 # loop and keep track of whether or not it times out. 92 exit_code = None 93 sleep_time = INITIAL_SLEEP_TIME 94 while exit_code is None: 95 if (not end_time is None) and (time.time() >= end_time): 96 # Kill the process and wait for it to exit. 97 KillProcessWithID(process.pid) 98 exit_code = process.wait() 99 timed_out = True 100 else: 101 exit_code = process.poll() 102 time.sleep(sleep_time) 103 sleep_time = sleep_time * SLEEP_TIME_FACTOR 104 if sleep_time > MAX_SLEEP_TIME: 105 sleep_time = MAX_SLEEP_TIME 106 return (exit_code, timed_out) 107 108 109 def PrintError(string): 110 sys.stderr.write(string) 111 sys.stderr.write("\n") 112 113 114 def CheckedUnlink(name): 115 # On Windows, when run with -jN in parallel processes, 116 # OS often fails to unlink the temp file. Not sure why. 117 # Need to retry. 118 # Idea from https://bugs.webkit.org/attachment.cgi?id=75982&action=prettypatch 119 retry_count = 0 120 while retry_count < 30: 121 try: 122 os.unlink(name) 123 return 124 except OSError, e: 125 retry_count += 1 126 time.sleep(retry_count * 0.1) 127 PrintError("os.unlink() " + str(e)) 128 129 130 def Execute(args, verbose=False, timeout=None): 131 try: 132 args = [ c for c in args if c != "" ] 133 (fd_out, outname) = tempfile.mkstemp() 134 (fd_err, errname) = tempfile.mkstemp() 135 (exit_code, timed_out) = RunProcess( 136 verbose, 137 timeout, 138 args=args, 139 stdout=fd_out, 140 stderr=fd_err 141 ) 142 finally: 143 # TODO(machenbach): A keyboard interrupt before the assignment to 144 # fd_out|err can lead to reference errors here. 145 os.close(fd_out) 146 os.close(fd_err) 147 out = file(outname).read() 148 errors = file(errname).read() 149 CheckedUnlink(outname) 150 CheckedUnlink(errname) 151 return output.Output(exit_code, timed_out, out, errors) 152