2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19 * All rights reserved.
21 * The Original Code is: all of this file.
23 * Contributor(s): Brecht Van Lommel
26 * ***** END GPL LICENSE BLOCK *****
29 /** \file guardedalloc/intern/mallocn.c
32 * Guarded memory allocation, and boundary-write detection.
36 #include <string.h> /* memcpy */
38 #include <sys/types.h>
40 #include "MEM_guardedalloc.h"
42 /* to ensure strict conversions */
43 #include "../../source/blender/blenlib/BLI_strict_flags.h"
45 #include "mallocn_intern.h"
46 #include "atomic_ops.h"
48 /* Only for debugging:
49 * store original buffer's name when doing MEM_dupallocN
50 * helpful to profile issues with non-freed "dup_alloc" buffers,
51 * but this introduces some overhead to memory header and makes
52 * things slower a bit, so better to keep disabled by default
54 //#define DEBUG_MEMDUPLINAME
56 /* Only for debugging:
57 * lets you count the allocations so as to find the allocator of unfreed memory
58 * in situations where the leak is predictable */
60 //#define DEBUG_MEMCOUNTER
62 /* Only for debugging:
63 * defining DEBUG_THREADS will enable check whether memory manager
64 * is locked with a mutex when allocation is called from non-main
67 * This helps troubleshooting memory issues caused by the fact
68 * guarded allocator is not thread-safe, however this check will
69 * fail to check allocations from openmp threads.
71 //#define DEBUG_THREADS
73 /* Only for debugging:
74 * Defining DEBUG_BACKTRACE will store a backtrace from where
75 * memory block was allocated and print this trace for all
78 //#define DEBUG_BACKTRACE
80 #ifdef DEBUG_BACKTRACE
81 # define BACKTRACE_SIZE 100
84 #ifdef DEBUG_MEMCOUNTER
85 /* set this to the value that isn't being freed */
86 # define DEBUG_MEMCOUNTER_ERROR_VAL 0
87 static int _mallocn_count = 0;
90 static void memcount_raise(const char *name)
92 fprintf(stderr, "%s: memcount-leak, %d\n", name, _mallocn_count);
96 /* --------------------------------------------------------------------- */
98 /* --------------------------------------------------------------------- */
99 /* all memory chunks are put in linked lists */
100 typedef struct localLink {
101 struct localLink *next, *prev;
104 typedef struct localListBase {
108 /* note: keep this struct aligned (e.g., irix/gcc) - Hos */
109 typedef struct MemHead {
112 struct MemHead *next, *prev;
114 const char *nextname;
116 int mmap; /* if true, memory was mmapped */
117 #ifdef DEBUG_MEMCOUNTER
121 #ifdef DEBUG_MEMDUPLINAME
122 int need_free_name, pad;
125 #ifdef DEBUG_BACKTRACE
126 void *backtrace[BACKTRACE_SIZE];
131 /* for openmp threading asserts, saves time troubleshooting
132 * we may need to extend this if blender code starts using MEM_
133 * functions inside OpenMP correctly with omp_set_lock() */
135 #if 0 /* disable for now, only use to debug openmp code which doesn lock threads for malloc */
136 #if defined(_OPENMP) && defined(DEBUG)
139 # define DEBUG_OMP_MALLOC
145 # include <pthread.h>
146 static pthread_t mainid;
149 #ifdef DEBUG_BACKTRACE
150 # if defined(__linux__) || defined(__APPLE__)
151 # include <execinfo.h>
152 // Windows is not supported yet.
153 //# elif defined(_MSV_VER)
154 //# include <DbgHelp.h>
158 typedef struct MemTail {
163 /* --------------------------------------------------------------------- */
164 /* local functions */
165 /* --------------------------------------------------------------------- */
167 static void addtail(volatile localListBase *listbase, void *vlink);
168 static void remlink(volatile localListBase *listbase, void *vlink);
169 static void rem_memblock(MemHead *memh);
170 static void MemorY_ErroR(const char *block, const char *error);
171 static const char *check_memlist(MemHead *memh);
173 /* --------------------------------------------------------------------- */
174 /* locally used defines */
175 /* --------------------------------------------------------------------- */
177 #ifdef __BIG_ENDIAN__
178 # define MAKE_ID(a, b, c, d) ((int)(a) << 24 | (int)(b) << 16 | (c) << 8 | (d))
180 # define MAKE_ID(a, b, c, d) ((int)(d) << 24 | (int)(c) << 16 | (b) << 8 | (a))
183 #define MEMTAG1 MAKE_ID('M', 'E', 'M', 'O')
184 #define MEMTAG2 MAKE_ID('R', 'Y', 'B', 'L')
185 #define MEMTAG3 MAKE_ID('O', 'C', 'K', '!')
186 #define MEMFREE MAKE_ID('F', 'R', 'E', 'E')
189 ((MemHead *)(((char *) x) - ((char *) &(((MemHead *)0)->next))))
191 /* --------------------------------------------------------------------- */
193 /* --------------------------------------------------------------------- */
196 static unsigned int totblock = 0;
197 static size_t mem_in_use = 0, mmap_in_use = 0, peak_mem = 0;
199 static volatile struct localListBase _membase;
200 static volatile struct localListBase *membase = &_membase;
201 static void (*error_callback)(const char *) = NULL;
202 static void (*thread_lock_callback)(void) = NULL;
203 static void (*thread_unlock_callback)(void) = NULL;
205 static bool malloc_debug_memset = false;
220 /* --------------------------------------------------------------------- */
222 /* --------------------------------------------------------------------- */
225 __attribute__ ((format(printf, 1, 2)))
227 static void print_error(const char *str, ...)
233 vsnprintf(buf, sizeof(buf), str, ap);
235 buf[sizeof(buf) - 1] = '\0';
237 if (error_callback) error_callback(buf);
240 static void mem_lock_thread(void)
243 static int initialized = 0;
245 if (initialized == 0) {
246 /* assume first allocation happens from main thread */
247 mainid = pthread_self();
251 if (!pthread_equal(pthread_self(), mainid) && thread_lock_callback == NULL) {
252 assert(!"Memory function is called from non-main thread without lock");
256 #ifdef DEBUG_OMP_MALLOC
257 assert(omp_in_parallel() == 0);
260 if (thread_lock_callback)
261 thread_lock_callback();
264 static void mem_unlock_thread(void)
267 if (!pthread_equal(pthread_self(), mainid) && thread_lock_callback == NULL) {
268 assert(!"Thread lock was removed while allocation from thread is in progress");
272 if (thread_unlock_callback)
273 thread_unlock_callback();
276 bool MEM_guarded_check_memory_integrity(void)
278 const char *err_val = NULL;
280 /* check_memlist starts from the front, and runs until it finds
281 * the requested chunk. For this test, that's the last one. */
282 listend = membase->last;
284 err_val = check_memlist(listend);
286 return (err_val != NULL);
290 void MEM_guarded_set_error_callback(void (*func)(const char *))
292 error_callback = func;
295 void MEM_guarded_set_lock_callback(void (*lock)(void), void (*unlock)(void))
297 thread_lock_callback = lock;
298 thread_unlock_callback = unlock;
301 void MEM_guarded_set_memory_debug(void)
303 malloc_debug_memset = true;
306 size_t MEM_guarded_allocN_len(const void *vmemh)
309 const MemHead *memh = vmemh;
319 void *MEM_guarded_dupallocN(const void *vmemh)
324 const MemHead *memh = vmemh;
327 #ifndef DEBUG_MEMDUPLINAME
329 newp = MEM_guarded_mapallocN(memh->len, "dupli_mapalloc");
331 newp = MEM_guarded_mallocN(memh->len, "dupli_alloc");
333 if (newp == NULL) return NULL;
337 char *name = malloc(strlen(memh->name) + 24);
340 sprintf(name, "%s %s", "dupli_mapalloc", memh->name);
341 newp = MEM_guarded_mapallocN(memh->len, name);
344 sprintf(name, "%s %s", "dupli_alloc", memh->name);
345 newp = MEM_guarded_mallocN(memh->len, name);
348 if (newp == NULL) return NULL;
353 nmemh->need_free_name = 1;
357 memcpy(newp, vmemh, memh->len);
363 void *MEM_guarded_reallocN_id(void *vmemh, size_t len, const char *str)
368 MemHead *memh = vmemh;
371 newp = MEM_guarded_mallocN(len, memh->name);
373 if (len < memh->len) {
375 memcpy(newp, vmemh, len);
378 /* grow (or remain same size) */
379 memcpy(newp, vmemh, memh->len);
383 MEM_guarded_freeN(vmemh);
386 newp = MEM_guarded_mallocN(len, str);
392 void *MEM_guarded_recallocN_id(void *vmemh, size_t len, const char *str)
397 MemHead *memh = vmemh;
400 newp = MEM_guarded_mallocN(len, memh->name);
402 if (len < memh->len) {
404 memcpy(newp, vmemh, len);
407 memcpy(newp, vmemh, memh->len);
409 if (len > memh->len) {
412 memset(((char *)newp) + memh->len, 0, len - memh->len);
417 MEM_guarded_freeN(vmemh);
420 newp = MEM_guarded_callocN(len, str);
426 #ifdef DEBUG_BACKTRACE
427 # if defined(__linux__) || defined(__APPLE__)
428 static void make_memhead_backtrace(MemHead *memh)
430 memh->backtrace_size = backtrace(memh->backtrace, BACKTRACE_SIZE);
433 static void print_memhead_backtrace(MemHead *memh)
438 strings = backtrace_symbols(memh->backtrace, memh->backtrace_size);
439 for (i = 0; i < memh->backtrace_size; i++) {
440 print_error(" %s\n", strings[i]);
446 static void make_memhead_backtrace(MemHead *memh)
448 (void) memh; /* Ignored. */
451 static void print_memhead_backtrace(MemHead *memh)
453 (void) memh; /* Ignored. */
455 # endif /* defined(__linux__) || defined(__APPLE__) */
456 #endif /* DEBUG_BACKTRACE */
458 static void make_memhead_header(MemHead *memh, size_t len, const char *str)
462 memh->tag1 = MEMTAG1;
464 memh->nextname = NULL;
467 memh->tag2 = MEMTAG2;
469 #ifdef DEBUG_MEMDUPLINAME
470 memh->need_free_name = 0;
473 #ifdef DEBUG_BACKTRACE
474 make_memhead_backtrace(memh);
477 memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + len);
478 memt->tag3 = MEMTAG3;
480 atomic_add_u(&totblock, 1);
481 atomic_add_z(&mem_in_use, len);
484 addtail(membase, &memh->next);
486 memh->nextname = MEMNEXT(memh->next)->name;
488 peak_mem = mem_in_use > peak_mem ? mem_in_use : peak_mem;
492 void *MEM_guarded_mallocN(size_t len, const char *str)
496 len = SIZET_ALIGN_4(len);
498 memh = (MemHead *)malloc(len + sizeof(MemHead) + sizeof(MemTail));
501 make_memhead_header(memh, len, str);
502 if (malloc_debug_memset && len)
503 memset(memh + 1, 255, len);
505 #ifdef DEBUG_MEMCOUNTER
506 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
507 memcount_raise(__func__);
508 memh->_count = _mallocn_count++;
512 print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
513 SIZET_ARG(len), str, (unsigned int) mem_in_use);
517 void *MEM_guarded_callocN(size_t len, const char *str)
521 len = SIZET_ALIGN_4(len);
523 memh = (MemHead *)calloc(len + sizeof(MemHead) + sizeof(MemTail), 1);
526 make_memhead_header(memh, len, str);
527 #ifdef DEBUG_MEMCOUNTER
528 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
529 memcount_raise(__func__);
530 memh->_count = _mallocn_count++;
534 print_error("Calloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
535 SIZET_ARG(len), str, (unsigned int) mem_in_use);
539 /* note; mmap returns zero'd memory */
540 void *MEM_guarded_mapallocN(size_t len, const char *str)
544 len = SIZET_ALIGN_4(len);
547 /* our windows mmap implementation is not thread safe */
550 memh = mmap(NULL, len + sizeof(MemHead) + sizeof(MemTail),
551 PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
556 if (memh != (MemHead *)-1) {
557 make_memhead_header(memh, len, str);
559 atomic_add_z(&mmap_in_use, len);
561 peak_mem = mmap_in_use > peak_mem ? mmap_in_use : peak_mem;
563 #ifdef DEBUG_MEMCOUNTER
564 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
565 memcount_raise(__func__);
566 memh->_count = _mallocn_count++;
571 print_error("Mapalloc returns null, fallback to regular malloc: "
572 "len=" SIZET_FORMAT " in %s, total %u\n",
573 SIZET_ARG(len), str, (unsigned int) mmap_in_use);
574 return MEM_guarded_callocN(len, str);
578 /* Memory statistics print */
579 typedef struct MemPrintBlock {
585 static int compare_name(const void *p1, const void *p2)
587 const MemPrintBlock *pb1 = (const MemPrintBlock *)p1;
588 const MemPrintBlock *pb2 = (const MemPrintBlock *)p2;
590 return strcmp(pb1->name, pb2->name);
593 static int compare_len(const void *p1, const void *p2)
595 const MemPrintBlock *pb1 = (const MemPrintBlock *)p1;
596 const MemPrintBlock *pb2 = (const MemPrintBlock *)p2;
598 if (pb1->len < pb2->len)
600 else if (pb1->len == pb2->len)
606 void MEM_guarded_printmemlist_stats(void)
609 MemPrintBlock *pb, *printblock;
610 unsigned int totpb, a, b;
611 size_t mem_in_use_slop = 0;
615 /* put memory blocks into array */
616 printblock = malloc(sizeof(MemPrintBlock) * totblock);
621 membl = membase->first;
622 if (membl) membl = MEMNEXT(membl);
625 pb->name = membl->name;
626 pb->len = membl->len;
633 mem_in_use_slop += (sizeof(MemHead) + sizeof(MemTail) +
634 malloc_usable_size((void *)membl)) - membl->len;
638 membl = MEMNEXT(membl->next);
642 /* sort by name and add together blocks with the same name */
643 qsort(printblock, totpb, sizeof(MemPrintBlock), compare_name);
644 for (a = 0, b = 0; a < totpb; a++) {
648 else if (strcmp(printblock[a].name, printblock[b].name) == 0) {
649 printblock[b].len += printblock[a].len;
650 printblock[b].items++;
654 memcpy(&printblock[b], &printblock[a], sizeof(MemPrintBlock));
659 /* sort by length and print */
660 qsort(printblock, totpb, sizeof(MemPrintBlock), compare_len);
661 printf("\ntotal memory len: %.3f MB\n",
662 (double)mem_in_use / (double)(1024 * 1024));
663 printf("peak memory len: %.3f MB\n",
664 (double)peak_mem / (double)(1024 * 1024));
665 printf("slop memory len: %.3f MB\n",
666 (double)mem_in_use_slop / (double)(1024 * 1024));
667 printf(" ITEMS TOTAL-MiB AVERAGE-KiB TYPE\n");
668 for (a = 0, pb = printblock; a < totpb; a++, pb++) {
669 printf("%6d (%8.3f %8.3f) %s\n",
670 pb->items, (double)pb->len / (double)(1024 * 1024),
671 (double)pb->len / 1024.0 / (double)pb->items, pb->name);
677 #ifdef HAVE_MALLOC_STATS
678 printf("System Statistics:\n");
683 static const char mem_printmemlist_pydict_script[] =
686 "for mb_item in membase:\n"
687 " mb_item_user_size = mb_userinfo.setdefault(mb_item['name'], [0,0])\n"
688 " mb_item_user_size[0] += 1 # Add a user\n"
689 " mb_item_user_size[1] += mb_item['len'] # Increment the size\n"
690 " totmem += mb_item['len']\n"
691 "print('(membase) items:', len(membase), '| unique-names:',\n"
692 " len(mb_userinfo), '| total-mem:', totmem)\n"
693 "mb_userinfo_sort = list(mb_userinfo.items())\n"
694 "for sort_name, sort_func in (('size', lambda a: -a[1][1]),\n"
695 " ('users', lambda a: -a[1][0]),\n"
696 " ('name', lambda a: a[0])):\n"
697 " print('\\nSorting by:', sort_name)\n"
698 " mb_userinfo_sort.sort(key = sort_func)\n"
699 " for item in mb_userinfo_sort:\n"
700 " print('name:%%s, users:%%i, len:%%i' %%\n"
701 " (item[0], item[1][0], item[1][1]))\n";
703 /* Prints in python syntax for easy */
704 static void MEM_guarded_printmemlist_internal(int pydict)
710 membl = membase->first;
711 if (membl) membl = MEMNEXT(membl);
714 print_error("# membase_debug.py\n");
715 print_error("membase = [\n");
720 " {'len':" SIZET_FORMAT ", "
722 "'pointer':'%p'},\n",
723 SIZET_ARG(membl->len), membl->name, (void *)(membl + 1));
726 #ifdef DEBUG_MEMCOUNTER
727 print_error("%s len: " SIZET_FORMAT " %p, count: %d\n",
728 membl->name, SIZET_ARG(membl->len), membl + 1,
731 print_error("%s len: " SIZET_FORMAT " %p\n",
732 membl->name, SIZET_ARG(membl->len), membl + 1);
734 #ifdef DEBUG_BACKTRACE
735 print_memhead_backtrace(membl);
739 membl = MEMNEXT(membl->next);
743 fprintf(stderr, "]\n\n");
744 fprintf(stderr, mem_printmemlist_pydict_script);
750 void MEM_guarded_callbackmemlist(void (*func)(void *))
756 membl = membase->first;
757 if (membl) membl = MEMNEXT(membl);
762 membl = MEMNEXT(membl->next);
770 short MEM_guarded_testN(void *vmemh)
776 membl = membase->first;
777 if (membl) membl = MEMNEXT(membl);
780 if (vmemh == membl + 1) {
786 membl = MEMNEXT(membl->next);
792 print_error("Memoryblock %p: pointer not in memlist\n", vmemh);
797 void MEM_guarded_printmemlist(void)
799 MEM_guarded_printmemlist_internal(0);
801 void MEM_guarded_printmemlist_pydict(void)
803 MEM_guarded_printmemlist_internal(1);
806 void MEM_guarded_freeN(void *vmemh)
809 MemHead *memh = vmemh;
813 MemorY_ErroR("free", "attempt to free NULL pointer");
814 /* print_error(err_stream, "%d\n", (memh+4000)->tag1); */
818 if (sizeof(intptr_t) == 8) {
819 if (((intptr_t) memh) & 0x7) {
820 MemorY_ErroR("free", "attempt to free illegal pointer");
825 if (((intptr_t) memh) & 0x3) {
826 MemorY_ErroR("free", "attempt to free illegal pointer");
832 if (memh->tag1 == MEMFREE && memh->tag2 == MEMFREE) {
833 MemorY_ErroR(memh->name, "double free");
837 if ((memh->tag1 == MEMTAG1) &&
838 (memh->tag2 == MEMTAG2) &&
839 ((memh->len & 0x3) == 0))
841 memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + memh->len);
842 if (memt->tag3 == MEMTAG3) {
844 memh->tag1 = MEMFREE;
845 memh->tag2 = MEMFREE;
846 memt->tag3 = MEMFREE;
852 MemorY_ErroR(memh->name, "end corrupt");
853 name = check_memlist(memh);
855 if (name != memh->name) MemorY_ErroR(name, "is also corrupt");
860 name = check_memlist(memh);
863 MemorY_ErroR("free", "pointer not in memlist");
865 MemorY_ErroR(name, "error in header");
869 /* here a DUMP should happen */
874 /* --------------------------------------------------------------------- */
875 /* local functions */
876 /* --------------------------------------------------------------------- */
878 static void addtail(volatile localListBase *listbase, void *vlink)
880 struct localLink *link = vlink;
882 /* for a generic API error checks here is fine but
883 * the limited use here they will never be NULL */
885 if (link == NULL) return;
886 if (listbase == NULL) return;
890 link->prev = listbase->last;
892 if (listbase->last) ((struct localLink *)listbase->last)->next = link;
893 if (listbase->first == NULL) listbase->first = link;
894 listbase->last = link;
897 static void remlink(volatile localListBase *listbase, void *vlink)
899 struct localLink *link = vlink;
901 /* for a generic API error checks here is fine but
902 * the limited use here they will never be NULL */
904 if (link == NULL) return;
905 if (listbase == NULL) return;
908 if (link->next) link->next->prev = link->prev;
909 if (link->prev) link->prev->next = link->next;
911 if (listbase->last == link) listbase->last = link->prev;
912 if (listbase->first == link) listbase->first = link->next;
915 static void rem_memblock(MemHead *memh)
918 remlink(membase, &memh->next);
921 MEMNEXT(memh->prev)->nextname = MEMNEXT(memh->next)->name;
923 MEMNEXT(memh->prev)->nextname = NULL;
927 atomic_sub_u(&totblock, 1);
928 atomic_sub_z(&mem_in_use, memh->len);
930 #ifdef DEBUG_MEMDUPLINAME
931 if (memh->need_free_name)
932 free((char *) memh->name);
936 atomic_sub_z(&mmap_in_use, memh->len);
938 /* our windows mmap implementation is not thread safe */
941 if (munmap(memh, memh->len + sizeof(MemHead) + sizeof(MemTail)))
942 printf("Couldn't unmap memory %s\n", memh->name);
948 if (malloc_debug_memset && memh->len)
949 memset(memh + 1, 255, memh->len);
954 static void MemorY_ErroR(const char *block, const char *error)
956 print_error("Memoryblock %s: %s\n", block, error);
958 #ifdef WITH_ASSERT_ABORT
963 static const char *check_memlist(MemHead *memh)
965 MemHead *forw, *back, *forwok, *backok;
968 forw = membase->first;
969 if (forw) forw = MEMNEXT(forw);
972 if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) break;
974 if (forw->next) forw = MEMNEXT(forw->next);
978 back = (MemHead *) membase->last;
979 if (back) back = MEMNEXT(back);
982 if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) break;
984 if (back->prev) back = MEMNEXT(back->prev);
988 if (forw != back) return ("MORE THAN 1 MEMORYBLOCK CORRUPT");
990 if (forw == NULL && back == NULL) {
991 /* no wrong headers found then but in search of memblock */
993 forw = membase->first;
994 if (forw) forw = MEMNEXT(forw);
997 if (forw == memh) break;
998 if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) break;
1000 if (forw->next) forw = MEMNEXT(forw->next);
1003 if (forw == NULL) return NULL;
1005 back = (MemHead *) membase->last;
1006 if (back) back = MEMNEXT(back);
1009 if (back == memh) break;
1010 if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) break;
1012 if (back->prev) back = MEMNEXT(back->prev);
1017 if (forwok) name = forwok->nextname;
1018 else name = "No name found";
1021 /* to be sure but this block is removed from the list */
1024 forwok->next = (MemHead *)&backok->next;
1025 backok->prev = (MemHead *)&forwok->next;
1026 forwok->nextname = backok->name;
1029 forwok->next = NULL;
1030 membase->last = (struct localLink *) &forwok->next;
1035 backok->prev = NULL;
1036 membase->first = &backok->next;
1039 membase->first = membase->last = NULL;
1044 MemorY_ErroR(name, "Additional error in header");
1045 return("Additional error in header");
1051 size_t MEM_guarded_get_peak_memory(void)
1056 _peak_mem = peak_mem;
1057 mem_unlock_thread();
1062 void MEM_guarded_reset_peak_memory(void)
1066 mem_unlock_thread();
1069 uintptr_t MEM_guarded_get_memory_in_use(void)
1071 uintptr_t _mem_in_use;
1074 _mem_in_use = mem_in_use;
1075 mem_unlock_thread();
1080 uintptr_t MEM_guarded_get_mapped_memory_in_use(void)
1082 uintptr_t _mmap_in_use;
1085 _mmap_in_use = mmap_in_use;
1086 mem_unlock_thread();
1088 return _mmap_in_use;
1091 unsigned int MEM_guarded_get_memory_blocks_in_use(void)
1093 unsigned int _totblock;
1096 _totblock = totblock;
1097 mem_unlock_thread();
1103 const char *MEM_guarded_name_ptr(void *vmemh)
1106 MemHead *memh = vmemh;
1111 return "MEM_guarded_name_ptr(NULL)";