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 try: 95 while exit_code is None: 96 if (not end_time is None) and (time.time() >= end_time): 97 # Kill the process and wait for it to exit. 98 KillProcessWithID(process.pid) 99 exit_code = process.wait() 100 timed_out = True 101 else: 102 exit_code = process.poll() 103 time.sleep(sleep_time) 104 sleep_time = sleep_time * SLEEP_TIME_FACTOR 105 if sleep_time > MAX_SLEEP_TIME: 106 sleep_time = MAX_SLEEP_TIME 107 return (exit_code, timed_out) 108 except KeyboardInterrupt: 109 raise 110 111 112 def PrintError(string): 113 sys.stderr.write(string) 114 sys.stderr.write("\n") 115 116 117 def CheckedUnlink(name): 118 # On Windows, when run with -jN in parallel processes, 119 # OS often fails to unlink the temp file. Not sure why. 120 # Need to retry. 121 # Idea from https://bugs.webkit.org/attachment.cgi?id=75982&action=prettypatch 122 retry_count = 0 123 while retry_count < 30: 124 try: 125 os.unlink(name) 126 return 127 except OSError, e: 128 retry_count += 1 129 time.sleep(retry_count * 0.1) 130 PrintError("os.unlink() " + str(e)) 131 132 133 def Execute(args, verbose=False, timeout=None): 134 args = [ c for c in args if c != "" ] 135 (fd_out, outname) = tempfile.mkstemp() 136 (fd_err, errname) = tempfile.mkstemp() 137 try: 138 (exit_code, timed_out) = RunProcess( 139 verbose, 140 timeout, 141 args=args, 142 stdout=fd_out, 143 stderr=fd_err 144 ) 145 except: 146 raise 147 os.close(fd_out) 148 os.close(fd_err) 149 out = file(outname).read() 150 errors = file(errname).read() 151 CheckedUnlink(outname) 152 CheckedUnlink(errname) 153 return output.Output(exit_code, timed_out, out, errors) 154