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