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