Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
stop being a stupidly rushing boar (hopefuly)
[simgrid.git] / tools / tesh / tesh.py
index bec4533..0db9022 100755 (executable)
@@ -79,14 +79,24 @@ def fatal_error(msg):
     print("[Tesh/CRITICAL] " + str(msg))
     tesh_exit(1)
 
+# retrocompatibility: support ${aaa:=.} variable format
+def replace_perl_variables(arg):
+    vname = arg.group(1)
+    vdefault = arg.group(2)
+    if vname in os.environ:
+        return "$" + vname
+    return vdefault
 
 def setenv(arg):
     """
     Set an environment variable.
     arg must be a string with the format "variable=value"
     """
-    print("[Tesh/INFO] setenv " + arg)
+    if '$' in arg:
+        arg = re.sub(r"\${(\w+):=([^}]*)}", replace_perl_variables, arg)
+        arg = expandvars2(arg)
     (var, val) = arg.split("=", 1)
+    print("[Tesh/INFO] setenv " + var + "=" + val)
     os.environ[var] = val
     # os.putenv(var, val) does not work
     # see http://stackoverflow.com/questions/17705419/python-os-environ-os-putenv-usr-bin-env
@@ -196,6 +206,7 @@ class TeshState(Singleton):
         self.args_suffix = ""
         self.ignore_regexps_common = []
         self.jenkins = False  # not a Jenkins run by default
+        self.auto_valgrind = True
         self.timeout = 10  # default value: 10 sec
         self.wrapper = None
         self.keep = False
@@ -237,6 +248,7 @@ class Cmd:
         self.output_display = False
 
         self.sort = -1
+        self.rerun_with_valgrind = False
 
         self.ignore_regexps = TeshState().ignore_regexps_common
 
@@ -302,20 +314,21 @@ class Cmd:
             _thread.start_new_thread(Cmd._run, (self, lock))
         else:
             self._run()
+            if self.rerun_with_valgrind and TeshState().auto_valgrind:
+                print('\n\n\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')
+                print(      'XXXXXXXXX Rerunning this test with valgrind to help debugging it XXXXXXXXX')
+                print(      'XXXXXXXX (this will fail if valgrind is not installed, of course) XXXXXXXX')
+                print(      'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n\n\n')
+
+                self.args = "valgrind " + self.args
+                self._run()
         return True
 
+
     def _run(self, lock=None):
         # Python threads loose the cwd
         os.chdir(self.cwd)
 
-        # retrocompatibility: support ${aaa:=.} variable format
-        def replace_perl_variables(arg):
-            vname = arg.group(1)
-            vdefault = arg.group(2)
-            if vname in os.environ:
-                return "$" + vname
-            return vdefault
-
         self.args = re.sub(r"\${(\w+):=([^}]*)}", replace_perl_variables, self.args)
 
         # replace bash environment variables ($THINGS) to their values
@@ -344,8 +357,11 @@ class Cmd:
         self.args += TeshState().args_suffix
 
         logs = list()
-        logs.append("[{file}:{number}] {args}".format(file=FileReader().filename,
-                                                      number=self.linenumber, args=self.args))
+        msg = "[{file}:{number}] {args}".format(file=FileReader().filename, number=self.linenumber, args=self.args)
+        if self.background:
+            logs.append(msg)
+        else:
+            print(msg, flush=True)
 
         args = shlex.split(self.args)
 
@@ -411,13 +427,17 @@ class Cmd:
                 print('\n'.join(logs))
                 return
 
-        if self.output_display:
-            logs.append(str(stdout_data))
-
         # remove text colors
         ansi_escape = re.compile(r'\x1b[^m]*m')
         stdout_data = ansi_escape.sub('', stdout_data)
 
+        if self.output_display:
+            logs.append(str(stdout_data))
+
+        if self.rerun_with_valgrind:
+            print(str(stdout_data), file=sys.stderr)
+            return
+
         if self.ignore_output:
             logs.append("(ignoring the output of <{cmd}> as requested)".format(cmd=cmd_name))
         else:
