Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
last changes of Tesh tools
[simgrid.git] / tools / tesh2 / src / runner.c
1 /*\r
2  * src/runner.c - type representing the runner.\r
3  *\r
4  * Copyright 2008,2009 Martin Quinson, Malek Cherier All right reserved. \r
5  *\r
6  * This program is free software; you can redistribute it and/or modify it \r
7  * under the terms of the license (GNU LGPL) which comes with this package.\r
8  *\r
9  * Purpose:\r
10  *              This file contains all the definitions of the functions related with\r
11  *              the tesh runner type.\r
12  *\r
13  */\r
14 #include <runner.h>\r
15 #include <units.h>\r
16 #include <unit.h>\r
17 #include <xerrno.h>\r
18 #include <variable.h>\r
19 \r
20 #include <errno.h>      /* for error code       */\r
21 #include <stdlib.h>     /* for calloc()         */\r
22 #include <stdio.h>\r
23 \r
24 #include <readline.h>\r
25 #include <explode.h>\r
26 \r
27 #ifndef WIN32\r
28 #include <sys/resource.h>\r
29 #endif\r
30 \r
31 \r
32 \r
33 \r
34 \r
35 #define _RUNNER_HASHCODE                0xFEFEAAAA      \r
36 \r
37 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh);\r
38 \r
39 #if (!defined(__BUILTIN) && defined(__CHKCMD) && !defined(WIN32))\r
40 static const char* builtin[] =\r
41 {\r
42         "alias",\r
43         "bind",\r
44         "builtin",\r
45         "caller",\r
46         "cd",\r
47         "command",\r
48         "compgen",\r
49         "complete",\r
50         "declare",\r
51         "disown",\r
52         "echo",\r
53         "enable",\r
54         "eval",\r
55         "exec",\r
56         "export",\r
57         "false",\r
58         "fc",\r
59         "function",\r
60         "getopts",\r
61         "hash",\r
62         "history",\r
63         "jobs",\r
64         "let",\r
65         "logout",\r
66         "printf",\r
67         "pwd",\r
68         "readonly",\r
69         "shift",\r
70         "shopt",\r
71         "source",\r
72         "suspend",\r
73         "test",\r
74         "time",\r
75         "times",\r
76         "trap",\r
77         "true",\r
78         "type",\r
79         "typeset",\r
80         "ulimit",\r
81         "umask",\r
82         "unalias",\r
83         "unset",\r
84         NULL\r
85 };\r
86 \r
87 #define __BUILTIN_MAX ((size_t)42)\r
88 #endif\r
89 \r
90 \r
91 # ifdef __APPLE__\r
92 /* under darwin, the environment gets added to the process at startup time. So, it's not defined at library link time, forcing us to extra tricks */\r
93 # include <crt_externs.h>\r
94 # define environ (*_NSGetEnviron())\r
95 # elif !defined(WIN32)\r
96  /* the environment, as specified by the opengroup, used to initialize the process properties */\r
97  extern char **environ;\r
98 # endif\r
99 \r
100 #ifndef WIN32\r
101 extern char**\r
102 environ;\r
103 #endif\r
104 \r
105 /* the unique tesh runner */\r
106 static runner_t\r
107 runner = NULL;\r
108 \r
109 /* wait for the tesh runner terminaison */\r
110 static void\r
111 runner_wait(void);\r
112 \r
113 static void*\r
114 runner_start_routine(void* p);\r
115 \r
116 \r
117 /* check the syntax of the tesh files if \r
118  * the check_syntax_flag is specified. Returns\r
119  * 0 if the syntax is clean.\r
120  */\r
121 /*static void\r
122 check_syntax(void);*/\r
123 \r
124 #ifdef WIN32\r
125 \r
126 static HANDLE \r
127 timer_handle = NULL;\r
128 \r
129 \r
130 static void*\r
131 runner_start_routine(void* p)\r
132 {\r
133         \r
134     LARGE_INTEGER li;\r
135 \r
136     li.QuadPart=- runner->timeout * 10000000;   /* 10000000 = 10 000 000 * 100 nanoseconds = 1 second */\r
137 \r
138     /* create the waitable timer */\r
139     timer_handle = CreateWaitableTimer(NULL, TRUE, NULL);\r
140 \r
141     /* set a timer to wait for timeout seconds */\r
142     SetWaitableTimer(timer_handle, &li, 0, NULL, NULL, 0);\r
143     \r
144     /* wait for the timer */\r
145     WaitForSingleObject(timer_handle, INFINITE);\r
146 \r
147         if(runner->waiting)\r
148         {\r
149                 exit_code = ELEADTIME;\r
150                 err_kind = 1;\r
151                 runner->timeouted = 1;\r
152                 xbt_os_sem_release(units_sem);\r
153         }\r
154 \r
155         return NULL;\r
156 }\r
157 \r
158 #else\r
159 static void*\r
160 runner_start_routine(void* p)\r
161 {\r
162         struct timespec ts;\r
163         int timeout = runner->timeout;\r
164         \r
165         \r
166         while(timeout-- && runner->waiting)\r
167         {\r
168                 ts.tv_sec = 1;\r
169                 ts.tv_nsec = 0L;\r
170 \r
171                 do\r
172                 {\r
173                         nanosleep(&ts, &ts);\r
174                 }while(EINTR == errno);\r
175         }\r
176         \r
177         if(errno)\r
178         {\r
179                 /* TODO process the error */\r
180         }\r
181         else\r
182         {\r
183                 if(runner->waiting)\r
184                 {\r
185                         exit_code = ELEADTIME;\r
186                         err_kind = 1;\r
187                         runner->timeouted = 1;\r
188                         xbt_os_sem_release(units_sem);\r
189                 }\r
190         }\r
191         \r
192         return NULL;\r
193 }\r
194 #endif\r
195 \r
196 \r
197 int\r
198 runner_init(/*int check_syntax_flag, */int timeout, fstreams_t fstreams)\r
199 {\r
200         \r
201         int i;\r
202         char* val;\r
203         char buffer[PATH_MAX + 1] = {0};\r
204 \r
205         int code;\r
206         const char* cstr;\r
207         variable_t variable;\r
208         \r
209         #if (defined(__CHKCMD) && defined(__BUILTIN) && !defined(WIN32))\r
210         FILE* s;\r
211         int n = 0;\r
212         size_t len;\r
213         char* line = NULL;\r
214         int is_blank;\r
215         #endif\r
216         \r
217         \r
218         if(runner)\r
219         {\r
220                 ERROR0("The runner is already initialized");\r
221                 return -1;\r
222         }\r
223                 \r
224         runner = xbt_new0(s_runner_t, 1);\r
225         \r
226         runner->path = NULL;\r
227         runner->builtin = NULL;\r
228         \r
229         if(!(runner->units = units_new(runner, fstreams)))\r
230         {\r
231                 free(runner);\r
232                 runner = NULL;\r
233                 return -1;\r
234         }\r
235 \r
236         runner->timeout = timeout;\r
237         runner->timeouted = 0;\r
238         runner->interrupted = 0;\r
239         runner->number_of_ended_units = 0;\r
240         runner->number_of_runned_units = 0;\r
241         runner->waiting = 0;\r
242         \r
243         runner->total_of_tests = 0;\r
244         runner->total_of_successeded_tests = 0;\r
245         runner->total_of_failed_tests = 0;\r
246         runner->total_of_interrupted_tests = 0;\r
247         \r
248         runner->total_of_units = 0;\r
249         runner->total_of_successeded_units = 0;\r
250         runner->total_of_failed_units = 0;\r
251         runner->total_of_interrupted_units = 0;\r
252         \r
253         runner->total_of_suites = 0;\r
254         runner->total_of_successeded_suites = 0;\r
255         runner->total_of_failed_suites = 0;\r
256         runner->total_of_interrupted_suites = 0;\r
257         \r
258         /* initialize the vector of variables */\r
259         runner->variables = xbt_dynar_new(sizeof(variable_t), (void_f_pvoid_t)variable_free);\r
260         \r
261         /* add the environment variables in the vector */\r
262         for(i = 0; environ[i] != NULL; i++)\r
263         {\r
264                 val = strchr(environ[i], '=');\r
265                 \r
266                 if(val)\r
267                 {\r
268                         val++;\r
269                                 \r
270                         if(val[0] != '\0')\r
271                                 strncpy(buffer, environ[i], (val - environ[i] -1));\r
272                                 \r
273                         if(!strcmp("TESH_PPID", buffer))\r
274                                 is_tesh_root = 0;\r
275                         \r
276                         variable = variable_new(buffer, val);\r
277                         variable->env = 1;\r
278                         xbt_dynar_push(runner->variables, &variable);\r
279                         \r
280                         #ifndef WIN32\r
281                         if(!strcmp("PATH", buffer))\r
282                         #else\r
283                         if(!strcmp("Path", buffer) || !strcmp("PATH", buffer))\r
284                         #endif\r
285                         {\r
286                                 char* p;\r
287                                 size_t j,k, len;\r
288                                 \r
289                                 /* get the list of paths */\r
290                                 \r
291                                 #ifdef WIN32\r
292                                 runner->path = explode(';', val);\r
293                                 #else\r
294                                 runner->path = explode(':', val);\r
295                                 #endif\r
296 \r
297                                 /* remove spaces and backslahes at the end of the path */\r
298                                 for (k = 0; runner->path[k] != NULL; k++)\r
299                                 {\r
300                                 p = runner->path[k];\r
301                                 \r
302                                 len = strlen(p);\r
303                                 \r
304                                         #ifndef WIN32\r
305                                 for(j = len - 1; p[j] == '/' || p[j] == ' '; j--)\r
306                                         #else\r
307                                         for(j = len - 1; p[j] == '\\' || p[j] == ' '; j--)\r
308                                         #endif\r
309                                         p[j] = '\0';\r
310                                 }\r
311                         }\r
312                         \r
313                                 \r
314                         memset(buffer, 0, PATH_MAX + 1);\r
315                 }\r
316         }\r
317         \r
318         if(is_tesh_root)\r
319         {\r
320                 char* tesh_dir = getcwd(NULL, 0);\r
321                 \r
322                 sprintf(buffer,"%d",getpid());\r
323                 \r
324                 #ifndef WIN32\r
325                 setenv("TESH_PPID", buffer, 0);\r
326                 setenv("TESH_DIR", tesh_dir, 0);\r
327                 #else\r
328                 SetEnvironmentVariable("TESH_PPID", buffer);\r
329                 SetEnvironmentVariable("TESH_DIR", tesh_dir);\r
330                 #endif\r
331                 \r
332                 variable = variable_new("TESH_PPID", buffer);\r
333                 variable->err = 1;\r
334                         \r
335                 xbt_dynar_push(runner->variables, &variable);\r
336 \r
337                 variable = variable_new("TESH_DIR", tesh_dir);\r
338                 variable->err = 1;\r
339                         \r
340                 xbt_dynar_push(runner->variables, &variable);\r
341                 \r
342                 free(tesh_dir);\r
343         }\r
344         \r
345         variable = variable_new("EXIT_SUCCESS", "0");\r
346         variable->err = 1;\r
347                         \r
348         xbt_dynar_push(runner->variables, &variable);\r
349 \r
350         variable = variable_new("EXIT_FAILURE", "1");\r
351         variable->err = 1;\r
352                         \r
353         xbt_dynar_push(runner->variables, &variable);\r
354 \r
355         variable = variable_new("TRUE", "0");\r
356         variable->err = 1;\r
357                         \r
358         xbt_dynar_push(runner->variables, &variable);\r
359 \r
360         variable = variable_new("FALSE", "1");\r
361         variable->err = 1;\r
362                         \r
363         xbt_dynar_push(runner->variables, &variable);\r
364 \r
365         i = 0;\r
366         \r
367         /* add the errors variables */\r
368         while((cstr = error_get_at(i++, &code)))\r
369         {\r
370                 sprintf(buffer,"%d",code);\r
371                 variable = variable_new(cstr, buffer);\r
372                 variable->err = 1;\r
373                 xbt_dynar_push(runner->variables, &variable);\r
374         }\r
375         \r
376         /* if the user want check the syntax, check it */\r
377         /*if(check_syntax_flag)\r
378                 check_syntax();\r
379         */\r
380         \r
381         #if (!defined(WIN32) && defined(__CHKCMD))\r
382         #if defined(__BUILTIN)\r
383         \r
384         if(!is_tesh_root)\r
385         {\r
386                 /* compute the full path the builtin.def file */\r
387                 sprintf(buffer,"%s/builtin.def",getenv("TESH_DIR"));\r
388                 \r
389                 if(!(s = fopen(buffer, "r")))   \r
390                 {\r
391                         ERROR1("File `(%s)' not found", buffer);\r
392                         return -1;\r
393                 }\r
394                 \r
395         }\r
396         else\r
397         {\r
398                 if(!(s = fopen("builtin.def", "r")))    \r
399                 {\r
400                         ERROR0("File `(builtin.def)' not found");\r
401                         return -1;\r
402                 }\r
403         }\r
404         \r
405         if(s)\r
406         {\r
407                 fpos_t begin;\r
408 \r
409                 fgetpos(s, &begin);\r
410 \r
411                 while(readline(s, &line, &len) != -1)\r
412                 {\r
413                         i = 0;\r
414                         is_blank = 1;\r
415                         \r
416 \r
417                         while(line[i] != '\0') \r
418                         {\r
419                                 if (line[i] != ' ' && line[i] != '\t' && line[i]!='\n' && line[i]!='\r')\r
420                                 {\r
421                                         is_blank = 0;\r
422                                         break;\r
423                                 }\r
424                                 \r
425                                 i++;\r
426                         }\r
427 \r
428                         if(!is_blank)\r
429                                 n++;\r
430                 }\r
431 \r
432                 fsetpos(s, &begin);\r
433                 free(line);\r
434                 line = NULL;\r
435 \r
436                 if(n)\r
437                 {\r
438                         char* l;\r
439                         \r
440                         runner->builtin = xbt_new0(char*, n + 1); /* (char**) calloc(n + 1, sizeof(char*));*/\r
441                         \r
442                         n = 0;\r
443                         \r
444                         while(readline(s, &line, &len) != -1)\r
445                         {\r
446                                 i = 0;\r
447                                 is_blank = 1;\r
448 \r
449                                 while(line[i] != '\0') \r
450                                 {\r
451                                         if (line[i] != ' ' && line[i] != '\t' && line[i]!='\n' && line[i]!='\r')\r
452                                         {\r
453                                                 is_blank = 0;\r
454                                                 break;\r
455                                         }\r
456                                         \r
457                                         i++;\r
458                                 }\r
459 \r
460                                 if(!is_blank)\r
461                                 {\r
462                                         l = strdup(line);\r
463 \r
464                                         l[strlen(l) - 1] = '\0';\r
465 \r
466                                         (runner->builtin)[n++] = l;\r
467                                         \r
468                                 }\r
469                         }\r
470                         \r
471                 }\r
472                 else\r
473                 {\r
474                         WARN0("The file `(builtin.def)' is empty");\r
475                         free(runner->builtin);\r
476                         runner->builtin = NULL;\r
477                 }\r
478                 \r
479 \r
480                 fclose(s);\r
481                 \r
482                 if(line)\r
483                         free(line);\r
484                 \r
485         }\r
486         \r
487         #else\r
488                 runner->builtin = xbt_new0(char*, __BUILTIN_MAX + 1); /* (char**) calloc(__BUILTIN_MAX + 1, sizeof(char*));*/\r
489                 \r
490                 for(i = 0; i < __BUILTIN_MAX; i++)\r
491                         runner->builtin[i] = strdup(builtin[i]);        \r
492         #endif\r
493         #endif\r
494 \r
495         return exit_code ? -1 : 0;\r
496 }\r
497 \r
498 void\r
499 runner_destroy(void)\r
500 {\r
501         int i;\r
502         \r
503         if(runner->units)\r
504                 units_free((void**)(&(runner->units)));\r
505         \r
506         if(runner->variables)\r
507                 xbt_dynar_free(&runner->variables);\r
508         \r
509         #ifdef WIN32\r
510         CloseHandle(timer_handle);\r
511         #endif\r
512 \r
513         if(runner->thread)\r
514                 xbt_os_thread_join(runner->thread, NULL);\r
515         \r
516         if(runner->path)\r
517         {\r
518                 for (i = 0; runner->path[i] != NULL; i++)\r
519                         free(runner->path[i]);\r
520                 \r
521                 free(runner->path);\r
522         }\r
523 \r
524         if(runner->builtin)\r
525         {\r
526                 for (i = 0; runner->builtin[i] != NULL; i++)\r
527                         free(runner->builtin[i]);\r
528                 \r
529                 free(runner->builtin);\r
530         }\r
531 \r
532         free(runner);\r
533         \r
534 \r
535         runner = NULL;\r
536 }\r
537 \r
538 void\r
539 runner_run(void)\r
540 {\r
541         /* allocate the mutex used by the units to asynchronously access \r
542          * to the properties of the runner.\r
543          */\r
544         xbt_os_mutex_t mutex = xbt_os_mutex_init();\r
545         \r
546         /* run all the units */\r
547         units_run_all(runner->units, mutex);\r
548         \r
549         \r
550         if(!interrupted)\r
551                 runner_wait();\r
552 \r
553         \r
554         /* if the runner is timeouted or receive a interruption request\r
555          * , interrupt all the active units.\r
556          */\r
557         if(runner->timeouted || interrupted)\r
558                 runner_interrupt();\r
559         \r
560         /* joins all the units */\r
561         units_join_all(runner->units);\r
562         \r
563         /* release the mutex resource */\r
564         xbt_os_mutex_destroy(mutex);\r
565 \r
566 }\r
567 \r
568 static void\r
569 runner_wait(void)\r
570 {\r
571         if(runner->timeout > 0)\r
572                 runner->thread = xbt_os_thread_create("", runner_start_routine, NULL);\r
573         \r
574         /* signal that the runner is waiting */\r
575         runner->waiting = 1;\r
576         \r
577         /* wait for the end of all the units */\r
578         xbt_os_sem_acquire(units_sem);\r
579 \r
580         \r
581         runner->waiting = 0;\r
582 }\r
583 \r
584 \r
585 \r
586 /*\r
587  * interrupt all the active units.\r
588  * this function is called when the lead time of the execution is reached\r
589  * or when a failed unit requests an interruption of the execution.\r
590  */\r
591 void\r
592 runner_interrupt(void)\r
593 {\r
594         units_interrupt_all(runner->units);\r
595 }\r
596 \r
597 void\r
598 runner_summarize(void)\r
599 {\r
600         \r
601         if(!dry_run_flag)\r
602         {\r
603                 #ifndef WIN32\r
604                 struct rusage r_usage;\r
605                 #else\r
606                 FILETIME start_time;\r
607                 FILETIME exit_time;\r
608                 FILETIME kernel_time;\r
609                 FILETIME user_time;\r
610                 SYSTEMTIME si;\r
611                 #endif\r
612                 \r
613                 printf("\n  TEst SHell utility - mini shell specialized in running test units.\n");\r
614                 printf(" =============================================================================\n");\r
615                 \r
616                 units_summuarize(runner->units);\r
617                 \r
618                 printf(" =====================================================================%s\n",\r
619                 runner->total_of_failed_tests ? "== FAILED": (runner->total_of_interrupted_tests || runner->total_of_interrupted_units) ? "==== INTR" : "====== OK");\r
620                 \r
621                 printf(" TOTAL : Suite(s): %.0f%% ok (%d suite(s): %d ok",\r
622                 (runner->total_of_suites ? (1-((double)runner->total_of_failed_suites + (double)runner->total_of_interrupted_suites)/(double)runner->total_of_suites)*100.0 : 100.0),\r
623                 runner->total_of_suites, runner->total_of_successeded_suites);\r
624                 \r
625                 if(runner->total_of_failed_suites > 0)\r
626                         printf(", %d failed", runner->total_of_failed_suites);\r
627                 \r
628                 if(runner->total_of_interrupted_suites > 0)\r
629                         printf(", %d interrupted)", runner->total_of_interrupted_suites);\r
630                 \r
631                 printf(")\n");  \r
632                 \r
633                 printf("         Unit(s):  %.0f%% ok (%d unit(s): %d ok",\r
634                 (runner->total_of_units ? (1-((double)runner->total_of_failed_units + (double)runner->total_of_interrupted_units)/(double)runner->total_of_units)*100.0 : 100.0),\r
635                 runner->total_of_units, runner->total_of_successeded_units);\r
636                 \r
637                 if(runner->total_of_failed_units > 0)\r
638                         printf(", %d failed", runner->total_of_failed_units);\r
639                 \r
640                 if(runner->total_of_interrupted_units > 0)\r
641                         printf(", %d interrupted)", runner->total_of_interrupted_units);\r
642                 \r
643                 printf(")\n");\r
644                 \r
645                 printf("         Test(s):  %.0f%% ok (%d test(s): %d ok",\r
646                 (runner->total_of_tests ? (1-((double)runner->total_of_failed_tests + (double)runner->total_of_interrupted_tests)/(double)runner->total_of_tests)*100.0 : 100.0),\r
647                 runner->total_of_tests, runner->total_of_successeded_tests);\r
648                 \r
649                 if(runner->total_of_failed_tests > 0)\r
650                         printf(", %d failed", runner->total_of_failed_tests);\r
651                 \r
652                 if(runner->total_of_interrupted_tests > 0)\r
653                         printf(", %d interrupted)", runner->total_of_interrupted_tests);\r
654                 \r
655                 printf(")\n\n");\r
656                 \r
657                 #ifndef WIN32\r
658                 if(!getrusage(RUSAGE_SELF, &r_usage))\r
659                 {\r
660                 \r
661                         printf("         Total tesh user time used:       %ld second(s) %ld microsecond(s)\n", r_usage.ru_utime.tv_sec, r_usage.ru_utime.tv_usec);\r
662                         printf("         Total tesh system time used:     %ld second(s) %ld microsecond(s)\n\n", r_usage.ru_stime.tv_sec, r_usage.ru_stime.tv_usec);\r
663                 \r
664                         if(!getrusage(RUSAGE_CHILDREN, &r_usage))\r
665                         {\r
666                                 printf("         Total children user time used:   %ld second(s) %ld microsecond(s)\n", r_usage.ru_utime.tv_sec, r_usage.ru_utime.tv_usec);\r
667                                 printf("         Total children system time used: %ld second(s) %ld microsecond(s)\n\n", r_usage.ru_stime.tv_sec, r_usage.ru_stime.tv_usec);\r
668                 \r
669                         }       \r
670                 }\r
671                 #else\r
672         \r
673                 if(GetProcessTimes(GetCurrentProcess(), &start_time, &exit_time, &kernel_time, &user_time))\r
674                 {\r
675                         FileTimeToSystemTime(&user_time, &si);\r
676                         \r
677                         printf(" User time used:   %2u Hour(s) %2u Minute(s) %2u Second(s) %3u Millisecond(s)\n", si.wHour, si.wMinute, si.wSecond, si.wMilliseconds );\r
678                         \r
679                         FileTimeToSystemTime(&kernel_time, &si);\r
680                         \r
681                         printf(" Kernel time used: %2u Hour(s) %2u Minute(s) %2u Second(s) %3u Millisecond(s)\n", si.wHour, si.wMinute, si.wSecond, si.wMilliseconds );\r
682                 }\r
683 \r
684 \r
685 \r
686                 #endif\r
687         }\r
688         else\r
689         {\r
690                 if(exit_code)\r
691                         ERROR0("Syntax NOK");\r
692                 else if(!exit_code)\r
693                         INFO0("Syntax 0K");\r
694         }\r
695 }\r
696 \r
697 int\r
698 runner_is_timedout(void)\r
699 {\r
700         return runner->timeouted;\r
701 }\r
702 \r