Scilab cli: add a "--timeout delay" argument 06/18106/7
Clément DAVID [Fri, 22 Apr 2016 10:42:26 +0000 (12:42 +0200)]
This argument will kill the current Scilab process after the provided
delay. It is used inside test_run() to implement a 15min watchdog on each
test.

Change-Id: Ic30b8370b011cf4ebb39525d3b195fe68ff21e27

15 files changed:
scilab/bin/scilab
scilab/modules/core/includes/InitScilab.h
scilab/modules/core/includes/signal_mgmt.h
scilab/modules/core/src/c/signal_mgmt.c
scilab/modules/core/src/cpp/InitScilab.cpp
scilab/modules/development_tools/help/en_US/test_run.xml
scilab/modules/development_tools/help/fr_FR/test_run.xml
scilab/modules/development_tools/help/ja_JP/test_run.xml
scilab/modules/development_tools/macros/test_run.sci
scilab/modules/startup/src/cpp/scilab.cpp
scilab/modules/windows_tools/includes/SignalManagement.h [new file with mode: 0644]
scilab/modules/windows_tools/src/c/scilab_windows_Import.def
scilab/modules/windows_tools/src/c/windows_tools.vcxproj
scilab/modules/windows_tools/src/c/windows_tools.vcxproj.filters
scilab/modules/windows_tools/src/cpp/SignalManagement.cpp [new file with mode: 0644]

index 1a698d6..d19c8f5 100755 (executable)
@@ -1036,6 +1036,7 @@ do_help()
     echo     "  --no-exec        : Only do Lexing/parsing do not execute instructions."
     echo     "  --context-dump   : Display context status."
     echo     "  --exec-verbose   : Display command before execute it."
+    echo     "  --timeout delay  : Kill the Scilab process after a delay (s, m, h, d)."
     echo     " "
     echo     "      All these arguments can be retrieved by the Scilab function sciargs."
     echo     " "
@@ -1105,6 +1106,10 @@ if test "$rest" = "yes"; then
             --parse-trace|--pretty-print|--help|--AST-trace|--no-exec|--context-dump|--exec-verbose|--timed|--AST-timed|--serialize|-quit)
            #This are Scilab6 dedicated options.
                    ;;
+            --timeout)
+           #This are Scilab6 dedicated options with an argument
+                prevarg=1
+               ;;
             -psn_*)
             # Under Mac OS X, .app is adding the arg -psn_0_xxxx
             # (psn = ProcessSerialNumber). It is not used (but could be in the future)
index f0d2007..88fce67 100644 (file)
@@ -46,6 +46,7 @@ typedef struct
     int isPrioritary;
     int iStartConsoleThread;
     int iForceQuit;
+    int iTimeoutDelay;
     int iCodeAction;
     enum command_origin_t iCommandOrigin;
 } ScilabEngineInfo;
index 9fa07ab..febc1d3 100644 (file)
 #ifndef __SIGNAL_MGMT_H__
 #define __SIGNAL_MGMT_H__
 
-
 /**
  * Initialize the signal management system
  */
 void base_error_init(void);
 
+/**
+ * Install a timeout on the process (watchdog behavior)
+ * @param timeoutDelay delay in seconds (0 will disable the timeout)
+ */
+void timeout_process_after(int timeoutDelay);
+
 #endif /* __SIGNAL_MGMT_H__ */
index 7ddfdfe..503f88a 100644 (file)
@@ -1,6 +1,7 @@
 /*
   Copyright (C) 2006  EDF - Code Saturne
   Copyright (C) 2001 - DIGITEO - Sylvestre LEDRU. Adapted for Scilab
+  Copyright (C) 2016 - Scilab Enterprises - Clement DAVID
 
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
@@ -22,6 +23,7 @@
 #include <stdio.h>
 #include <stdarg.h>
 #include <signal.h>
+#include <time.h>
 #include <string.h>
 #include <libintl.h>
 
@@ -484,4 +486,25 @@ void base_error_init(void)
     }
 }
 
+void timeout_process_after(int timeoutDelay)
+{
+    struct sigevent event_timer;
+    timer_t timerid;
+    struct itimerspec value;
+
+    /*
+     * Send a SIGABRT to ensure process termination, if used with the signal
+     * trap a backtrace might be displayed.
+     */
+    memset(&event_timer, 0, sizeof(struct sigevent));
+    event_timer.sigev_notify = SIGEV_SIGNAL;
+    event_timer.sigev_signo = SIGABRT;
+
+    timer_create(CLOCK_MONOTONIC, &event_timer, &timerid);
+
+    memset(&value, 0, sizeof(struct itimerspec));
+    value.it_value.tv_sec = timeoutDelay;
+    timer_settime(timerid, 0, &value, NULL);
+}
+
 /*--------------------------------------------------------------------------*/
