1 /*============================================================================
2  * Low-level functions and global variables definition.
3  *============================================================================*/
4 
5 /*
6   This file is part of Code_Saturne, a general-purpose CFD tool.
7 
8   Copyright (C) 1998-2021 EDF S.A.
9 
10   This program is free software; you can redistribute it and/or modify it under
11   the terms of the GNU General Public License as published by the Free Software
12   Foundation; either version 2 of the License, or (at your option) any later
13   version.
14 
15   This program is distributed in the hope that it will be useful, but WITHOUT
16   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17   FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
18   details.
19 
20   You should have received a copy of the GNU General Public License along with
21   this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
22   Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 */
24 
25 /*----------------------------------------------------------------------------*/
26 
27 #include "cs_defs.h"
28 
29 /*----------------------------------------------------------------------------
30  * Standard C library headers
31  *----------------------------------------------------------------------------*/
32 
33 #include <assert.h>
34 #include <ctype.h>
35 #include <errno.h>
36 #include <signal.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <stdarg.h>
41 #include <time.h>
42 
43 #if defined(HAVE_UNISTD_H)
44 #include <unistd.h>
45 #endif
46 
47 #if defined(HAVE_DLOPEN)
48 #include <dlfcn.h>
49 #endif
50 
51 #if defined(HAVE_CATALYST)
52 #  define _CS_EXIT_DEPLIB_CRASH_WORKAROUND 1
53 #else
54 #  define _CS_EXIT_DEPLIB_CRASH_WORKAROUND 0
55 #endif
56 
57 #if _CS_EXIT_DEPLIB_CRASH_WORKAROUND
58 #include <setjmp.h>
59 #endif
60 
61 /*----------------------------------------------------------------------------
62  * PLE library headers
63  *----------------------------------------------------------------------------*/
64 
65 #include <ple_defs.h>
66 #include <ple_coupling.h>
67 
68 /*----------------------------------------------------------------------------
69  * Local headers
70  *----------------------------------------------------------------------------*/
71 
72 #include "bft_backtrace.h"
73 #include "bft_mem_usage.h"
74 #include "bft_mem.h"
75 #include "bft_printf.h"
76 
77 #include "cs_file.h"
78 #include "cs_fp_exception.h"
79 #include "cs_log.h"
80 #include "cs_timer.h"
81 #include "cs_version.h"
82 
83 /*----------------------------------------------------------------------------
84  *  Header for the current file
85  *----------------------------------------------------------------------------*/
86 
87 #include "cs_base.h"
88 
89 /*----------------------------------------------------------------------------*/
90 
91 BEGIN_C_DECLS
92 
93 /*! \cond DOXYGEN_SHOULD_SKIP_THIS */
94 
95 /*============================================================================
96  * Local Macro Definitions
97  *============================================================================*/
98 
99 #define DIR_SEPARATOR '/'
100 
101 /* Fortran API */
102 /*-------------*/
103 
104 /*
105  * 'usual' maximum name length; a longer name is possible, but will
106  * provoke a dynamic memory allocation.
107  */
108 
109 #define CS_BASE_N_STRINGS                               5
110 
111 /*============================================================================
112  * Local Type Definitions
113  *============================================================================*/
114 
115 #if defined(HAVE_MPI)
116 
117 typedef struct
118 {
119   long val;
120   int  rank;
121 } _cs_base_mpi_long_int_t;
122 
123 typedef struct
124 {
125   double val;
126   int    rank;
127 } _cs_base_mpi_double_int_t;
128 
129 #endif
130 
131 /* Type to backup signal handlers */
132 
133 typedef void (*_cs_base_sighandler_t) (int);
134 
135 /*============================================================================
136  *  Global variables
137  *============================================================================*/
138 
139 static bft_error_handler_t  *cs_glob_base_err_handler_save = NULL;
140 
141 static bool  cs_glob_base_bft_mem_init = false;
142 
143 static bool  cs_glob_base_str_init = false;
144 static bool  cs_glob_base_str_is_free[CS_BASE_N_STRINGS];
145 static char  cs_glob_base_str[CS_BASE_N_STRINGS][CS_BASE_STRING_LEN + 1];
146 
147 
148 /* Global variables associated with signal handling */
149 
150 static bool _cs_base_sighandlers_set = false;
151 
152 #if defined(SIGHUP)
153 static _cs_base_sighandler_t cs_glob_base_sighup_save = SIG_DFL;
154 #endif
155 
156 static _cs_base_sighandler_t cs_glob_base_sigabrt_save = SIG_DFL;
157 static _cs_base_sighandler_t cs_glob_base_sigint_save = SIG_DFL;
158 static _cs_base_sighandler_t cs_glob_base_sigterm_save = SIG_DFL;
159 static _cs_base_sighandler_t cs_glob_base_sigfpe_save = SIG_DFL;
160 static _cs_base_sighandler_t cs_glob_base_sigsegv_save = SIG_DFL;
161 
162 #if defined(SIGXCPU)
163 static _cs_base_sighandler_t cs_glob_base_sigcpu_save = SIG_DFL;
164 #endif
165 
166 /* Workaround for SIGSEGV at exit
167    with some ParaView Catalyst/OpenGL driver combinations */
168 
169 #if _CS_EXIT_DEPLIB_CRASH_WORKAROUND
170 static jmp_buf _cs_exit_jmp_buf;
171 #endif
172 
173 /* Dynamic library handling */
174 
175 #if defined(HAVE_DLOPEN)
176 #if defined(CS_DLOPEN_USE_RTLD_GLOBAL)
177 static int _cs_dlopen_flags = RTLD_LAZY | RTLD_GLOBAL;
178 #else
179 static int _cs_dlopen_flags = RTLD_LAZY;
180 #endif
181 #endif
182 
183 /* Installation paths */
184 
185 static const char _cs_base_build_localedir[] = LOCALEDIR;
186 static const char _cs_base_build_pkgdatadir[] = PKGDATADIR;
187 static const char _cs_base_build_pkglibdir[] = PKGLIBDIR;
188 static char *_cs_base_env_localedir = NULL;
189 static char *_cs_base_env_pkgdatadir = NULL;
190 static char *_cs_base_env_pkglibdir = NULL;
191 
192 /* Log file */
193 
194 static FILE  *_bft_printf_file = NULL;
195 static char  *_bft_printf_file_name = NULL;
196 static bool   _bft_printf_suppress = false;
197 static bool   _cs_trace = false;
198 
199 /* Additional cleanup steps */
200 
201 static cs_base_atexit_t  * _cs_base_atexit = NULL;
202 
203 /* Additional MPI communicators */
204 
205 #if defined(HAVE_MPI)
206 static int        _n_step_comms = 0;
207 static int       *_step_ranks = NULL;
208 static MPI_Comm  *_step_comm = NULL;
209 #endif
210 
211 /*============================================================================
212  * Private function definitions
213  *============================================================================*/
214 
215 /*----------------------------------------------------------------------------
216  * False print of a message to standard output for discarded logs
217  *----------------------------------------------------------------------------*/
218 
219 static int
_cs_base_bft_printf_null(const char * format,va_list arg_ptr)220 _cs_base_bft_printf_null(const char  *format,
221                          va_list      arg_ptr)
222 {
223   CS_UNUSED(format);
224   CS_UNUSED(arg_ptr);
225 
226   return 0;
227 }
228 
229 /*----------------------------------------------------------------------------
230  * False print of a message to standard output for discarded logs
231  *----------------------------------------------------------------------------*/
232 
233 static int
_cs_base_bft_printf_file(const char * format,va_list arg_ptr)234 _cs_base_bft_printf_file(const char  *format,
235                          va_list      arg_ptr)
236 {
237   return  vfprintf(_bft_printf_file, format, arg_ptr);
238 }
239 
240 /*----------------------------------------------------------------------------
241  * Flush of log output buffer
242  *----------------------------------------------------------------------------*/
243 
244 static int
_cs_base_bft_printf_flush(void)245 _cs_base_bft_printf_flush(void)
246 {
247   return fflush(stdout);
248 }
249 
250 /*----------------------------------------------------------------------------
251  * False flush of log output buffer for discarded logs
252  *----------------------------------------------------------------------------*/
253 
254 static int
_cs_base_bft_printf_flush_null(void)255 _cs_base_bft_printf_flush_null(void)
256 {
257   return 0;
258 }
259 
260 /*----------------------------------------------------------------------------
261  * False flush of log output buffer for discarded logs
262  *----------------------------------------------------------------------------*/
263 
264 static int
_cs_base_bft_printf_flush_file(void)265 _cs_base_bft_printf_flush_file(void)
266 {
267   return fflush(_bft_printf_file);
268 }
269 
270 /*----------------------------------------------------------------------------
271  * Print a message to the error output
272  *
273  * The message is repeated on the standard output and an error file.
274  *----------------------------------------------------------------------------*/
275 
276 static void
_cs_base_err_vprintf(const char * format,va_list arg_ptr)277 _cs_base_err_vprintf(const char  *format,
278                      va_list      arg_ptr)
279 {
280   static bool  initialized = false;
281 
282   /* message to the standard output */
283 
284 #if defined(va_copy) || defined(__va_copy)
285   {
286     va_list arg_ptr_2;
287     bft_printf_proxy_t  *_bft_printf_proxy = bft_printf_proxy_get();
288 
289 #if defined(va_copy)
290     va_copy(arg_ptr_2, arg_ptr);
291 #else
292     __va_copy(arg_ptr_2, arg_ptr);
293 #endif
294     _bft_printf_proxy(format, arg_ptr_2);
295     va_end(arg_ptr_2);
296   }
297 #endif
298 
299   /* message on a specific error output, initialized only if the
300      error output is really necessary */
301 
302   if (initialized == false) {
303 
304     char err_file_name[81];
305     int i;
306     int n_dec = 1;
307 
308     if (cs_glob_rank_id < 1)
309       strcpy(err_file_name, "error");
310 
311     else {
312 #if defined(HAVE_SLEEP)
313       /* Wait a few seconds, so that if rank 0 also has encountered an error,
314          it may kill other ranks through MPI_Abort, so that only rank 0 will
315          generate an error file. If rank 0 has not encountered the error,
316          proceed normally after the wait.
317          As sleep() may be interrupted by a signal, repeat as long as the wait
318          time is not elapsed; */
319       int wait_time = (cs_glob_n_ranks < 64) ? 1: 10;
320       double stime = cs_timer_wtime();
321       double etime = 0.0;
322       do {
323         sleep(wait_time);
324         etime = cs_timer_wtime();
325       }
326       while (etime > -0.5 && etime - stime < wait_time); /* etime = -1 only if
327                                                             cs_timer_wtime()
328                                                             is unusable. */
329 #endif
330       for (i = cs_glob_n_ranks; i >= 10; i /= 10, n_dec += 1);
331       sprintf(err_file_name, "error_r%0*d", n_dec, cs_glob_rank_id);
332     }
333 
334     freopen(err_file_name, "w", stderr);
335 
336     initialized = true;
337   }
338 
339   vfprintf(stderr, format, arg_ptr);
340 }
341 
342 /*----------------------------------------------------------------------------
343  * Print a message to error output
344  *
345  * The message is repeated on the standard output and an error file.
346  *----------------------------------------------------------------------------*/
347 
348 #if defined(__GNUC__)
349 __attribute__((format(printf, 1, 2)))
350 #endif
351 
352 static void
_cs_base_err_printf(const char * format,...)353 _cs_base_err_printf(const char  *format,
354                     ...)
355 {
356   /* Initialize arguments list */
357 
358   va_list  arg_ptr;
359   va_start(arg_ptr, format);
360 
361   /* message on outputs */
362 
363   _cs_base_err_vprintf(format, arg_ptr);
364 
365   /* Finalize arguments list */
366 
367   va_end(arg_ptr);
368 }
369 
370 #if _CS_EXIT_DEPLIB_CRASH_WORKAROUND
371 
372 /*----------------------------------------------------------------------------
373  * Handle a signal as a warning at exit
374  *----------------------------------------------------------------------------*/
375 
376 static void
_cs_base_sig_exit_crash_workaround(int signum)377 _cs_base_sig_exit_crash_workaround(int  signum)
378 {
379   bft_printf_flush();
380 
381   char sig_name[32] = "";
382 
383   switch (signum) {
384 
385   case SIGFPE:
386     snprintf(sig_name, 31, "SIGFPE");
387     break;
388 
389   case SIGSEGV:
390     snprintf(sig_name, 31, "SIGSEGV");
391     break;
392 
393   }
394 
395   if (cs_glob_rank_id <= 0) {
396     fprintf(stderr, _("Warning: signal %s received during program exit.\n"),
397             sig_name);
398     cs_base_backtrace_dump(stderr, 3);
399   }
400 
401   longjmp(_cs_exit_jmp_buf, 1);
402 }
403 
404 /*----------------------------------------------------------------------------
405  * Set signal handlers to workaround an external library issue at exit.
406  *
407  * This is used at exit to transform errors into warnings on rank 0
408  * only during a standard exit routine.
409  *
410  * This is an ugly workaround to an ugly crash observed when exiting after
411  * have used VTK (Paraview Catalyst) libraries (on Debian Stretch with
412  * NVIDIA 390 driver at least).
413  *
414  * It is based on changing to a different signal handler for SIGSEGV
415  * (but could also be used for other signals), and using setjmp/longjmp
416  * to return from that handler and call _exit to avoid further issues.
417  * This avoids returning a nonzero error code to the caller when the
418  * code had finished correctly before calling exit(EXIT_SUCCESS).
419  *----------------------------------------------------------------------------*/
420 
421 static void
_set_atexit_crash_workaround(void)422 _set_atexit_crash_workaround(void)
423 {
424 #if defined(HAVE_SIGACTION)
425 
426   if (_cs_base_sighandlers_set) {
427     struct sigaction sa;
428     sa.sa_handler = *_cs_base_sig_exit_crash_workaround;
429     sigfillset(&sa.sa_mask);
430     sa.sa_flags = 0;
431     sa.sa_restorer = NULL;
432     if (sigaction(SIGSEGV, &sa, NULL) == -1 && cs_glob_rank_id <= 0)
433       perror("sigaction");
434 
435     int i = setjmp(_cs_exit_jmp_buf);
436 
437     if (i == 1)
438       _exit(EXIT_SUCCESS); /* This handler is only used during normal exit */
439   }
440 
441 #endif
442 }
443 
444 #endif /* _CS_EXIT_DEPLIB_CRASH_WORKAROUND */
445 
446 /*----------------------------------------------------------------------------
447  * Exit function
448  *----------------------------------------------------------------------------*/
449 
450 static void
_cs_base_exit(int status)451 _cs_base_exit(int status)
452 {
453   if (status == EXIT_SUCCESS)
454     cs_base_update_status(NULL);
455 
456 #if defined(HAVE_MPI)
457   {
458     int mpi_flag;
459 
460     MPI_Initialized(&mpi_flag);
461 
462 #if (MPI_VERSION >= 2)
463     if (mpi_flag != 0) {
464       int finalized_flag;
465       MPI_Finalized(&finalized_flag);
466       if (finalized_flag != 0)
467         mpi_flag = 0;
468     }
469 #endif
470 
471     if (mpi_flag != 0) {
472 
473       /* For safety, flush all streams before calling MPI_Abort
474        * (should be done by exit, but in case we call MPI_Abort
475        * due to a SIGTERM received from another rank's MPI_Abort,
476        * make sure we avoid ill-defined behavior) */
477 
478       fflush(NULL);
479 
480       if (status != EXIT_SUCCESS)
481         MPI_Abort(cs_glob_mpi_comm, EXIT_FAILURE);
482 
483       else { /*  if (status == EXIT_SUCCESS) */
484 
485         MPI_Barrier(MPI_COMM_WORLD);
486         MPI_Finalize();
487 
488       }
489     }
490   }
491 #endif /* HAVE_MPI */
492 
493 #if _CS_EXIT_DEPLIB_CRASH_WORKAROUND
494 
495   if (status == EXIT_SUCCESS)
496     _set_atexit_crash_workaround();
497 
498 #endif
499 
500   exit(status);
501 }
502 
503 /*----------------------------------------------------------------------------
504  * Stop the code in case of error
505  *----------------------------------------------------------------------------*/
506 
507 static void
_cs_base_error_handler(const char * nom_fic,int num_ligne,int code_err_sys,const char * format,va_list arg_ptr)508 _cs_base_error_handler(const char  *nom_fic,
509                        int          num_ligne,
510                        int          code_err_sys,
511                        const char  *format,
512                        va_list      arg_ptr)
513 {
514   if (_cs_base_atexit != NULL) {
515     _cs_base_atexit();
516     _cs_base_atexit = NULL;
517   }
518 
519   bft_printf_flush();
520 
521   _cs_base_err_printf("\n");
522 
523   if (code_err_sys != 0)
524     _cs_base_err_printf(_("\nSystem error: %s\n"), strerror(code_err_sys));
525 
526   _cs_base_err_printf(_("\n%s:%d: Fatal error.\n\n"), nom_fic, num_ligne);
527 
528   _cs_base_err_vprintf(format, arg_ptr);
529 
530   _cs_base_err_printf("\n\n");
531 
532   bft_backtrace_print(3);
533 
534   _cs_base_exit(EXIT_FAILURE);
535 }
536 
537 /*----------------------------------------------------------------------------
538  * Print memory usage summary in case of error
539  *----------------------------------------------------------------------------*/
540 
541 static void
_error_mem_summary(void)542 _error_mem_summary(void)
543 {
544   size_t mem_usage;
545 
546   _cs_base_err_printf(_("\n\n"
547                         "Memory allocation summary\n"
548                         "-------------------------\n\n"));
549 
550   /* Available memory usage information */
551 
552   _cs_base_err_printf
553     (_("Theoretical current allocated memory:   %llu kB\n"),
554      (unsigned long long)(bft_mem_size_current()));
555 
556   mem_usage = bft_mem_size_max();
557 
558   if (mem_usage > 0)
559     _cs_base_err_printf
560       (_("Theoretical maximum allocated memory:   %llu kB\n"),
561        (unsigned long long)(bft_mem_size_max()));
562 
563   if (bft_mem_usage_initialized() == 1) {
564 
565     /* Maximum measured memory */
566 
567     mem_usage = bft_mem_usage_max_pr_size();
568     if (mem_usage > 0)
569       _cs_base_err_printf
570         (_("Maximum program memory measure:         %llu kB\n"),
571          (unsigned long long)mem_usage);
572 
573     /* Current measured memory */
574 
575     mem_usage = bft_mem_usage_pr_size();
576     if (mem_usage > 0)
577       _cs_base_err_printf
578         (_("Current program memory measure:         %llu kB\n"),
579          (unsigned long long)mem_usage);
580   }
581 }
582 
583 /*----------------------------------------------------------------------------
584  * Memory allocation error handler.
585  *
586  * Memory status is written to the error output, and the general error
587  * handler used by bft_error() is called (which results in the termination
588  * of the current process).
589  *
590  * parameters:
591  *   file_name      <-- name of source file from which error handler called.
592  *   line_num       <-- line of source file from which error handler called.
593  *   sys_error_code <-- error code if error in system or libc call, 0 otherwise.
594  *   format         <-- format string, as printf() and family.
595  *   arg_ptr        <-> variable argument list based on format string.
596  *----------------------------------------------------------------------------*/
597 
598 static void
_cs_mem_error_handler(const char * file_name,int line_num,int sys_error_code,const char * format,va_list arg_ptr)599 _cs_mem_error_handler(const char  *file_name,
600                       int          line_num,
601                       int          sys_error_code,
602                       const char  *format,
603                       va_list      arg_ptr)
604 {
605   bft_error_handler_t * general_err_handler;
606 
607   _error_mem_summary();
608 
609   general_err_handler = bft_error_handler_get();
610   general_err_handler(file_name, line_num, sys_error_code, format, arg_ptr);
611 }
612 
613 /*----------------------------------------------------------------------------
614  * Print a stack trace
615  *----------------------------------------------------------------------------*/
616 
617 static void
_cs_base_backtrace_print(int lv_start)618 _cs_base_backtrace_print(int  lv_start)
619 {
620   bft_backtrace_t  *tr = NULL;
621 
622   tr = bft_backtrace_create();
623 
624   if (tr != NULL) {
625 
626     char s_func_buf[67];
627 
628     const char *s_file;
629     const char *s_func;
630     const char *s_addr;
631 
632     const char s_unknown[] = "?";
633     const char s_vide[] = "";
634     const char *s_prefix = s_vide;
635 
636     int nbr = bft_backtrace_size(tr);
637 
638     if (nbr > 0)
639       _cs_base_err_printf(_("\nCall stack:\n"));
640 
641     for (int ind = lv_start; ind < nbr; ind++) {
642 
643       s_file = bft_backtrace_file(tr, ind);
644       s_func = bft_backtrace_function(tr, ind);
645       s_addr = bft_backtrace_address(tr, ind);
646 
647       if (s_file == NULL)
648         s_file = s_unknown;
649       if (s_func == NULL)
650         strcpy(s_func_buf, "?");
651       else {
652         s_func_buf[0] = '<';
653         strncpy(s_func_buf + 1, s_func, 64);
654         strcat(s_func_buf, ">");
655       }
656       if (s_addr == NULL)
657         s_addr = s_unknown;
658 
659       _cs_base_err_printf("%s%4d: %-12s %-32s (%s)\n", s_prefix,
660                           ind-lv_start+1, s_addr, s_func_buf, s_file);
661 
662     }
663 
664     bft_backtrace_destroy(tr);
665 
666     if (nbr > 0)
667       _cs_base_err_printf(_("End of stack\n\n"));
668   }
669 
670 }
671 
672 /*----------------------------------------------------------------------------
673  * Handle a fatal signal (such as SIGFPE or SIGSEGV)
674  *----------------------------------------------------------------------------*/
675 
676 static void
_cs_base_sig_fatal(int signum)677 _cs_base_sig_fatal(int  signum)
678 {
679   if (_cs_base_atexit != NULL) {
680     _cs_base_atexit();
681     _cs_base_atexit = NULL;
682   }
683 
684   bft_printf_flush();
685 
686   switch (signum) {
687 
688 #if defined(SIGHUP)
689   case SIGHUP:
690     _cs_base_err_printf(_("SIGHUP signal (hang-up) intercepted.\n"
691                           "--> computation interrupted.\n"));
692     break;
693 #endif
694 
695   case SIGABRT:
696     _cs_base_err_printf(_("SIGABRT signal (abort) intercepted.\n"));
697     break;
698 
699   case SIGINT:
700     _cs_base_err_printf(_("SIGINT signal (Control+C or equivalent) received.\n"
701                           "--> computation interrupted by user.\n"));
702     break;
703 
704   case SIGTERM:
705     _cs_base_err_printf(_("SIGTERM signal (termination) received.\n"
706                           "--> computation interrupted by environment.\n"));
707     break;
708 
709   case SIGFPE:
710     _cs_base_err_printf(_("SIGFPE signal (floating point exception) "
711                           "intercepted!\n"));
712     break;
713 
714   case SIGSEGV:
715     _cs_base_err_printf(_("SIGSEGV signal (forbidden memory area access) "
716                           "intercepted!\n"));
717     break;
718 
719 #if defined(SIGXCPU)
720   case SIGXCPU:
721     _cs_base_err_printf(_("SIGXCPU signal (CPU time limit reached) "
722                           "intercepted.\n"));
723     break;
724 #endif
725 
726   default:
727     _cs_base_err_printf(_("Signal %d intercepted!\n"), signum);
728   }
729 
730   bft_backtrace_print(3);
731 
732   _cs_base_exit(EXIT_FAILURE);
733 }
734 
735 /*----------------------------------------------------------------------------
736  * Return a string providing path information.
737  *
738  * This is normally the path determined upon configuration, but may be
739  * adapted for movable installs using the CS_ROOT_DIR environment variable
740  * or by a guess on the assumed relative path.
741  *----------------------------------------------------------------------------*/
742 
743 static const char *
_get_path(const char * dir_path,const char * build_path,char ** env_path)744 _get_path(const char   *dir_path,
745           const char   *build_path,
746           char        **env_path)
747 {
748 #if defined(HAVE_RELOCATABLE)
749   {
750     const char *cs_root_dir = NULL;
751     const char *rel_path = NULL;
752 
753     /* Allow for displaceable install */
754 
755     if (*env_path != NULL)
756       return *env_path;
757 
758     /* First try with an environment variable CS_ROOT_DIR */
759 
760     if (getenv("CS_ROOT_DIR") != NULL) {
761       cs_root_dir = getenv("CS_ROOT_DIR");
762       rel_path = "/";
763     }
764 
765     /* Second try with an environment variable CFDSTUDY_ROOT_DIR */
766 
767     else if (getenv("CFDSTUDY_ROOT_DIR") != NULL) {
768       cs_root_dir = getenv("CFDSTUDY_ROOT_DIR");
769       rel_path = "/";
770     }
771 
772 #if defined(HAVE_GETCWD)
773 
774     /*
775       Then, try to guess a relative path, knowing that executables are
776       located in libexecdir/code_saturne
777     */
778 
779     else {
780 
781       int buf_size = 128;
782       char *buf = NULL;
783 
784       while (cs_root_dir == NULL) {
785         buf_size *= 2;
786         BFT_REALLOC(buf, buf_size, char);
787         cs_root_dir = getcwd(buf, buf_size);
788         if (cs_root_dir == NULL && errno != ERANGE)
789           bft_error(__FILE__, __LINE__, errno,
790                     _("Error querying working directory.\n"));
791       }
792 
793       rel_path = "/../../";
794 
795     }
796 #endif /* defined(HAVE_GETCWD) */
797 
798     BFT_MALLOC(*env_path,
799                strlen(cs_root_dir) + strlen(rel_path) + strlen(dir_path) + 1,
800                char);
801     strcpy(*env_path, cs_root_dir);
802     strcat(*env_path, rel_path);
803     strcat(*env_path, dir_path);
804 
805     return *env_path;
806   }
807 #else
808 
809   CS_UNUSED(dir_path);
810   CS_UNUSED(env_path);
811 
812 #endif /* defined(HAVE_RELOCATABLE) */
813 
814   /* Standard install */
815 
816   return build_path;
817 }
818 
819 #if defined(HAVE_MPI)
820 
821 /*----------------------------------------------------------------------------
822  * Destroy a set of reduced communicators
823  *----------------------------------------------------------------------------*/
824 
825 static void
_finalize_reduced_communicators(void)826 _finalize_reduced_communicators(void)
827 {
828   int comm_id;
829 
830   for (comm_id = 1; comm_id < _n_step_comms; comm_id++) {
831     if (   _step_comm[comm_id] != MPI_COMM_NULL
832         && _step_comm[comm_id] != cs_glob_mpi_comm)
833       MPI_Comm_free(&(_step_comm[comm_id]));
834   }
835 
836   BFT_FREE(_step_comm);
837   BFT_FREE(_step_ranks);
838 
839   _n_step_comms = 0;
840 }
841 
842 /*----------------------------------------------------------------------------
843  *  MPI finalization
844  *----------------------------------------------------------------------------*/
845 
846 static void
_cs_base_mpi_fin(void)847 _cs_base_mpi_fin(void)
848 {
849   bft_error_handler_set(cs_glob_base_err_handler_save);
850   ple_error_handler_set(cs_glob_base_err_handler_save);
851 
852   if (   cs_glob_mpi_comm != MPI_COMM_NULL
853       && cs_glob_mpi_comm != MPI_COMM_WORLD)
854     MPI_Comm_free(&cs_glob_mpi_comm);
855 }
856 
857 
858 #if defined(DEBUG) || !defined(NDEBUG)
859 
860 /*----------------------------------------------------------------------------
861  * MPI error handler
862  *----------------------------------------------------------------------------*/
863 
864 static void
_cs_base_mpi_error(MPI_Comm * comm,int * errcode,...)865 _cs_base_mpi_error(MPI_Comm  *comm,
866                    int       *errcode,
867                    ...)
868 {
869   int err_len;
870   char err_string[MPI_MAX_ERROR_STRING + 1];
871 
872 #if defined MPI_MAX_OBJECT_NAME
873   int name_len = 0;
874   char comm_name[MPI_MAX_OBJECT_NAME + 1];
875 #endif
876 
877   if (_cs_base_atexit != NULL) {
878     _cs_base_atexit();
879     _cs_base_atexit = NULL;
880   }
881 
882   bft_printf_flush();
883 
884   _cs_base_err_printf("\n");
885 
886   MPI_Error_string(*errcode, err_string, &err_len);
887   err_string[err_len] = '\0';
888 
889 #if defined MPI_MAX_OBJECT_NAME
890   MPI_Comm_get_name(*comm, comm_name, &name_len);
891   comm_name[name_len] = '\0';
892   _cs_base_err_printf(_("\nMPI error (communicator %s):\n"
893                         "%s\n"), comm_name, err_string);
894 #else
895   _cs_base_err_printf(_("\nMPI error:\n"
896                         "%s\n"), err_string);
897 #endif
898 
899   _cs_base_err_printf("\n\n");
900 
901   bft_backtrace_print(3);
902 
903   _cs_base_exit(EXIT_FAILURE);
904 }
905 
906 #endif
907 
908 /*----------------------------------------------------------------------------
909  * Ensure Code_Saturne to MPI datatype conversion has correct values.
910  *----------------------------------------------------------------------------*/
911 
912 static void
_cs_datatype_to_mpi_init(void)913 _cs_datatype_to_mpi_init(void)
914 {
915   int size_short, size_int, size_long, size_long_long;
916 
917   MPI_Type_size(MPI_SHORT, &size_short);
918   MPI_Type_size(MPI_INT,   &size_int);
919   MPI_Type_size(MPI_LONG,  &size_long);
920 
921 #if defined(MPI_LONG_LONG)
922   MPI_Type_size(MPI_LONG_LONG, &size_long_long);
923 #else
924   size_long_long = 0;
925 #endif
926 
927   if (size_int == 4) {
928     cs_datatype_to_mpi[CS_INT32] = MPI_INT;
929     cs_datatype_to_mpi[CS_UINT32] = MPI_UNSIGNED;
930   }
931   else if (size_short == 4) {
932     cs_datatype_to_mpi[CS_INT32] = MPI_SHORT;
933     cs_datatype_to_mpi[CS_UINT32] = MPI_UNSIGNED_SHORT;
934   }
935   else if (size_long == 4) {
936     cs_datatype_to_mpi[CS_INT32] = MPI_LONG;
937     cs_datatype_to_mpi[CS_UINT32] = MPI_UNSIGNED_LONG;
938   }
939 
940   if (size_int == 8) {
941     cs_datatype_to_mpi[CS_INT64] = MPI_INT;
942     cs_datatype_to_mpi[CS_UINT64] = MPI_UNSIGNED;
943   }
944   else if (size_long == 8) {
945     cs_datatype_to_mpi[CS_INT64] = MPI_LONG;
946     cs_datatype_to_mpi[CS_UINT64] = MPI_UNSIGNED_LONG;
947   }
948 #if defined(MPI_LONG_LONG)
949   else if (size_long_long == 8) {
950     cs_datatype_to_mpi[CS_INT64] = MPI_LONG_LONG;
951 #if defined(MPI_UNSIGNED_LONG_LONG)
952     cs_datatype_to_mpi[CS_UINT64] = MPI_UNSIGNED_LONG_LONG;
953 #else
954     cs_datatype_to_mpi[CS_UINT64] = MPI_LONG_LONG;
955 #endif
956   }
957 #endif
958 }
959 
960 /*----------------------------------------------------------------------------*/
961 /*!
962  * \brief Determine node-local MPI info
963  */
964 /*----------------------------------------------------------------------------*/
965 
966 static void
_mpi_node_info(void)967 _mpi_node_info(void)
968 {
969 #if (MPI_VERSION >= 3)
970   MPI_Comm sh_comm;
971   MPI_Comm_split_type(cs_glob_mpi_comm, MPI_COMM_TYPE_SHARED, 0,
972                       MPI_INFO_NULL, &sh_comm);
973 
974   MPI_Comm_rank(sh_comm, &cs_glob_node_rank_id);
975   MPI_Comm_size(sh_comm, &cs_glob_node_n_ranks);
976 
977   MPI_Comm_free(&sh_comm);
978 #endif
979 }
980 
981 /*----------------------------------------------------------------------------
982  * Complete MPI setup.
983  *
984  * MPI should have been initialized by cs_base_mpi_init().
985  *
986  * The application name is used to build subgroups of processes with
987  * identical name from the MPI_COMM_WORLD communicator, thus separating
988  * this instance of Code_Saturne from other coupled codes. It may be
989  * defined using the --app-num argument, and is based on the working
990  * directory's base name otherwise.
991  *
992  * parameters:
993  *   app_name <-- pointer to application instance name.
994  *----------------------------------------------------------------------------*/
995 
996 static void
_cs_base_mpi_setup(const char * app_name)997 _cs_base_mpi_setup(const char *app_name)
998 {
999   int nbr, rank;
1000 
1001   int app_num = -1;
1002 
1003 #if (defined(DEBUG) || !defined(NDEBUG)) && (MPI_VERSION >= 2)
1004   MPI_Errhandler errhandler;
1005 #endif
1006 
1007   app_num = ple_coupling_mpi_name_to_id(MPI_COMM_WORLD, app_name);
1008 
1009   /*
1010     Split MPI_COMM_WORLD to separate different coupled applications
1011     (collective operation, like all MPI communicator creation operations).
1012 
1013     app_num is equal to -1 if all applications have the same instance
1014     name, in which case no communicator split is necessary.
1015   */
1016 
1017   MPI_Comm_rank(MPI_COMM_WORLD, &rank);
1018 
1019   if (app_num > -1)
1020     MPI_Comm_split(MPI_COMM_WORLD, app_num, rank, &cs_glob_mpi_comm);
1021   else
1022     cs_glob_mpi_comm = MPI_COMM_WORLD;
1023 
1024   MPI_Comm_size(cs_glob_mpi_comm, &nbr);
1025   MPI_Comm_rank(cs_glob_mpi_comm, &rank);
1026 
1027   cs_glob_n_ranks = nbr;
1028 
1029   if (cs_glob_n_ranks > 1)
1030     cs_glob_rank_id = rank;
1031 
1032   /* cs_glob_mpi_comm may not be freed at this stage, as it
1033      it may be needed to build intercommunicators for couplings,
1034      but we may set cs_glob_rank_id to its serial value if
1035      we are only using MPI for coupling. */
1036 
1037   if (cs_glob_n_ranks == 1 && app_num > -1)
1038     cs_glob_rank_id = -1;
1039 
1040   _mpi_node_info();
1041 
1042   /* Initialize datatype conversion */
1043 
1044   _cs_datatype_to_mpi_init();
1045 
1046   /* Initialize error handlers */
1047 
1048 #if (defined(DEBUG) || !defined(NDEBUG)) && (MPI_VERSION >= 2)
1049   if (nbr > 1 || cs_glob_mpi_comm != MPI_COMM_NULL) {
1050     MPI_Comm_create_errhandler(&_cs_base_mpi_error, &errhandler);
1051     MPI_Comm_set_errhandler(MPI_COMM_WORLD, errhandler);
1052     if (   cs_glob_mpi_comm != MPI_COMM_WORLD
1053         && cs_glob_mpi_comm != MPI_COMM_NULL)
1054       MPI_Comm_set_errhandler(cs_glob_mpi_comm, errhandler);
1055     MPI_Errhandler_free(&errhandler);
1056   }
1057 #endif
1058 }
1059 
1060 #endif /* HAVE_MPI */
1061 
1062 /*! (DOXYGEN_SHOULD_SKIP_THIS) \endcond */
1063 
1064 /*============================================================================
1065  * Public function definitions
1066  *============================================================================*/
1067 
1068 /*----------------------------------------------------------------------------
1069  * First analysis of the command line to determine an application name.
1070  *
1071  * If no name is defined by the command line, a name is determined based
1072  * on the working directory.
1073  *
1074  * The caller is responsible for freeing the returned string.
1075  *
1076  * parameters:
1077  *   argc  <-- number of command line arguments
1078  *   argv  <-- array of command line arguments
1079  *
1080  * returns:
1081  *   pointer to character string with application name
1082  *----------------------------------------------------------------------------*/
1083 
1084 char *
cs_base_get_app_name(int argc,const char * argv[])1085 cs_base_get_app_name(int          argc,
1086                      const char  *argv[])
1087 {
1088   char *app_name = NULL;
1089   int arg_id = 0;
1090 
1091   /* Loop on command line arguments */
1092 
1093   arg_id = 0;
1094 
1095   while (++arg_id < argc) {
1096     const char *s = argv[arg_id];
1097     if (strcmp(s, "--app-name") == 0) {
1098       if (arg_id + 1 < argc) {
1099         BFT_MALLOC(app_name, strlen(argv[arg_id + 1]) + 1, char);
1100         strcpy(app_name, argv[arg_id + 1]);
1101       }
1102     }
1103   }
1104 
1105   /* Use execution directory if name is unavailable */
1106 
1107 #if defined(HAVE_GETCWD)
1108 
1109   if (app_name == NULL) {
1110 
1111     int i;
1112     int buf_size = 128;
1113     char *wd = NULL, *buf = NULL;
1114 
1115     while (wd == NULL) {
1116       buf_size *= 2;
1117       BFT_REALLOC(buf, buf_size, char);
1118       wd = getcwd(buf, buf_size);
1119       if (wd == NULL && errno != ERANGE)
1120         bft_error(__FILE__, __LINE__, errno,
1121                   _("Error querying working directory.\n"));
1122     }
1123 
1124     for (i = strlen(buf) - 1; i > 0 && buf[i-1] != '/'; i--);
1125     BFT_MALLOC(app_name, strlen(buf + i) + 1, char);
1126     strcpy(app_name, buf + i);
1127     BFT_FREE(buf);
1128   }
1129 
1130 #endif /* defined(HAVE_GETCWD) */
1131 
1132   return app_name;
1133 }
1134 
1135 /*----------------------------------------------------------------------------
1136  * Print logfile header
1137  *
1138  * parameters:
1139  *   argc  <-- number of command line arguments
1140  *   argv  <-- array of command line arguments
1141  *----------------------------------------------------------------------------*/
1142 
1143 void
cs_base_logfile_head(int argc,char * argv[])1144 cs_base_logfile_head(int    argc,
1145                      char  *argv[])
1146 {
1147   char str[81];
1148   int ii;
1149   char date_str[] = __DATE__;
1150   char time_str[] = __TIME__;
1151   const char mon_name[12][4]
1152     = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
1153        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
1154   struct tm time_cnv;
1155 
1156   /* Determine compilation date */
1157 
1158   for (ii = 0; ii < 12; ii++) {
1159     if (strncmp(date_str, mon_name[ii], 3) == 0) {
1160       time_cnv.tm_mon = ii ;
1161       break;
1162     }
1163   }
1164 
1165   sscanf(date_str + 3, "%d", &(time_cnv.tm_mday)) ;
1166   sscanf(date_str + 6, "%d", &(time_cnv.tm_year)) ;
1167 
1168   time_cnv.tm_year -= 1900 ;
1169 
1170   sscanf(time_str    , "%d", &(time_cnv.tm_hour)) ;
1171   sscanf(time_str + 3, "%d", &(time_cnv.tm_min)) ;
1172   sscanf(time_str + 6, "%d", &(time_cnv.tm_sec)) ;
1173 
1174   time_cnv.tm_isdst = -1 ;
1175 
1176   /* Re-compute and internationalize build date */
1177 
1178   mktime(&time_cnv) ;
1179   strftime(str, 80, "%c", &time_cnv) ;
1180 
1181   /* Now print info */
1182 
1183   bft_printf(_("command: \n"));
1184 
1185   for (ii = 0 ; ii < argc ; ii++)
1186     bft_printf(" %s", argv[ii]);
1187 
1188   bft_printf("\n");
1189   bft_printf("\n************************************"
1190              "***************************\n\n");
1191   bft_printf("                                  (R)\n"
1192              "                      Code_Saturne\n\n"
1193              "                      Version %s\n\n",
1194              CS_APP_VERSION);
1195 
1196   bft_printf("\n  Copyright (C) 1998-2021 EDF S.A., France\n\n");
1197 
1198 #if defined(CS_REVISION)
1199   if (strlen(CS_REVISION) > 0)
1200     bft_printf(_("  revision %s\n"), CS_REVISION);
1201 #endif
1202 
1203   bft_printf(_("  build %s\n"), str);
1204 
1205 #if defined(MPI_SUBVERSION)
1206 #endif
1207 
1208   bft_printf("\n");
1209   bft_printf("  The Code_Saturne CFD tool  is free software;\n"
1210              "  you can redistribute it and/or modify it under the terms\n"
1211              "  of the GNU General Public License as published by the\n"
1212              "  Free Software Foundation; either version 2 of the License,\n"
1213              "  or (at your option) any later version.\n\n");
1214 
1215   bft_printf("  The Code_Saturne CFD tool is distributed in the hope that\n"
1216              "  it will be useful, but WITHOUT ANY WARRANTY; without even\n"
1217              "  the implied warranty of MERCHANTABILITY or FITNESS FOR A\n"
1218              "  PARTICULAR PURPOSE.  See the GNU General Public License\n"
1219              "  for more details.\n");
1220 
1221   bft_printf("\n************************************"
1222              "***************************\n\n");
1223 }
1224 
1225 #if defined(HAVE_MPI)
1226 
1227 /*----------------------------------------------------------------------------
1228  * First analysis of the command line and environment variables to determine
1229  * if we require MPI, and initialization if necessary.
1230  *
1231  * parameters:
1232  *   argc  <-> number of command line arguments
1233  *   argv  <-> array of command line arguments
1234  *
1235  * Global variables `cs_glob_n_ranks' (number of Code_Saturne processes)
1236  * and `cs_glob_rank_id' (rank of local process) are set by this function.
1237  *----------------------------------------------------------------------------*/
1238 
1239 void
cs_base_mpi_init(int * argc,char ** argv[])1240 cs_base_mpi_init(int    *argc,
1241                  char  **argv[])
1242 {
1243 #if defined(HAVE_MPI)
1244 
1245   char *s;
1246 
1247   int arg_id = 0, flag = 0;
1248   int use_mpi = false;
1249 
1250 #if   defined(__bg__) || defined(__CRAYXT_COMPUTE_LINUX_TARGET)
1251 
1252   /* Blue Gene/Q or Cray: assume MPI is always used. */
1253 
1254   use_mpi = true;
1255 
1256 #elif defined(MPICH) || defined(MPICH2) || defined(MSMPI_VER)
1257 
1258   /* Notes: Microsoft MPI is based on MPICH */
1259 
1260   if (getenv("PMI_RANK") != NULL)
1261     use_mpi = true;
1262 
1263   else if (getenv("PCMPI") != NULL) /* Platform MPI */
1264     use_mpi = true;
1265 
1266 #elif defined(OPEN_MPI)
1267   if (getenv("OMPI_MCA_ns_nds_vpid") != NULL)         /* OpenMPI 1.2 */
1268     use_mpi = true;
1269   else if (getenv("OMPI_COMM_WORLD_RANK") != NULL)    /* OpenMPI 1.3 + */
1270     use_mpi = true;
1271 
1272 #endif /* Tests for known MPI variants */
1273 
1274   /* Test for run through SLURM's srun */
1275 
1276   if (getenv("SLURM_SRUN_COMM_HOST") != NULL)
1277     use_mpi = true;
1278 
1279   /* If we have determined from known MPI environment variables
1280      of command line arguments that we are running under MPI,
1281      initialize MPI */
1282 
1283   if (use_mpi == true) {
1284     MPI_Initialized(&flag);
1285     if (!flag) {
1286 #if (MPI_VERSION >= 2) && defined(HAVE_OPENMP)
1287       int mpi_threads;
1288       MPI_Init_thread(argc, argv, MPI_THREAD_FUNNELED, &mpi_threads);
1289 #else
1290       MPI_Init(argc, argv);
1291 #endif
1292     }
1293   }
1294 
1295   /* Loop on command line arguments */
1296 
1297   arg_id = 0;
1298 
1299   while (++arg_id < *argc) {
1300 
1301     s = (*argv)[arg_id];
1302 
1303     /* Parallel run */
1304 
1305     if (strcmp(s, "--mpi") == 0)
1306       use_mpi = true;
1307 
1308   } /* End of loop on command line arguments */
1309 
1310   if (use_mpi == true) {
1311 
1312     MPI_Initialized(&flag);
1313     if (!flag) {
1314 #if (MPI_VERSION >= 2) && defined(HAVE_OPENMP)
1315       int mpi_threads;
1316       MPI_Init_thread(argc, argv, MPI_THREAD_FUNNELED, &mpi_threads);
1317 #else
1318       MPI_Init(argc, argv);
1319 #endif
1320     }
1321 
1322   }
1323 
1324   /* Now setup global variables and communicators */
1325 
1326   if (use_mpi == true) {
1327 
1328     char *app_name = cs_base_get_app_name(*argc, (const char **)(*argv));
1329 
1330     _cs_base_mpi_setup(app_name);
1331 
1332     BFT_FREE(app_name);
1333   }
1334 
1335 #endif
1336 }
1337 
1338 /*----------------------------------------------------------------------------
1339  * Return a reduced communicator matching a multiple of the total
1340  * number of ranks.
1341  *
1342  * This updates the number of reduced communicators if necessary.
1343  *
1344  * parameters:
1345  *   rank_step <-- associated multiple of total ranks
1346  *----------------------------------------------------------------------------*/
1347 
1348 MPI_Comm
cs_base_get_rank_step_comm(int rank_step)1349 cs_base_get_rank_step_comm(int  rank_step)
1350 {
1351   if (rank_step <= 1)
1352     return cs_glob_mpi_comm;
1353 
1354   int n_ranks = cs_glob_n_ranks / rank_step;
1355   if (cs_glob_n_ranks % rank_step > 0)
1356     n_ranks += 1;
1357 
1358   if (n_ranks <= 1)
1359     return MPI_COMM_NULL;
1360 
1361   int comm_id = 0;
1362   if (_n_step_comms > 0) {
1363     while (   _step_ranks[comm_id] != n_ranks
1364            && comm_id < _n_step_comms)
1365       comm_id++;
1366   }
1367 
1368   /* Add communicator if required */
1369 
1370   if (comm_id >= _n_step_comms) {
1371 
1372     _n_step_comms += 1;
1373     BFT_REALLOC(_step_comm, _n_step_comms, MPI_Comm);
1374     BFT_REALLOC(_step_ranks, _n_step_comms, int);
1375 
1376     _step_ranks[comm_id] = n_ranks;
1377 
1378     if (n_ranks == cs_glob_n_ranks)
1379       _step_comm[comm_id] = cs_glob_mpi_comm;
1380 
1381     else if (n_ranks == 1)
1382       _step_comm[comm_id] = MPI_COMM_NULL;
1383 
1384     else {
1385 
1386       int ranges[1][3];
1387       MPI_Group old_group, new_group;
1388 
1389       MPI_Barrier(cs_glob_mpi_comm); /* For debugging */
1390 
1391       MPI_Comm_size(cs_glob_mpi_comm, &n_ranks);
1392       MPI_Comm_group(cs_glob_mpi_comm, &old_group);
1393 
1394       ranges[0][0] = 0;
1395       ranges[0][1] = n_ranks - 1;
1396       ranges[0][2] = rank_step;
1397 
1398       MPI_Group_range_incl(old_group, 1, ranges, &new_group);
1399       MPI_Comm_create(cs_glob_mpi_comm, new_group, &(_step_comm[comm_id]));
1400       MPI_Group_free(&new_group);
1401 
1402       MPI_Group_free(&old_group);
1403 
1404       MPI_Barrier(cs_glob_mpi_comm); /* For debugging */
1405 
1406     }
1407 
1408   }
1409 
1410   return _step_comm[comm_id];
1411 }
1412 
1413 #endif /* HAVE_MPI */
1414 
1415 /*----------------------------------------------------------------------------
1416  * Exit, with handling for both normal and error cases.
1417  *
1418  * Finalize MPI if necessary.
1419  *
1420  * parameters:
1421  *   status <-- value to be returned to the parent:
1422  *              EXIT_SUCCESS / 0 for the normal case,
1423  *              EXIT_FAILURE or other nonzero code for error cases.
1424  *----------------------------------------------------------------------------*/
1425 
1426 void
cs_exit(int status)1427 cs_exit(int  status)
1428 {
1429   if (_cs_base_atexit != NULL) {
1430     _cs_base_atexit();
1431     _cs_base_atexit = NULL;
1432   }
1433 
1434   if (status == EXIT_FAILURE) {
1435 
1436     bft_printf_flush();
1437     bft_backtrace_print(2);
1438 
1439   }
1440 
1441 #if defined(HAVE_MPI)
1442 
1443   {
1444     int mpi_flag;
1445 
1446     MPI_Initialized(&mpi_flag);
1447 
1448     if (mpi_flag != 0) {
1449 
1450       if (status != EXIT_FAILURE) {
1451         _cs_base_mpi_fin();
1452       }
1453     }
1454   }
1455 
1456 #endif /* HAVE_MPI */
1457 
1458   _cs_base_exit(status);
1459 }
1460 
1461 /*----------------------------------------------------------------------------
1462  * Initialize error and signal handlers.
1463  *
1464  * parameters:
1465  *   signal_defaults <-- leave default signal handlers in place if true
1466  *----------------------------------------------------------------------------*/
1467 
1468 void
cs_base_error_init(bool signal_defaults)1469 cs_base_error_init(bool  signal_defaults)
1470 {
1471   /* Error handler */
1472 
1473   cs_glob_base_err_handler_save = bft_error_handler_get();
1474   bft_error_handler_set(_cs_base_error_handler);
1475   ple_error_handler_set(_cs_base_error_handler);
1476 
1477   /* Signal handlers */
1478 
1479   if (signal_defaults == false) {
1480 
1481     bft_backtrace_print_set(_cs_base_backtrace_print);
1482 
1483 #if defined(SIGHUP)
1484     if (cs_glob_rank_id <= 0)
1485       cs_glob_base_sighup_save  = signal(SIGHUP, _cs_base_sig_fatal);
1486 #endif
1487 
1488     cs_glob_base_sigabrt_save  = signal(SIGABRT, _cs_base_sig_fatal);
1489 
1490     if (cs_glob_rank_id <= 0) {
1491       cs_glob_base_sigint_save  = signal(SIGINT, _cs_base_sig_fatal);
1492       cs_glob_base_sigterm_save = signal(SIGTERM, _cs_base_sig_fatal);
1493     }
1494 
1495     cs_glob_base_sigfpe_save  = signal(SIGFPE, _cs_base_sig_fatal);
1496     cs_glob_base_sigsegv_save = signal(SIGSEGV, _cs_base_sig_fatal);
1497 
1498 #if defined(SIGXCPU)
1499     if (cs_glob_rank_id <= 0)
1500       cs_glob_base_sigcpu_save = signal(SIGXCPU, _cs_base_sig_fatal);
1501 #endif
1502 
1503     _cs_base_sighandlers_set = true;
1504   }
1505 }
1506 
1507 /*----------------------------------------------------------------------------
1508  * Initialize management of memory allocated through BFT.
1509  *----------------------------------------------------------------------------*/
1510 
1511 void
cs_base_mem_init(void)1512 cs_base_mem_init(void)
1513 {
1514   /* Set error handler */
1515 
1516   bft_mem_error_handler_set(_cs_mem_error_handler);
1517 
1518   /* Set PLE library memory handler */
1519 
1520   ple_mem_functions_set(bft_mem_malloc,
1521                         bft_mem_realloc,
1522                         bft_mem_free);
1523 
1524   /* Memory usage measure initialization */
1525 
1526   bft_mem_usage_init();
1527 
1528   /* Memory management initialization */
1529 
1530   if (bft_mem_initialized())
1531     cs_glob_base_bft_mem_init = false;
1532 
1533   else {
1534 
1535     const char  *base_name  = getenv("CS_MEM_LOG");
1536 
1537     if (base_name != NULL) {
1538 
1539       /* We may not use BFT_MALLOC here as memory management has
1540          not yet been initialized using bft_mem_init() */
1541 
1542       char  *file_name = NULL;
1543 
1544       /* In parallel, we will have one trace file per MPI process */
1545       if (cs_glob_rank_id >= 0) {
1546         int i;
1547         int n_dec = 1;
1548         for (i = cs_glob_n_ranks; i >= 10; i /= 10, n_dec += 1);
1549         file_name = malloc((strlen(base_name) + n_dec + 2) * sizeof (char));
1550         sprintf(file_name, "%s.%0*d", base_name, n_dec, cs_glob_rank_id);
1551       }
1552       else {
1553         file_name = malloc((strlen(base_name) + 1) * sizeof (char));
1554         strcpy(file_name, base_name);
1555       }
1556 
1557       /* Actually initialize bft_mem instrumentation only when
1558          CS_MEM_LOG is defined (for better performance) */
1559 
1560       bft_mem_init(file_name);
1561 
1562       free(file_name);
1563 
1564     }
1565 
1566 #if defined(HAVE_ACCEL)
1567     else
1568       bft_mem_init(NULL);
1569 #endif
1570 
1571     cs_glob_base_bft_mem_init = true;
1572 
1573   }
1574 }
1575 
1576 /*----------------------------------------------------------------------------
1577  * Finalize management of memory allocated through BFT.
1578  *
1579  * A summary of the consumed memory is given.
1580  *----------------------------------------------------------------------------*/
1581 
1582 void
cs_base_mem_finalize(void)1583 cs_base_mem_finalize(void)
1584 {
1585   int    ind_bil, itot;
1586   double valreal[4];
1587 
1588 #if defined(HAVE_MPI)
1589   int  imax = 0, imin = 0;
1590   double val_sum[4];
1591   int  ind_min[4];
1592   _cs_base_mpi_double_int_t  val_in[4], val_min[4], val_max[4];
1593 #endif
1594 
1595   int   ind_val[4] = {1, 1, 1, 1};
1596   const char  unit[8] = {'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'};
1597 
1598   const char  * type_bil[] = {N_("Total memory used:                       "),
1599                               N_("Theoretical instrumented dynamic memory: "),
1600                               N_("Virtual memory used:                     "),
1601                               N_("Shared libraries memory used:            ")};
1602 
1603   /* Memory summary */
1604 
1605   cs_log_printf(CS_LOG_PERFORMANCE,
1606                 _("\nMemory use summary:\n\n"));
1607 
1608   valreal[0] = (double)bft_mem_usage_max_pr_size();
1609   valreal[1] = (double)bft_mem_size_max();
1610   valreal[2] = (double)bft_mem_usage_max_vm_size();
1611   valreal[3] = (double)bft_mem_usage_shared_lib_size();
1612 
1613   /* Ignore inconsistent measurements */
1614 
1615   for (ind_bil = 0; ind_bil < 4; ind_bil++) {
1616     if (valreal[ind_bil] < 1.0)
1617       ind_val[ind_bil] = 0;
1618   }
1619 
1620 #if defined(HAVE_MPI)
1621   if (cs_glob_n_ranks > 1) {
1622     MPI_Reduce(ind_val, ind_min, 4, MPI_INT, MPI_MIN,
1623                0, cs_glob_mpi_comm);
1624     MPI_Reduce(valreal, val_sum, 4, MPI_DOUBLE, MPI_SUM,
1625                0, cs_glob_mpi_comm);
1626     for (ind_bil = 0; ind_bil < 4; ind_bil++) {
1627       val_in[ind_bil].val = valreal[ind_bil];
1628       val_in[ind_bil].rank = cs_glob_rank_id;
1629     }
1630     MPI_Reduce(val_in, val_min, 4, MPI_DOUBLE_INT, MPI_MINLOC,
1631                0, cs_glob_mpi_comm);
1632     MPI_Reduce(val_in, val_max, 4, MPI_DOUBLE_INT, MPI_MAXLOC,
1633                0, cs_glob_mpi_comm);
1634     if (cs_glob_rank_id == 0) {
1635       for (ind_bil = 0; ind_bil < 4; ind_bil++) {
1636         ind_val[ind_bil]  = ind_min[ind_bil];
1637         valreal[ind_bil] = val_sum[ind_bil];
1638       }
1639     }
1640   }
1641 #endif
1642 
1643   /* Similar handling of several instrumentation methods */
1644 
1645   for (ind_bil = 0; ind_bil < 4; ind_bil++) {
1646 
1647     /* If an instrumentation method returns an apparently consistent
1648        result, print it. */
1649 
1650     if (ind_val[ind_bil] == 1) {
1651 
1652       for (itot = 0;
1653            valreal[ind_bil] > 1024. && itot < 8;
1654            itot++)
1655         valreal[ind_bil] /= 1024.;
1656 #if defined(HAVE_MPI)
1657       if (cs_glob_n_ranks > 1 && cs_glob_rank_id == 0) {
1658         for (imin = 0;
1659              val_min[ind_bil].val > 1024. && imin < 8;
1660              imin++)
1661           val_min[ind_bil].val /= 1024.;
1662         for (imax = 0;
1663              val_max[ind_bil].val > 1024. && imax < 8;
1664              imax++)
1665           val_max[ind_bil].val /= 1024.;
1666       }
1667 #endif
1668 
1669       /* Print to log file */
1670 
1671       if (ind_bil < 2 || cs_glob_n_ranks < 2)
1672         cs_log_printf(CS_LOG_PERFORMANCE,
1673                       _("  %s %12.3f %ciB\n"),
1674                       _(type_bil[ind_bil]), valreal[ind_bil], unit[itot]);
1675       else
1676         cs_log_printf(CS_LOG_PERFORMANCE,
1677                       _("  %s\n"),
1678                       _(type_bil[ind_bil]));
1679 
1680 #if defined(HAVE_MPI)
1681       if (cs_glob_n_ranks > 1 && cs_glob_rank_id == 0) {
1682         cs_log_printf(CS_LOG_PERFORMANCE,
1683                       _("                             "
1684                         "local minimum: %12.3f %ciB  (rank %d)\n"),
1685                       val_min[ind_bil].val, unit[imin], val_min[ind_bil].rank);
1686         cs_log_printf(CS_LOG_PERFORMANCE,
1687                       _("                             "
1688                         "local maximum: %12.3f %ciB  (rank %d)\n"),
1689                       val_max[ind_bil].val, unit[imax], val_max[ind_bil].rank);
1690       }
1691 #endif
1692     }
1693 
1694   }
1695 
1696   cs_log_printf(CS_LOG_PERFORMANCE, "\n");
1697   cs_log_separator(CS_LOG_PERFORMANCE);
1698 
1699   /* Finalize extra communicators now as they use memory allocated through
1700      bft_mem_* API */
1701 
1702 #if defined(HAVE_MPI)
1703   _finalize_reduced_communicators();
1704 #endif
1705 
1706   /* Finalize memory handling */
1707 
1708   if (cs_glob_base_bft_mem_init == true) {
1709 
1710     BFT_FREE(_cs_base_env_localedir);
1711     BFT_FREE(_cs_base_env_pkgdatadir);
1712     BFT_FREE(_cs_base_env_pkglibdir);
1713     BFT_FREE(_bft_printf_file_name);
1714 
1715     bft_mem_end();
1716 
1717   }
1718 
1719   /* Finalize memory usage count */
1720 
1721   bft_mem_usage_end();
1722 }
1723 
1724 /*----------------------------------------------------------------------------
1725  * Print summary of running time, including CPU and elapsed times.
1726  *----------------------------------------------------------------------------*/
1727 
1728 void
cs_base_time_summary(void)1729 cs_base_time_summary(void)
1730 {
1731   double  utime;
1732   double  stime;
1733   double  time_cpu;
1734   double  time_tot;
1735 
1736   /*xxxxxxxxxxxxxxxxxxxxxxxxxxx Instructions xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
1737 
1738   cs_log_printf(CS_LOG_PERFORMANCE,
1739                 _("\nCalculation time summary:\n"));
1740 
1741   cs_timer_cpu_times(&utime, &stime);
1742 
1743   if (utime > 0. || stime > 0.)
1744     time_cpu = utime + stime;
1745 
1746   else
1747     time_cpu = cs_timer_cpu_time();
1748 
1749   /* CPU time */
1750 
1751   if (utime > 0. || stime > 0.) {
1752     cs_log_printf(CS_LOG_PERFORMANCE,
1753                   _("\n  User CPU time:       %12.3f s\n"),
1754                   (float)utime);
1755     cs_log_printf(CS_LOG_PERFORMANCE,
1756                   _("  System CPU time:     %12.3f s\n"),
1757                   (float)stime);
1758   }
1759 
1760   else if (time_cpu > 0.)
1761     cs_log_printf(CS_LOG_PERFORMANCE,
1762                   _("\n  CPU time:            %12.3f s\n"),
1763                   (float)time_cpu);
1764 
1765 #if defined(HAVE_MPI)
1766   if (cs_glob_n_ranks > 1) {
1767     double time_cumul;
1768     MPI_Reduce (&time_cpu, &time_cumul, 1, MPI_DOUBLE, MPI_SUM,
1769                 0, cs_glob_mpi_comm);
1770     if (cs_glob_rank_id == 0)
1771       cs_log_printf(CS_LOG_PERFORMANCE,
1772                     _("  Total CPU time:      %12.3f s\n"),
1773                     time_cumul);
1774   }
1775 #endif
1776 
1777   /* Elapsed (wall-clock) time */
1778 
1779   time_tot = cs_timer_wtime();
1780 
1781   if (time_tot > 0.) {
1782 
1783     cs_log_printf(CS_LOG_PERFORMANCE,
1784                   _("\n  Elapsed time:        %12.3f s\n"),
1785                   time_tot);
1786 
1787     cs_log_printf(CS_LOG_PERFORMANCE,
1788                   _("  CPU / elapsed time   %12.3f\n"),
1789                   (float)(time_cpu/time_tot));
1790 
1791   }
1792 
1793   cs_log_printf(CS_LOG_PERFORMANCE, "\n");
1794   cs_log_separator(CS_LOG_PERFORMANCE);
1795 }
1796 
1797 /*----------------------------------------------------------------------------*/
1798 /*!
1799  * \brief Update status file.
1800  *
1801  * If the format string is NULL, the file is removed.
1802 
1803  * \param[in]  format  format string, or NULL
1804  * \param[in]  ...     format arguments
1805  */
1806 /*----------------------------------------------------------------------------*/
1807 
1808 void
cs_base_update_status(const char * format,...)1809 cs_base_update_status(const char  *format,
1810                       ...)
1811 {
1812   static const char _status_file_name[] = "run_status.running";
1813   static FILE *_status_file = NULL;
1814 
1815   if (cs_glob_rank_id < 1) {
1816 
1817     if (format == NULL) {
1818       if (_status_file != NULL) {
1819         if (fclose(_status_file) == 0) {
1820           _status_file = NULL;
1821           remove(_status_file_name);
1822         }
1823       }
1824     }
1825 
1826     else {
1827 
1828       va_list  arg_ptr;
1829       va_start(arg_ptr, format);
1830 
1831       /* Output to trace */
1832 
1833 #if defined(va_copy) || defined(__va_copy)
1834       if (_cs_trace && format != NULL) {
1835         va_list arg_ptr_2;
1836 #if defined(va_copy)
1837         va_copy(arg_ptr_2, arg_ptr);
1838 #else
1839         __va_copy(arg_ptr_2, arg_ptr);
1840 #endif
1841         vprintf(format, arg_ptr_2);
1842         va_end(arg_ptr_2);
1843       }
1844 #endif
1845 
1846       /* Status file */
1847 
1848       if (_status_file == NULL)
1849         _status_file = fopen(_status_file_name, "w");
1850 
1851       if (_status_file != NULL) {
1852         long p_size = ftell(_status_file);
1853         fseek(_status_file, 0, SEEK_SET);
1854         vfprintf(_status_file, format, arg_ptr);
1855         long c_size = ftell(_status_file);
1856 
1857         while (p_size > c_size) {
1858           size_t l = 0;
1859           char buf[64];
1860           while (l < 64 && p_size > c_size) {
1861             buf[l++] = ' ';
1862             p_size--;
1863           }
1864           fwrite(buf, 1, l, _status_file);
1865         }
1866       }
1867 
1868       va_end(arg_ptr);
1869 
1870     }
1871 
1872   }
1873 }
1874 
1875 /*----------------------------------------------------------------------------
1876  * Set tracing of progress on or off.
1877  *
1878  * This function should be called before cs_base_bft_printf_set() if tracing
1879  * is activated.
1880  *
1881  * parameters:
1882  *   trace  <-- trace progress to stdout
1883  *----------------------------------------------------------------------------*/
1884 
1885 void
cs_base_trace_set(bool trace)1886 cs_base_trace_set(bool  trace)
1887 {
1888   if (_bft_printf_file_name == NULL)
1889     _cs_trace = trace;
1890 }
1891 
1892 /*----------------------------------------------------------------------------
1893  * Set output file name and suppression flag for bft_printf().
1894  *
1895  * This allows redirecting or suppressing logging for different ranks.
1896  *
1897  * parameters:
1898  *   log_name    <-- base file name for log
1899  *   rn_log_flag <-- redirection for ranks > 0 log:
1900  *                   false: to "/dev/null" (suppressed)
1901  *                   true: to <log_name>_r*.log" file;
1902  *----------------------------------------------------------------------------*/
1903 
1904 void
cs_base_bft_printf_init(const char * log_name,bool rn_log_flag)1905 cs_base_bft_printf_init(const char  *log_name,
1906                         bool         rn_log_flag)
1907 {
1908   BFT_FREE(_bft_printf_file_name);
1909   _bft_printf_suppress = false;
1910 
1911   const char ext[] = ".log";
1912 
1913   /* Allow bypassing this with environment variable to accommodate
1914      some debug habits */
1915 
1916   bool log_to_stdout = false;
1917   const char *p = getenv("CS_LOG_TO_STDOUT");
1918   if (p != NULL) {
1919     if (atoi(p) > 0)
1920       log_to_stdout = true;
1921   }
1922 
1923   /* Rank 0 */
1924 
1925   if (   cs_glob_rank_id < 1
1926       && log_name != NULL
1927       && log_to_stdout == false) {
1928 
1929     BFT_MALLOC(_bft_printf_file_name,
1930                strlen(log_name) + strlen(ext) + 1,
1931                char);
1932     strcpy(_bft_printf_file_name, log_name);
1933     strcat(_bft_printf_file_name, ext);
1934 
1935   }
1936 
1937   /* Other ranks */
1938 
1939   else if (cs_glob_rank_id > 0) {
1940 
1941     if (log_name != NULL && rn_log_flag > 0) { /* Non-suppressed logs */
1942 
1943       if (log_to_stdout == false) {
1944         int n_dec = 1;
1945         for (int i = cs_glob_n_ranks; i >= 10; i /= 10, n_dec += 1);
1946         BFT_MALLOC(_bft_printf_file_name,
1947                    strlen(log_name) + n_dec + 3 + strlen(ext), char);
1948         sprintf(_bft_printf_file_name,
1949                 "%s_r%0*d%s",
1950                 log_name,
1951                 n_dec,
1952                 cs_glob_rank_id,
1953                 ext);
1954       }
1955 
1956     }
1957 
1958     else { /* Suppressed logs */
1959 
1960       _bft_printf_suppress = true;
1961       bft_printf_proxy_set(_cs_base_bft_printf_null);
1962       bft_printf_flush_proxy_set(_cs_base_bft_printf_flush_null);
1963       ple_printf_function_set(_cs_base_bft_printf_null);
1964 
1965     }
1966 
1967   }
1968 }
1969 
1970 /*----------------------------------------------------------------------------
1971  * Replace default bft_printf() mechanism with internal mechanism.
1972  *
1973  * This allows redirecting or suppressing logging for different ranks.
1974  *
1975  * parameters:
1976  *   log_name    <-- base file name for log
1977  *   rn_log_flag <-- redirection for ranks > 0 log:
1978  *                   false: to "/dev/null" (suppressed)
1979  *                   true: to <log_name>_r*.log" file;
1980  *----------------------------------------------------------------------------*/
1981 
1982 void
cs_base_bft_printf_set(const char * log_name,bool rn_log_flag)1983 cs_base_bft_printf_set(const char  *log_name,
1984                        bool         rn_log_flag)
1985 {
1986   cs_base_bft_printf_init(log_name, rn_log_flag);
1987 
1988   if (_bft_printf_file_name != NULL && _bft_printf_suppress == false) {
1989 
1990     /* Redirect log */
1991 
1992     if (_bft_printf_file_name != NULL) {
1993 
1994       bft_printf_proxy_set(vprintf);
1995       bft_printf_flush_proxy_set(_cs_base_bft_printf_flush);
1996       ple_printf_function_set(vprintf);
1997 
1998       if (cs_glob_rank_id > 0 || _cs_trace == false) {
1999 
2000         FILE *fp = freopen(_bft_printf_file_name, "w", stdout);
2001 
2002         if (fp == NULL)
2003           bft_error(__FILE__, __LINE__, errno,
2004                     _("It is impossible to redirect the standard output "
2005                       "to file:\n%s"), _bft_printf_file_name);
2006 
2007 #if defined(HAVE_DUP2)
2008         if (dup2(fileno(fp), fileno(stderr)) == -1)
2009           bft_error(__FILE__, __LINE__, errno,
2010                     _("It is impossible to redirect the standard error "
2011                       "to file:\n%s"), _bft_printf_file_name);
2012 #endif
2013 
2014       }
2015       else {
2016 
2017         _bft_printf_file = fopen(_bft_printf_file_name, "w");
2018         if (_bft_printf_file == NULL)
2019           bft_error(__FILE__, __LINE__, errno,
2020                     _("Error opening log file:\n%s"),
2021                     _bft_printf_file_name);
2022 
2023         bft_printf_proxy_set(_cs_base_bft_printf_file);
2024         bft_printf_flush_proxy_set(_cs_base_bft_printf_flush_file);
2025         ple_printf_function_set(_cs_base_bft_printf_file);
2026 
2027       }
2028 
2029     }
2030 
2031   }
2032 
2033 }
2034 
2035 /*----------------------------------------------------------------------------
2036  * Return name of default log file.
2037  *
2038  * cs_base_bft_printf_set or cs_base_c_bft_printf_set() must have
2039  * been called before this.
2040  *
2041  * returns:
2042  *   name of default log file
2043  *----------------------------------------------------------------------------*/
2044 
2045 const char *
cs_base_bft_printf_name(void)2046 cs_base_bft_printf_name(void)
2047 {
2048   return _bft_printf_file_name;
2049 }
2050 
2051 /*----------------------------------------------------------------------------
2052  * Return flag indicating if the default log file output is suppressed.
2053  *
2054  * cs_base_bft_printf_set or cs_base_c_bft_printf_set() must have
2055  * been called before this.
2056  *
2057  * returns:
2058  *   name of default log file
2059  *----------------------------------------------------------------------------*/
2060 
2061 bool
cs_base_bft_printf_suppressed(void)2062 cs_base_bft_printf_suppressed(void)
2063 {
2064   return _bft_printf_suppress;
2065 }
2066 
2067 /*----------------------------------------------------------------------------
2068  * Print a warning message header.
2069  *
2070  * parameters:
2071  *   file_name <-- name of source file
2072  *   line_nume <-- line number in source file
2073  *----------------------------------------------------------------------------*/
2074 
2075 void
cs_base_warn(const char * file_name,int line_num)2076 cs_base_warn(const char  *file_name,
2077              int          line_num)
2078 {
2079   bft_printf(_("\n\nCode_Saturne: %s:%d: Warning\n"),
2080              file_name, line_num);
2081 }
2082 
2083 /*----------------------------------------------------------------------------
2084  * Define a function to be called when entering cs_exit() or bft_error().
2085  *
2086  * Compared to the C atexit(), only one function may be called (latest
2087  * setting wins), but the function is called slightly before exit,
2088  * so it is well adapted to cleanup such as flushing of non-C API logging.
2089  *
2090  * parameters:
2091  *   fct <-- pointer tu function to be called
2092  *----------------------------------------------------------------------------*/
2093 
2094 void
cs_base_atexit_set(cs_base_atexit_t * const fct)2095 cs_base_atexit_set(cs_base_atexit_t  *const fct)
2096 {
2097   _cs_base_atexit = fct;
2098 }
2099 
2100 /*----------------------------------------------------------------------------
2101  * Convert a character string from the Fortran API to the C API.
2102  *
2103  * Eventual leading and trailing blanks are removed.
2104  *
2105  * parameters:
2106  *   f_str <-- Fortran string
2107  *   f_len <-- Fortran string length
2108  *
2109  * returns:
2110  *   pointer to C string
2111  *----------------------------------------------------------------------------*/
2112 
2113 char *
cs_base_string_f_to_c_create(const char * f_str,int f_len)2114 cs_base_string_f_to_c_create(const char  *f_str,
2115                              int          f_len)
2116 {
2117   char * c_str = NULL;
2118   int    i, i1, i2, l;
2119 
2120   /* Initialization if necessary */
2121 
2122   if (cs_glob_base_str_init == false) {
2123     for (i = 0 ; i < CS_BASE_N_STRINGS ; i++)
2124       cs_glob_base_str_is_free[i] = true;
2125     cs_glob_base_str_init = true;
2126   }
2127 
2128   /* Handle name for C API */
2129 
2130   for (i1 = 0 ;
2131        i1 < f_len && (f_str[i1] == ' ' || f_str[i1] == '\t') ;
2132        i1++);
2133 
2134   for (i2 = f_len - 1 ;
2135        i2 > i1 && (f_str[i2] == ' ' || f_str[i2] == '\t') ;
2136        i2--);
2137 
2138   l = i2 - i1 + 1;
2139 
2140   /* Allocation if necessary */
2141 
2142   if (l < CS_BASE_STRING_LEN) {
2143     for (i = 0 ; i < CS_BASE_N_STRINGS ; i++) {
2144       if (cs_glob_base_str_is_free[i] == true) {
2145         c_str = cs_glob_base_str[i];
2146         cs_glob_base_str_is_free[i] = false;
2147         break;
2148       }
2149     }
2150   }
2151 
2152   if (c_str == NULL)
2153     BFT_MALLOC(c_str, l + 1, char);
2154 
2155   for (i = 0 ; i < l ; i++, i1++)
2156     c_str[i] = f_str[i1];
2157 
2158   c_str[l] = '\0';
2159 
2160   return c_str;
2161 }
2162 
2163 /*----------------------------------------------------------------------------
2164  * Free a string converted from the Fortran API to the C API.
2165  *
2166  * parameters:
2167  *   str <-> pointer to C string
2168  *----------------------------------------------------------------------------*/
2169 
2170 void
cs_base_string_f_to_c_free(char ** c_str)2171 cs_base_string_f_to_c_free(char  **c_str)
2172 {
2173   int ind;
2174 
2175   for (ind = 0; ind < CS_BASE_N_STRINGS; ind++) {
2176     if (*c_str == cs_glob_base_str[ind]) {
2177       cs_glob_base_str_is_free[ind] = true;
2178       *c_str = NULL;
2179       break;
2180     }
2181   }
2182 
2183   if (ind == CS_BASE_N_STRINGS && *c_str != NULL)
2184     BFT_FREE(*c_str);
2185 }
2186 
2187 /*----------------------------------------------------------------------------
2188  * Clean a string representing options.
2189  *
2190  * Characters are converted to lowercase, leading and trailing whitespace
2191  * is removed, and multiple whitespaces or tabs are replaced by single
2192  * spaces.
2193  *
2194  * parameters:
2195  *   s <-> string to be cleaned
2196  *----------------------------------------------------------------------------*/
2197 
2198 void
cs_base_option_string_clean(char * s)2199 cs_base_option_string_clean(char  *s)
2200 {
2201   if (s != NULL) {
2202 
2203     int i, j;
2204 
2205     int l = strlen(s);
2206 
2207     for (i = 0, j = 0 ; i < l ; i++) {
2208       s[j] = tolower(s[i]);
2209       if (s[j] == ',' || s[j] == ';' || s[j] == '\t')
2210         s[j] = ' ';
2211       if (s[j] != ' ' || (j > 0 && s[j-1] != ' '))
2212         j++;
2213     }
2214     if (j > 0 && s[j-1] == ' ')
2215       j--;
2216 
2217     s[j] = '\0';
2218   }
2219 }
2220 
2221 /*----------------------------------------------------------------------------
2222  * Return a string providing locale path information.
2223  *
2224  * returns:
2225  *   locale path
2226  *----------------------------------------------------------------------------*/
2227 
2228 const char *
cs_base_get_localedir(void)2229 cs_base_get_localedir(void)
2230 {
2231   return _get_path("share/locale",
2232                    _cs_base_build_localedir,
2233                    &_cs_base_env_localedir);
2234 }
2235 
2236 /*----------------------------------------------------------------------------
2237  * Return a string providing package data path information.
2238  *
2239  * returns:
2240  *   package data path
2241  *----------------------------------------------------------------------------*/
2242 
2243 const char *
cs_base_get_pkgdatadir(void)2244 cs_base_get_pkgdatadir(void)
2245 {
2246   return _get_path("share/" PACKAGE_NAME,
2247                    _cs_base_build_pkgdatadir,
2248                    &_cs_base_env_pkgdatadir);
2249 }
2250 
2251 /*----------------------------------------------------------------------------
2252  * Return a string providing loadable library path information.
2253  *
2254  * This is normally the path determined upon configuration, but may be
2255  * adapted for movable installs using the CS_ROOT_DIR environment variable.
2256  *
2257  * returns:
2258  *   package loadable library (plugin) path
2259  *----------------------------------------------------------------------------*/
2260 
2261 const char *
cs_base_get_pkglibdir(void)2262 cs_base_get_pkglibdir(void)
2263 {
2264   return _get_path("lib/" PACKAGE_NAME,
2265                    _cs_base_build_pkglibdir,
2266                    &_cs_base_env_pkglibdir);
2267 }
2268 
2269 /*----------------------------------------------------------------------------
2270  * Ensure bool argument has value 0 or 1.
2271  *
2272  * This allows working around issues with Intel compiler C bindings,
2273  * which seem to pass incorrect values in some cases.
2274  *
2275  * parameters:
2276  *   b <-> pointer to bool
2277  *----------------------------------------------------------------------------*/
2278 
2279 void
cs_base_check_bool(bool * b)2280 cs_base_check_bool(bool *b)
2281 {
2282   if (sizeof(bool) == 1) {
2283     char *pb = (char *)b;
2284     int i = *pb;
2285     if (i != 0 && i != 1)
2286       *b = true;
2287   }
2288   else if (sizeof(bool) == sizeof(int)) {
2289     int *pb = (int *)b;
2290     if (*pb != 0 && *pb != 1)
2291       *b = true;
2292   }
2293 }
2294 
2295 /*----------------------------------------------------------------------------
2296  * Open a data file in read mode.
2297  *
2298  * If a file of the given name in the working directory is found, it
2299  * will be opened. Otherwise, it will be searched for in the "data/thch"
2300  * subdirectory of pkgdatadir.
2301  *
2302  * parameters:
2303  *   base_name      <-- base file name
2304  *
2305  * returns:
2306  *   pointer to opened file
2307  *----------------------------------------------------------------------------*/
2308 
2309 FILE *
cs_base_open_properties_data_file(const char * base_name)2310 cs_base_open_properties_data_file(const char  *base_name)
2311 {
2312   FILE *f = NULL;
2313 
2314   char *_f_name = NULL;
2315   const char *file_name = base_name;
2316 
2317   /* choose local file if present, default otherwise */
2318 
2319   if (! cs_file_isreg(file_name)) {
2320     const char *datadir = cs_base_get_pkgdatadir();
2321     const char subdir[] = "/data/thch/";
2322     BFT_MALLOC(_f_name,
2323                strlen(datadir) + strlen(subdir) + strlen(base_name) + 1,
2324                char);
2325     sprintf(_f_name, "%s%s%s", datadir, subdir, base_name);
2326     file_name = _f_name;
2327   }
2328 
2329   f = fopen(file_name, "r");
2330 
2331   if (f == NULL)
2332     bft_error(__FILE__, __LINE__, errno,
2333               _("Error opening data file \"%s\""), file_name);
2334 
2335   BFT_FREE(_f_name);
2336 
2337   return f;
2338 }
2339 
2340 #if defined(HAVE_DLOPEN)
2341 
2342 /*----------------------------------------------------------------------------*/
2343 /*!
2344  * \brief Load a dynamic library.
2345  *
2346  * \param[in]  filename  path to shared library file
2347  *
2348  * \return  handle to shared library
2349  */
2350 /*----------------------------------------------------------------------------*/
2351 
2352 void*
cs_base_dlopen(const char * filename)2353 cs_base_dlopen(const char *filename)
2354 {
2355   void *retval = NULL;
2356 
2357   /* Disable floating-point traps as the initialization of some libraries
2358      may interfere with this (for example, embree, and optional ParaView
2359      dependency) */
2360 
2361   cs_fp_exception_disable_trap();
2362 
2363   /* Load symbols from shared library */
2364 
2365   retval = dlopen(filename, _cs_dlopen_flags);
2366 
2367   if (retval == NULL)
2368     bft_error(__FILE__, __LINE__, 0,
2369               _("Error loading %s: %s."), filename, dlerror());
2370 
2371   /* Restore floating-point trap behavior */
2372 
2373   cs_fp_exception_restore_trap();
2374 
2375   return retval;
2376 }
2377 
2378 /*----------------------------------------------------------------------------*/
2379 /*!
2380  * \brief Load a plugin's dynamic library
2381  *
2382  * This function is similar to \ref cs_base_dlopen, except that only
2383  * the base plugin file name (with no extension) needs to be given.
2384  * It is assumed the file is available in the code's "pkglibdir" directory,
2385  *
2386  * \param[in]  name  path to shared library file
2387  *
2388  * \return  handle to shared library
2389  */
2390 /*----------------------------------------------------------------------------*/
2391 
2392 void *
cs_base_dlopen_plugin(const char * name)2393 cs_base_dlopen_plugin(const char *name)
2394 {
2395   void *retval = NULL;
2396 
2397   char  *lib_path = NULL;
2398   const char *pkglibdir = cs_base_get_pkglibdir();
2399 
2400   /* Open shared library */
2401 
2402   BFT_MALLOC(lib_path,
2403              strlen(pkglibdir) + 1 + 3 + strlen(name) + 3 + 1,
2404              char);
2405 
2406   sprintf(lib_path, "%s%c%s.so", pkglibdir, DIR_SEPARATOR, name);
2407 
2408   retval = cs_base_dlopen(lib_path);
2409 
2410   BFT_FREE(lib_path);
2411 
2412   return retval;
2413 }
2414 
2415 /*----------------------------------------------------------------------------*/
2416 /*!
2417  * \brief Get flags for dlopen.
2418  *
2419  * \return  flags used for dlopen.
2420  */
2421 /*----------------------------------------------------------------------------*/
2422 
2423 int
cs_base_dlopen_get_flags(void)2424 cs_base_dlopen_get_flags(void)
2425 {
2426   return _cs_dlopen_flags;
2427 }
2428 
2429 /*----------------------------------------------------------------------------*/
2430 /*!
2431  * \brief Set flags for dlopen.
2432  *
2433  * \param[in]  flags  flags to set
2434  */
2435 /*----------------------------------------------------------------------------*/
2436 
2437 void
cs_base_dlopen_set_flags(int flags)2438 cs_base_dlopen_set_flags(int flags)
2439 {
2440   _cs_dlopen_flags = flags;
2441 }
2442 
2443 /*----------------------------------------------------------------------------*/
2444 /*!
2445  * \brief Unload a dynamic library.
2446  *
2447  * Note that the dlopen underlying mechanism uses a reference count, so
2448  * a library is really unloaded only one \ref cs_base_dlclose has been called
2449  * the same number of times as \ref cs_base_dlopen.
2450  *
2451  * \param[in]  filename  optional path to shared library file name for error
2452  *                       logging, or NULL
2453  * \param[in]  handle    handle to shared library
2454  */
2455 /*----------------------------------------------------------------------------*/
2456 
2457 void
cs_base_dlclose(const char * filename,void * handle)2458 cs_base_dlclose(const char  *filename,
2459                 void        *handle)
2460 {
2461   int retval = 0;
2462 
2463   if (handle != NULL)
2464     retval = dlclose(handle);
2465 
2466   if (retval != 0) {
2467     if (filename != NULL)
2468       bft_error(__FILE__, __LINE__, 0,
2469                 _("Error decrementing count or unloading %s: %s."),
2470                 filename, dlerror());
2471     else
2472       bft_error(__FILE__, __LINE__, 0,
2473                 _("Error decrementing count or unloading %s."),
2474                 dlerror());
2475   }
2476 }
2477 
2478 /*----------------------------------------------------------------------------*/
2479 /*!
2480  * \brief Get a shared library function pointer
2481  *
2482  * \param[in]  handle            handle to shared library
2483  * \param[in]  name              name of function symbol in library
2484  * \param[in]  errors_are_fatal  abort if true, silently ignore if false
2485  *
2486  * \return  pointer to function in shared library
2487  */
2488 /*----------------------------------------------------------------------------*/
2489 
2490 void *
cs_base_get_dl_function_pointer(void * handle,const char * name,bool errors_are_fatal)2491 cs_base_get_dl_function_pointer(void        *handle,
2492                                 const char  *name,
2493                                 bool         errors_are_fatal)
2494 {
2495   void  *retval = NULL;
2496   char  *error = NULL;
2497 
2498   dlerror();    /* Clear any existing error */
2499 
2500   retval = dlsym(handle, name);
2501   error = dlerror();
2502 
2503   if (error != NULL && errors_are_fatal)
2504     bft_error(__FILE__, __LINE__, 0,
2505               _("Error calling dlsym for %s: %s\n"), name, error);
2506 
2507   return retval;
2508 }
2509 
2510 #endif /* defined(HAVE_DLOPEN) */
2511 
2512 /*----------------------------------------------------------------------------*/
2513 /*!
2514  * \brief Dump a stack trace to a file
2515  *
2516  * \param[in]  f         pointer to file in which to dump trace
2517  * \param[in]  lv_start  start level in stack trace
2518  */
2519 /*----------------------------------------------------------------------------*/
2520 
2521 void
cs_base_backtrace_dump(FILE * f,int lv_start)2522 cs_base_backtrace_dump(FILE  *f,
2523                        int    lv_start)
2524 {
2525   bft_backtrace_t  *tr = NULL;
2526 
2527   tr = bft_backtrace_create();
2528 
2529   if (tr != NULL) {
2530 
2531     char s_func_buf[67];
2532 
2533     const char *s_file;
2534     const char *s_func;
2535     const char *s_addr;
2536 
2537     const char s_unknown[] = "?";
2538     const char s_vide[] = "";
2539     const char *s_prefix = s_vide;
2540 
2541     int nbr = bft_backtrace_size(tr);
2542 
2543     if (nbr > 0)
2544       fprintf(f, "\nCall stack:\n");
2545 
2546     for (int ind = lv_start; ind < nbr; ind++) {
2547 
2548       s_file = bft_backtrace_file(tr, ind);
2549       s_func = bft_backtrace_function(tr, ind);
2550       s_addr = bft_backtrace_address(tr, ind);
2551 
2552       if (s_file == NULL)
2553         s_file = s_unknown;
2554       if (s_func == NULL)
2555         strcpy(s_func_buf, "?");
2556       else {
2557         s_func_buf[0] = '<';
2558         strncpy(s_func_buf + 1, s_func, 64);
2559         strcat(s_func_buf, ">");
2560       }
2561       if (s_addr == NULL)
2562         s_addr = s_unknown;
2563 
2564       fprintf(f, "%s%4d: %-12s %-32s (%s)\n", s_prefix,
2565               ind-lv_start+1, s_addr, s_func_buf, s_file);
2566 
2567     }
2568 
2569     bft_backtrace_destroy(tr);
2570 
2571     if (nbr > 0)
2572       fprintf(f, "End of stack\n\n");
2573   }
2574 }
2575 
2576 /*----------------------------------------------------------------------------*/
2577 /*!
2578  * \brief Query run-time directory info, using working directory names.
2579  *
2580  * Returned names are allocated if non-NULL, so should be deallocated by
2581  * the caller when no longer needed.
2582  *
2583  * Names are extracted from the working directory structure, which is expected
2584  * to be of the form:
2585  * <prefix>/study_name/case_name/RESU/run_id
2586  *
2587  * or, in the case o a coupled run:
2588  * <prefix>/study_name/RESU_COUPLING/run_id/case_name
2589  *
2590  * If some names cannot be queried, NULL is returned.
2591  *
2592  * \param[out]  run_id      run_id, or NULL
2593  * \param[out]  case_name   case name, or NULL
2594  * \param[out]  study_name  study name, or NULL
2595  */
2596 /*----------------------------------------------------------------------------*/
2597 
2598 void
cs_base_get_run_identity(char ** run_id,char ** case_name,char ** study_name)2599 cs_base_get_run_identity(char  **run_id,
2600                          char  **case_name,
2601                          char  **study_name)
2602 {
2603   /* Use execution directory if name is unavailable */
2604 
2605   const char *c[4] = {NULL, NULL, NULL, NULL};
2606 
2607   if (run_id != NULL)
2608     *run_id = NULL;
2609   if (case_name != NULL)
2610     *case_name = NULL;
2611   if (study_name != NULL)
2612     *study_name = NULL;
2613 
2614 #if defined(HAVE_GETCWD)
2615 
2616   int buf_size = 128;
2617   char *wd = NULL, *buf = NULL;
2618 
2619   while (wd == NULL) {
2620     buf_size *= 2;
2621     BFT_REALLOC(buf, buf_size, char);
2622     wd = getcwd(buf, buf_size);
2623     if (wd == NULL && errno != ERANGE)
2624       bft_error(__FILE__, __LINE__, errno,
2625                 _("Error querying working directory.\n"));
2626   }
2627 
2628   int i = strlen(buf) - 1;
2629 
2630   for (int comp_id = 0; comp_id < 4; comp_id++) {
2631     while (i > 0 && buf[i-1] != '/')
2632       i--;
2633     if (i >= 0) {
2634       c[comp_id] = buf+i;
2635       if (i > 0) {
2636         i--;
2637         buf[i] = '\0';
2638       }
2639     }
2640     else
2641       break;
2642   }
2643 
2644   const char *_run_id = c[0];
2645   const char *_case_name = NULL;
2646   const char *_study_name = NULL;
2647 
2648   if (c[1] != NULL) {
2649     if (strcmp(c[1], "RESU") == 0) {
2650       _case_name = c[2];
2651       _study_name = c[3];
2652     }
2653     else if (c[2] != NULL) {
2654       if (strcmp(c[2], "RESU_COUPLING") == 0) {
2655         _run_id = c[1];
2656         _case_name = c[0];
2657         _study_name = c[3];
2658       }
2659     }
2660   }
2661 
2662   if (run_id != NULL && _run_id != NULL) {
2663     BFT_MALLOC(*run_id, strlen(_run_id) + 1, char);
2664     strcpy(*run_id, _run_id);
2665   }
2666   if (case_name != NULL && _case_name != NULL) {
2667     BFT_MALLOC(*case_name, strlen(_case_name) + 1, char);
2668     strcpy(*case_name, _case_name);
2669   }
2670   if (study_name != NULL && _study_name != NULL) {
2671     BFT_MALLOC(*study_name, strlen(_study_name) + 1, char);
2672     strcpy(*study_name, _study_name);
2673   }
2674 
2675   BFT_FREE(buf);
2676 
2677 #endif /* defined(HAVE_GETCWD) */
2678 
2679 }
2680 
2681 /*----------------------------------------------------------------------------*/
2682 
2683 END_C_DECLS
2684