X-Git-Url: http://info.iut-bm.univ-fcomte.fr/pub/gitweb/simgrid.git/blobdiff_plain/a001b13d7aa269a90f035f58c71778c6812b07f6..4e2035d7b4730d535ebd7619c4e85878f71f8cb2:/tools/tesh/tesh.py diff --git a/tools/tesh/tesh.py b/tools/tesh/tesh.py index fd5b08836a..2cd1dce335 100755 --- a/tools/tesh/tesh.py +++ b/tools/tesh/tesh.py @@ -39,8 +39,6 @@ if sys.version_info[0] == 3: else: raise "This program is expected to run with Python3 only" - - ############## # # Utilities @@ -65,7 +63,7 @@ SIGNALS_TO_NAMES_DICT = dict((getattr(signal, n), n) \ #exit correctly -def exit(errcode): +def tesh_exit(errcode): #If you do not flush some prints are skipped sys.stdout.flush() #os._exit exit even when executed within a thread @@ -74,7 +72,7 @@ def exit(errcode): def fatal_error(msg): print("[Tesh/CRITICAL] "+str(msg)) - exit(1) + tesh_exit(1) #Set an environment variable. @@ -98,6 +96,30 @@ except NameError: #py2 FileNotFoundError = OSError +############## +# +# Cleanup on signal +# +# + +# Global variable. Stores which process group should be killed (or None otherwise) +pgtokill = None + +def kill_process_group(pgid): + # print("Kill process group {}".format(pgid)) + try: + os.killpg(pgid, signal.SIGTERM) + except OSError: + # os.killpg failed. OK. Some subprocesses may still be running. + pass + +def signal_handler(signal, frame): + print("Caught signal {}".format(SIGNALS_TO_NAMES_DICT[signal])) + if pgtokill is not None: + kill_process_group(pgtokill) + tesh_exit(5) + + ############## # @@ -224,7 +246,7 @@ class Cmd(object): except FileNotFoundError: print("Chdir to "+args[1]+" failed: No such file or directory") print("Test suite `"+FileReader().filename+"': NOK (system error)") - exit(4) + tesh_exit(4) #Run the Cmd if possible. @@ -289,23 +311,31 @@ class Cmd(object): args = shlex.split(self.args) #print (args) + global pgtokill + try: - proc = subprocess.Popen(args, bufsize=1, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) + proc = subprocess.Popen(args, bufsize=1, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, start_new_session=True) + try: + pgtokill = os.getpgid(proc.pid) + except OSError: + # os.getpgid failed. OK. No cleanup. + pass except FileNotFoundError: print("["+FileReader().filename+":"+str(self.linenumber)+"] Cannot start '"+args[0]+"': File not found") - exit(3) + tesh_exit(3) except OSError as osE: if osE.errno == 8: - osE.strerror += "\nOSError: [Errno 8] Executed scripts should start with shebang line (like #!/bin/sh)" + osE.strerror += "\nOSError: [Errno 8] Executed scripts should start with shebang line (like #!/usr/bin/env sh)" raise osE cmdName = FileReader().filename+":"+str(self.linenumber) try: (stdout_data, stderr_data) = proc.communicate("\n".join(self.input_pipe), self.timeout) + pgtokill = None except subprocess.TimeoutExpired: print("Test suite `"+FileReader().filename+"': NOK (<"+cmdName+"> timeout after "+str(self.timeout)+" sec)") - proc.kill() - exit(3) + kill_process_group(pgtokill) + tesh_exit(3) if self.output_display: print(stdout_data) @@ -363,7 +393,7 @@ class Cmd(object): f.write("> "+line+"\n") f.close() print("Obtained output kept as requested: "+os.path.abspath("obtained")) - exit(2) + tesh_exit(2) #print ((proc.returncode, self.expect_return)) @@ -371,11 +401,11 @@ class Cmd(object): if proc.returncode >= 0: print("Test suite `"+FileReader().filename+"': NOK (<"+cmdName+"> returned code "+str(proc.returncode)+")") if lock is not None: lock.release() - exit(2) + tesh_exit(2) else: print("Test suite `"+FileReader().filename+"': NOK (<"+cmdName+"> got signal "+SIGNALS_TO_NAMES_DICT[-proc.returncode]+")") if lock is not None: lock.release() - exit(-proc.returncode) + tesh_exit(-proc.returncode) if lock is not None: lock.release() @@ -396,6 +426,8 @@ class Cmd(object): if __name__ == '__main__': + signal.signal(signal.SIGINT, signal_handler) + signal.signal(signal.SIGTERM, signal_handler) parser = argparse.ArgumentParser(description='tesh -- testing shell', add_help=True) group1 = parser.add_argument_group('Options') @@ -410,27 +442,32 @@ if __name__ == '__main__': try: options = parser.parse_args() - except: - exit(1) + except SystemExit: + tesh_exit(1) if options.cd is not None: + print("[Tesh/INFO] change directory to " + options.cd) os.chdir(options.cd) if options.ignore_jenkins: print("Ignore all cruft seen on SimGrid's continous integration servers") # Note: regexps should match at the beginning of lines TeshState().ignore_regexps_common = [ - re.compile("profiling:"), - re.compile("Unable to clean temporary file C:"), - re.compile(".*Configuration change: Set \'contexts/"), - re.compile("Picked up JAVA_TOOL_OPTIONS: "), - re.compile("Picked up _JAVA_OPTIONS: "), - re.compile("==[0-9]+== ?WARNING: ASan doesn\'t fully support"), - re.compile("==[0-9]+== ?WARNING: ASan is ignoring requested __asan_handle_no_return: stack top:"), - re.compile("False positive error reports may follow"), - re.compile("For details see http://code.google.com/p/address-sanitizer/issues/detail\\?id=189"), - re.compile("For details see https://github.com/google/sanitizers/issues/189"), - re.compile("Python runtime initialized with LC_CTYPE=C .*"), + re.compile(r"profiling:"), + re.compile(r"Unable to clean temporary file C:"), + re.compile(r".*Configuration change: Set 'contexts/"), + re.compile(r"Picked up JAVA_TOOL_OPTIONS: "), + re.compile(r"Picked up _JAVA_OPTIONS: "), + re.compile(r"==[0-9]+== ?WARNING: ASan doesn't fully support"), + re.compile(r"==[0-9]+== ?WARNING: ASan is ignoring requested __asan_handle_no_return: stack top:"), + re.compile(r"False positive error reports may follow"), + re.compile(r"For details see http://code.google.com/p/address-sanitizer/issues/detail\?id=189"), + re.compile(r"For details see https://github.com/google/sanitizers/issues/189"), + re.compile(r"Python runtime initialized with LC_CTYPE=C .*"), + re.compile(r"cmake: /usr/local/lib/libcurl\.so\.4: no version information available \(required by cmake\)"), # Seen on CircleCI + re.compile(r".*mmap broken on FreeBSD, but dlopen\+thread broken too. Switching to dlopen\+raw contexts\."), + re.compile(r".*dlopen\+thread broken on Apple and BSD\. Switching to raw contexts\."), + re.compile(r"Sanitizers don.t like dlopen, switching to mmap privatization instead\."), ] TeshState().jenkins = True # This is a Jenkins build @@ -440,7 +477,7 @@ if __name__ == '__main__': else: if not os.path.isfile(options.teshfile): print("Cannot open teshfile '"+options.teshfile+"': File not found") - exit(3) + tesh_exit(3) f = FileReader(options.teshfile) print("Test suite '"+f.abspath+"'")