Home | History | Annotate | Download | only in local
      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