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