add MEM_recallocN(), so bytes are ensured to be zero'd when growing.
[blender.git] / intern / guardedalloc / intern / mallocn.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
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.
8  *
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.
13  *
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.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Brecht Van Lommel
24  *                 Campbell Barton
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 /** \file guardedalloc/intern/mallocn.c
30  *  \ingroup MEM
31  *
32  * Guarded memory allocation, and boundary-write detection.
33  */
34
35 #include <stdlib.h>
36 #include <string.h> /* memcpy */
37 #include <stdarg.h>
38 #include <sys/types.h>
39 /* Blame Microsoft for LLP64 and no inttypes.h, quick workaround needed: */
40 #if defined(WIN64)
41 #  define SIZET_FORMAT "%I64u"
42 #  define SIZET_ARG(a) ((unsigned long long)(a))
43 #else
44 #  define SIZET_FORMAT "%lu"
45 #  define SIZET_ARG(a) ((unsigned long)(a))
46 #endif
47
48 /* mmap exception */
49 #if defined(WIN32)
50 #  include "mmap_win.h"
51 #else
52 #  include <sys/mman.h>
53 #endif
54
55 #include "MEM_guardedalloc.h"
56
57 /* Only for debugging:
58  * store original buffer's name when doing MEM_dupallocN
59  * helpful to profile issues with non-freed "dup_alloc" buffers,
60  * but this introduces some overhead to memory header and makes
61  * things slower a bit, so betterto keep disabled by default
62  */
63 //#define DEBUG_MEMDUPLINAME
64
65 /* Only for debugging:
66  * lets you count the allocations so as to find the allocator of unfreed memory
67  * in situations where the leak is predictable */
68
69 //#define DEBUG_MEMCOUNTER
70
71 #ifdef DEBUG_MEMCOUNTER
72    /* set this to the value that isn't being freed */
73 #  define DEBUG_MEMCOUNTER_ERROR_VAL 0
74 static int _mallocn_count = 0;
75
76 /* breakpoint here */
77 static void memcount_raise(const char *name)
78 {
79         fprintf(stderr, "%s: memcount-leak, %d\n", name, _mallocn_count);
80 }
81 #endif
82
83 /* --------------------------------------------------------------------- */
84 /* Data definition                                                       */
85 /* --------------------------------------------------------------------- */
86 /* all memory chunks are put in linked lists */
87 typedef struct localLink {
88         struct localLink *next, *prev;
89 } localLink;
90
91 typedef struct localListBase {
92         void *first, *last;
93 } localListBase;
94
95 /* note: keep this struct aligned (e.g., irix/gcc) - Hos */
96 typedef struct MemHead {
97         int tag1;
98         size_t len;
99         struct MemHead *next, *prev;
100         const char *name;
101         const char *nextname;
102         int tag2;
103         int mmap;  /* if true, memory was mmapped */
104 #ifdef DEBUG_MEMCOUNTER
105         int _count;
106 #endif
107
108 #ifdef DEBUG_MEMDUPLINAME
109         int need_free_name, pad;
110 #endif
111 } MemHead;
112
113 typedef struct MemTail {
114         int tag3, pad;
115 } MemTail;
116
117
118 /* --------------------------------------------------------------------- */
119 /* local functions                                                       */
120 /* --------------------------------------------------------------------- */
121
122 static void addtail(volatile localListBase *listbase, void *vlink);
123 static void remlink(volatile localListBase *listbase, void *vlink);
124 static void rem_memblock(MemHead *memh);
125 static void MemorY_ErroR(const char *block, const char *error);
126 static const char *check_memlist(MemHead *memh);
127
128 /* --------------------------------------------------------------------- */
129 /* locally used defines                                                  */
130 /* --------------------------------------------------------------------- */
131
132 #ifdef __BIG_ENDIAN__
133 #  define MAKE_ID(a, b, c, d) ((int)(a) << 24 | (int)(b) << 16 | (c) << 8 | (d))
134 #else
135 #  define MAKE_ID(a, b, c, d) ((int)(d) << 24 | (int)(c) << 16 | (b) << 8 | (a))
136 #endif
137
138 #define MEMTAG1 MAKE_ID('M', 'E', 'M', 'O')
139 #define MEMTAG2 MAKE_ID('R', 'Y', 'B', 'L')
140 #define MEMTAG3 MAKE_ID('O', 'C', 'K', '!')
141 #define MEMFREE MAKE_ID('F', 'R', 'E', 'E')
142
143 #define MEMNEXT(x) \
144         ((MemHead *)(((char *) x) - ((char *) &(((MemHead *)0)->next))))
145         
146 /* --------------------------------------------------------------------- */
147 /* vars                                                                  */
148 /* --------------------------------------------------------------------- */
149         
150
151 static volatile int totblock = 0;
152 static volatile uintptr_t mem_in_use = 0, mmap_in_use = 0, peak_mem = 0;
153
154 static volatile struct localListBase _membase;
155 static volatile struct localListBase *membase = &_membase;
156 static void (*error_callback)(const char *) = NULL;
157 static void (*thread_lock_callback)(void) = NULL;
158 static void (*thread_unlock_callback)(void) = NULL;
159
160 static int malloc_debug_memset = 0;
161
162 #ifdef malloc
163 #undef malloc
164 #endif
165
166 #ifdef calloc
167 #undef calloc
168 #endif
169
170 #ifdef free
171 #undef free
172 #endif
173
174
175 /* --------------------------------------------------------------------- */
176 /* implementation                                                        */
177 /* --------------------------------------------------------------------- */
178
179 #ifdef __GNUC__
180 __attribute__ ((format(printf, 1, 2)))
181 #endif
182 static void print_error(const char *str, ...)
183 {
184         char buf[512];
185         va_list ap;
186
187         va_start(ap, str);
188         vsnprintf(buf, sizeof(buf), str, ap);
189         va_end(ap);
190         buf[sizeof(buf) - 1] = '\0';
191
192         if (error_callback) error_callback(buf);
193 }
194
195 static void mem_lock_thread(void)
196 {
197         if (thread_lock_callback)
198                 thread_lock_callback();
199 }
200
201 static void mem_unlock_thread(void)
202 {
203         if (thread_unlock_callback)
204                 thread_unlock_callback();
205 }
206
207 int MEM_check_memory_integrity(void)
208 {
209         const char *err_val = NULL;
210         MemHead *listend;
211         /* check_memlist starts from the front, and runs until it finds
212          * the requested chunk. For this test, that's the last one. */
213         listend = membase->last;
214         
215         err_val = check_memlist(listend);
216
217         if (err_val == NULL) return 0;
218         return 1;
219 }
220
221
222 void MEM_set_error_callback(void (*func)(const char *))
223 {
224         error_callback = func;
225 }
226
227 void MEM_set_lock_callback(void (*lock)(void), void (*unlock)(void))
228 {
229         thread_lock_callback = lock;
230         thread_unlock_callback = unlock;
231 }
232
233 void MEM_set_memory_debug(void)
234 {
235         malloc_debug_memset = 1;
236 }
237
238 size_t MEM_allocN_len(const void *vmemh)
239 {
240         if (vmemh) {
241                 const MemHead *memh = vmemh;
242         
243                 memh--;
244                 return memh->len;
245         }
246         else {
247                 return 0;
248         }
249 }
250
251 void *MEM_dupallocN(void *vmemh)
252 {
253         void *newp = NULL;
254         
255         if (vmemh) {
256                 MemHead *memh = vmemh;
257                 memh--;
258
259 #ifndef DEBUG_MEMDUPLINAME
260                 if (memh->mmap)
261                         newp = MEM_mapallocN(memh->len, "dupli_mapalloc");
262                 else
263                         newp = MEM_mallocN(memh->len, "dupli_alloc");
264
265                 if (newp == NULL) return NULL;
266 #else
267                 {
268                         MemHead *nmemh;
269                         char *name = malloc(strlen(memh->name) + 24);
270
271                         if (memh->mmap) {
272                                 sprintf(name, "%s %s", "dupli_mapalloc", memh->name);
273                                 newp = MEM_mapallocN(memh->len, name);
274                         }
275                         else {
276                                 sprintf(name, "%s %s", "dupli_alloc", memh->name);
277                                 newp = MEM_mallocN(memh->len, name);
278                         }
279
280                         if (newp == NULL) return NULL;
281
282                         nmemh = newp;
283                         nmemh--;
284
285                         nmemh->need_free_name = 1;
286                 }
287 #endif
288
289                 memcpy(newp, vmemh, memh->len);
290         }
291
292         return newp;
293 }
294
295 void *MEM_reallocN(void *vmemh, size_t len)
296 {
297         void *newp = NULL;
298         
299         if (vmemh) {
300                 MemHead *memh = vmemh;
301                 memh--;
302
303                 newp = MEM_mallocN(len, memh->name);
304                 if (newp) {
305                         if (len < memh->len) {
306                                 /* shrink */
307                                 memcpy(newp, vmemh, len);
308                         }
309                         else {
310                                 /* grow (or remain same size) */
311                                 memcpy(newp, vmemh, memh->len);
312                         }
313                 }
314
315                 MEM_freeN(vmemh);
316         }
317
318         return newp;
319 }
320
321 void *MEM_recallocN(void *vmemh, size_t len)
322 {
323         void *newp = NULL;
324
325         if (vmemh) {
326                 MemHead *memh = vmemh;
327                 memh--;
328
329                 newp = MEM_mallocN(len, memh->name);
330                 if (newp) {
331                         if (len < memh->len) {
332                                 /* shrink */
333                                 memcpy(newp, vmemh, len);
334                         }
335                         else {
336                                 memcpy(newp, vmemh, memh->len);
337
338                                 if (len > memh->len) {
339                                         /* grow */
340                                         /* zero new bytes */
341                                         memset(((char *)newp) + memh->len, 0, len - memh->len);
342                                 }
343                         }
344                 }
345
346                 MEM_freeN(vmemh);
347         }
348
349         return newp;
350 }
351
352 static void make_memhead_header(MemHead *memh, size_t len, const char *str)
353 {
354         MemTail *memt;
355         
356         memh->tag1 = MEMTAG1;
357         memh->name = str;
358         memh->nextname = NULL;
359         memh->len = len;
360         memh->mmap = 0;
361         memh->tag2 = MEMTAG2;
362
363 #ifdef DEBUG_MEMDUPLINAME
364         memh->need_free_name = 0;
365 #endif
366         
367         memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + len);
368         memt->tag3 = MEMTAG3;
369         
370         addtail(membase, &memh->next);
371         if (memh->next) {
372                 memh->nextname = MEMNEXT(memh->next)->name;
373         }
374         
375         totblock++;
376         mem_in_use += len;
377
378         peak_mem = mem_in_use > peak_mem ? mem_in_use : peak_mem;
379 }
380
381 void *MEM_mallocN(size_t len, const char *str)
382 {
383         MemHead *memh;
384
385         mem_lock_thread();
386
387         len = (len + 3) & ~3;   /* allocate in units of 4 */
388         
389         memh = (MemHead *)malloc(len + sizeof(MemHead) + sizeof(MemTail));
390
391         if (memh) {
392                 make_memhead_header(memh, len, str);
393                 mem_unlock_thread();
394                 if (malloc_debug_memset && len)
395                         memset(memh + 1, 255, len);
396
397 #ifdef DEBUG_MEMCOUNTER
398                 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
399                         memcount_raise(__func__);
400                 memh->_count = _mallocn_count++;
401 #endif
402                 return (++memh);
403         }
404         mem_unlock_thread();
405         print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
406                     SIZET_ARG(len), str, (unsigned int) mem_in_use);
407         return NULL;
408 }
409
410 void *MEM_callocN(size_t len, const char *str)
411 {
412         MemHead *memh;
413
414         mem_lock_thread();
415
416         len = (len + 3) & ~3;   /* allocate in units of 4 */
417
418         memh = (MemHead *)calloc(len + sizeof(MemHead) + sizeof(MemTail), 1);
419
420         if (memh) {
421                 make_memhead_header(memh, len, str);
422                 mem_unlock_thread();
423 #ifdef DEBUG_MEMCOUNTER
424                 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
425                         memcount_raise(__func__);
426                 memh->_count = _mallocn_count++;
427 #endif
428                 return (++memh);
429         }
430         mem_unlock_thread();
431         print_error("Calloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
432                     SIZET_ARG(len), str, (unsigned int) mem_in_use);
433         return NULL;
434 }
435
436 /* note; mmap returns zero'd memory */
437 void *MEM_mapallocN(size_t len, const char *str)
438 {
439         MemHead *memh;
440
441         mem_lock_thread();
442         
443         len = (len + 3) & ~3;   /* allocate in units of 4 */
444
445         memh = mmap(NULL, len + sizeof(MemHead) + sizeof(MemTail),
446                     PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
447
448         if (memh != (MemHead *)-1) {
449                 make_memhead_header(memh, len, str);
450                 memh->mmap = 1;
451                 mmap_in_use += len;
452                 peak_mem = mmap_in_use > peak_mem ? mmap_in_use : peak_mem;
453                 mem_unlock_thread();
454 #ifdef DEBUG_MEMCOUNTER
455                 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
456                         memcount_raise(__func__);
457                 memh->_count = _mallocn_count++;
458 #endif
459                 return (++memh);
460         }
461         else {
462                 mem_unlock_thread();
463                 print_error("Mapalloc returns null, fallback to regular malloc: "
464                             "len=" SIZET_FORMAT " in %s, total %u\n",
465                             SIZET_ARG(len), str, (unsigned int) mmap_in_use);
466                 return MEM_callocN(len, str);
467         }
468 }
469
470 /* Memory statistics print */
471 typedef struct MemPrintBlock {
472         const char *name;
473         uintptr_t len;
474         int items;
475 } MemPrintBlock;
476
477 static int compare_name(const void *p1, const void *p2)
478 {
479         const MemPrintBlock *pb1 = (const MemPrintBlock *)p1;
480         const MemPrintBlock *pb2 = (const MemPrintBlock *)p2;
481
482         return strcmp(pb1->name, pb2->name);
483 }
484
485 static int compare_len(const void *p1, const void *p2)
486 {
487         const MemPrintBlock *pb1 = (const MemPrintBlock *)p1;
488         const MemPrintBlock *pb2 = (const MemPrintBlock *)p2;
489
490         if (pb1->len < pb2->len)
491                 return 1;
492         else if (pb1->len == pb2->len)
493                 return 0;
494         else
495                 return -1;
496 }
497
498 void MEM_printmemlist_stats(void)
499 {
500         MemHead *membl;
501         MemPrintBlock *pb, *printblock;
502         int totpb, a, b;
503
504         mem_lock_thread();
505
506         /* put memory blocks into array */
507         printblock = malloc(sizeof(MemPrintBlock) * totblock);
508
509         pb = printblock;
510         totpb = 0;
511
512         membl = membase->first;
513         if (membl) membl = MEMNEXT(membl);
514
515         while (membl) {
516                 pb->name = membl->name;
517                 pb->len = membl->len;
518                 pb->items = 1;
519
520                 totpb++;
521                 pb++;
522
523                 if (membl->next)
524                         membl = MEMNEXT(membl->next);
525                 else break;
526         }
527
528         /* sort by name and add together blocks with the same name */
529         qsort(printblock, totpb, sizeof(MemPrintBlock), compare_name);
530         for (a = 0, b = 0; a < totpb; a++) {
531                 if (a == b) {
532                         continue;
533                 }
534                 else if (strcmp(printblock[a].name, printblock[b].name) == 0) {
535                         printblock[b].len += printblock[a].len;
536                         printblock[b].items++;
537                 }
538                 else {
539                         b++;
540                         memcpy(&printblock[b], &printblock[a], sizeof(MemPrintBlock));
541                 }
542         }
543         totpb = b + 1;
544
545         /* sort by length and print */
546         qsort(printblock, totpb, sizeof(MemPrintBlock), compare_len);
547         printf("\ntotal memory len: %.3f MB\n",
548                (double)mem_in_use / (double)(1024 * 1024));
549         printf(" ITEMS TOTAL-MiB AVERAGE-KiB TYPE\n");
550         for (a = 0, pb = printblock; a < totpb; a++, pb++) {
551                 printf("%6d (%8.3f  %8.3f) %s\n",
552                        pb->items, (double)pb->len / (double)(1024 * 1024),
553                        (double)pb->len / 1024.0 / (double)pb->items, pb->name);
554         }
555         free(printblock);
556         
557         mem_unlock_thread();
558
559 #if 0 /* GLIBC only */
560         malloc_stats();
561 #endif
562 }
563
564 static const char mem_printmemlist_pydict_script[] =
565 "mb_userinfo = {}\n"
566 "totmem = 0\n"
567 "for mb_item in membase:\n"
568 "    mb_item_user_size = mb_userinfo.setdefault(mb_item['name'], [0,0])\n"
569 "    mb_item_user_size[0] += 1 # Add a user\n"
570 "    mb_item_user_size[1] += mb_item['len'] # Increment the size\n"
571 "    totmem += mb_item['len']\n"
572 "print('(membase) items:', len(membase), '| unique-names:',\n"
573 "      len(mb_userinfo), '| total-mem:', totmem)\n"
574 "mb_userinfo_sort = list(mb_userinfo.items())\n"
575 "for sort_name, sort_func in (('size', lambda a: -a[1][1]),\n"
576 "                             ('users', lambda a: -a[1][0]),\n"
577 "                             ('name', lambda a: a[0])):\n"
578 "    print('\\nSorting by:', sort_name)\n"
579 "    mb_userinfo_sort.sort(key = sort_func)\n"
580 "    for item in mb_userinfo_sort:\n"
581 "        print('name:%%s, users:%%i, len:%%i' %%\n"
582 "              (item[0], item[1][0], item[1][1]))\n";
583
584 /* Prints in python syntax for easy */
585 static void MEM_printmemlist_internal(int pydict)
586 {
587         MemHead *membl;
588
589         mem_lock_thread();
590
591         membl = membase->first;
592         if (membl) membl = MEMNEXT(membl);
593         
594         if (pydict) {
595                 print_error("# membase_debug.py\n");
596                 print_error("membase = [\n");
597         }
598         while (membl) {
599                 if (pydict) {
600                         fprintf(stderr,
601                                 "    {'len':" SIZET_FORMAT ", "
602                                 "'name':'''%s''', "
603                                 "'pointer':'%p'},\n",
604                                 SIZET_ARG(membl->len), membl->name, (void *)(membl + 1));
605                 }
606                 else {
607 #ifdef DEBUG_MEMCOUNTER
608                         print_error("%s len: " SIZET_FORMAT " %p, count: %d\n",
609                                     membl->name, SIZET_ARG(membl->len), membl + 1,
610                                     membl->_count);
611 #else
612                         print_error("%s len: " SIZET_FORMAT " %p\n",
613                                     membl->name, SIZET_ARG(membl->len), membl + 1);
614 #endif
615                 }
616                 if (membl->next)
617                         membl = MEMNEXT(membl->next);
618                 else break;
619         }
620         if (pydict) {
621                 fprintf(stderr, "]\n\n");
622                 fprintf(stderr, mem_printmemlist_pydict_script);
623         }
624         
625         mem_unlock_thread();
626 }
627
628 void MEM_callbackmemlist(void (*func)(void *))
629 {
630         MemHead *membl;
631
632         mem_lock_thread();
633
634         membl = membase->first;
635         if (membl) membl = MEMNEXT(membl);
636
637         while (membl) {
638                 func(membl + 1);
639                 if (membl->next)
640                         membl = MEMNEXT(membl->next);
641                 else break;
642         }
643
644         mem_unlock_thread();
645 }
646
647 short MEM_testN(void *vmemh)
648 {
649         MemHead *membl;
650
651         mem_lock_thread();
652
653         membl = membase->first;
654         if (membl) membl = MEMNEXT(membl);
655
656         while (membl) {
657                 if (vmemh == membl + 1) {
658                         mem_unlock_thread();
659                         return 1;
660                 }
661
662                 if (membl->next)
663                         membl = MEMNEXT(membl->next);
664                 else break;
665         }
666
667         mem_unlock_thread();
668
669         print_error("Memoryblock %p: pointer not in memlist\n", vmemh);
670         return 0;
671 }
672
673 void MEM_printmemlist(void)
674 {
675         MEM_printmemlist_internal(0);
676 }
677 void MEM_printmemlist_pydict(void)
678 {
679         MEM_printmemlist_internal(1);
680 }
681
682 short MEM_freeN(void *vmemh)
683 {
684         short error = 0;
685         MemTail *memt;
686         MemHead *memh = vmemh;
687         const char *name;
688
689         if (memh == NULL) {
690                 MemorY_ErroR("free", "attempt to free NULL pointer");
691                 /* print_error(err_stream, "%d\n", (memh+4000)->tag1); */
692                 return(-1);
693         }
694
695         if (sizeof(intptr_t) == 8) {
696                 if (((intptr_t) memh) & 0x7) {
697                         MemorY_ErroR("free", "attempt to free illegal pointer");
698                         return(-1);
699                 }
700         }
701         else {
702                 if (((intptr_t) memh) & 0x3) {
703                         MemorY_ErroR("free", "attempt to free illegal pointer");
704                         return(-1);
705                 }
706         }
707         
708         memh--;
709         if (memh->tag1 == MEMFREE && memh->tag2 == MEMFREE) {
710                 MemorY_ErroR(memh->name, "double free");
711                 return(-1);
712         }
713
714         mem_lock_thread();
715         if ((memh->tag1 == MEMTAG1) &&
716             (memh->tag2 == MEMTAG2) &&
717             ((memh->len & 0x3) == 0))
718         {
719                 memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + memh->len);
720                 if (memt->tag3 == MEMTAG3) {
721                         
722                         memh->tag1 = MEMFREE;
723                         memh->tag2 = MEMFREE;
724                         memt->tag3 = MEMFREE;
725                         /* after tags !!! */
726                         rem_memblock(memh);
727
728                         mem_unlock_thread();
729                         
730                         return(0);
731                 }
732                 error = 2;
733                 MemorY_ErroR(memh->name, "end corrupt");
734                 name = check_memlist(memh);
735                 if (name != NULL) {
736                         if (name != memh->name) MemorY_ErroR(name, "is also corrupt");
737                 }
738         }
739         else {
740                 error = -1;
741                 name = check_memlist(memh);
742                 if (name == NULL)
743                         MemorY_ErroR("free", "pointer not in memlist");
744                 else
745                         MemorY_ErroR(name, "error in header");
746         }
747
748         totblock--;
749         /* here a DUMP should happen */
750
751         mem_unlock_thread();
752
753         return(error);
754 }
755
756 /* --------------------------------------------------------------------- */
757 /* local functions                                                       */
758 /* --------------------------------------------------------------------- */
759
760 static void addtail(volatile localListBase *listbase, void *vlink)
761 {
762         struct localLink *link = vlink;
763
764         /* for a generic API error checks here is fine but
765          * the limited use here they will never be NULL */
766 #if 0
767         if (link == NULL) return;
768         if (listbase == NULL) return;
769 #endif
770
771         link->next = NULL;
772         link->prev = listbase->last;
773
774         if (listbase->last) ((struct localLink *)listbase->last)->next = link;
775         if (listbase->first == NULL) listbase->first = link;
776         listbase->last = link;
777 }
778
779 static void remlink(volatile localListBase *listbase, void *vlink)
780 {
781         struct localLink *link = vlink;
782
783         /* for a generic API error checks here is fine but
784          * the limited use here they will never be NULL */
785 #if 0
786         if (link == NULL) return;
787         if (listbase == NULL) return;
788 #endif
789
790         if (link->next) link->next->prev = link->prev;
791         if (link->prev) link->prev->next = link->next;
792
793         if (listbase->last == link) listbase->last = link->prev;
794         if (listbase->first == link) listbase->first = link->next;
795 }
796
797 static void rem_memblock(MemHead *memh)
798 {
799         remlink(membase, &memh->next);
800         if (memh->prev) {
801                 if (memh->next)
802                         MEMNEXT(memh->prev)->nextname = MEMNEXT(memh->next)->name;
803                 else
804                         MEMNEXT(memh->prev)->nextname = NULL;
805         }
806
807         totblock--;
808         mem_in_use -= memh->len;
809
810 #ifdef DEBUG_MEMDUPLINAME
811         if (memh->need_free_name)
812                 free((char *) memh->name);
813 #endif
814
815         if (memh->mmap) {
816                 mmap_in_use -= memh->len;
817                 if (munmap(memh, memh->len + sizeof(MemHead) + sizeof(MemTail)))
818                         printf("Couldn't unmap memory %s\n", memh->name);
819         }
820         else {
821                 if (malloc_debug_memset && memh->len)
822                         memset(memh + 1, 255, memh->len);
823                 free(memh);
824         }
825 }
826
827 static void MemorY_ErroR(const char *block, const char *error)
828 {
829         print_error("Memoryblock %s: %s\n", block, error);
830
831 #ifdef WITH_ASSERT_ABORT
832         abort();
833 #endif
834 }
835
836 static const char *check_memlist(MemHead *memh)
837 {
838         MemHead *forw, *back, *forwok, *backok;
839         const char *name;
840
841         forw = membase->first;
842         if (forw) forw = MEMNEXT(forw);
843         forwok = NULL;
844         while (forw) {
845                 if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) break;
846                 forwok = forw;
847                 if (forw->next) forw = MEMNEXT(forw->next);
848                 else forw = NULL;
849         }
850
851         back = (MemHead *) membase->last;
852         if (back) back = MEMNEXT(back);
853         backok = NULL;
854         while (back) {
855                 if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) break;
856                 backok = back;
857                 if (back->prev) back = MEMNEXT(back->prev);
858                 else back = NULL;
859         }
860
861         if (forw != back) return ("MORE THAN 1 MEMORYBLOCK CORRUPT");
862
863         if (forw == NULL && back == NULL) {
864                 /* no wrong headers found then but in search of memblock */
865
866                 forw = membase->first;
867                 if (forw) forw = MEMNEXT(forw);
868                 forwok = NULL;
869                 while (forw) {
870                         if (forw == memh) break;
871                         if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) break;
872                         forwok = forw;
873                         if (forw->next) forw = MEMNEXT(forw->next);
874                         else forw = NULL;
875                 }
876                 if (forw == NULL) return NULL;
877
878                 back = (MemHead *) membase->last;
879                 if (back) back = MEMNEXT(back);
880                 backok = NULL;
881                 while (back) {
882                         if (back == memh) break;
883                         if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) break;
884                         backok = back;
885                         if (back->prev) back = MEMNEXT(back->prev);
886                         else back = NULL;
887                 }
888         }
889
890         if (forwok) name = forwok->nextname;
891         else name = "No name found";
892
893         if (forw == memh) {
894                 /* to be sure but this block is removed from the list */
895                 if (forwok) {
896                         if (backok) {
897                                 forwok->next = (MemHead *)&backok->next;
898                                 backok->prev = (MemHead *)&forwok->next;
899                                 forwok->nextname = backok->name;
900                         }
901                         else {
902                                 forwok->next = NULL;
903                                 membase->last = (struct localLink *) &forwok->next;
904                         }
905                 }
906                 else {
907                         if (backok) {
908                                 backok->prev = NULL;
909                                 membase->first = &backok->next;
910                         }
911                         else {
912                                 membase->first = membase->last = NULL;
913                         }
914                 }
915         }
916         else {
917                 MemorY_ErroR(name, "Additional error in header");
918                 return("Additional error in header");
919         }
920
921         return(name);
922 }
923
924 uintptr_t MEM_get_peak_memory(void)
925 {
926         uintptr_t _peak_mem;
927
928         mem_lock_thread();
929         _peak_mem = peak_mem;
930         mem_unlock_thread();
931
932         return _peak_mem;
933 }
934
935 void MEM_reset_peak_memory(void)
936 {
937         mem_lock_thread();
938         peak_mem = 0;
939         mem_unlock_thread();
940 }
941
942 uintptr_t MEM_get_memory_in_use(void)
943 {
944         uintptr_t _mem_in_use;
945
946         mem_lock_thread();
947         _mem_in_use = mem_in_use;
948         mem_unlock_thread();
949
950         return _mem_in_use;
951 }
952
953 uintptr_t MEM_get_mapped_memory_in_use(void)
954 {
955         uintptr_t _mmap_in_use;
956
957         mem_lock_thread();
958         _mmap_in_use = mmap_in_use;
959         mem_unlock_thread();
960
961         return _mmap_in_use;
962 }
963
964 int MEM_get_memory_blocks_in_use(void)
965 {
966         int _totblock;
967
968         mem_lock_thread();
969         _totblock = totblock;
970         mem_unlock_thread();
971
972         return _totblock;
973 }
974
975 #ifndef NDEBUG
976 const char *MEM_name_ptr(void *vmemh)
977 {
978         if (vmemh) {
979                 MemHead *memh = vmemh;
980                 memh--;
981                 return memh->name;
982         }
983         else {
984                 return "MEM_name_ptr(NULL)";
985         }
986 }
987 #endif  /* NDEBUG */