@@ -470,6 +490,8 @@ class Cmd:
                     else:
                         logs.append("In addition, <{cmd}> got signal {sig}.".format(cmd=cmd_name,
                             sig=SIGNALS_TO_NAMES_DICT[-proc.returncode]))
+                    if proc.returncode == -signal.SIGSEGV:
+                        self.rerun_with_valgrind = True
 
                 if lock is not None:
                     lock.release()
@@ -505,6 +527,10 @@ class Cmd:
             logs.append("Test suite `{file}': NOK (<{cmd}> got signal {sig})".format(
                 file=FileReader().filename, cmd=cmd_name,
                 sig=SIGNALS_TO_NAMES_DICT[-proc.returncode]))
+
+            if proc.returncode == -signal.SIGSEGV:
+                self.rerun_with_valgrind = True
+
             if lock is not None:
                 lock.release()
             TeshState().set_return_code(max(-proc.returncode, 1))
@@ -545,6 +571,10 @@ def main():
         '--ignore-jenkins',
         action='store_true',
         help='ignore all cruft generated on SimGrid continuous integration servers')
+    group1.add_argument(
+        '--no-auto-valgrind',
+        action='store_true',
+        help='do not automaticall launch segfaulting commands in valgrind')
     group1.add_argument('--wrapper', metavar='arg', help='Run each command in the provided wrapper (eg valgrind)')
     group1.add_argument(
         '--keep',
@@ -570,6 +600,7 @@ def main():
             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"sthread is intercepting the execution of \.*"),
             # Seen on CircleCI
             re.compile(r"cmake: /usr/local/lib/libcurl\.so\.4: no version information available \(required by cmake\)"),
             re.compile(
@@ -578,6 +609,9 @@ def main():
         ]
         TeshState().jenkins = True  # This is a Jenkins build
 
+    if options.no_auto_valgrind:
+        TeshState().auto_valgrind = False
+
     if options.teshfile is None:
         file = FileReader(None)
         print("Test suite from stdin")
@@ -652,7 +686,10 @@ def main():
             cmd.output_display = True
             cmd.ignore_output = True
         elif line[0:15] == "! expect return":
-            cmd.expect_return = [int(line[16:])]
+            try:
+                cmd.expect_return = [int(line[16:])]
+            except ValueError as err:
+                fatal_error("Invalid expect return value: \""+(line[16:])+"\"")
             #print("expect return "+str(int(line[16:])))
         elif line[0:15] == "! expect signal":
             cmd.expect_return = []
@@ -667,11 +704,17 @@ def main():
             if "no" in line[len("! timeout "):]:
                 cmd.timeout = None
             else:
-                cmd.timeout = int(line[len("! timeout "):])
+                try:
+                    cmd.timeout = int(line[len("! timeout "):])
+                except ValueError as err:
+                    fatal_error("Invalid timeout value: \""+(line[len("! timeout "):])+"\"")
 
         elif line[0:len("! output sort")] == "! output sort":
             if len(line) >= len("! output sort "):
-                sort = int(line[len("! output sort "):])
+                try:
+                    sort = int(line[len("! output sort "):])
+                except ValueError as err:
+                    fatal_error("Invalid sort value: \""+(line[len("! output sort "):])+"\"")
             else:
                 sort = 0
             cmd.sort = sort
@@ -682,7 +725,16 @@ def main():
             cmd.add_ignore(line[len("! ignore "):])
 
         else:
-            fatal_error(f"UNRECOGNIZED OPTION LINE: {line}")
+            fatal_error(f"UNRECOGNIZED OPTION LINE: {line}\n"
+            "Valid requests:\n"
+            "   ! output ignore\n"
+            "   ! output sort\n"
+            "   ! output display\n"
+            "   ! setenv XX=YY\n"
+            "   ! ignore XYZ\n"
+            "   ! expect return NN\n"
+            "   ! expect signal NN\n"
+            "   ! timeout NN\n")
 
         line = file.readfullline()