2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17 * All rights reserved.
23 * Guarded memory allocation, and boundary-write detection.
28 #include <string.h> /* memcpy */
29 #include <sys/types.h>
31 #include "MEM_guardedalloc.h"
33 /* to ensure strict conversions */
34 #include "../../source/blender/blenlib/BLI_strict_flags.h"
36 #include "atomic_ops.h"
37 #include "mallocn_intern.h"
39 /* Only for debugging:
40 * store original buffer's name when doing MEM_dupallocN
41 * helpful to profile issues with non-freed "dup_alloc" buffers,
42 * but this introduces some overhead to memory header and makes
43 * things slower a bit, so better to keep disabled by default
45 //#define DEBUG_MEMDUPLINAME
47 /* Only for debugging:
48 * lets you count the allocations so as to find the allocator of unfreed memory
49 * in situations where the leak is predictable */
51 //#define DEBUG_MEMCOUNTER
53 /* Only for debugging:
54 * defining DEBUG_THREADS will enable check whether memory manager
55 * is locked with a mutex when allocation is called from non-main
58 * This helps troubleshooting memory issues caused by the fact
59 * guarded allocator is not thread-safe, however this check will
60 * fail to check allocations from openmp threads.
62 //#define DEBUG_THREADS
64 /* Only for debugging:
65 * Defining DEBUG_BACKTRACE will store a backtrace from where
66 * memory block was allocated and print this trace for all
69 //#define DEBUG_BACKTRACE
71 #ifdef DEBUG_BACKTRACE
72 # define BACKTRACE_SIZE 100
75 #ifdef DEBUG_MEMCOUNTER
76 /* set this to the value that isn't being freed */
77 # define DEBUG_MEMCOUNTER_ERROR_VAL 0
78 static int _mallocn_count = 0;
81 static void memcount_raise(const char *name)
83 fprintf(stderr, "%s: memcount-leak, %d\n", name, _mallocn_count);
87 /* --------------------------------------------------------------------- */
89 /* --------------------------------------------------------------------- */
90 /* all memory chunks are put in linked lists */
91 typedef struct localLink {
92 struct localLink *next, *prev;
95 typedef struct localListBase {
99 /* note: keep this struct aligned (e.g., irix/gcc) - Hos */
100 typedef struct MemHead {
103 struct MemHead *next, *prev;
105 const char *nextname;
107 short mmap; /* if true, memory was mmapped */
108 short alignment; /* if non-zero aligned alloc was used
109 * and alignment is stored here.
111 #ifdef DEBUG_MEMCOUNTER
115 #ifdef DEBUG_MEMDUPLINAME
116 int need_free_name, pad;
119 #ifdef DEBUG_BACKTRACE
120 void *backtrace[BACKTRACE_SIZE];
125 typedef MemHead MemHeadAligned;
127 /* for openmp threading asserts, saves time troubleshooting
128 * we may need to extend this if blender code starts using MEM_
129 * functions inside OpenMP correctly with omp_set_lock() */
131 #if 0 /* disable for now, only use to debug openmp code which doesn lock threads for malloc */
132 # if defined(_OPENMP) && defined(DEBUG)
135 # define DEBUG_OMP_MALLOC
141 # include <pthread.h>
142 static pthread_t mainid;
145 #ifdef DEBUG_BACKTRACE
146 # if defined(__linux__) || defined(__APPLE__)
147 # include <execinfo.h>
148 // Windows is not supported yet.
149 //# elif defined(_MSV_VER)
150 //# include <DbgHelp.h>
154 typedef struct MemTail {
158 /* --------------------------------------------------------------------- */
159 /* local functions */
160 /* --------------------------------------------------------------------- */
162 static void addtail(volatile localListBase *listbase, void *vlink);
163 static void remlink(volatile localListBase *listbase, void *vlink);
164 static void rem_memblock(MemHead *memh);
165 static void MemorY_ErroR(const char *block, const char *error);
166 static const char *check_memlist(MemHead *memh);
168 /* --------------------------------------------------------------------- */
169 /* locally used defines */
170 /* --------------------------------------------------------------------- */
172 #ifdef __BIG_ENDIAN__
173 # define MAKE_ID(a, b, c, d) ((int)(a) << 24 | (int)(b) << 16 | (c) << 8 | (d))
175 # define MAKE_ID(a, b, c, d) ((int)(d) << 24 | (int)(c) << 16 | (b) << 8 | (a))
178 #define MEMTAG1 MAKE_ID('M', 'E', 'M', 'O')
179 #define MEMTAG2 MAKE_ID('R', 'Y', 'B', 'L')
180 #define MEMTAG3 MAKE_ID('O', 'C', 'K', '!')
181 #define MEMFREE MAKE_ID('F', 'R', 'E', 'E')
183 #define MEMNEXT(x) ((MemHead *)(((char *)x) - ((char *)&(((MemHead *)0)->next))))
185 /* --------------------------------------------------------------------- */
187 /* --------------------------------------------------------------------- */
189 static unsigned int totblock = 0;
190 static size_t mem_in_use = 0, mmap_in_use = 0, peak_mem = 0;
192 static volatile struct localListBase _membase;
193 static volatile struct localListBase *membase = &_membase;
194 static void (*error_callback)(const char *) = NULL;
195 static void (*thread_lock_callback)(void) = NULL;
196 static void (*thread_unlock_callback)(void) = NULL;
198 static bool malloc_debug_memset = false;
212 /* --------------------------------------------------------------------- */
214 /* --------------------------------------------------------------------- */
217 __attribute__((format(printf, 1, 2)))
220 print_error(const char *str, ...)
226 vsnprintf(buf, sizeof(buf), str, ap);
228 buf[sizeof(buf) - 1] = '\0';
236 static void mem_lock_thread(void)
239 static int initialized = 0;
241 if (initialized == 0) {
242 /* assume first allocation happens from main thread */
243 mainid = pthread_self();
247 if (!pthread_equal(pthread_self(), mainid) && thread_lock_callback == NULL) {
248 assert(!"Memory function is called from non-main thread without lock");
252 #ifdef DEBUG_OMP_MALLOC
253 assert(omp_in_parallel() == 0);
256 if (thread_lock_callback)
257 thread_lock_callback();
260 static void mem_unlock_thread(void)
263 if (!pthread_equal(pthread_self(), mainid) && thread_lock_callback == NULL) {
264 assert(!"Thread lock was removed while allocation from thread is in progress");
268 if (thread_unlock_callback)
269 thread_unlock_callback();
272 bool MEM_guarded_consistency_check(void)
274 const char *err_val = NULL;
276 /* check_memlist starts from the front, and runs until it finds
277 * the requested chunk. For this test, that's the last one. */
278 listend = membase->last;
280 err_val = check_memlist(listend);
282 return (err_val == NULL);
285 void MEM_guarded_set_error_callback(void (*func)(const char *))
287 error_callback = func;
290 void MEM_guarded_set_lock_callback(void (*lock)(void), void (*unlock)(void))
292 thread_lock_callback = lock;
293 thread_unlock_callback = unlock;
296 void MEM_guarded_set_memory_debug(void)
298 malloc_debug_memset = true;
301 size_t MEM_guarded_allocN_len(const void *vmemh)
304 const MemHead *memh = vmemh;
314 void *MEM_guarded_dupallocN(const void *vmemh)
319 const MemHead *memh = vmemh;
322 #ifndef DEBUG_MEMDUPLINAME
323 if (UNLIKELY(memh->mmap))
324 newp = MEM_guarded_mapallocN(memh->len, "dupli_mapalloc");
325 else if (LIKELY(memh->alignment == 0))
326 newp = MEM_guarded_mapallocN(memh->len, "dupli_mapalloc");
328 newp = MEM_guarded_mallocN_aligned(memh->len, (size_t)memh->alignment, "dupli_alloc");
335 char *name = malloc(strlen(memh->name) + 24);
337 if (UNLIKELY(memh->mmap)) {
338 sprintf(name, "%s %s", "dupli_mapalloc", memh->name);
339 newp = MEM_guarded_mapallocN(memh->len, name);
341 else if (LIKELY(memh->alignment == 0)) {
342 sprintf(name, "%s %s", "dupli_alloc", memh->name);
343 newp = MEM_guarded_mallocN(memh->len, name);
346 sprintf(name, "%s %s", "dupli_alloc", memh->name);
347 newp = MEM_guarded_mallocN_aligned(memh->len, (size_t)memh->alignment, name);
356 nmemh->need_free_name = 1;
360 memcpy(newp, vmemh, memh->len);
366 void *MEM_guarded_reallocN_id(void *vmemh, size_t len, const char *str)
371 MemHead *memh = vmemh;
374 if (LIKELY(memh->alignment == 0)) {
375 newp = MEM_guarded_mallocN(len, memh->name);
378 newp = MEM_guarded_mallocN_aligned(len, (size_t)memh->alignment, memh->name);
382 if (len < memh->len) {
384 memcpy(newp, vmemh, len);
387 /* grow (or remain same size) */
388 memcpy(newp, vmemh, memh->len);
392 MEM_guarded_freeN(vmemh);
395 newp = MEM_guarded_mallocN(len, str);
401 void *MEM_guarded_recallocN_id(void *vmemh, size_t len, const char *str)
406 MemHead *memh = vmemh;
409 if (LIKELY(memh->alignment == 0)) {
410 newp = MEM_guarded_mallocN(len, memh->name);
413 newp = MEM_guarded_mallocN_aligned(len, (size_t)memh->alignment, memh->name);
417 if (len < memh->len) {
419 memcpy(newp, vmemh, len);
422 memcpy(newp, vmemh, memh->len);
424 if (len > memh->len) {
427 memset(((char *)newp) + memh->len, 0, len - memh->len);
432 MEM_guarded_freeN(vmemh);
435 newp = MEM_guarded_callocN(len, str);
441 #ifdef DEBUG_BACKTRACE
442 # if defined(__linux__) || defined(__APPLE__)
443 static void make_memhead_backtrace(MemHead *memh)
445 memh->backtrace_size = backtrace(memh->backtrace, BACKTRACE_SIZE);
448 static void print_memhead_backtrace(MemHead *memh)
453 strings = backtrace_symbols(memh->backtrace, memh->backtrace_size);
454 for (i = 0; i < memh->backtrace_size; i++) {
455 print_error(" %s\n", strings[i]);
461 static void make_memhead_backtrace(MemHead *memh)
463 (void)memh; /* Ignored. */
466 static void print_memhead_backtrace(MemHead *memh)
468 (void)memh; /* Ignored. */
470 # endif /* defined(__linux__) || defined(__APPLE__) */
471 #endif /* DEBUG_BACKTRACE */
473 static void make_memhead_header(MemHead *memh, size_t len, const char *str)
477 memh->tag1 = MEMTAG1;
479 memh->nextname = NULL;
483 memh->tag2 = MEMTAG2;
485 #ifdef DEBUG_MEMDUPLINAME
486 memh->need_free_name = 0;
489 #ifdef DEBUG_BACKTRACE
490 make_memhead_backtrace(memh);
493 memt = (MemTail *)(((char *)memh) + sizeof(MemHead) + len);
494 memt->tag3 = MEMTAG3;
496 atomic_add_and_fetch_u(&totblock, 1);
497 atomic_add_and_fetch_z(&mem_in_use, len);
500 addtail(membase, &memh->next);
502 memh->nextname = MEMNEXT(memh->next)->name;
504 peak_mem = mem_in_use > peak_mem ? mem_in_use : peak_mem;
508 void *MEM_guarded_mallocN(size_t len, const char *str)
512 len = SIZET_ALIGN_4(len);
514 memh = (MemHead *)malloc(len + sizeof(MemHead) + sizeof(MemTail));
517 make_memhead_header(memh, len, str);
518 if (UNLIKELY(malloc_debug_memset && len))
519 memset(memh + 1, 255, len);
521 #ifdef DEBUG_MEMCOUNTER
522 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
523 memcount_raise(__func__);
524 memh->_count = _mallocn_count++;
528 print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
531 (unsigned int)mem_in_use);
535 void *MEM_guarded_malloc_arrayN(size_t len, size_t size, const char *str)
538 if (UNLIKELY(!MEM_size_safe_multiply(len, size, &total_size))) {
540 "Malloc array aborted due to integer overflow: "
541 "len=" SIZET_FORMAT "x" SIZET_FORMAT " in %s, total %u\n",
545 (unsigned int)mem_in_use);
550 return MEM_guarded_mallocN(total_size, str);
553 void *MEM_guarded_mallocN_aligned(size_t len, size_t alignment, const char *str)
555 /* We only support alignment to a power of two. */
556 assert(IS_POW2(alignment));
558 /* Use a minimal alignment of 8. Otherwise MEM_guarded_freeN thinks it is an illegal pointer. */
563 /* It's possible that MemHead's size is not properly aligned,
564 * do extra padding to deal with this.
566 * We only support small alignments which fits into short in
567 * order to save some bits in MemHead structure.
569 size_t extra_padding = MEMHEAD_ALIGN_PADDING(alignment);
571 /* Huge alignment values doesn't make sense and they
572 * wouldn't fit into 'short' used in the MemHead.
574 assert(alignment < 1024);
576 len = SIZET_ALIGN_4(len);
578 MemHead *memh = (MemHead *)aligned_malloc(
579 len + extra_padding + sizeof(MemHead) + sizeof(MemTail), alignment);
582 /* We keep padding in the beginning of MemHead,
583 * this way it's always possible to get MemHead
584 * from the data pointer.
586 memh = (MemHead *)((char *)memh + extra_padding);
588 make_memhead_header(memh, len, str);
589 memh->alignment = (short)alignment;
590 if (UNLIKELY(malloc_debug_memset && len))
591 memset(memh + 1, 255, len);
593 #ifdef DEBUG_MEMCOUNTER
594 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
595 memcount_raise(__func__);
596 memh->_count = _mallocn_count++;
600 print_error("aligned_malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
603 (unsigned int)mem_in_use);
607 void *MEM_guarded_callocN(size_t len, const char *str)
611 len = SIZET_ALIGN_4(len);
613 memh = (MemHead *)calloc(len + sizeof(MemHead) + sizeof(MemTail), 1);
616 make_memhead_header(memh, len, str);
617 #ifdef DEBUG_MEMCOUNTER
618 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
619 memcount_raise(__func__);
620 memh->_count = _mallocn_count++;
624 print_error("Calloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
627 (unsigned int)mem_in_use);
631 void *MEM_guarded_calloc_arrayN(size_t len, size_t size, const char *str)
634 if (UNLIKELY(!MEM_size_safe_multiply(len, size, &total_size))) {
636 "Calloc array aborted due to integer overflow: "
637 "len=" SIZET_FORMAT "x" SIZET_FORMAT " in %s, total %u\n",
641 (unsigned int)mem_in_use);
646 return MEM_guarded_callocN(total_size, str);
649 /* note; mmap returns zero'd memory */
650 void *MEM_guarded_mapallocN(size_t len, const char *str)
654 /* on 64 bit, simply use calloc instead, as mmap does not support
655 * allocating > 4 GB on Windows. the only reason mapalloc exists
656 * is to get around address space limitations in 32 bit OSes. */
657 if (sizeof(void *) >= 8)
658 return MEM_guarded_callocN(len, str);
660 len = SIZET_ALIGN_4(len);
663 /* our windows mmap implementation is not thread safe */
667 len + sizeof(MemHead) + sizeof(MemTail),
668 PROT_READ | PROT_WRITE,
669 MAP_SHARED | MAP_ANON,
676 if (memh != (MemHead *)-1) {
677 make_memhead_header(memh, len, str);
679 atomic_add_and_fetch_z(&mmap_in_use, len);
681 peak_mem = mmap_in_use > peak_mem ? mmap_in_use : peak_mem;
683 #ifdef DEBUG_MEMCOUNTER
684 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
685 memcount_raise(__func__);
686 memh->_count = _mallocn_count++;
692 "Mapalloc returns null, fallback to regular malloc: "
693 "len=" SIZET_FORMAT " in %s, total %u\n",
696 (unsigned int)mmap_in_use);
697 return MEM_guarded_callocN(len, str);
701 /* Memory statistics print */
702 typedef struct MemPrintBlock {
708 static int compare_name(const void *p1, const void *p2)
710 const MemPrintBlock *pb1 = (const MemPrintBlock *)p1;
711 const MemPrintBlock *pb2 = (const MemPrintBlock *)p2;
713 return strcmp(pb1->name, pb2->name);
716 static int compare_len(const void *p1, const void *p2)
718 const MemPrintBlock *pb1 = (const MemPrintBlock *)p1;
719 const MemPrintBlock *pb2 = (const MemPrintBlock *)p2;
721 if (pb1->len < pb2->len)
723 else if (pb1->len == pb2->len)
729 void MEM_guarded_printmemlist_stats(void)
732 MemPrintBlock *pb, *printblock;
733 unsigned int totpb, a, b;
734 size_t mem_in_use_slop = 0;
739 /* put memory blocks into array */
740 printblock = malloc(sizeof(MemPrintBlock) * totblock);
742 if (UNLIKELY(!printblock)) {
744 print_error("malloc returned null while generating stats");
755 membl = membase->first;
757 membl = MEMNEXT(membl);
759 while (membl && pb) {
760 pb->name = membl->name;
761 pb->len = membl->len;
767 #ifdef USE_MALLOC_USABLE_SIZE
768 if (!membl->mmap && membl->alignment == 0) {
769 mem_in_use_slop += (sizeof(MemHead) + sizeof(MemTail) + malloc_usable_size((void *)membl)) -
775 membl = MEMNEXT(membl->next);
780 /* sort by name and add together blocks with the same name */
782 qsort(printblock, totpb, sizeof(MemPrintBlock), compare_name);
785 for (a = 0, b = 0; a < totpb; a++) {
789 else if (strcmp(printblock[a].name, printblock[b].name) == 0) {
790 printblock[b].len += printblock[a].len;
791 printblock[b].items++;
795 memcpy(&printblock[b], &printblock[a], sizeof(MemPrintBlock));
800 /* sort by length and print */
802 qsort(printblock, totpb, sizeof(MemPrintBlock), compare_len);
805 printf("\ntotal memory len: %.3f MB\n", (double)mem_in_use / (double)(1024 * 1024));
806 printf("peak memory len: %.3f MB\n", (double)peak_mem / (double)(1024 * 1024));
807 printf("slop memory len: %.3f MB\n", (double)mem_in_use_slop / (double)(1024 * 1024));
808 printf(" ITEMS TOTAL-MiB AVERAGE-KiB TYPE\n");
809 for (a = 0, pb = printblock; a < totpb; a++, pb++) {
810 printf("%6d (%8.3f %8.3f) %s\n",
812 (double)pb->len / (double)(1024 * 1024),
813 (double)pb->len / 1024.0 / (double)pb->items,
817 if (printblock != NULL) {
823 #ifdef HAVE_MALLOC_STATS
824 printf("System Statistics:\n");
829 static const char mem_printmemlist_pydict_script[] =
832 "for mb_item in membase:\n"
833 " mb_item_user_size = mb_userinfo.setdefault(mb_item['name'], [0,0])\n"
834 " mb_item_user_size[0] += 1 # Add a user\n"
835 " mb_item_user_size[1] += mb_item['len'] # Increment the size\n"
836 " totmem += mb_item['len']\n"
837 "print('(membase) items:', len(membase), '| unique-names:',\n"
838 " len(mb_userinfo), '| total-mem:', totmem)\n"
839 "mb_userinfo_sort = list(mb_userinfo.items())\n"
840 "for sort_name, sort_func in (('size', lambda a: -a[1][1]),\n"
841 " ('users', lambda a: -a[1][0]),\n"
842 " ('name', lambda a: a[0])):\n"
843 " print('\\nSorting by:', sort_name)\n"
844 " mb_userinfo_sort.sort(key = sort_func)\n"
845 " for item in mb_userinfo_sort:\n"
846 " print('name:%%s, users:%%i, len:%%i' %%\n"
847 " (item[0], item[1][0], item[1][1]))\n";
849 /* Prints in python syntax for easy */
850 static void MEM_guarded_printmemlist_internal(int pydict)
856 membl = membase->first;
858 membl = MEMNEXT(membl);
861 print_error("# membase_debug.py\n");
862 print_error("membase = [\n");
866 print_error(" {'len':" SIZET_FORMAT
869 "'pointer':'%p'},\n",
870 SIZET_ARG(membl->len),
872 (void *)(membl + 1));
875 #ifdef DEBUG_MEMCOUNTER
876 print_error("%s len: " SIZET_FORMAT " %p, count: %d\n",
878 SIZET_ARG(membl->len),
882 print_error("%s len: " SIZET_FORMAT " %p\n",
884 SIZET_ARG(membl->len),
885 (void *)(membl + 1));
887 #ifdef DEBUG_BACKTRACE
888 print_memhead_backtrace(membl);
892 membl = MEMNEXT(membl->next);
897 print_error("]\n\n");
898 print_error(mem_printmemlist_pydict_script);
904 void MEM_guarded_callbackmemlist(void (*func)(void *))
910 membl = membase->first;
912 membl = MEMNEXT(membl);
917 membl = MEMNEXT(membl->next);
926 short MEM_guarded_testN(void *vmemh)
932 membl = membase->first;
934 membl = MEMNEXT(membl);
937 if (vmemh == membl + 1) {
943 membl = MEMNEXT(membl->next);
950 print_error("Memoryblock %p: pointer not in memlist\n", vmemh);
955 void MEM_guarded_printmemlist(void)
957 MEM_guarded_printmemlist_internal(0);
959 void MEM_guarded_printmemlist_pydict(void)
961 MEM_guarded_printmemlist_internal(1);
964 void MEM_guarded_freeN(void *vmemh)
967 MemHead *memh = vmemh;
971 MemorY_ErroR("free", "attempt to free NULL pointer");
972 /* print_error(err_stream, "%d\n", (memh+4000)->tag1); */
976 if (sizeof(intptr_t) == 8) {
977 if (((intptr_t)memh) & 0x7) {
978 MemorY_ErroR("free", "attempt to free illegal pointer");
983 if (((intptr_t)memh) & 0x3) {
984 MemorY_ErroR("free", "attempt to free illegal pointer");
990 if (memh->tag1 == MEMFREE && memh->tag2 == MEMFREE) {
991 MemorY_ErroR(memh->name, "double free");
995 if ((memh->tag1 == MEMTAG1) && (memh->tag2 == MEMTAG2) && ((memh->len & 0x3) == 0)) {
996 memt = (MemTail *)(((char *)memh) + sizeof(MemHead) + memh->len);
997 if (memt->tag3 == MEMTAG3) {
999 memh->tag1 = MEMFREE;
1000 memh->tag2 = MEMFREE;
1001 memt->tag3 = MEMFREE;
1002 /* after tags !!! */
1007 MemorY_ErroR(memh->name, "end corrupt");
1008 name = check_memlist(memh);
1010 if (name != memh->name)
1011 MemorY_ErroR(name, "is also corrupt");
1016 name = check_memlist(memh);
1017 mem_unlock_thread();
1019 MemorY_ErroR("free", "pointer not in memlist");
1021 MemorY_ErroR(name, "error in header");
1025 /* here a DUMP should happen */
1030 /* --------------------------------------------------------------------- */
1031 /* local functions */
1032 /* --------------------------------------------------------------------- */
1034 static void addtail(volatile localListBase *listbase, void *vlink)
1036 struct localLink *link = vlink;
1038 /* for a generic API error checks here is fine but
1039 * the limited use here they will never be NULL */
1043 if (listbase == NULL)
1048 link->prev = listbase->last;
1051 ((struct localLink *)listbase->last)->next = link;
1052 if (listbase->first == NULL)
1053 listbase->first = link;
1054 listbase->last = link;
1057 static void remlink(volatile localListBase *listbase, void *vlink)
1059 struct localLink *link = vlink;
1061 /* for a generic API error checks here is fine but
1062 * the limited use here they will never be NULL */
1066 if (listbase == NULL)
1071 link->next->prev = link->prev;
1073 link->prev->next = link->next;
1075 if (listbase->last == link)
1076 listbase->last = link->prev;
1077 if (listbase->first == link)
1078 listbase->first = link->next;
1081 static void rem_memblock(MemHead *memh)
1084 remlink(membase, &memh->next);
1087 MEMNEXT(memh->prev)->nextname = MEMNEXT(memh->next)->name;
1089 MEMNEXT(memh->prev)->nextname = NULL;
1091 mem_unlock_thread();
1093 atomic_sub_and_fetch_u(&totblock, 1);
1094 atomic_sub_and_fetch_z(&mem_in_use, memh->len);
1096 #ifdef DEBUG_MEMDUPLINAME
1097 if (memh->need_free_name)
1098 free((char *)memh->name);
1102 atomic_sub_and_fetch_z(&mmap_in_use, memh->len);
1104 /* our windows mmap implementation is not thread safe */
1107 if (munmap(memh, memh->len + sizeof(MemHead) + sizeof(MemTail)))
1108 printf("Couldn't unmap memory %s\n", memh->name);
1110 mem_unlock_thread();
1114 if (UNLIKELY(malloc_debug_memset && memh->len))
1115 memset(memh + 1, 255, memh->len);
1116 if (LIKELY(memh->alignment == 0)) {
1120 aligned_free(MEMHEAD_REAL_PTR(memh));
1125 static void MemorY_ErroR(const char *block, const char *error)
1127 print_error("Memoryblock %s: %s\n", block, error);
1129 #ifdef WITH_ASSERT_ABORT
1134 static const char *check_memlist(MemHead *memh)
1136 MemHead *forw, *back, *forwok, *backok;
1139 forw = membase->first;
1141 forw = MEMNEXT(forw);
1144 if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2)
1148 forw = MEMNEXT(forw->next);
1153 back = (MemHead *)membase->last;
1155 back = MEMNEXT(back);
1158 if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2)
1162 back = MEMNEXT(back->prev);
1168 return ("MORE THAN 1 MEMORYBLOCK CORRUPT");
1170 if (forw == NULL && back == NULL) {
1171 /* no wrong headers found then but in search of memblock */
1173 forw = membase->first;
1175 forw = MEMNEXT(forw);
1180 if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2)
1184 forw = MEMNEXT(forw->next);
1191 back = (MemHead *)membase->last;
1193 back = MEMNEXT(back);
1198 if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2)
1202 back = MEMNEXT(back->prev);
1209 name = forwok->nextname;
1211 name = "No name found";
1214 /* to be sure but this block is removed from the list */
1217 forwok->next = (MemHead *)&backok->next;
1218 backok->prev = (MemHead *)&forwok->next;
1219 forwok->nextname = backok->name;
1222 forwok->next = NULL;
1223 membase->last = (struct localLink *)&forwok->next;
1228 backok->prev = NULL;
1229 membase->first = &backok->next;
1232 membase->first = membase->last = NULL;
1237 MemorY_ErroR(name, "Additional error in header");
1238 return ("Additional error in header");
1244 size_t MEM_guarded_get_peak_memory(void)
1249 _peak_mem = peak_mem;
1250 mem_unlock_thread();
1255 void MEM_guarded_reset_peak_memory(void)
1258 peak_mem = mem_in_use;
1259 mem_unlock_thread();
1262 size_t MEM_guarded_get_memory_in_use(void)
1267 _mem_in_use = mem_in_use;
1268 mem_unlock_thread();
1273 size_t MEM_guarded_get_mapped_memory_in_use(void)
1275 size_t _mmap_in_use;
1278 _mmap_in_use = mmap_in_use;
1279 mem_unlock_thread();
1281 return _mmap_in_use;
1284 unsigned int MEM_guarded_get_memory_blocks_in_use(void)
1286 unsigned int _totblock;
1289 _totblock = totblock;
1290 mem_unlock_thread();
1296 const char *MEM_guarded_name_ptr(void *vmemh)
1299 MemHead *memh = vmemh;
1304 return "MEM_guarded_name_ptr(NULL)";