1 /*============================================================================
2 * Base memory allocation wrappers with optional tracing
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 /*
28 Define _GNU_SOURCE if necessary before including any headers, to ensure
29 the correct feature macros are defined first.
30 */
31
32 #if defined(__linux__)
33 # define _GNU_SOURCE
34 #endif
35
36 #include "cs_defs.h"
37
38 /*-----------------------------------------------------------------------------*/
39
40 /*
41 * Standard C library headers
42 */
43
44 #include <assert.h>
45 #include <errno.h>
46 #include <stdlib.h>
47 #include <stdio.h>
48 #include <string.h>
49 #include <stdlib.h>
50
51 /*
52 * Optional library and BFT headers
53 */
54
55 #include "bft_error.h"
56 #include "bft_mem_usage.h"
57 #include "bft_mem.h"
58 #include "bft_printf.h"
59
60 /*-----------------------------------------------------------------------------*/
61
62 BEGIN_C_DECLS
63
64 /*=============================================================================
65 * Additional doxygen documentation
66 *============================================================================*/
67
68 /*!
69 \file bft_mem.c
70 Base memory allocation wrappers with optional tracing.
71
72 The memory managment function provided here provide optional logging,
73 and tracking of non-freed pointers.
74
75 Since in most of the intended applications, failure to allocate memory
76 is considered fatal, failed allocations from these functions are
77 considedered as errors, which are fatal by default but can be handled
78 differently if an appropriate error handler is defined. So additional
79 checking of the return values in the calling code is not needed.
80
81 The functions provided here are otherwise based on the matching C library
82 functions.
83 */
84
85 /*-------------------------------------------------------------------------------
86 * Local macro documentation
87 *-----------------------------------------------------------------------------*/
88
89 /*! \fn BFT_MALLOC(_ptr, _ni, _type)
90 * \brief Allocate memory for _ni elements of type _type.
91 *
92 * This macro calls bft_mem_malloc(), automatically setting the
93 * allocated variable name and source file name and line arguments.
94 *
95 * \param [out] _ptr pointer to allocated memory.
96 * \param [in] _ni number of elements.
97 * \param [in] _type element type.
98 */
99
100 /*! \fn BFT_REALLOC(_ptr, _ni, _type)
101 * \brief Reallocate memory for _ni elements of type _type.
102 *
103 * This macro calls bft_mem_realloc(), automatically setting the
104 * allocated variable name and source file name and line arguments.
105 *
106 * \param [in, out] _ptr pointer to allocated memory.
107 * \param [in] _ni number of elements.
108 * \param [in] _type element type.
109 */
110
111 /*! \fn BFT_FREE(_ptr)
112 * \brief Free allocated memory.
113 *
114 * This macro calls bft_mem_free(), automatically setting the
115 * allocated variable name and source file name and line arguments.
116 *
117 * The freed pointer is set to NULL to avoid accidental reuse.
118 *
119 * \param [in, out] _ptr pointer to allocated memory.
120 */
121
122 /*! \fn BFT_MEMALIGN(_ptr, _align, _ni, _type)
123 * \brief Allocate aligned memory for _ni elements of type _type.
124 *
125 * This macro calls bft_mem_memalign(), automatically setting the
126 * allocated variable name and source file name and line arguments.
127 *
128 * \param [out] _ptr pointer to allocated memory.
129 * \param [in] _align alignment.
130 * \param [in] _ni number of elements.
131 * \param [in] _type element type.
132 */
133
134 /*! \cond DOXYGEN_SHOULD_SKIP_THIS */
135
136 /*-------------------------------------------------------------------------------
137 * Local macro definitions
138 *-----------------------------------------------------------------------------*/
139
140 /* Directory name separator
141 (historically, '/' for Unix/Linux, '\' for Windows, ':' for Mac
142 but '/' should work for all on modern systems) */
143
144 #define DIR_SEPARATOR '/'
145
146 /*-------------------------------------------------------------------------------
147 * Local type definitions
148 *-----------------------------------------------------------------------------*/
149
150 /*
151 * Structure defining an allocated memory block (for memory tracing)
152 */
153
154 struct _bft_mem_block_t {
155
156 void *p_bloc; /* Allocated memory block start adress */
157 size_t size; /* Allocated memory block length */
158
159 };
160
161 /*-----------------------------------------------------------------------------
162 * Local function prototypes
163 *-----------------------------------------------------------------------------*/
164
165 /*
166 * Default memory error handler.
167 *
168 * Memory status is written to stderr, and the general error handler
169 * used by bft_error() is then called (which results in the
170 * termination of the current process).
171 *
172 * parameters:
173 * file_name: <-- name of source file from which error handler called.
174 * line_num: <-- line of source file from which error handler called.
175 * sys_error_code: <-- error code if error in system or libc call, 0 otherwise.
176 * format: <-- format string, as printf() and family.
177 * arg_ptr: <-> variable argument list based on format string.
178 */
179
180 static void
181 _bft_mem_error_handler_default(const char *file_name,
182 int line_num,
183 int sys_error_code,
184 const char *format,
185 va_list arg_ptr);
186
187 /*-----------------------------------------------------------------------------
188 * Local static variable definitions
189 *-----------------------------------------------------------------------------*/
190
191 static int _bft_mem_global_initialized = 0;
192
193 static FILE *_bft_mem_global_file = NULL;
194
195 static struct _bft_mem_block_t *_bft_mem_global_block_array = NULL;
196
197 static unsigned long _bft_mem_global_block_nbr = 0 ;
198 static unsigned long _bft_mem_global_block_max = 512 ;
199
200 static size_t _bft_mem_global_alloc_cur = 0;
201 static size_t _bft_mem_global_alloc_max = 0;
202
203 static size_t _bft_mem_global_n_allocs = 0;
204 static size_t _bft_mem_global_n_reallocs = 0;
205 static size_t _bft_mem_global_n_frees = 0;
206
207 static bft_error_handler_t *_bft_mem_error_handler
208 = (_bft_mem_error_handler_default);
209
210 static bft_mem_get_size_t *_bft_alt_get_size_func = NULL;
211 static bft_mem_realloc_t *_bft_alt_realloc_func = NULL;
212 static bft_mem_free_t *_bft_alt_free_func = NULL;
213
214 #if defined(HAVE_OPENMP)
215 static omp_lock_t _bft_mem_lock;
216 #endif
217
218 /*-----------------------------------------------------------------------------
219 * Local function definitions
220 *-----------------------------------------------------------------------------*/
221
222 /*
223 * Given a character string representing a file name, returns
224 * pointer to that part of the string corresponding to the base name.
225 *
226 * parameters:
227 * file_name: <-- full name of source file.
228 *
229 * return:
230 * pointer to part of file name corresponding to base name.
231 */
232
233 static const char *
_bft_mem_basename(const char * file_name)234 _bft_mem_basename(const char *file_name)
235 {
236 int i;
237
238 if (file_name == NULL)
239 return NULL;
240
241 for (i = strlen(file_name) - 1;
242 i > 0 && file_name[i] != DIR_SEPARATOR;
243 i--);
244
245 if (file_name[i] == DIR_SEPARATOR)
246 i++;
247
248 return (file_name + i);
249 }
250
251 /*
252 * Determines values associated with an array representing a
253 * long integer
254 *
255 * parameters:
256 * counter: <-- counter to update.
257 * value: --> counter values in output unit [out].
258 * unit: --> counter value unit : ' ', 'k', 'm', 'g', 't', or 'p'
259 * for bytes, Kilobytes, Megabytes, Gigabytes, Terabytes,
260 * or Petabytes [out].
261 */
262
263 static void
_bft_mem_size_val(const size_t counter,unsigned long value[2],char * unit)264 _bft_mem_size_val(const size_t counter,
265 unsigned long value[2],
266 char *unit)
267 {
268 int i;
269 size_t _counter[2];
270 const char units[] = {' ', 'k', 'm', 'g', 't', 'p', 'e'};
271
272 for (i = 0, _counter[0] = counter, _counter[1] = 0;
273 _counter[0] >= 1024 && i < 6;
274 i++) {
275 _counter[1] = _counter[0] % 1024;
276 _counter[0] /= 1024;
277 }
278
279 value[0] = _counter[0];
280 value[1] = _counter[1];
281 *unit = units[i];
282 }
283
284 /*
285 * Memory usage summary.
286 */
287
_bft_mem_summary(FILE * f)288 static void _bft_mem_summary(FILE *f)
289 {
290 char unit;
291 unsigned long value[2];
292 size_t mem_usage;
293
294 if (f == NULL)
295 return;
296
297 fprintf(f, "\n\n");
298 fprintf(f, "Memory allocation summary\n"
299 "-------------------------\n\n");
300
301 /* Available memory usage information */
302
303 _bft_mem_size_val(_bft_mem_global_alloc_cur, value, &unit);
304 fprintf(f,
305 "Theoretical current allocated memory: %8lu.%lu %cB\n",
306 value[0], value[1], unit);
307
308 _bft_mem_size_val(_bft_mem_global_alloc_max, value, &unit);
309 fprintf(f,
310 "Theoretical maximum allocated memory: %8lu.%lu %cB\n",
311 value[0], value[1], unit);
312
313 fprintf(f,
314 "\n"
315 "Number of allocations: %lu\n"
316 " reallocations: %lu\n"
317 " frees: %lu\n\n",
318 (unsigned long)_bft_mem_global_n_allocs,
319 (unsigned long)_bft_mem_global_n_reallocs,
320 (unsigned long)_bft_mem_global_n_frees);
321
322 if (bft_mem_usage_initialized() == 1) {
323
324 /* Maximum measured memory */
325
326 mem_usage = bft_mem_usage_max_pr_size();
327 if (mem_usage > 0) {
328 fprintf(f,
329 "Maximum program memory measure: %8lu kB\n",
330 (unsigned long)mem_usage);
331 }
332
333 /* Current measured memory */
334
335 mem_usage = bft_mem_usage_pr_size();
336 if (mem_usage > 0)
337 fprintf(f,
338 "Current program memory measure: %8lu kB\n",
339 (unsigned long)mem_usage);
340 }
341
342 }
343
344 /*
345 * Default memory error handler.
346 *
347 * Memory status is written to stderr (after bft_print_flush() is called),
348 * and the general error handler used by bft_error() is then called
349 * (which results in the termination of the current process).
350 *
351 * parameters:
352 * file_name: <-- name of source file from which error handler called.
353 * line_num: <-- line of source file from which error handler called.
354 * sys_error_code: <-- error code if error in system or libc call, 0 otherwise.
355 * format: <-- format string, as printf() and family.
356 * arg_ptr: <-> variable argument list based on format string.
357 */
358
359 static void
_bft_mem_error_handler_default(const char * file_name,int line_num,int sys_error_code,const char * format,va_list arg_ptr)360 _bft_mem_error_handler_default(const char *file_name,
361 int line_num,
362 int sys_error_code,
363 const char *format,
364 va_list arg_ptr)
365 {
366 bft_error_handler_t * general_err_handler;
367
368 bft_printf_flush();
369
370 _bft_mem_summary(stderr);
371
372 general_err_handler = bft_error_handler_get();
373 general_err_handler(file_name, line_num, sys_error_code, format, arg_ptr);
374 }
375
376 /*
377 * Calls the error handler (set by bft_mem_error_handler_set() or default).
378 *
379 * With the default error handler, an error message is output to stderr,
380 * (after bft_print_flush() is called), and the general error handler used
381 * by bft_error() is then called (which results in the termination of the
382 * current process or process group).
383 *
384 * parameters:
385 * file_name <-- name of source file from which failed bft_mem_...()
386 * function was called.
387 * line_num <-- line of source file from which failed bft_mem_...()
388 * function was called.
389 * sys_error_code <-- error code if error in system or libc call,
390 * 0 otherwise.
391 * format <-- format string, as printf() and family.
392 * ... <-- variable arguments based on format string.
393 */
394
395 static void
_bft_mem_error(const char * file_name,int line_num,int sys_error_code,const char * format,...)396 _bft_mem_error(const char *file_name,
397 int line_num,
398 int sys_error_code,
399 const char *format,
400 ...)
401 {
402 va_list arg_ptr;
403
404 if (_bft_mem_global_file != NULL) {
405 _bft_mem_summary(_bft_mem_global_file);
406 fflush(_bft_mem_global_file);
407 }
408
409 va_start(arg_ptr, format);
410
411 _bft_mem_error_handler(file_name, line_num, sys_error_code, format, arg_ptr);
412
413 va_end(arg_ptr);
414 }
415
416 /*
417 * Call error function when pointer is not found in allocation info.
418 *
419 * parameters:
420 * p: <-- allocated block's start adress.
421 *
422 * returns:
423 * corresponding _bft_mem_block structure.
424 */
425
426 static void
_bft_mem_block_info_error(const void * p)427 _bft_mem_block_info_error(const void *p)
428 {
429 _bft_mem_error(__FILE__, __LINE__, 0,
430 _("Adress [%p] does not correspond to "
431 "the beginning of an allocated block."),
432 p);
433 }
434
435 /*
436 * Return the _bft_mem_block structure corresponding to a given
437 * allocated block.
438 *
439 * parameters:
440 * p_in: <-- allocated block's start adress.
441 *
442 * returns:
443 * corresponding _bft_mem_block structure.
444 */
445
446 static struct _bft_mem_block_t *
_bft_mem_block_info(const void * p_get)447 _bft_mem_block_info(const void *p_get)
448 {
449 struct _bft_mem_block_t *pinfo = NULL;
450 unsigned long idx;
451
452 if (_bft_mem_global_block_array != NULL) {
453
454 for (idx = _bft_mem_global_block_nbr - 1;
455 idx > 0 && (_bft_mem_global_block_array + idx)->p_bloc != p_get;
456 idx--);
457
458 if ((_bft_mem_global_block_array + idx)->p_bloc != p_get)
459 _bft_mem_block_info_error(p_get);
460 else {
461 pinfo = _bft_mem_global_block_array + idx;
462 assert(p_get == pinfo->p_bloc);
463 }
464
465 }
466
467 return pinfo;
468 }
469
470 /*
471 * Return the _bft_mem_block structure corresponding to a given
472 * allocated block if available.
473 *
474 * parameters:
475 * p_in: <-- allocated block's start adress.
476 *
477 * returns:
478 * corresponding _bft_mem_block structure.
479 */
480
481 static struct _bft_mem_block_t *
_bft_mem_block_info_try(const void * p_get)482 _bft_mem_block_info_try(const void *p_get)
483 {
484 struct _bft_mem_block_t *pinfo = NULL;
485
486 if (_bft_mem_global_block_array != NULL) {
487
488 unsigned long idx = _bft_mem_global_block_nbr - 1;
489 while (idx > 0 && (_bft_mem_global_block_array + idx)->p_bloc != p_get)
490 idx--;
491
492 if ((_bft_mem_global_block_array + idx)->p_bloc == p_get) {
493 pinfo = _bft_mem_global_block_array + idx;
494 assert(p_get == pinfo->p_bloc);
495 }
496
497 }
498
499 return pinfo;
500 }
501
502 /*
503 * Return the size of a given allocated block.
504 *
505 * parameters:
506 * p_in: <-- allocated block's start adress.
507 *
508 * returns:
509 * block size.
510 */
511
512 static size_t
_bft_mem_block_size(const void * p_in)513 _bft_mem_block_size(const void *p_in)
514 {
515 struct _bft_mem_block_t *pinfo = _bft_mem_block_info(p_in);
516
517 if (pinfo != NULL)
518 return pinfo->size;
519 else
520 return 0;
521 }
522
523 /*
524 * Fill a _bft_mem_block_t structure for an allocated pointer.
525 */
526
527 static void
_bft_mem_block_malloc(void * p_new,const size_t size_new)528 _bft_mem_block_malloc(void *p_new,
529 const size_t size_new)
530 {
531 struct _bft_mem_block_t *pinfo;
532
533 assert(size_new != 0);
534
535 if (_bft_mem_global_block_array == NULL)
536 return;
537
538 if (_bft_mem_global_block_nbr >= _bft_mem_global_block_max) {
539
540 _bft_mem_global_block_max *= 2;
541 _bft_mem_global_block_array
542 = (struct _bft_mem_block_t *) realloc(_bft_mem_global_block_array,
543 sizeof(struct _bft_mem_block_t)
544 * _bft_mem_global_block_max);
545
546 if (_bft_mem_global_block_array == NULL) {
547 _bft_mem_error(__FILE__, __LINE__, errno,
548 _("Memory allocation failure"));
549 return;
550 }
551
552 }
553
554 _bft_mem_global_block_nbr += 1;
555
556 pinfo = _bft_mem_global_block_array + _bft_mem_global_block_nbr - 1;
557
558 /* Start adress and size of allocated block */
559
560 pinfo->p_bloc = p_new;
561 pinfo->size = size_new;
562 }
563
564 /*
565 * Update a _bft_mem_block_t structure for an reallocated pointer.
566 */
567
568 static void
_bft_mem_block_realloc(const void * p_old,void * p_new,size_t size_new)569 _bft_mem_block_realloc(const void *p_old,
570 void *p_new,
571 size_t size_new)
572 {
573 struct _bft_mem_block_t *pinfo;
574
575 assert(size_new != 0);
576
577 pinfo = _bft_mem_block_info(p_old);
578
579 if (pinfo != NULL) {
580 pinfo->p_bloc = p_new;
581 pinfo->size = size_new;
582 }
583 }
584
585 /*
586 * Free a _bft_mem_block_t structure for a freed pointer.
587 */
588
589 static void
_bft_mem_block_free(const void * p_free)590 _bft_mem_block_free(const void *p_free)
591 {
592 struct _bft_mem_block_t *pinfo, *pmove;
593 unsigned long idx;
594
595 if (_bft_mem_global_block_array == NULL)
596 return;
597
598 for (idx = _bft_mem_global_block_nbr - 1;
599 idx > 0 && (_bft_mem_global_block_array + idx)->p_bloc != p_free;
600 idx--);
601
602 if ((_bft_mem_global_block_array + idx)->p_bloc != p_free)
603 _bft_mem_error(__FILE__, __LINE__, 0,
604 _("Adress [%10p] does not correspond to "
605 "the beginning of an allocated block."),
606 p_free);
607
608 else {
609
610 /* We move the contents of the array's final block to the position
611 of the freed block, and shorten the array's useful part by one. */
612
613 pinfo = _bft_mem_global_block_array + idx;
614 pmove = _bft_mem_global_block_array + _bft_mem_global_block_nbr - 1;
615 pinfo->p_bloc = pmove->p_bloc;
616 pinfo->size = pmove->size;
617
618 _bft_mem_global_block_nbr -= 1;
619
620 }
621 }
622
623 /*! (DOXYGEN_SHOULD_SKIP_THIS) \endcond */
624
625 /*============================================================================
626 * Public function definitions
627 *============================================================================*/
628
629 /*!
630 * \brief Initialize memory handling.
631 *
632 * This function should be called before any other bft_mem_...()
633 * function. To activate memory allocation logging, a logfile
634 * name should be given as an argument. The resulting file will
635 * be a regular, local file. If this file cannot be opened for
636 * some reason, logging is silently de-activated.
637 *
638 * \param log_file_name name of optional log_file (if NULL, no log).
639 */
640
641 void
bft_mem_init(const char * log_file_name)642 bft_mem_init(const char *log_file_name)
643 {
644 size_t alloc_size;
645
646 #if defined(HAVE_OPENMP)
647 if (omp_in_parallel()) {
648 if (omp_get_thread_num() != 0)
649 return;
650 }
651 omp_init_lock(&_bft_mem_lock);
652 #endif
653
654 if (_bft_mem_global_initialized == 1) {
655 _bft_mem_error(__FILE__, __LINE__, 0,
656 _("bft_mem_init() has already been called"));
657 }
658 _bft_mem_global_initialized = 1;
659
660 alloc_size = sizeof(struct _bft_mem_block_t) * _bft_mem_global_block_max;
661
662 _bft_mem_global_block_array
663 = malloc(sizeof(struct _bft_mem_block_t) * _bft_mem_global_block_max);
664
665 if (_bft_mem_global_block_array == NULL) {
666 _bft_mem_error(__FILE__, __LINE__, errno,
667 _("Failure to allocate \"%s\" (%lu bytes)"),
668 "_bft_mem_global_block_array", (unsigned long)alloc_size);
669 return;
670 }
671
672 if (log_file_name != NULL) {
673
674 _bft_mem_global_file = fopen(log_file_name, "w");
675
676 /*
677 If the file could not be opened, we do not abort, as it is not
678 absolutely necessary. We silently continue.
679 (We could warn the user, but this would require either using
680 bft_printf(), which we prefer to keep independent of the bft_mem_...()
681 functions to avoid evental crossed definitions when user-defined, or
682 "warning handling" similar to error handling, with a possibility
683 of user-defined warning handlers, as we are not sure if the calling
684 code uses stderr (especially in a distributed environment). This
685 is probably not worth the bother.
686 */
687
688 if (_bft_mem_global_file == NULL)
689 fprintf(stderr,
690 _("Failure to open memory log file \"%s\"\n"),
691 log_file_name);
692
693 }
694
695 /* Log file header */
696
697 if (_bft_mem_global_file != NULL) {
698
699 fprintf(_bft_mem_global_file,
700 " : FILE NAME : LINE :"
701 " POINTER NAME : N BYTES :"
702 " (+- N BYTES) : TOTAL BYTES : [ ADRESS]\n"
703 "-------:----------------------------:-------:"
704 "----------------------------------------:-----------:"
705 "-----------------------------:--------------");
706
707 }
708
709 }
710
711 /*!
712 * \brief End memory handling.
713 *
714 * This function should be called after all other bft_mem_...()
715 * functions. In case of memory allocation logging, it
716 * writes final information to the log file and closes is.
717 */
718
bft_mem_end(void)719 void bft_mem_end(void)
720 {
721 if (_bft_mem_global_initialized == 0)
722 return;
723
724 #if defined(HAVE_OPENMP)
725 if (omp_in_parallel()) {
726 if (omp_get_thread_num() != 0)
727 return;
728 }
729 omp_destroy_lock(&_bft_mem_lock);
730 #endif
731
732 _bft_mem_global_initialized = 0;
733
734 if (_bft_mem_global_file != NULL) {
735
736 unsigned long non_free = 0;
737 struct _bft_mem_block_t *pinfo;
738
739 /* Memory usage summary */
740
741 _bft_mem_summary(_bft_mem_global_file);
742
743 /* List of non-freed pointers */
744
745 if (_bft_mem_global_block_array != NULL) {
746
747 fprintf(_bft_mem_global_file, "List of non freed pointers:\n");
748
749 for (pinfo = _bft_mem_global_block_array;
750 pinfo < _bft_mem_global_block_array + _bft_mem_global_block_nbr;
751 pinfo++) {
752
753 fprintf(_bft_mem_global_file,"[%10p]\n", pinfo->p_bloc);
754 non_free++;
755
756 }
757
758 fprintf(_bft_mem_global_file,
759 "Number of non freed pointers remaining: %lu\n",
760 non_free);
761
762 }
763
764 fclose(_bft_mem_global_file);
765 }
766
767 /* Reset defaults in case of later initialization */
768
769 if (_bft_mem_global_block_array != NULL) {
770 free(_bft_mem_global_block_array);
771 _bft_mem_global_block_array = NULL;
772 }
773
774 _bft_mem_global_block_nbr = 0 ;
775 _bft_mem_global_block_max = 512 ;
776
777 _bft_mem_global_alloc_cur = 0;
778 _bft_mem_global_alloc_max = 0;
779
780 _bft_mem_global_n_allocs = 0;
781 _bft_mem_global_n_reallocs = 0;
782 _bft_mem_global_n_frees = 0;
783
784 }
785
786 /*!
787 * \brief Indicates if bft_mem_...() functions are initialized.
788 *
789 * \returns 1 if bft_mem_init has been called, 0 otherwise.
790 */
791
792 int
bft_mem_initialized(void)793 bft_mem_initialized(void)
794 {
795 return _bft_mem_global_initialized;
796 }
797
798 /*!
799 * \brief Allocate memory for ni elements of size bytes.
800 *
801 * This function calls malloc(), but adds tracing capabilities, and
802 * automatically calls the bft_error() errorhandler if it fails to
803 * allocate the required memory.
804 *
805 * \param [in] ni number of elements.
806 * \param [in] size element size.
807 * \param [in] var_name allocated variable name string.
808 * \param [in] file_name name of calling source file.
809 * \param [in] line_num line number in calling source file.
810 *
811 * \returns pointer to allocated memory.
812 */
813
814 void *
bft_mem_malloc(size_t ni,size_t size,const char * var_name,const char * file_name,int line_num)815 bft_mem_malloc(size_t ni,
816 size_t size,
817 const char *var_name,
818 const char *file_name,
819 int line_num)
820 {
821 void *p_loc;
822 size_t alloc_size = ni * size;
823
824 if (ni == 0)
825 return NULL;
826
827 /* Allocate memory and check return */
828
829 p_loc = malloc(alloc_size);
830
831 if (p_loc == NULL) {
832 _bft_mem_error(file_name, line_num, errno,
833 _("Failure to allocate \"%s\" (%lu bytes)"),
834 var_name, (unsigned long)alloc_size);
835 return NULL;
836 }
837 else if (_bft_mem_global_initialized == 0)
838 return p_loc;
839
840 /* Memory allocation counting */
841
842 {
843 #if defined(HAVE_OPENMP)
844 int in_parallel = omp_in_parallel();
845 if (in_parallel)
846 omp_set_lock(&_bft_mem_lock);
847 #endif
848
849 _bft_mem_global_alloc_cur += alloc_size;
850
851 if (_bft_mem_global_alloc_max < _bft_mem_global_alloc_cur)
852 _bft_mem_global_alloc_max = _bft_mem_global_alloc_cur;
853
854 if (_bft_mem_global_file != NULL) {
855 fprintf(_bft_mem_global_file, "\n alloc: %-27s:%6d : %-39s: %9lu",
856 _bft_mem_basename(file_name), line_num,
857 var_name, (unsigned long)alloc_size);
858 fprintf(_bft_mem_global_file, " : (+%9lu) : %12lu : [%10p]",
859 (unsigned long)alloc_size,
860 (unsigned long)_bft_mem_global_alloc_cur,
861 p_loc);
862 fflush(_bft_mem_global_file);
863 }
864
865 _bft_mem_block_malloc(p_loc, alloc_size);
866
867 _bft_mem_global_n_allocs += 1;
868
869 #if defined(HAVE_OPENMP)
870 if (in_parallel)
871 omp_unset_lock(&_bft_mem_lock);
872 #endif
873 }
874
875 /* Return pointer to allocated memory */
876
877 return p_loc;
878 }
879
880 /*!
881 * \brief Reallocate memory for ni elements of size bytes.
882 *
883 * This function calls realloc(), but adds tracing capabilities, and
884 * automatically calls the bft_error() errorhandler if it fails to
885 * allocate the required memory.
886 *
887 * \param [in] ptr pointer to previous memory location
888 * (if NULL, bft_alloc() called).
889 * \param [in] ni number of elements.
890 * \param [in] size element size.
891 * \param [in] var_name allocated variable name string.
892 * \param [in] file_name name of calling source file.
893 * \param [in] line_num line number in calling source file.
894 *
895 * \returns pointer to reallocated memory.
896 */
897
898 void *
bft_mem_realloc(void * ptr,size_t ni,size_t size,const char * var_name,const char * file_name,int line_num)899 bft_mem_realloc(void *ptr,
900 size_t ni,
901 size_t size,
902 const char *var_name,
903 const char *file_name,
904 int line_num)
905 {
906 void *p_loc;
907
908 size_t old_size = 0;
909 size_t new_size = ni * size;
910
911 #if defined(HAVE_OPENMP)
912 int in_parallel = 0;
913 #endif
914
915 /*
916 Behave as bft_mem_malloc() if the previous pointer is equal to NULL.
917 Note that the operation will then appear as a first allocation
918 ('alloc') in the _bft_mem_global_file trace file.
919 */
920
921 if (ptr == NULL)
922 return bft_mem_malloc(ni,
923 size,
924 var_name,
925 file_name,
926 line_num);
927
928 /*
929 Behave as bft_mem_free() if the requested size is zero.
930 Note that in this case, the operation will appear as 'free'
931 in the _bft_mem_global_file trace file.
932 */
933
934 else if (ni == 0)
935 return bft_mem_free(ptr,
936 var_name,
937 file_name,
938 line_num);
939
940 /* When possible, get previous size to compute difference. */
941
942 if (_bft_mem_global_initialized) {
943
944 #if defined(HAVE_OPENMP)
945 in_parallel = omp_in_parallel();
946 if (in_parallel)
947 omp_set_lock(&_bft_mem_lock);
948 #endif
949
950 struct _bft_mem_block_t *pinfo = _bft_mem_block_info_try(ptr);
951 if (pinfo != NULL)
952 old_size = pinfo->size;
953
954 #if defined(HAVE_OPENMP)
955 if (in_parallel)
956 omp_unset_lock(&_bft_mem_lock);
957 #endif
958
959 if (pinfo == NULL) {
960 if (_bft_alt_get_size_func != NULL) {
961 old_size = _bft_alt_get_size_func(ptr);
962 if (old_size > 0)
963 return _bft_alt_realloc_func(ptr, ni, size,
964 var_name, file_name, line_num);
965 }
966 _bft_mem_block_info_error(ptr);
967 }
968
969 /* If the old size is known to equal the new size,
970 nothing needs to be done. */
971
972 if (new_size == old_size)
973 return ptr;
974
975 }
976
977 /* In the general case, we have a true reallocation. */
978
979 p_loc = realloc(ptr, new_size);
980
981 if (p_loc == NULL) {
982 _bft_mem_error(file_name, line_num, errno,
983 _("Failure to reallocate \"%s\" (%lu bytes)"),
984 var_name, (unsigned long)new_size);
985 return NULL;
986 }
987 else if (_bft_mem_global_initialized == 0)
988 return p_loc;
989
990 {
991 #if defined(HAVE_OPENMP)
992 if (in_parallel)
993 omp_set_lock(&_bft_mem_lock);
994 #endif
995
996 /* FIXME: size_diff overestimated when _bft_mem_global_initialized == 0,
997 so bft_mem_size_current/bft_mem_size_max will return incorrect values
998 in this case.
999 Maybe these functions should simply return 0 in the case. */
1000
1001 long size_diff = new_size - old_size;
1002
1003 _bft_mem_global_alloc_cur += size_diff;
1004
1005 if (size_diff > 0) {
1006 if (_bft_mem_global_alloc_max < _bft_mem_global_alloc_cur)
1007 _bft_mem_global_alloc_max = _bft_mem_global_alloc_cur;
1008 }
1009
1010 if (_bft_mem_global_file != NULL) {
1011 char sgn = (size_diff > 0) ? '+' : '-';
1012 fprintf(_bft_mem_global_file, "\nrealloc: %-27s:%6d : %-39s: %9lu",
1013 _bft_mem_basename(file_name), line_num,
1014 var_name, (unsigned long)new_size);
1015 fprintf(_bft_mem_global_file, " : (%c%9lu) : %12lu : [%10p]",
1016 sgn,
1017 (unsigned long) ((size_diff > 0) ? size_diff : -size_diff),
1018 (unsigned long)_bft_mem_global_alloc_cur,
1019 p_loc);
1020 fflush(_bft_mem_global_file);
1021 }
1022
1023 _bft_mem_block_realloc(ptr, p_loc, new_size);
1024
1025 _bft_mem_global_n_reallocs += 1;
1026
1027 #if defined(HAVE_OPENMP)
1028 if (in_parallel)
1029 omp_unset_lock(&_bft_mem_lock);
1030 #endif
1031
1032 return p_loc;
1033 }
1034
1035 return NULL; /* Avoid a compiler warning */
1036 }
1037
1038 /*!
1039 * \brief Free allocated memory.
1040 *
1041 * This function calls free(), but adds tracing capabilities, and
1042 * automatically calls the bft_error() errorhandler if it fails to
1043 * free the corresponding memory. In case of a NULL pointer argument,
1044 * the function simply returns.
1045 *
1046 * \param [in] ptr pointer to previous memory location
1047 * (if NULL, bft_alloc() called).
1048 * \param [in] var_name allocated variable name string
1049 * \param [in] file_name name of calling source file
1050 * \param [in] line_num line number in calling source file
1051 *
1052 * \returns NULL pointer.
1053 */
1054
1055 void *
bft_mem_free(void * ptr,const char * var_name,const char * file_name,int line_num)1056 bft_mem_free(void *ptr,
1057 const char *var_name,
1058 const char *file_name,
1059 int line_num)
1060 {
1061 /* NULL pointer case (non-allocated location) */
1062
1063 if (ptr == NULL)
1064 return NULL;
1065
1066 /* General case (free allocated memory) */
1067
1068 if (_bft_mem_global_initialized != 0) {
1069
1070 #if defined(HAVE_OPENMP)
1071 int in_parallel = omp_in_parallel();
1072 if (in_parallel)
1073 omp_set_lock(&_bft_mem_lock);
1074 #endif
1075
1076 struct _bft_mem_block_t *pinfo = _bft_mem_block_info_try(ptr);
1077
1078 if (pinfo != NULL) {
1079
1080 size_t size_info = pinfo->size;
1081
1082 _bft_mem_global_alloc_cur -= size_info;
1083
1084 if (_bft_mem_global_file != NULL) {
1085 fprintf(_bft_mem_global_file,"\n free: %-27s:%6d : %-39s: %9lu",
1086 _bft_mem_basename(file_name), line_num,
1087 var_name, (unsigned long)size_info);
1088 fprintf(_bft_mem_global_file, " : (-%9lu) : %12lu : [%10p]",
1089 (unsigned long)size_info,
1090 (unsigned long)_bft_mem_global_alloc_cur,
1091 ptr);
1092 fflush(_bft_mem_global_file);
1093 }
1094
1095 _bft_mem_block_free(ptr);
1096
1097 _bft_mem_global_n_frees += 1;
1098
1099 }
1100
1101 #if defined(HAVE_OPENMP)
1102 if (in_parallel)
1103 omp_unset_lock(&_bft_mem_lock);
1104 #endif
1105
1106 if (pinfo == NULL) {
1107 if (_bft_alt_get_size_func != NULL) {
1108 size_t size_info = _bft_alt_get_size_func(ptr);
1109 if (size_info > 0) {
1110 _bft_alt_free_func(ptr, var_name, file_name, line_num);
1111 return NULL;
1112 }
1113 }
1114 _bft_mem_block_info_error(ptr);
1115 }
1116
1117 }
1118
1119 free(ptr);
1120
1121 return NULL;
1122 }
1123
1124 /*!
1125 * \brief Allocate aligned memory for ni elements of size bytes.
1126 *
1127 * This function calls posix_memalign() if available, but adds tracing
1128 * capabilities, and automatically calls the bft_error() errorhandler if
1129 * it fails to allocate the required memory.
1130 *
1131 * The associated function bft_mem_have_memalign() indicates if this
1132 * type of allocation may be used on this system.
1133 *
1134 * \param [in] alignment alignment.
1135 * \param [in] ni number of elements.
1136 * \param [in] size element size.
1137 * \param [in] var_name allocated variable name string.
1138 * \param [in] file_name name of calling source file.
1139 * \param [in] line_num line number in calling source file.
1140 *
1141 * \returns pointer to allocated memory.
1142 */
1143
1144 void *
bft_mem_memalign(size_t alignment,size_t ni,size_t size,const char * var_name,const char * file_name,int line_num)1145 bft_mem_memalign(size_t alignment,
1146 size_t ni,
1147 size_t size,
1148 const char *var_name,
1149 const char *file_name,
1150 int line_num)
1151 {
1152 #if defined(HAVE_POSIX_MEMALIGN)
1153
1154 int retval;
1155 void *p_loc;
1156 size_t alloc_size = ni * size;
1157
1158 if (ni == 0)
1159 return NULL;
1160
1161 /* Allocate memory and check return */
1162
1163 retval = posix_memalign(&p_loc, alignment, alloc_size);
1164
1165 if (retval != 0) {
1166 switch (retval) {
1167 case EINVAL:
1168 _bft_mem_error(file_name, line_num, 0,
1169 _("Alignment %lu for \"%s\" not a power of 2\n"
1170 "or a multiple of sizeof(void *) = %lu"),
1171 (unsigned long)alignment, var_name,
1172 (unsigned long)(sizeof(void *)));
1173 break;
1174 default:
1175 _bft_mem_error(file_name, line_num, 0,
1176 _("Failure to allocate \"%s\" (%lu bytes)"),
1177 var_name, (unsigned long)alloc_size);
1178 }
1179 return NULL;
1180 }
1181 else if (_bft_mem_global_initialized == 0)
1182 return p_loc;
1183
1184 /* Memory allocation counting */
1185
1186 {
1187 #if defined(HAVE_OPENMP)
1188 int in_parallel = omp_in_parallel();
1189 if (in_parallel)
1190 omp_set_lock(&_bft_mem_lock);
1191 #endif
1192
1193 _bft_mem_global_alloc_cur += alloc_size;
1194
1195 if (_bft_mem_global_alloc_max < _bft_mem_global_alloc_cur)
1196 _bft_mem_global_alloc_max = _bft_mem_global_alloc_cur;
1197
1198 if (_bft_mem_global_file != NULL) {
1199 fprintf(_bft_mem_global_file, "\n alloc: %-27s:%6d : %-39s: %9lu",
1200 _bft_mem_basename(file_name), line_num,
1201 var_name, (unsigned long)alloc_size);
1202 fprintf(_bft_mem_global_file, " : (+%9lu) : %12lu : [%10p]",
1203 (unsigned long)alloc_size,
1204 (unsigned long)_bft_mem_global_alloc_cur,
1205 p_loc);
1206 fflush(_bft_mem_global_file);
1207 }
1208
1209 _bft_mem_block_malloc(p_loc, alloc_size);
1210
1211 _bft_mem_global_n_allocs += 1;
1212
1213 #if defined(HAVE_OPENMP)
1214 if (in_parallel)
1215 omp_unset_lock(&_bft_mem_lock);
1216 #endif
1217 }
1218
1219 /* Return pointer to allocated memory */
1220
1221 return p_loc;
1222
1223 #else
1224
1225 _bft_mem_error(file_name, line_num, errno,
1226 _("No aligned allocation function available on this system"));
1227
1228 return NULL;
1229
1230 #endif
1231 }
1232
1233 /*!
1234 * \brief Return block size associated with a given pointer.
1235 *
1236 * bft_mem_init() must have beed called before this function can be used.
1237 *
1238 * \param [in] ptr pointer to previous memory location
1239 *
1240 * \returns size of associated memory block.
1241 */
1242
1243 size_t
bft_mem_get_block_size(void * ptr)1244 bft_mem_get_block_size(void *ptr)
1245 {
1246 size_t block_size = 0;
1247
1248 if (_bft_mem_global_initialized) {
1249
1250 #if defined(HAVE_OPENMP)
1251 int in_parallel = omp_in_parallel();
1252 if (in_parallel)
1253 omp_set_lock(&_bft_mem_lock);
1254 #endif
1255
1256 block_size = _bft_mem_block_size(ptr);
1257
1258 }
1259 else
1260 _bft_mem_error(__FILE__, __LINE__, 0,
1261 _("%s: should not be called before %s\n"),
1262 __func__, "bft_mem_init");
1263
1264 return block_size;
1265 }
1266
1267 /*!
1268 * \brief Return current theoretical dynamic memory allocated.
1269 *
1270 * \return current memory handled through bft_mem_...() (in kB).
1271 */
1272
1273 size_t
bft_mem_size_current(void)1274 bft_mem_size_current(void)
1275 {
1276 return (_bft_mem_global_alloc_cur / 1024);
1277 }
1278
1279 /*!
1280 * \brief Return maximum theoretical dynamic memory allocated.
1281 *
1282 * \return maximum memory handled through bft_mem_...() (in kB).
1283 */
1284
1285 size_t
bft_mem_size_max(void)1286 bft_mem_size_max(void)
1287 {
1288 return (_bft_mem_global_alloc_max / 1024);
1289 }
1290
1291 /*!
1292 * \brief Indicate if a memory aligned allocation variant is available.
1293 *
1294 * If no such function is available, bft_mem_memalign() will always fail.
1295 *
1296 * \returns 1 if memory aligned allocation is possible, 0 otherwise.
1297 */
1298
1299 int
bft_mem_have_memalign(void)1300 bft_mem_have_memalign(void)
1301 {
1302 #if defined(HAVE_POSIX_MEMALIGN)
1303 return 1;
1304 #else
1305 return 0;
1306 #endif
1307 }
1308
1309 /*!
1310 * \brief Returns the error handler associated with the bft_mem_...() functions.
1311 *
1312 * \return pointer to the error handler function.
1313 */
1314
1315 bft_error_handler_t *
bft_mem_error_handler_get(void)1316 bft_mem_error_handler_get(void)
1317 {
1318 return _bft_mem_error_handler;
1319 }
1320
1321 /*!
1322 * \brief Associates an error handler with the bft_mem_...() functions.
1323 *
1324 * With the default error handler, an error message is output to stderr,
1325 * (after bft_print_flush() is called), and the general error handler used
1326 * by bft_error() is then called (which results in the termination of the
1327 * current process or process group).
1328 *
1329 * \param handler pointer to the error handler function [in].
1330 */
1331
1332 void
bft_mem_error_handler_set(bft_error_handler_t * handler)1333 bft_mem_error_handler_set(bft_error_handler_t *handler)
1334 {
1335 _bft_mem_error_handler = handler;
1336 }
1337
1338 /*
1339 * Associates alternative functions with the bft_mem_...() functions.
1340 *
1341 * When memory allocated with another mechanism is reallocated or
1342 * freed using a bft_mem_... function, this allows trying the
1343 * matching alternative function rather than throwing an error.
1344 *
1345 * Though using matching methods is recommended, this allows handling
1346 * compatibility between methods which might be used in different parts
1347 * of the code.
1348 *
1349 * parameter:
1350 * realloc_func <-- pointer to alternative reallocation function.
1351 * realloc_func <-- pointer to alternative free function.
1352 */
1353
1354 void
bft_mem_alternative_set(bft_mem_get_size_t * get_size_func,bft_mem_realloc_t * realloc_func,bft_mem_free_t * free_func)1355 bft_mem_alternative_set(bft_mem_get_size_t *get_size_func,
1356 bft_mem_realloc_t *realloc_func,
1357 bft_mem_free_t *free_func)
1358 {
1359 _bft_alt_get_size_func = get_size_func;
1360 _bft_alt_realloc_func = realloc_func;
1361 _bft_alt_free_func = free_func;
1362 }
1363
1364 /*----------------------------------------------------------------------------*/
1365
1366 END_C_DECLS
1367