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 #include "ecs_def.h"
28
29 /*
30 * Standard C library headers
31 */
32
33 #include <assert.h>
34 #include <errno.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <stdlib.h>
39
40 /*
41 * Optional library and ECS headers
42 */
43
44 #include "ecs_def.h"
45 #include "ecs_mem.h"
46 #include "ecs_mem_usage.h"
47
48 /*----------------------------------------------------------------------------*/
49
50 BEGIN_C_DECLS
51
52 /*-----------------------------------------------------------------------------
53 * Local type definitions
54 *----------------------------------------------------------------------------*/
55
56 #ifndef DOXYGEN_SHOULD_SKIP_THIS
57
58 /*
59 * Structure defining an allocated memory block (for memory tracing)
60 */
61
62 struct _ecs_mem_block_t {
63
64 void *p_bloc; /* Allocated memory block start adress */
65 size_t size; /* Allocated memory block length */
66
67 };
68
69 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
70
71 /*-----------------------------------------------------------------------------
72 * Local macro documentation
73 *----------------------------------------------------------------------------*/
74
75 /*! \fn ECS_MALLOC(_ptr, _ni, _type)
76 * \brief Allocate memory for _ni elements of type _type.
77 *
78 * This macro calls ecs_mem_malloc(), automatically setting the
79 * allocated variable name and source file name and line arguments.
80 *
81 * \param [out] _ptr pointer to allocated memory.
82 * \param [in] _ni number of elements.
83 * \param [in] _type element type.
84 */
85
86 /*! \fn ECS_REALLOC(_ptr, _ni, _type)
87 * \brief Reallocate memory for _ni elements of type _type.
88 *
89 * This macro calls ecs_mem_realloc(), automatically setting the
90 * allocated variable name and source file name and line arguments.
91 *
92 * \param [in, out] _ptr pointer to allocated memory.
93 * \param [in] _ni number of elements.
94 * \param [in] _type element type.
95 */
96
97 /*! \fn ECS_FREE(_ptr)
98 * \brief Free allocated memory.
99 *
100 * This macro calls ecs_mem_free(), automatically setting the
101 * allocated variable name and source file name and line arguments.
102 *
103 * The freed pointer is set to NULL to avoid accidental reuse.
104 *
105 * \param [in, out] _ptr pointer to allocated memory.
106 */
107
108 /*-----------------------------------------------------------------------------
109 * Local macro definitions
110 *----------------------------------------------------------------------------*/
111
112 /*-----------------------------------------------------------------------------
113 * Local function prototypes
114 *----------------------------------------------------------------------------*/
115
116 /*-----------------------------------------------------------------------------
117 * Local static variable definitions
118 *----------------------------------------------------------------------------*/
119
120 static int _ecs_mem_global_initialized = 0;
121
122 static FILE *_ecs_mem_global_file = NULL;
123
124 static struct _ecs_mem_block_t *_ecs_mem_global_block_array = NULL;
125
126 static unsigned long _ecs_mem_global_block_nbr = 0 ;
127 static unsigned long _ecs_mem_global_block_max = 512 ;
128
129 static size_t _ecs_mem_global_alloc_cur = 0;
130 static size_t _ecs_mem_global_alloc_max = 0;
131
132 static size_t _ecs_mem_global_n_allocs = 0;
133 static size_t _ecs_mem_global_n_reallocs = 0;
134 static size_t _ecs_mem_global_n_frees = 0;
135
136 /*-----------------------------------------------------------------------------
137 * Local function definitions
138 *----------------------------------------------------------------------------*/
139
140 /*
141 * Given a character string representing a file name, returns
142 * pointer to that part of the string corresponding to the base name.
143 *
144 * parameters:
145 * file_name: <-- full name of source file.
146 *
147 * return:
148 * pointer to part of file name corresponding to base name.
149 */
150
151 static const char *
_ecs_mem_basename(const char * file_name)152 _ecs_mem_basename(const char *file_name)
153 {
154 int i;
155
156 if (file_name == NULL)
157 return NULL;
158
159 for (i = strlen(file_name) - 1;
160 i > 0 && file_name[i] != ECS_PATH_SEP;
161 i--);
162
163 if (file_name[i] == ECS_PATH_SEP)
164 i++;
165
166 return (file_name + i);
167 }
168
169 /*
170 * Determines values associated with an array representing a
171 * long integer
172 *
173 * parameters:
174 * counter: <-- counter to update.
175 * value: --> counter values in output unit [out].
176 * unit: --> counter value unit : ' ', 'k', 'm', 'g', 't', or 'p'
177 * for bytes, Kilobytes, Megabytes, Gigabytes, Terabytes,
178 * or Petabytes [out].
179 */
180
181 static void
_ecs_mem_size_val(const size_t counter,unsigned long value[2],char * unit)182 _ecs_mem_size_val(const size_t counter,
183 unsigned long value[2],
184 char *unit)
185 {
186 int i;
187 size_t _counter[2];
188 const char units[] = {' ', 'k', 'm', 'g', 't', 'p', 'e'};
189
190 for (i = 0, _counter[0] = counter, _counter[1] = 0;
191 _counter[0] >= 1024 && i < 6;
192 i++) {
193 _counter[1] = _counter[0] % 1024;
194 _counter[0] /= 1024;
195 }
196
197 value[0] = _counter[0];
198 value[1] = _counter[1];
199 *unit = units[i];
200 }
201
202 /*
203 * Memory usage summary.
204 */
205
_ecs_mem_summary(FILE * f)206 static void _ecs_mem_summary(FILE *f)
207 {
208 char unit;
209 unsigned long value[2];
210 size_t mem_usage;
211
212 if (f == NULL)
213 return;
214
215 fprintf(f, "\n\n");
216 fprintf(f,
217 "Memory allocation summary\n"
218 "-------------------------\n\n");
219
220 /* Available memory usage information */
221
222 _ecs_mem_size_val(_ecs_mem_global_alloc_cur, value, &unit);
223 fprintf(f,
224 "Theoretical current allocated memory: %8lu.%lu %cB\n",
225 value[0], value[1], unit);
226
227 _ecs_mem_size_val(_ecs_mem_global_alloc_max, value, &unit);
228 fprintf(f,
229 "Theoretical maximum allocated memory: %8lu.%lu %cB\n",
230 value[0], value[1], unit);
231
232 fprintf(f,
233 "\n"
234 "Number of allocations: %lu\n"
235 " reallocations: %lu\n"
236 " frees: %lu\n\n",
237 (unsigned long)_ecs_mem_global_n_allocs,
238 (unsigned long)_ecs_mem_global_n_reallocs,
239 (unsigned long)_ecs_mem_global_n_frees);
240
241 if (ecs_mem_usage_initialized() == 1) {
242
243 /* Maximum measured memory */
244
245 mem_usage = ecs_mem_usage_max_pr_size();
246 if (mem_usage > 0) {
247 fprintf(f,
248 "Maximum program memory measure: %8lu kB\n",
249 (unsigned long)mem_usage);
250 }
251
252 /* Current measured memory */
253
254 mem_usage = ecs_mem_usage_pr_size();
255 if (mem_usage > 0)
256 fprintf(f,
257 "Current program memory measure: %8lu kB\n",
258 (unsigned long)mem_usage);
259 }
260
261 }
262
263 /*
264 * Return the _ecs_mem_block structure corresponding to a given
265 * allocated block.
266 *
267 * parameters:
268 * p_in: <-- allocated block's start adress.
269 *
270 * returns:
271 * corresponding _ecs_mem_block structure.
272 */
273
274 static struct _ecs_mem_block_t *
_ecs_mem_block_info(const void * p_get)275 _ecs_mem_block_info(const void *p_get)
276 {
277 struct _ecs_mem_block_t *pinfo = NULL;
278 unsigned long idx;
279
280 if (_ecs_mem_global_block_array != NULL) {
281
282 for (idx = _ecs_mem_global_block_nbr - 1;
283 idx > 0 && (_ecs_mem_global_block_array + idx)->p_bloc != p_get;
284 idx--);
285
286 if ((_ecs_mem_global_block_array + idx)->p_bloc != p_get) {
287 _ecs_mem_summary(stderr);
288 ecs_error(__FILE__, __LINE__, 0,
289 _("Adress [%10p] does not correspond to "
290 "the beginning of an allocated block."),
291 p_get);
292 }
293 else {
294 pinfo = _ecs_mem_global_block_array + idx;
295 assert(p_get == pinfo->p_bloc);
296 }
297
298 }
299
300 return pinfo;
301 }
302
303 /*
304 * Return the size of a given allocated block.
305 *
306 * parameters:
307 * p_in: <-- allocated block's start adress.
308 *
309 * returns:
310 * block size.
311 */
312
313 static size_t
_ecs_mem_block_size(const void * p_in)314 _ecs_mem_block_size(const void *p_in)
315 {
316 struct _ecs_mem_block_t *pinfo = _ecs_mem_block_info(p_in);
317
318 if (pinfo != NULL)
319 return pinfo->size;
320 else
321 return 0;
322 }
323
324 /*
325 * Fill a _ecs_mem_block_t structure for an allocated pointer.
326 */
327
328 static void
_ecs_mem_block_malloc(void * p_new,const size_t size_new)329 _ecs_mem_block_malloc(void *p_new,
330 const size_t size_new)
331 {
332 struct _ecs_mem_block_t *pinfo;
333
334 assert(size_new != 0);
335
336 if (_ecs_mem_global_block_array == NULL)
337 return;
338
339 if (_ecs_mem_global_block_nbr >= _ecs_mem_global_block_max) {
340
341 _ecs_mem_global_block_max *= 2;
342 _ecs_mem_global_block_array
343 = (struct _ecs_mem_block_t *) realloc(_ecs_mem_global_block_array,
344 sizeof(struct _ecs_mem_block_t)
345 * _ecs_mem_global_block_max);
346
347 if (_ecs_mem_global_block_array == NULL) {
348 _ecs_mem_summary(stderr);
349 ecs_error(__FILE__, __LINE__, errno,
350 _("Memory allocation failure"));
351 return;
352 }
353
354 }
355
356 _ecs_mem_global_block_nbr += 1;
357
358 pinfo = _ecs_mem_global_block_array + _ecs_mem_global_block_nbr - 1;
359
360 /* Start adress and size of allocated block */
361
362 pinfo->p_bloc = p_new;
363 pinfo->size = size_new;
364 }
365
366 /*
367 * Update a _ecs_mem_block_t structure for an reallocated pointer.
368 */
369
370 static void
_ecs_mem_block_realloc(const void * p_old,void * p_new,size_t size_new)371 _ecs_mem_block_realloc(const void *p_old,
372 void *p_new,
373 size_t size_new)
374 {
375 struct _ecs_mem_block_t *pinfo;
376
377 assert(size_new != 0);
378
379 pinfo = _ecs_mem_block_info(p_old);
380
381 if (pinfo != NULL) {
382 pinfo->p_bloc = p_new;
383 pinfo->size = size_new;
384 }
385 }
386
387 /*
388 * Free a _ecs_mem_block_t structure for a freed pointer.
389 */
390
391 static void
_ecs_mem_block_free(const void * p_free)392 _ecs_mem_block_free(const void *p_free)
393 {
394 struct _ecs_mem_block_t *pinfo, *pmove;
395 unsigned long idx;
396
397 if (_ecs_mem_global_block_array == NULL)
398 return;
399
400 for (idx = _ecs_mem_global_block_nbr - 1;
401 idx > 0 && (_ecs_mem_global_block_array + idx)->p_bloc != p_free;
402 idx--);
403
404 if ((_ecs_mem_global_block_array + idx)->p_bloc != p_free) {
405 _ecs_mem_summary(stderr);
406 ecs_error(__FILE__, __LINE__, 0,
407 _("Adress [%10p] does not correspond to "
408 "the beginning of an allocated block."),
409 p_free);
410 }
411
412 else {
413
414 /* We move the contents of the array's final block to the position
415 of the freed block, and shorten the array's useful part by one. */
416
417 pinfo = _ecs_mem_global_block_array + idx;
418 pmove = _ecs_mem_global_block_array + _ecs_mem_global_block_nbr - 1;
419 pinfo->p_bloc = pmove->p_bloc;
420 pinfo->size = pmove->size;
421
422 _ecs_mem_global_block_nbr -= 1;
423
424 }
425 }
426
427 /*============================================================================
428 * Public function definitions
429 *============================================================================*/
430
431 /*!
432 * \brief Initialize memory handling.
433 *
434 * This function should be called before any other ecs_mem_...()
435 * function. To activate memory allocation logging, a logfile
436 * name should be given as an argument. The resulting file will
437 * be a regular, local file. If this file cannot be opened for
438 * some reason, logging is silently de-activated.
439 *
440 * \param log_file_name name of optional log_file (if NULL, no log).
441 */
442
443 void
ecs_mem_init(const char * log_file_name)444 ecs_mem_init(const char *log_file_name)
445 {
446 size_t alloc_size;
447
448 if (_ecs_mem_global_initialized == 1) {
449 _ecs_mem_summary(stderr);
450 ecs_error(__FILE__, __LINE__, 0,
451 _("ecs_mem_init() has already been called"));
452 }
453 _ecs_mem_global_initialized = 1;
454
455 alloc_size = sizeof(struct _ecs_mem_block_t) * _ecs_mem_global_block_max;
456
457 _ecs_mem_global_block_array
458 = malloc(sizeof(struct _ecs_mem_block_t) * _ecs_mem_global_block_max);
459
460 if (_ecs_mem_global_block_array == NULL) {
461 _ecs_mem_summary(stderr);
462 ecs_error(__FILE__, __LINE__, errno,
463 _("Failure to allocate \"%s\" (%lu bytes)"),
464 "_ecs_mem_global_block_array", (unsigned long)alloc_size);
465 return;
466 }
467
468 if (log_file_name != NULL) {
469
470 _ecs_mem_global_file = fopen(log_file_name, "w");
471
472 /*
473 If the file could not be opened, we do not abort, as it is not
474 absolutely necessary. We silently continue.
475 (We could warn the user, but this would require either using
476 ecs_printf(), which we prefer to keep independent of the ecs_mem_...()
477 functions to avoid evental crossed definitions when user-defined, or
478 "warning handling" similar to error handling, with a possibility
479 of user-defined warning handlers, as we are not sure if the calling
480 code uses stderr (especially in a distributed environment). This
481 is probably not worth the bother.
482 */
483
484 if (_ecs_mem_global_file == NULL)
485 fprintf(stderr,
486 _("Failure to open memory log file \"%s\"\n"),
487 log_file_name);
488
489 }
490
491 /* Log file header */
492
493 if (_ecs_mem_global_file != NULL) {
494
495 fprintf(_ecs_mem_global_file,
496 " : FILE NAME : LINE :"
497 " POINTER NAME : N BYTES :"
498 " (+- N BYTES) : TOTAL BYTES : [ ADRESS]\n"
499 "-------:----------------------------:-------:"
500 "----------------------------------------:-----------:"
501 "-----------------------------:--------------");
502
503 }
504
505 }
506
507 /*!
508 * \brief End memory handling.
509 *
510 * This function should be called after all other ecs_mem_...()
511 * functions. In case of memory allocation logging, it
512 * writes final information to the log file and closes is.
513 */
514
ecs_mem_end(void)515 void ecs_mem_end(void)
516 {
517 if (_ecs_mem_global_initialized == 0) {
518 _ecs_mem_summary(stderr);
519 ecs_error(__FILE__, __LINE__, 0,
520 _("ecs_mem_end() called before ecs_mem_init()"));
521 }
522 _ecs_mem_global_initialized = 0;
523
524 if (_ecs_mem_global_file != NULL) {
525
526 unsigned long non_free = 0;
527 struct _ecs_mem_block_t *pinfo;
528
529 /* Memory usage summary */
530
531 _ecs_mem_summary(_ecs_mem_global_file);
532
533 /* List of non-freed pointers */
534
535 if (_ecs_mem_global_block_array != NULL) {
536
537 fprintf(_ecs_mem_global_file, "List of non freed pointers:\n");
538
539 for (pinfo = _ecs_mem_global_block_array;
540 pinfo < _ecs_mem_global_block_array + _ecs_mem_global_block_nbr;
541 pinfo++) {
542
543 fprintf(_ecs_mem_global_file,"[%10p]\n", pinfo->p_bloc);
544 non_free++;
545
546 }
547
548 fprintf(_ecs_mem_global_file,
549 "Number of non freed pointers remaining: %lu\n",
550 non_free);
551
552 }
553
554 fclose(_ecs_mem_global_file);
555 }
556
557 /* Reset defaults in case of later initialization */
558
559 if (_ecs_mem_global_block_array != NULL) {
560 free(_ecs_mem_global_block_array);
561 _ecs_mem_global_block_array = NULL;
562 }
563
564 _ecs_mem_global_block_nbr = 0 ;
565 _ecs_mem_global_block_max = 512 ;
566
567 _ecs_mem_global_alloc_cur = 0;
568 _ecs_mem_global_alloc_max = 0;
569
570 _ecs_mem_global_n_allocs = 0;
571 _ecs_mem_global_n_reallocs = 0;
572 _ecs_mem_global_n_frees = 0;
573
574 }
575
576 /*!
577 * \brief Indicates if ecs_mem_...() functions are initialized.
578 *
579 * \returns 1 if ecs_mem_init has been called, 0 otherwise.
580 */
581
582 int
ecs_mem_initialized(void)583 ecs_mem_initialized(void)
584 {
585 return _ecs_mem_global_initialized;
586 }
587
588 /*!
589 * \brief Allocate memory for ni elements of size bytes.
590 *
591 * This function calls malloc(), but adds tracing capabilities, and
592 * automatically calls the ecs_error() errorhandler if it fails to
593 * allocate the required memory.
594 *
595 * \param [in] ni number of elements.
596 * \param [in] size element size.
597 * \param [in] var_name allocated variable name string.
598 * \param [in] file_name name of calling source file.
599 * \param [in] line_num line number in calling source file.
600 *
601 * \returns pointer to allocated memory.
602 */
603
604 void *
ecs_mem_malloc(size_t ni,size_t size,const char * var_name,const char * file_name,int line_num)605 ecs_mem_malloc(size_t ni,
606 size_t size,
607 const char *var_name,
608 const char *file_name,
609 int line_num)
610 {
611 void *p_loc;
612 size_t alloc_size = ni * size;
613
614 if (ni == 0)
615 return NULL;
616
617 /* Allocate memory and check return */
618
619 p_loc = malloc(alloc_size);
620
621 if (p_loc == NULL) {
622 _ecs_mem_summary(stderr);
623 ecs_error(file_name, line_num, errno,
624 _("Failure to allocate \"%s\" (%lu bytes)"),
625 var_name, (unsigned long)alloc_size);
626 return NULL;
627 }
628 else if (_ecs_mem_global_initialized == 0)
629 return p_loc;
630
631 /* Memory allocation counting */
632
633 _ecs_mem_global_alloc_cur += alloc_size;
634
635 if (_ecs_mem_global_alloc_max < _ecs_mem_global_alloc_cur)
636 _ecs_mem_global_alloc_max = _ecs_mem_global_alloc_cur;
637
638 if (_ecs_mem_global_file != NULL) {
639 fprintf(_ecs_mem_global_file, "\n alloc: %-27s:%6d : %-39s: %9lu",
640 _ecs_mem_basename(file_name), line_num,
641 var_name, (unsigned long)alloc_size);
642 fprintf(_ecs_mem_global_file, " : (+%9lu) : %12lu : [%10p]",
643 (unsigned long)alloc_size,
644 (unsigned long)_ecs_mem_global_alloc_cur,
645 p_loc);
646 fflush(_ecs_mem_global_file);
647 }
648
649 _ecs_mem_block_malloc(p_loc, alloc_size);
650
651 _ecs_mem_global_n_allocs += 1;
652
653 /* Return pointer to allocated memory */
654
655 return p_loc;
656 }
657
658 /*!
659 * \brief Reallocate memory for ni elements of size bytes.
660 *
661 * This function calls realloc(), but adds tracing capabilities, and
662 * automatically calls the ecs_error() errorhandler if it fails to
663 * allocate the required memory.
664 *
665 * \param [in] ptr pointer to previous memory location
666 * (if NULL, ecs_alloc() called).
667 * \param [in] ni number of elements.
668 * \param [in] size element size.
669 * \param [in] var_name allocated variable name string.
670 * \param [in] file_name name of calling source file.
671 * \param [in] line_num line number in calling source file.
672 *
673 * \returns pointer to reallocated memory.
674 */
675
676 void *
ecs_mem_realloc(void * ptr,size_t ni,size_t size,const char * var_name,const char * file_name,int line_num)677 ecs_mem_realloc(void *ptr,
678 size_t ni,
679 size_t size,
680 const char *var_name,
681 const char *file_name,
682 int line_num)
683 {
684 void *p_loc;
685
686 long size_diff;
687 size_t old_size;
688 size_t new_size = ni * size;
689
690 /*
691 Behave as ecs_malloc() if the previous pointer is equal to NULL.
692 Note that the operation will then appear as a first allocation
693 ('alloc') in the _ecs_mem_global_file trace file.
694 */
695
696 if (ptr == NULL)
697 return ecs_mem_malloc(ni,
698 size,
699 var_name,
700 file_name,
701 line_num);
702
703 /* If the old size equals the new size, nothing needs to be done. */
704
705 old_size = _ecs_mem_block_size(ptr);
706
707 if (new_size == old_size)
708 return ptr;
709
710 /*
711 We may also simply free memory. Note that in this case, the operation
712 appears as 'free' in the _ecs_mem_global_file trace file.
713 */
714
715 else if (ni == 0)
716 return ecs_mem_free(ptr,
717 var_name,
718 file_name,
719 line_num);
720
721 /* In the final case, we have a true reallocation */
722
723 else {
724
725 size_diff = new_size - old_size;
726
727 p_loc = realloc(ptr, new_size);
728
729 if (p_loc == NULL) {
730 _ecs_mem_summary(stderr);
731 ecs_error(file_name, line_num, errno,
732 _("Failure to reallocate \"%s\" (%lu bytes)"),
733 var_name, (unsigned long)new_size);
734 return NULL;
735 }
736 else if (_ecs_mem_global_initialized == 0)
737 return p_loc;
738
739 _ecs_mem_global_alloc_cur += size_diff;
740
741 if (size_diff > 0) {
742 if (_ecs_mem_global_alloc_max < _ecs_mem_global_alloc_cur)
743 _ecs_mem_global_alloc_max = _ecs_mem_global_alloc_cur;
744 }
745
746 if (_ecs_mem_global_file != NULL) {
747 char sgn = (size_diff > 0) ? '+' : '-';
748 fprintf(_ecs_mem_global_file, "\nrealloc: %-27s:%6d : %-39s: %9lu",
749 _ecs_mem_basename(file_name), line_num,
750 var_name, (unsigned long)new_size);
751 fprintf(_ecs_mem_global_file, " : (%c%9lu) : %12lu : [%10p]",
752 sgn,
753 (unsigned long) ((size_diff > 0) ? size_diff : -size_diff),
754 (unsigned long)_ecs_mem_global_alloc_cur,
755 p_loc);
756 fflush(_ecs_mem_global_file);
757 }
758
759 _ecs_mem_block_realloc(ptr, p_loc, new_size);
760
761 _ecs_mem_global_n_reallocs += 1;
762
763 return p_loc;
764 }
765
766 }
767
768 /*!
769 * \brief Free allocated memory.
770 *
771 * This function calls free(), but adds tracing capabilities, and
772 * automatically calls the ecs_error() errorhandler if it fails to
773 * free the corresponding memory. In case of a NULL pointer argument,
774 * the function simply returns.
775 *
776 * \param [in] ptr pointer to previous memory location
777 * (if NULL, ecs_alloc() called).
778 * \param [in] var_name allocated variable name string
779 * \param [in] file_name name of calling source file
780 * \param [in] line_num line number in calling source file
781 *
782 * \returns NULL pointer.
783 */
784
785 void *
ecs_mem_free(void * ptr,const char * var_name,const char * file_name,int line_num)786 ecs_mem_free(void *ptr,
787 const char *var_name,
788 const char *file_name,
789 int line_num)
790 {
791 size_t size_info;
792
793 /* NULL pointer case (non-allocated location) */
794
795 if (ptr == NULL)
796 return NULL;
797
798 /* General case (free allocated memory) */
799
800 if (_ecs_mem_global_initialized != 0) {
801
802 size_info = _ecs_mem_block_size(ptr);
803
804 _ecs_mem_global_alloc_cur -= size_info;
805
806 if (_ecs_mem_global_file != NULL) {
807 fprintf(_ecs_mem_global_file,"\n free: %-27s:%6d : %-39s: %9lu",
808 _ecs_mem_basename(file_name), line_num,
809 var_name, (unsigned long)size_info);
810 fprintf(_ecs_mem_global_file, " : (-%9lu) : %12lu : [%10p]",
811 (unsigned long)size_info,
812 (unsigned long)_ecs_mem_global_alloc_cur,
813 ptr);
814 fflush(_ecs_mem_global_file);
815 }
816
817 _ecs_mem_block_free(ptr);
818
819 _ecs_mem_global_n_frees += 1;
820 }
821
822 free(ptr);
823
824 return NULL;
825 }
826
827 /*!
828 * \brief Return current theoretical dynamic memory allocated.
829 *
830 * \return current memory handled through ecs_mem_...() (in kB).
831 */
832
833 size_t
ecs_mem_size_current(void)834 ecs_mem_size_current(void)
835 {
836 return (_ecs_mem_global_alloc_cur / 1024);
837 }
838
839 /*!
840 * \brief Return maximum theoretical dynamic memory allocated.
841 *
842 * \return maximum memory handled through ecs_mem_...() (in kB).
843 */
844
845 size_t
ecs_mem_size_max(void)846 ecs_mem_size_max(void)
847 {
848 return (_ecs_mem_global_alloc_max / 1024);
849 }
850
851 /*----------------------------------------------------------------------------*/
852
853 END_C_DECLS
854