index c151e42..dd94be3 100644 (file)
@@ -77,6 +77,7 @@ extern "C"
 #include "InnosetupMutex.h"
 #include "MutexClosingScilab.h"
 #include "WinConsole.h"
+#include "SignalManagement.h"
 #else
 #include "signal_mgmt.h"
 #include "initConsoleMode.h"
@@ -133,6 +134,7 @@ ScilabEngineInfo* InitScilabEngineInfo()
     pSEI->isPrioritary = 0;         // by default all thread are non-prioritary
     pSEI->iStartConsoleThread = 1;  // used in call_scilab to avoid "prompt" thread execution
     pSEI->iForceQuit = 0;           // management of -quit argument
+    pSEI->iTimeoutDelay = 0;        // watchdog delay to avoid deadlocking tests
     pSEI->iCommandOrigin = NONE;
 
     pSEI->iCodeAction = -1; //default value, no code action ( used on windows by file associations -O -X -P arguments)
@@ -154,6 +156,12 @@ int StartScilabEngine(ScilabEngineInfo* _pSEI)
     _pSEI->iForceQuit = _pSEI->iForceQuit && (_pSEI->pstExec || _pSEI->pstFile);
     ConfigVariable::setForceQuit(_pSEI->iForceQuit == 1);
 
+    // setup timeout delay
+    if (_pSEI->iTimeoutDelay != 0)
+    {
+        timeout_process_after(_pSEI->iTimeoutDelay);
+    }
+
     /* This bug only occurs under Linux 32 bits
      * See: http://wiki.scilab.org/Scilab_precision
      */
index 6d99cdc..48384d2 100644 (file)
@@ -460,12 +460,12 @@ test_run([],[],['no_check_ref','mode_nw']);
 // Console mode
 test_run time [] no_check_ref //tests time module with no_check_ref option
  ]]></programlisting>
-
+        
         <programlisting role="example"><![CDATA[
 // Run unitary tests of an external module (with his path)
 test_run('SCI/contrib/toolbox_skeleton')
  ]]></programlisting>
-
+        
         <programlisting role="example"><![CDATA[
 // Export to a XML Xunit file
 test_run('boolean',[],[],TMPDIR+"/boolean_test_run.xml");
@@ -486,6 +486,11 @@ test_run('time','datenum',[],TMPDIR+"/time_datenum_test_run.xml");
             into the .dia file, so that the user can have a log file once the test
             is performed.
         </para>
+        <para>
+            An execution timeout delay (watchdog timer) is setup to 5 minutes
+            for each regular test. To ignore this timeout use the long-time
+            execution (<literal>LONG TIME EXECUTION</literal>) flag.
+        </para>
     </refsection>
     <refsection>
         <title>History</title>
@@ -520,7 +525,12 @@ test_run('time','datenum',[],TMPDIR+"/time_datenum_test_run.xml");
             </revision>
             <revision>
                 <revnumber>6.0.0</revnumber>
-                <revdescription>profiling mode added to profile execution with valgrind (Linux only)</revdescription>
+                <revdescription>
+                    <para>profiling mode added to profile execution with valgrind (Linux only)</para>
+                    <para>
+                        timeout delay (watchdog timer) set to 5 minutes for single tests without <literal>LONG TIME EXECUTION</literal>
+                    </para>
+                </revdescription>
             </revision>
         </revhistory>
     </refsection>
index 3fd24e0..4944473 100644 (file)
@@ -448,12 +448,12 @@ test_run([],[],['no_check_ref','mode_nw']);
 // Console mode
 test_run time [] no_check_ref //tests time module with no_check_ref option
  ]]></programlisting>
-
+        
         <programlisting role="example"><![CDATA[
 // lance les tests d'un module externe
 test_run('SCI/contrib/toolbox_skeleton')
  ]]></programlisting>
