1 /*============================================================================
2  * Base definitions and utility functions for PLE.
3  *============================================================================*/
4 
5 /*
6   This file is part of the "Parallel Location and Exchange" library,
7   intended to provide mesh or particle-based code coupling services.
8 
9   Copyright (C) 2005-2021  EDF S.A.
10 
11   This library is free software; you can redistribute it and/or
12   modify it under the terms of the GNU Lesser General Public
13   License as published by the Free Software Foundation; either
14   version 2.1 of the License, or (at your option) any later version.
15 
16   This library is distributed in the hope that it will be useful,
17   but WITHOUT ANY WARRANTY; without even the implied warranty of
18   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19   Lesser General Public License for more details.
20 
21   You should have received a copy of the GNU Lesser General Public
22   License along with this library; if not, write to the Free Software
23   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
24 */
25 
26 /*!
27  * \file ple_defs.c
28  *
29  * \brief Configurable error and memory handling with default functions.
30  */
31 
32 /*-----------------------------------------------------------------------------*/
33 
34 #include "ple_config_defs.h"
35 
36 /*
37  * Standard C library headers
38  */
39 
40 #include <assert.h>
41 #include <errno.h>
42 #include <stdarg.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 
47 #include <time.h>
48 
49 #if defined (HAVE_GETTIMEOFDAY)
50 #include <sys/time.h>
51 #endif
52 
53 #if defined (HAVE_GETRUSAGE)
54 #include <sys/time.h>
55 #include <sys/resource.h>
56 #include <unistd.h>
57 #elif defined(_POSIX_SOURCE)
58 #include <sys/times.h>
59 #include <unistd.h>
60 #endif
61 
62 /*
63  * Optional library and PLE headers
64  */
65 
66 #include "ple_defs.h"
67 
68 /*-----------------------------------------------------------------------------*/
69 
70 #ifdef __cplusplus
71 extern "C" {
72 #if 0
73 } /* Fake brace to force Emacs auto-indentation back to column 0 */
74 #endif
75 #endif /* __cplusplus */
76 
77 /*-------------------------------------------------------------------------------
78  * Local macro documentation
79  *-----------------------------------------------------------------------------*/
80 
81 /*! \fn PLE_MALLOC(_ptr, _ni, _type)
82  * \brief Allocate memory for _ni elements of type _type.
83  *
84  * This macro calls ple_mem_malloc(), automatically setting the
85  * allocated variable name and source file name and line arguments.
86  *
87  * \param [out] _ptr  pointer to allocated memory.
88  * \param [in]  _ni   number of elements.
89  * \param [in]  _type element type.
90  */
91 
92 /*! \fn PLE_REALLOC(_ptr, _ni, _type)
93  * \brief Reallocate memory for _ni elements of type _type.
94  *
95  * This macro calls ple_mem_realloc(), automatically setting the
96  * allocated variable name and source file name and line arguments.
97  *
98  * \param [in, out] _ptr  pointer to allocated memory.
99  * \param [in]      _ni   number of elements.
100  * \param [in]      _type element type.
101  */
102 
103 /*! \fn PLE_FREE(_ptr)
104  * \brief Free allocated memory.
105  *
106  * This macro calls ple_mem_free(), automatically setting the
107  * allocated variable name and source file name and line arguments.
108  *
109  * The freed pointer is set to NULL to avoid accidental reuse.
110  *
111  * \param [in, out] _ptr  pointer to allocated memory.
112  */
113 
114 /*-----------------------------------------------------------------------------
115  * Internationalization (future)
116  *-----------------------------------------------------------------------------*/
117 
118 #if defined(ENABLE_NLS)
119 
120 #  include <libintl.h>
121 #  define _(String) dgettext(PACKAGE,String)
122 #  ifdef gettext_noop
123 #    define N_(String) gettext_noop(String)
124 #  else
125 #    define N_(String) String
126 #  endif /* gettext_noop */
127 
128 #else
129 
130 #  define _(String) (String)
131 #  define N_(String) String
132 #  define textdomain(String) (String)
133 #  define gettext(String) (String)
134 #  define dgettext(Domain,String) (String)
135 #  define dcgettext(Domain,String,Type) (String)
136 #  define bindtextdomain(Domain,Directory) (Domain)
137 
138 #endif
139 
140 /*-----------------------------------------------------------------------------
141  * Local function definitions
142  *-----------------------------------------------------------------------------*/
143 
144 /*
145  * Defauly error handler.
146  *
147  * parameters:
148  *   file_name:      <-- name of source file from which error handler called.
149  *   line_num:       <-- line of source file from which error handler called.
150  *   sys_error_code: <-- error code if error in system or libc call, 0 otherwise.
151  *   format:         <-- format string, as printf() and family.
152  *   arg_ptr:        <-> variable argument list based on format string.
153  */
154 
155 static void
_ple_error_default(const char * file_name,int line_num,int sys_error_code,const char * format,va_list arg_ptr)156 _ple_error_default(const char  *file_name,
157                    int          line_num,
158                    int          sys_error_code,
159                    const char  *format,
160                    va_list      arg_ptr)
161 {
162   fprintf(stderr, "\n");
163 
164   if (sys_error_code != 0)
165     fprintf(stderr, _("\nSystem error: %s\n"), strerror(sys_error_code));
166 
167   fprintf(stderr, _("\n%s:%d: Fatal error.\n\n"), file_name, line_num);
168 
169   vfprintf(stderr, format, arg_ptr);
170 
171   fprintf(stderr, "\n\n");
172 
173   assert(0);
174 
175   exit(EXIT_FAILURE);
176 }
177 
178 /*
179  * Default memory allocation.
180  *
181  * This function simply wraps malloc() and calls _ple_error() if it fails
182  * to allocate the required memory.
183  *
184  * parameters:
185  *   ni        <-- number of elements.
186  *   size      <-- element size.
187  *   var_name  <-- allocated variable name string.
188  *   file_name <-- name of calling source file.
189  *   line_num  <-- line number in calling source file.
190  *
191  * returns:
192  *   pointer to allocated memory.
193  */
194 
195 static void *
_ple_mem_malloc_default(size_t ni,size_t size,const char * var_name,const char * file_name,int line_num)196 _ple_mem_malloc_default(size_t       ni,
197                         size_t       size,
198                         const char  *var_name,
199                         const char  *file_name,
200                         int          line_num)
201 {
202   void  *p_ret;
203   size_t  alloc_size = ni * size;
204 
205   if (ni == 0)
206     return NULL;
207 
208   /* Allocate memory and check return */
209 
210   p_ret = malloc(alloc_size);
211 
212   if (p_ret == NULL)
213     ple_error(file_name, line_num, errno,
214               _("Failure to allocate \"%s\" (%lu bytes)"),
215               var_name, (unsigned long)alloc_size);
216 
217   return p_ret;
218 }
219 
220 /*
221  * Default memory reallocation.
222  *
223  * This function simply wraps realloc() and calls _ple_error() if it fails
224  * to reallocate the required memory.
225  *
226  * parameters:
227  *   ptr       <-- pointer to previous memory location
228  *   ni        <-- number of elements.
229  *   size      <-- element size.
230  *   var_name  <-- allocated variable name string.
231  *   file_name <-- name of calling source file.
232  *   line_num  <-- line number in calling source file.
233  *
234  * returns:
235  *   pointer to reallocated memory.
236  */
237 
238 static void *
_ple_mem_realloc_default(void * ptr,size_t ni,size_t size,const char * var_name,const char * file_name,int line_num)239 _ple_mem_realloc_default(void        *ptr,
240                          size_t       ni,
241                          size_t       size,
242                          const char  *var_name,
243                          const char  *file_name,
244                          int          line_num)
245 {
246   void  *p_ret;
247 
248   size_t realloc_size = ni * size;
249 
250   p_ret = realloc(ptr, realloc_size);
251 
252   if (realloc_size != 0 && p_ret == NULL)
253     ple_error(file_name, line_num, errno,
254               _("Failure to reallocate \"%s\" (%lu bytes)"),
255               var_name, (unsigned long)realloc_size);
256 
257   return p_ret;
258 }
259 
260 /*
261  * Default memory free.
262  *
263  * This function simply wraps free().
264  *
265  * parameters:
266  *   ptr       <-- pointer to previous memory location
267  *   var_name  <-- allocated variable name string
268  *   file_name <-- name of calling source file
269  *   line_num  <-- line number in calling source file
270  *
271  * returns:
272  *   NULL pointer.
273  */
274 
275 static void *
_ple_mem_free_default(void * ptr,const char * var_name,const char * file_name,int line_num)276 _ple_mem_free_default(void        *ptr,
277                       const char  *var_name,
278                       const char  *file_name,
279                       int          line_num)
280 {
281   PLE_UNUSED(var_name);
282   PLE_UNUSED(file_name);
283   PLE_UNUSED(line_num);
284 
285   if (ptr != NULL)
286     free(ptr);
287 
288   return NULL;
289 }
290 
291 /*-----------------------------------------------------------------------------
292  * Local static variable definitions
293  *-----------------------------------------------------------------------------*/
294 
295 /* Standard output handler */
296 
297 static ple_printf_t *_ple_printf = vprintf;
298 
299 /* Error handler */
300 
301 static ple_error_handler_t *_ple_error = _ple_error_default;
302 
303 /* Memory allocation */
304 
305 static ple_mem_malloc_t *_ple_mem_malloc = _ple_mem_malloc_default;
306 static ple_mem_realloc_t *_ple_mem_realloc = _ple_mem_realloc_default;
307 static ple_mem_free_t *_ple_mem_free = _ple_mem_free_default;
308 
309 /* Timer */
310 
311 static _Bool _ple_timer_initialized = false;
312 
313 #if defined (HAVE_GETTIMEOFDAY)
314 static struct timeval  _ple_timer_wtime_tv_start;
315 #else
316 static time_t _ple_timer_wtime_start;
317 #endif
318 
319 #if defined (HAVE_GETRUSAGE)
320 #elif defined(_POSIX_SOURCE)
321 static time_t _ple_timer_unit = 0;
322 #else
323 static clock_t _ple_timer_clock_start;
324 #endif
325 
326 /*-----------------------------------------------------------------------------
327  * Local function definitions
328  *-----------------------------------------------------------------------------*/
329 
330 static void
_ple_timer_initialize(void)331 _ple_timer_initialize(void)
332 {
333 #if defined (HAVE_GETTIMEOFDAY)
334   (void)gettimeofday(&_ple_timer_wtime_tv_start, NULL);
335 #else
336   (void)time(&_ple_timer_wtime_start);
337 #endif
338 
339 #if defined (HAVE_GETRUSAGE)
340 #elif defined(_POSIX_SOURCE)
341   _ple_timer_unit = (double)sysconf(_SC_CLK_TCK);
342 #else
343   _ple_timer_clock_start = clock();
344 #endif
345 
346   _ple_timer_initialized = true;
347 }
348 
349 /*============================================================================
350  * Public function definitions
351  *============================================================================*/
352 
353 /*!
354  * \brief Replacement for printf() with modifiable behavior.
355  *
356  * This function calls vprintf() by default, or a function with similar
357  * arguments indicated by ple_printf_function_set().
358  *
359  * \param [in] format format string, as printf() and family.
360  * \param [in] ...    variable arguments based on format string.
361  *
362  * \return number of characters printed, not counting the trailing '\\0'
363  *         used to end output strings
364  */
365 
366 int
ple_printf(const char * const format,...)367 ple_printf(const char *const format,
368            ...)
369 {
370   int  retval;
371   va_list  arg_ptr;
372 
373   va_start(arg_ptr, format);
374 
375   retval = _ple_printf(format, arg_ptr);
376 
377   va_end(arg_ptr);
378 
379   return retval;
380 }
381 
382 /*!
383  * \brief Returns function associated with the ple_printf() function.
384  *
385  * \return pointer to the vprintf() or replacement function.
386  */
387 
388 ple_printf_t *
ple_printf_function_get(void)389 ple_printf_function_get(void)
390 {
391   return _ple_printf;
392 }
393 
394 /*!
395  * \brief Associates a vprintf() type function with the ple_printf() function.
396  *
397  * \param [in] fct pointer to a vprintf() type function.
398  */
399 
400 void
ple_printf_function_set(ple_printf_t * const fct)401 ple_printf_function_set(ple_printf_t *const fct)
402 {
403   _ple_printf = fct;
404 }
405 
406 /*!
407  * \brief Calls the error handler (set by ple_error_handler_set() or default).
408  *
409  * With the default error handler, an error message is output to stderr,
410  * and the current process exits with an EXIT_FAILURE code.
411  *
412  * \param [in] file_name      name of source file from which error handler
413  *                            called.
414  * \param [in] line_num       line of source file from which error handler
415  *                            called.
416  * \param [in] sys_error_code error code if error in system or libc call,
417  *                            0 otherwise.
418  * \param [in] format         format string, as printf() and family.
419  * \param [in] ...            variable arguments based on format string.
420  */
421 
422 void
ple_error(const char * const file_name,const int line_num,const int sys_error_code,const char * const format,...)423 ple_error(const char  *const file_name,
424           const int          line_num,
425           const int          sys_error_code,
426           const char  *const format,
427           ...)
428 {
429   va_list  arg_ptr;
430 
431   va_start(arg_ptr, format);
432 
433   _ple_error(file_name, line_num, sys_error_code, format, arg_ptr);
434 
435   va_end(arg_ptr);
436 }
437 
438 /*!
439  * \brief Returns the error handler associated with the ple_error() function.
440  *
441  * \return pointer to the error handler function.
442  */
443 
444 ple_error_handler_t *
ple_error_handler_get(void)445 ple_error_handler_get(void)
446 {
447   return _ple_error;
448 }
449 
450 /*!
451  * \brief Associates an error handler with the ple_error() function.
452  *
453  * \param [in] handler pointer to the error handler function.
454  */
455 
456 void
ple_error_handler_set(ple_error_handler_t * handler)457 ple_error_handler_set(ple_error_handler_t  *handler)
458 {
459   _ple_error = handler;
460 }
461 
462 /*!
463  * \brief Allocate memory for ni elements of size bytes.
464  *
465  * This function calls malloc(), but adds tracing capabilities, and
466  * automatically calls the ple_error() errorhandler if it fails to
467  * allocate the required memory.
468  *
469  * \param [in] ni        number of elements.
470  * \param [in] size      element size.
471  * \param [in] var_name  allocated variable name string.
472  * \param [in] file_name name of calling source file.
473  * \param [in] line_num  line number in calling source file.
474  *
475  * \returns pointer to allocated memory.
476  */
477 
478 void *
ple_mem_malloc(size_t ni,size_t size,const char * var_name,const char * file_name,int line_num)479 ple_mem_malloc(size_t       ni,
480                size_t       size,
481                const char  *var_name,
482                const char  *file_name,
483                int          line_num)
484 {
485   return _ple_mem_malloc(ni, size, var_name, file_name, line_num);
486 }
487 
488 /*!
489  * \brief Reallocate memory for ni elements of size bytes.
490  *
491  * This function calls realloc(), but adds tracing capabilities, and
492  * automatically calls the ple_error() errorhandler if it fails to
493  * allocate the required memory.
494  *
495  * \param [in] ptr       pointer to previous memory location
496  *                       (if NULL, ple_alloc() called).
497  * \param [in] ni        number of elements.
498  * \param [in] size      element size.
499  * \param [in] var_name  allocated variable name string.
500  * \param [in] file_name name of calling source file.
501  * \param [in] line_num  line number in calling source file.
502  *
503  * \returns pointer to reallocated memory.
504  */
505 
506 void *
ple_mem_realloc(void * ptr,size_t ni,size_t size,const char * var_name,const char * file_name,int line_num)507 ple_mem_realloc(void        *ptr,
508                 size_t       ni,
509                 size_t       size,
510                 const char  *var_name,
511                 const char  *file_name,
512                 int          line_num)
513 {
514   return _ple_mem_realloc(ptr, ni, size, var_name, file_name, line_num);
515 }
516 
517 /*!
518  * \brief Free allocated memory.
519  *
520  * This function calls free(), but adds tracing capabilities, and
521  * automatically calls the ple_error() errorhandler if it fails to
522  * free the corresponding memory. In case of a NULL pointer argument,
523  * the function simply returns.
524  *
525  * \param [in] ptr       pointer to previous memory location
526  *                       (if NULL, ple_alloc() called).
527  * \param [in] var_name  allocated variable name string
528  * \param [in] file_name name of calling source file
529  * \param [in] line_num  line number in calling source file
530  *
531  * \returns NULL pointer.
532  */
533 
534 void *
ple_mem_free(void * ptr,const char * var_name,const char * file_name,int line_num)535 ple_mem_free(void        *ptr,
536              const char  *var_name,
537              const char  *file_name,
538              int          line_num)
539 {
540   return _ple_mem_free(ptr, var_name, file_name, line_num);
541 }
542 
543 /*!
544  * \brief Return the function pointers associated with PLE's memory management.
545  *
546  * All arguments are optional.
547  *
548  * \param [out] malloc_func  pointer to ple_mem_malloc function pointer
549  *                           (or NULL).
550  * \param [out] realloc_func pointer to ple_mem_realloc function pointer
551  *                           (or NULL).
552  * \param [out] free_func    pointer to ple_mem_free function pointer
553  *                           (or NULL).
554  */
555 
556 void
ple_mem_functions_get(ple_mem_malloc_t ** malloc_func,ple_mem_realloc_t ** realloc_func,ple_mem_free_t ** free_func)557 ple_mem_functions_get(ple_mem_malloc_t   **malloc_func,
558                       ple_mem_realloc_t  **realloc_func,
559                       ple_mem_free_t     **free_func)
560 {
561   if (malloc_func != NULL)
562     *malloc_func = _ple_mem_malloc;
563 
564   if (realloc_func != NULL)
565     *realloc_func = _ple_mem_realloc;
566 
567   if (free_func != NULL)
568     *free_func = _ple_mem_free;
569 }
570 
571 /*!
572  * \brief Associate functions to modifiy PLE's memory management.
573  *
574  * All arguments are optional, so the previously set functions pointers will
575  * not be modified if an argument value is NULL.
576  *
577  * \param [in] malloc_func  ple_mem_malloc function pointer (or NULL).
578  * \param [in] realloc_func ple_mem_realloc function pointer (or NULL).
579  * \param [in] free_func    ple_mem_free function pointer (or NULL).
580  */
581 
582 void
ple_mem_functions_set(ple_mem_malloc_t * malloc_func,ple_mem_realloc_t * realloc_func,ple_mem_free_t * free_func)583 ple_mem_functions_set(ple_mem_malloc_t   *malloc_func,
584                       ple_mem_realloc_t  *realloc_func,
585                       ple_mem_free_t     *free_func)
586 {
587   if (malloc_func != NULL)
588     _ple_mem_malloc = malloc_func;
589 
590   if (realloc_func != NULL)
591     _ple_mem_realloc = realloc_func;
592 
593   if (free_func != NULL)
594     _ple_mem_free = free_func;
595 }
596 
597 /*!
598  * \brief Return Wall clock time
599  *
600  * \return elapsed time from first call of a function of the ple_timer_...()
601  *         series, or -1 if unable to compute.
602  */
603 
604 double
ple_timer_wtime(void)605 ple_timer_wtime(void)
606 {
607   double this_wtime = -1.;
608 
609   /* Ensure initialization */
610 
611   if (_ple_timer_initialized == false)
612     _ple_timer_initialize();
613 
614   /* Compute elapsed time */
615 
616 #if defined (HAVE_GETTIMEOFDAY)
617 
618   {
619     struct timeval  wtime_tv_current;
620 
621     if (gettimeofday(&wtime_tv_current, NULL) == 0) {
622 
623       /* Perform carry for later subtraction */
624       if (_ple_timer_wtime_tv_start.tv_usec > wtime_tv_current.tv_usec) {
625         int nsec = (_ple_timer_wtime_tv_start.tv_usec - wtime_tv_current.tv_usec)
626                    / 1000000 + 1;
627         wtime_tv_current.tv_usec += 1000000 * nsec;
628         wtime_tv_current.tv_sec -= nsec;
629       }
630       if (  wtime_tv_current.tv_usec - _ple_timer_wtime_tv_start.tv_usec
631           > 1000000) {
632         int nsec = (wtime_tv_current.tv_usec - _ple_timer_wtime_tv_start.tv_usec)
633                    / 1000000;
634         wtime_tv_current.tv_usec -= 1000000 * nsec;
635         wtime_tv_current.tv_sec += nsec;
636       }
637 
638       this_wtime =   (  wtime_tv_current.tv_sec
639                       - _ple_timer_wtime_tv_start.tv_sec)
640                    + (  wtime_tv_current.tv_usec
641                       - _ple_timer_wtime_tv_start.tv_usec) * 1.e-6 ;
642     }
643   }
644 
645 #else
646 
647   {
648     time_t wtime_current;
649 
650     if (time(&wtime_current) != (time_t)-1)
651       this_wtime = difftime(wtime_current, _ple_timer_wtime_start);
652   }
653 
654 #endif
655 
656   return this_wtime;
657 }
658 
659 /*!
660  * \brief Return CPU time.
661  *
662  * Note that in the rare case that only the minimal C library clock()
663  * method is available (see ple_timer_cpu_time_method()), at least one of
664  * the ple_timer_...() functions (possibly this one) must be called
665  * upon program start for this function to be used. In addition,
666  * in this case, time may "loop" back to 0 every multiple of
667  * 2^size_t / CLOCKS_PER_SEC seconds.
668  *
669  * \return current CPU time usage, or -1 if unable to compute.
670  */
671 
672 double
ple_timer_cpu_time(void)673 ple_timer_cpu_time(void)
674 {
675   double cpu_time = -1.;
676 
677   /* Ensure initialization */
678 
679   if (_ple_timer_initialized == 0)
680     _ple_timer_initialize();
681 
682   /* Compute CPU time */
683 
684 #if defined (HAVE_GETRUSAGE)
685 
686   {
687     struct rusage  usage;
688 
689     if (getrusage(RUSAGE_SELF, &usage) == 0) {
690       cpu_time  =    usage.ru_utime.tv_sec  + usage.ru_stime.tv_sec
691                   + (usage.ru_utime.tv_usec + usage.ru_stime.tv_usec) * 1.e-6;
692     }
693   }
694 
695 #elif defined(_POSIX_SOURCE)
696 
697   {
698     static struct tms  ptimer;
699 
700     if (_ple_timer_unit != -1 && times(&ptimer) != -1) {
701       cpu_time =   ((double)(ptimer.tms_utime + ptimer.tms_stime))
702                  / _ple_timer_unit;
703     }
704   }
705 
706 #else /* Use minimal C library function */
707 
708   if (_ple_timer_clock_start != -1) {
709 
710     static clock_t  clock_current;
711 
712     clock_current = clock();
713     if (clock_current != (clock_t)-1)
714       cpu_time
715         = ((double)(clock_current - _ple_timer_clock_start)) / CLOCKS_PER_SEC;
716 
717   }
718 
719 #endif
720 
721   return cpu_time;
722 }
723 
724 /*----------------------------------------------------------------------------*/
725 
726 #ifdef __cplusplus
727 }
728 #endif /* __cplusplus */
729