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