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