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