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