-
+        
         <programlisting role="example"><![CDATA[
 // Export XML Xunit
 test_run('boolean',[],[],TMPDIR+"/boolean_test_run.xml");
@@ -469,6 +469,11 @@ test_run('time','datenum',[],TMPDIR+"/time_datenum_test_run.xml");
             Les scripts de tests ne sont pas exécutés tels qu'écrit, un en-tête et un pied de page spécifiques sont rajoutés à chaque test.
             Le but est d'instrumenter le fichier de tests afin de rediriger les sorties dans un fichier de log spécique au test.
         </para>
+        <para>
+            La durée d'exécution pour chaque test est fixé à 5 minutes. Pour
+            désactiver la terminaison du test après ce délai, utilisez le tag
+            <literal>LONG TIME EXECUTION</literal>.
+        </para>
     </refsection>
     <refsection>
         <title>Historique</title>
@@ -503,7 +508,12 @@ test_run('time','datenum',[],TMPDIR+"/time_datenum_test_run.xml");
             </revision>
             <revision>
                 <revnumber>6.0.0</revnumber>
-                <revdescription>mode profiling ajouté pour permettre l'analyse du profil d'exécution avec valgrind (Linux uniquement)</revdescription>
+                <revdescription>
+                    <para>mode profiling ajouté pour permettre l'analyse du profil d'exécution avec valgrind (Linux uniquement)</para>
+                    <para>
+                        durée d'exécution maximale d'un test sans <literal>LONG TIME EXECUTION</literal> configurée à 5 minutes.
+                    </para>
+                </revdescription>
             </revision>
         </revhistory>
     </refsection>
index ab47d2d..f4cca92 100644 (file)
@@ -469,6 +469,11 @@ test_run('time','datenum',[],TMPDIR+"/time_datenum_test_run.xml");
             テストが実行された後に,ユーザがログファイルを取得できるように
             するためです.
         </para>
+        <para>
+            An execution timeout delay (watchdog timer) is setup to 5 minutes
+            for each regular test. To ignore this timeout use the long-time
+            execution (<literal>LONG TIME EXECUTION</literal>) flag.
+        </para>
     </refsection>
     <refsection>
         <title>履歴</title>
@@ -501,7 +506,12 @@ test_run('time','datenum',[],TMPDIR+"/time_datenum_test_run.xml");
                 <revnumber>5.5.0</revnumber>
                 <revdescription>32/64bits separation available</revdescription>
                 <revnumber>6.0.0</revnumber>
-                <revdescription>profiling mode added to profile execution with valgrind (Linux only)</revdescription>
+                <revdescription>
+                    <para>profiling mode added to profile execution with valgrind (Linux only)</para>
+                    <para>
+                        timeout delay (watchdog timer) set to 5 minutes for single tests without <literal>LONG TIME EXECUTION</literal>
+                    </para>
+                </revdescription>
             </revision>
         </revhistory>
     </refsection>
index a48518e..a52747c 100644 (file)
@@ -834,18 +834,23 @@ function status = test_single(_module, _testPath, _testName)
 
     loader_path = pathconvert(fullfile(_module.moduleName, "loader.sce"), %f);
 
+    SCI_ARGS = " -nb -quit "
+    if ~_module.longtime then
+        SCI_ARGS = SCI_ARGS + "--timeout 5m "
+    end
+
     // Build final command
     if getos() == "Windows" then
         if (isdir(_module.moduleName) & isfile(loader_path)) // external module not in Scilab
-            test_cmd = "( """ + SCI_BIN + "\bin\" + winbin + """" + " " + mode_arg + " " + language_arg + " -nb -quit -e ""exec(""""" + loader_path + """"");exec(""""" + tmp_tst + """"", -1);"" > """ + tmp_res + """ ) 2> """ + tmp_err + """";
+            test_cmd = "( """ + SCI_BIN + "\bin\" + winbin + """" + " " + mode_arg + " " + language_arg + SCI_ARGS + "-e ""exec(""""" + loader_path + """"");exec(""""" + tmp_tst + """"", -1);"" > """ + tmp_res + """ ) 2> """ + tmp_err + """";
         else // standard module
-            test_cmd = "( """ + SCI_BIN + "\bin\" + winbin + """" + " " + mode_arg + " " + language_arg + " -nb -quit -e ""exec(""""" + tmp_tst + """"", -1);"" > """ + tmp_res + """ ) 2> """ + tmp_err + """";
+            test_cmd = "( """ + SCI_BIN + "\bin\" + winbin + """" + " " + mode_arg + " " + language_arg + SCI_ARGS + "-e ""exec(""""" + tmp_tst + """"", -1);"" > """ + tmp_res + """ ) 2> """ + tmp_err + """";
         end
     else
         if (isdir(_module.moduleName) & isfile(loader_path))
-            test_cmd = "( " + valgrind_opt + " " + SCI_BIN + "/bin/scilab " + mode_arg + " " + language_arg + " -nb -quit -e ""exec(''" + loader_path + "'');exec(''" + tmp_tst +"'');""" + " > " + tmp_res + " ) 2> " + tmp_err;
+            test_cmd = "( " + valgrind_opt + " " + SCI_BIN + "/bin/scilab " + mode_arg + " " + language_arg + SCI_ARGS + "-e ""exec(''" + loader_path + "'');exec(''" + tmp_tst +"'');""" + " > " + tmp_res + " ) 2> " + tmp_err;
         else
-            test_cmd = "( " + valgrind_opt + " " + prefix_bin + " " + SCI_BIN + "/bin/scilab " + mode_arg + " " + language_arg + " -nb -quit -f " + tmp_tst + " > " + tmp_res + " ) 2> " + tmp_err;
+            test_cmd = "( " + valgrind_opt + " " + prefix_bin + " " + SCI_BIN + "/bin/scilab " + mode_arg + " " + language_arg + SCI_ARGS + " -f " + tmp_tst + " > " + tmp_res + " ) 2> " + tmp_err;
         end
     end
 
index 95fe7df..18145dc 100644 (file)
@@ -95,6 +95,7 @@ static void usage(void)
     std::cerr << "      --no-exec        : Only do Lexing/parsing do not execute instructions." << std::endl;
     std::cerr << "      --context-dump   : Display context status." << std::endl;
     std::cerr << "      --exec-verbose   : Display command before running it." << std::endl;
+    std::cerr << "      --timeout delay  : Kill the Scilab process after a delay." << std::endl;
 }
 
 /*
@@ -243,6 +244,49 @@ static int get_option(const int argc, char *argv[], ScilabEngineInfo* _pSEI)
         {
             _pSEI->iExecVerbose = 1;
         }
+        else if (!strcmp("--timeout", argv[i]))
+        {
+            i++;
+            if (argc > i)
+            {
+                char* timeout = argv[i];
+
+                char* str_end = NULL;
+                int iTimeoutDelay = strtol(timeout, &str_end, 0);
+
+                int modifier = 1;
+                if (str_end != '\0')
+                {
+                    switch (*str_end)
+                    {
+                        case 'd':
+                            modifier = 86400;
+                            break;
+                        case 'h':
+                            modifier = 3600;
+                            break;
+                        case 'm':
+                            modifier = 60;
+                            break;
+                        case 's':
+                            modifier = 1;
+                            break;
+                        default:
+                            std::cerr << "Invalid timeout delay unit: s (for seconds), m (for minutes), h (for hours), d (for days) are supported" << std::endl;
+                            exit(EXIT_FAILURE);
+                            break;
+                    }
+                }
+
+                _pSEI->iTimeoutDelay = iTimeoutDelay * modifier;
+            }
+            else
+            {
+                std::cerr << "Unspecified timeout delay" << std::endl;
+                exit(EXIT_FAILURE);
+            }
+
+        }
         else if (!strcmp("-keepconsole", argv[i]))
         {
             _pSEI->iKeepConsole = 1;
diff --git a/scilab/modules/windows_tools/includes/SignalManagement.h b/scilab/modules/windows_tools/includes/SignalManagement.h
new file mode 100644 (file)
index 0000000..1be5201
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
+ * Copyright (C) 2016 - 2016 - Scilab Enterprises - Clement DAVID
+ *
+ * Copyright (C) 2012 - 2016 - Scilab Enterprises
+ *
+ * This file is hereby licensed under the terms of the GNU GPL v2.0,
+ * pursuant to article 5.3.4 of the CeCILL v.2.1.
+ * This file was originally licensed under the terms of the CeCILL v2.1,
+ * and continues to be available under such terms.
+ * For more information, see the COPYING file which you should have received
+ * along with this program.
+ *
+ */
+
+#ifndef __SIGNAL_MGMT_H__
+#define __SIGNAL_MGMT_H__
+
+#include "dynlib_windows_tools.h"
+
+/**
+ * Install a timeout on the process (watchdog behavior)
+ * @param timeoutDelay delay in seconds (0 will disable the timeout)
+ */
+WINDOWS_TOOLS_IMPEXP void timeout_process_after(int timeoutDelay);
+
+#endif /* __SIGNAL_MGMT_H__ */
index 6c84388..efe662d 100644 (file)
@@ -262,6 +262,7 @@ lib /DEF:"$(ProjectDir)Call_scilab_Import.def" /SUBSYSTEM:WINDOWS /MACHINE:$(Pla
     <ClCompile Include="FindFileAssociation.c" />
     <ClCompile Include="httpdownloadfile.c" />
     <ClCompile Include="InitializeWindows_tools.c" />
+    <ClCompile Include="..\cpp\SignalManagement.cpp" />
     <ClCompile Include="TerminateWindows_tools.c" />
     <ClCompile Include="WinConsole.c" />
     <ClCompile Include="..\cpp\windows_tools_gw.cpp" />
@@ -318,6 +319,7 @@ lib /DEF:"$(ProjectDir)Call_scilab_Import.def" /SUBSYSTEM:WINDOWS /MACHINE:$(Pla
     <ClInclude Include="..\..\includes\gw_windows_tools.h" />
     <ClInclude Include="..\..\includes\InitializeWindows_tools.h" />
     <ClInclude Include="..\..\includes\MutexClosingScilab.h" />
+    <ClInclude Include="..\..\includes\SignalManagement.h" />
     <ClInclude Include="..\..\includes\TerminateWindows_tools.h" />
     <ClInclude Include="..\..\includes\windows_tools_gw.hxx" />
     <ClInclude Include="..\cpp\registry.hxx" />
@@ -331,4 +333,4 @@ lib /DEF:"$(ProjectDir)Call_scilab_Import.def" /SUBSYSTEM:WINDOWS /MACHINE:$(Pla
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
\ No newline at end of file
+</Project>
index ce5a06a..926aa66 100644 (file)
@@ -38,6 +38,9 @@
     <ClCompile Include="InitializeWindows_tools.c">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\cpp\SignalManagement.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="TerminateWindows_tools.c">
       <Filter>Source Files</Filter>
     </ClCompile>
     <ClInclude Include="..\..\includes\MutexClosingScilab.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\includes\SignalManagement.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\includes\TerminateWindows_tools.h">
       <Filter>Header Files</Filter>
     </ClInclude>
     <Library Include="..\..\..\..\bin\blasplus.lib" />
     <Library Include="..\..\..\..\bin\lapack.lib" />
   </ItemGroup>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/scilab/modules/windows_tools/src/cpp/SignalManagement.cpp b/scilab/modules/windows_tools/src/cpp/SignalManagement.cpp
new file mode 100644 (file)
index 0000000..82c27e6
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
+ * Copyright (C) 2016 - 2016 - Scilab Enterprises - Clement DAVID
+ *
+ * Copyright (C) 2012 - 2016 - Scilab Enterprises
+ *
+ * This file is hereby licensed under the terms of the GNU GPL v2.0,
+ * pursuant to article 5.3.4 of the CeCILL v.2.1.
+ * This file was originally licensed under the terms of the CeCILL v2.1,
+ * and continues to be available under such terms.
+ * For more information, see the COPYING file which you should have received
+ * along with this program.
+ *
+ */
+
+#include <windows.h>
+#include <iostream>
+
+extern "C"
+{
+#include <SignalManagement.h>
+}
+
+static void kill_process_callback(TP_CALLBACK_INSTANCE*, void*, TP_TIMER*);
+
+void timeout_process_after(int timeoutDelay)
+{
+    auto timerid = CreateThreadpoolTimer(kill_process_callback, nullptr, nullptr);
+
+    FILETIME FileDueTime;
+    ULARGE_INTEGER ulDueTime;
+
+    // Set the timer to fire in the delay in seconds
+    ulDueTime.QuadPart = (ULONGLONG) - (timeoutDelay * 10 * 1000 * 1000);
+    FileDueTime.dwHighDateTime = ulDueTime.HighPart;
+    FileDueTime.dwLowDateTime  = ulDueTime.LowPart;
+
+    SetThreadpoolTimer(timerid, &FileDueTime, 0, 0);
+}
+
+static void kill_process_callback(TP_CALLBACK_INSTANCE*, void*, TP_TIMER*)
+{
+    std::cerr << "Watchdog timer expired: Scilab killed" << std::endl;
+    ExitProcess(1);
+}
+