Fix #36384: Inconsistent curve fill behaviour
[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         if (thread_unlock_callback)
284                 thread_unlock_callback();
285 }
286
287 int MEM_check_memory_integrity(void)
288 {
289         const char *err_val = NULL;
290         MemHead *listend;
291         /* check_memlist starts from the front, and runs until it finds
292          * the requested chunk. For this test, that's the last one. */
293         listend = membase->last;
294         
295         err_val = check_memlist(listend);
296
297         return (err_val != NULL);
298 }
299
300
301 void MEM_set_error_callback(void (*func)(const char *))
302 {
303         error_callback = func;
304 }
305
306 void MEM_set_lock_callback(void (*lock)(void), void (*unlock)(void))
307 {
308         thread_lock_callback = lock;
309         thread_unlock_callback = unlock;
310 }
311
312 void MEM_set_memory_debug(void)
313 {
314         malloc_debug_memset = 1;
315 }
316
317 size_t MEM_allocN_len(const void *vmemh)
318 {
319         if (vmemh) {
320                 const MemHead *memh = vmemh;
321         
322                 memh--;
323                 return memh->len;
324         }
325         else {
326                 return 0;
327         }
328 }
329
330 void *MEM_dupallocN(const void *vmemh)
331 {
332         void *newp = NULL;
333         
334         if (vmemh) {
335                 const MemHead *memh = vmemh;
336                 memh--;
337
338 #ifndef DEBUG_MEMDUPLINAME
339                 if (memh->mmap)
340                         newp = MEM_mapallocN(memh->len, "dupli_mapalloc");
341                 else
342                         newp = MEM_mallocN(memh->len, "dupli_alloc");
343
344                 if (newp == NULL) return NULL;
345 #else
346                 {
347                         MemHead *nmemh;
348                         char *name = malloc(strlen(memh->name) + 24);
349
350                         if (memh->mmap) {
351                                 sprintf(name, "%s %s", "dupli_mapalloc", memh->name);
352                                 newp = MEM_mapallocN(memh->len, name);
353                         }
354                         else {
355                                 sprintf(name, "%s %s", "dupli_alloc", memh->name);
356                                 newp = MEM_mallocN(memh->len, name);
357                         }
358
359                         if (newp == NULL) return NULL;
360
361                         nmemh = newp;
362                         nmemh--;
363
364                         nmemh->need_free_name = 1;
365                 }
366 #endif
367
368                 memcpy(newp, vmemh, memh->len);
369         }
370
371         return newp;
372 }
373
374 void *MEM_reallocN_id(void *vmemh, size_t len, const char *str)
375 {
376         void *newp = NULL;
377         
378         if (vmemh) {
379                 MemHead *memh = vmemh;
380                 memh--;
381
382                 newp = MEM_mallocN(len, memh->name);
383                 if (newp) {
384                         if (len < memh->len) {
385                                 /* shrink */
386                                 memcpy(newp, vmemh, len);
387                         }
388                         else {
389                                 /* grow (or remain same size) */
390                                 memcpy(newp, vmemh, memh->len);
391                         }
392                 }
393
394                 MEM_freeN(vmemh);
395         }
396         else {
397                 newp = MEM_mallocN(len, str);
398         }
399
400         return newp;
401 }
402
403 void *MEM_recallocN_id(void *vmemh, size_t len, const char *str)
404 {
405         void *newp = NULL;
406
407         if (vmemh) {
408                 MemHead *memh = vmemh;
409                 memh--;
410
411                 newp = MEM_mallocN(len, memh->name);
412                 if (newp) {
413                         if (len < memh->len) {
414                                 /* shrink */
415                                 memcpy(newp, vmemh, len);
416                         }
417                         else {
418                                 memcpy(newp, vmemh, memh->len);
419
420                                 if (len > memh->len) {
421                                         /* grow */
422                                         /* zero new bytes */
423                                         memset(((char *)newp) + memh->len, 0, len - memh->len);
424                                 }
425                         }
426                 }
427
428                 MEM_freeN(vmemh);
429         }
430         else {
431                 newp = MEM_callocN(len, str);
432         }
433
434         return newp;
435 }
436
437 #ifdef DEBUG_BACKTRACE
438 #  if defined(__linux__) || defined(__APPLE__)
439 static void make_memhead_backtrace(MemHead *memh)
440 {
441         memh->backtrace_size = backtrace(memh->backtrace, BACKTRACE_SIZE);
442 }
443
444 static void print_memhead_backtrace(MemHead *memh)
445 {
446         char **strings;
447         int i;
448
449         strings = backtrace_symbols(memh->backtrace, memh->backtrace_size);
450         for (i = 0; i < memh->backtrace_size; i++) {
451                 print_error("  %s\n", strings[i]);
452         }
453
454         free(strings);
455 }
456 #  else
457 static void make_memhead_backtrace(MemHead *memh)
458 {
459         (void) memh;  /* Ignored. */
460 }
461
462 static void print_memhead_backtrace(MemHead *memh)
463 {
464         (void) memh;  /* Ignored. */
465 }
466 #  endif  /* defined(__linux__) || defined(__APPLE__) */
467 #endif  /* DEBUG_BACKTRACE */
468
469 static void make_memhead_header(MemHead *memh, size_t len, const char *str)
470 {
471         MemTail *memt;
472         
473         memh->tag1 = MEMTAG1;
474         memh->name = str;
475         memh->nextname = NULL;
476         memh->len = len;
477         memh->mmap = 0;
478         memh->tag2 = MEMTAG2;
479
480 #ifdef DEBUG_MEMDUPLINAME
481         memh->need_free_name = 0;
482 #endif
483
484 #ifdef DEBUG_BACKTRACE
485         make_memhead_backtrace(memh);
486 #endif
487
488         memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + len);
489         memt->tag3 = MEMTAG3;
490         
491         addtail(membase, &memh->next);
492         if (memh->next) {
493                 memh->nextname = MEMNEXT(memh->next)->name;
494         }
495         
496         totblock++;
497         mem_in_use += len;
498
499         peak_mem = mem_in_use > peak_mem ? mem_in_use : peak_mem;
500 }
501
502 void *MEM_mallocN(size_t len, const char *str)
503 {
504         MemHead *memh;
505
506         mem_lock_thread();
507
508         len = (len + 3) & ~3;   /* allocate in units of 4 */
509         
510         memh = (MemHead *)malloc(len + sizeof(MemHead) + sizeof(MemTail));
511
512         if (memh) {
513                 make_memhead_header(memh, len, str);
514                 mem_unlock_thread();
515                 if (malloc_debug_memset && len)
516                         memset(memh + 1, 255, len);
517
518 #ifdef DEBUG_MEMCOUNTER
519                 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
520                         memcount_raise(__func__);
521                 memh->_count = _mallocn_count++;
522 #endif
523                 return (++memh);
524         }
525         mem_unlock_thread();
526         print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
527                     SIZET_ARG(len), str, (unsigned int) mem_in_use);
528         return NULL;
529 }
530
531 void *MEM_callocN(size_t len, const char *str)
532 {
533         MemHead *memh;
534
535         mem_lock_thread();
536
537         len = (len + 3) & ~3;   /* allocate in units of 4 */
538
539         memh = (MemHead *)calloc(len + sizeof(MemHead) + sizeof(MemTail), 1);
540
541         if (memh) {
542                 make_memhead_header(memh, len, str);
543                 mem_unlock_thread();
544 #ifdef DEBUG_MEMCOUNTER
545                 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
546                         memcount_raise(__func__);
547                 memh->_count = _mallocn_count++;
548 #endif
549                 return (++memh);
550         }
551         mem_unlock_thread();
552         print_error("Calloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
553                     SIZET_ARG(len), str, (unsigned int) mem_in_use);
554         return NULL;
555 }
556
557 /* note; mmap returns zero'd memory */
558 void *MEM_mapallocN(size_t len, const char *str)
559 {
560         MemHead *memh;
561
562         mem_lock_thread();
563         
564         len = (len + 3) & ~3;   /* allocate in units of 4 */
565
566         memh = mmap(NULL, len + sizeof(MemHead) + sizeof(MemTail),
567                     PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
568
569         if (memh != (MemHead *)-1) {
570                 make_memhead_header(memh, len, str);
571                 memh->mmap = 1;
572                 mmap_in_use += len;
573                 peak_mem = mmap_in_use > peak_mem ? mmap_in_use : peak_mem;
574                 mem_unlock_thread();
575 #ifdef DEBUG_MEMCOUNTER
576                 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
577                         memcount_raise(__func__);
578                 memh->_count = _mallocn_count++;
579 #endif
580                 return (++memh);
581         }
582         else {
583                 mem_unlock_thread();
584                 print_error("Mapalloc returns null, fallback to regular malloc: "
585                             "len=" SIZET_FORMAT " in %s, total %u\n",
586                             SIZET_ARG(len), str, (unsigned int) mmap_in_use);
587                 return MEM_callocN(len, str);
588         }
589 }
590
591 /* Memory statistics print */
592 typedef struct MemPrintBlock {
593         const char *name;
594         uintptr_t len;
595         int items;
596 } MemPrintBlock;
597
598 static int compare_name(const void *p1, const void *p2)
599 {
600         const MemPrintBlock *pb1 = (const MemPrintBlock *)p1;
601         const MemPrintBlock *pb2 = (const MemPrintBlock *)p2;
602
603         return strcmp(pb1->name, pb2->name);
604 }
605
606 static int compare_len(const void *p1, const void *p2)
607 {
608         const MemPrintBlock *pb1 = (const MemPrintBlock *)p1;
609         const MemPrintBlock *pb2 = (const MemPrintBlock *)p2;
610
611         if (pb1->len < pb2->len)
612                 return 1;
613         else if (pb1->len == pb2->len)
614                 return 0;
615         else
616                 return -1;
617 }
618
619 void MEM_printmemlist_stats(void)
620 {
621         MemHead *membl;
622         MemPrintBlock *pb, *printblock;
623         int totpb, a, b;
624
625         mem_lock_thread();
626
627         /* put memory blocks into array */
628         printblock = malloc(sizeof(MemPrintBlock) * totblock);
629
630         pb = printblock;
631         totpb = 0;
632
633         membl = membase->first;
634         if (membl) membl = MEMNEXT(membl);
635
636         while (membl) {
637                 pb->name = membl->name;
638                 pb->len = membl->len;
639                 pb->items = 1;
640
641                 totpb++;
642                 pb++;
643
644                 if (membl->next)
645                         membl = MEMNEXT(membl->next);
646                 else break;
647         }
648
649         /* sort by name and add together blocks with the same name */
650         qsort(printblock, totpb, sizeof(MemPrintBlock), compare_name);
651         for (a = 0, b = 0; a < totpb; a++) {
652                 if (a == b) {
653                         continue;
654                 }
655                 else if (strcmp(printblock[a].name, printblock[b].name) == 0) {
656                         printblock[b].len += printblock[a].len;
657                         printblock[b].items++;
658                 }
659                 else {
660                         b++;
661                         memcpy(&printblock[b], &printblock[a], sizeof(MemPrintBlock));
662                 }
663         }
664         totpb = b + 1;
665
666         /* sort by length and print */
667         qsort(printblock, totpb, sizeof(MemPrintBlock), compare_len);
668         printf("\ntotal memory len: %.3f MB\n",
669                (double)mem_in_use / (double)(1024 * 1024));
670         printf("peak memory len: %.3f MB\n",
671                (double)peak_mem / (double)(1024 * 1024));
672         printf(" ITEMS TOTAL-MiB AVERAGE-KiB TYPE\n");
673         for (a = 0, pb = printblock; a < totpb; a++, pb++) {
674                 printf("%6d (%8.3f  %8.3f) %s\n",
675                        pb->items, (double)pb->len / (double)(1024 * 1024),
676                        (double)pb->len / 1024.0 / (double)pb->items, pb->name);
677         }
678         free(printblock);
679         
680         mem_unlock_thread();
681
682 #if 0 /* GLIBC only */
683         malloc_stats();
684 #endif
685 }
686
687 static const char mem_printmemlist_pydict_script[] =
688 "mb_userinfo = {}\n"
689 "totmem = 0\n"
690 "for mb_item in membase:\n"
691 "    mb_item_user_size = mb_userinfo.setdefault(mb_item['name'], [0,0])\n"
692 "    mb_item_user_size[0] += 1 # Add a user\n"
693 "    mb_item_user_size[1] += mb_item['len'] # Increment the size\n"
694 "    totmem += mb_item['len']\n"
695 "print('(membase) items:', len(membase), '| unique-names:',\n"
696 "      len(mb_userinfo), '| total-mem:', totmem)\n"
697 "mb_userinfo_sort = list(mb_userinfo.items())\n"
698 "for sort_name, sort_func in (('size', lambda a: -a[1][1]),\n"
699 "                             ('users', lambda a: -a[1][0]),\n"
700 "                             ('name', lambda a: a[0])):\n"
701 "    print('\\nSorting by:', sort_name)\n"
702 "    mb_userinfo_sort.sort(key = sort_func)\n"
703 "    for item in mb_userinfo_sort:\n"
704 "        print('name:%%s, users:%%i, len:%%i' %%\n"
705 "              (item[0], item[1][0], item[1][1]))\n";
706
707 /* Prints in python syntax for easy */
708 static void MEM_printmemlist_internal(int pydict)
709 {
710         MemHead *membl;
711
712         mem_lock_thread();
713
714         membl = membase->first;
715         if (membl) membl = MEMNEXT(membl);
716         
717         if (pydict) {
718                 print_error("# membase_debug.py\n");
719                 print_error("membase = [\n");
720         }
721         while (membl) {
722                 if (pydict) {
723                         fprintf(stderr,
724                                 "    {'len':" SIZET_FORMAT ", "
725                                 "'name':'''%s''', "
726                                 "'pointer':'%p'},\n",
727                                 SIZET_ARG(membl->len), membl->name, (void *)(membl + 1));
728                 }
729                 else {
730 #ifdef DEBUG_MEMCOUNTER
731                         print_error("%s len: " SIZET_FORMAT " %p, count: %d\n",
732                                     membl->name, SIZET_ARG(membl->len), membl + 1,
733                                     membl->_count);
734 #else
735                         print_error("%s len: " SIZET_FORMAT " %p\n",
736                                     membl->name, SIZET_ARG(membl->len), membl + 1);
737 #endif
738 #ifdef DEBUG_BACKTRACE
739                         print_memhead_backtrace(membl);
740 #endif
741                 }
742                 if (membl->next)
743                         membl = MEMNEXT(membl->next);
744                 else break;
745         }
746         if (pydict) {
747                 fprintf(stderr, "]\n\n");
748                 fprintf(stderr, mem_printmemlist_pydict_script);
749         }
750         
751         mem_unlock_thread();
752 }
753
754 void MEM_callbackmemlist(void (*func)(void *))
755 {
756         MemHead *membl;
757
758         mem_lock_thread();
759
760         membl = membase->first;
761         if (membl) membl = MEMNEXT(membl);
762
763         while (membl) {
764                 func(membl + 1);
765                 if (membl->next)
766                         membl = MEMNEXT(membl->next);
767                 else break;
768         }
769
770         mem_unlock_thread();
771 }
772
773 #if 0
774 short MEM_testN(void *vmemh)
775 {
776         MemHead *membl;
777
778         mem_lock_thread();
779
780         membl = membase->first;
781         if (membl) membl = MEMNEXT(membl);
782
783         while (membl) {
784                 if (vmemh == membl + 1) {
785                         mem_unlock_thread();
786                         return 1;
787                 }
788
789                 if (membl->next)
790                         membl = MEMNEXT(membl->next);
791                 else break;
792         }
793
794         mem_unlock_thread();
795
796         print_error("Memoryblock %p: pointer not in memlist\n", vmemh);
797         return 0;
798 }
799 #endif
800
801 void MEM_printmemlist(void)
802 {
803         MEM_printmemlist_internal(0);
804 }
805 void MEM_printmemlist_pydict(void)
806 {
807         MEM_printmemlist_internal(1);
808 }
809
810 void MEM_freeN(void *vmemh)
811 {
812         MemTail *memt;
813         MemHead *memh = vmemh;
814         const char *name;
815
816         if (memh == NULL) {
817                 MemorY_ErroR("free", "attempt to free NULL pointer");
818                 /* print_error(err_stream, "%d\n", (memh+4000)->tag1); */
819                 return;
820         }
821
822         if (sizeof(intptr_t) == 8) {
823                 if (((intptr_t) memh) & 0x7) {
824                         MemorY_ErroR("free", "attempt to free illegal pointer");
825                         return;
826                 }
827         }
828         else {
829                 if (((intptr_t) memh) & 0x3) {
830                         MemorY_ErroR("free", "attempt to free illegal pointer");
831                         return;
832                 }
833         }
834         
835         memh--;
836         if (memh->tag1 == MEMFREE && memh->tag2 == MEMFREE) {
837                 MemorY_ErroR(memh->name, "double free");
838                 return;
839         }
840
841         mem_lock_thread();
842         if ((memh->tag1 == MEMTAG1) &&
843             (memh->tag2 == MEMTAG2) &&
844             ((memh->len & 0x3) == 0))
845         {
846                 memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + memh->len);
847                 if (memt->tag3 == MEMTAG3) {
848                         
849                         memh->tag1 = MEMFREE;
850                         memh->tag2 = MEMFREE;
851                         memt->tag3 = MEMFREE;
852                         /* after tags !!! */
853                         rem_memblock(memh);
854
855                         mem_unlock_thread();
856
857                         return;
858                 }
859                 MemorY_ErroR(memh->name, "end corrupt");
860                 name = check_memlist(memh);
861                 if (name != NULL) {
862                         if (name != memh->name) MemorY_ErroR(name, "is also corrupt");
863                 }
864         }
865         else {
866                 name = check_memlist(memh);
867                 if (name == NULL)
868                         MemorY_ErroR("free", "pointer not in memlist");
869                 else
870                         MemorY_ErroR(name, "error in header");
871         }
872
873         totblock--;
874         /* here a DUMP should happen */
875
876         mem_unlock_thread();
877
878         return;
879 }
880
881 /* --------------------------------------------------------------------- */
882 /* local functions                                                       */
883 /* --------------------------------------------------------------------- */
884
885 static void addtail(volatile localListBase *listbase, void *vlink)
886 {
887         struct localLink *link = vlink;
888
889         /* for a generic API error checks here is fine but
890          * the limited use here they will never be NULL */
891 #if 0
892         if (link == NULL) return;
893         if (listbase == NULL) return;
894 #endif
895
896         link->next = NULL;
897         link->prev = listbase->last;
898
899         if (listbase->last) ((struct localLink *)listbase->last)->next = link;
900         if (listbase->first == NULL) listbase->first = link;
901         listbase->last = link;
902 }
903
904 static void remlink(volatile localListBase *listbase, void *vlink)
905 {
906         struct localLink *link = vlink;
907
908         /* for a generic API error checks here is fine but
909          * the limited use here they will never be NULL */
910 #if 0
911         if (link == NULL) return;
912         if (listbase == NULL) return;
913 #endif
914
915         if (link->next) link->next->prev = link->prev;
916         if (link->prev) link->prev->next = link->next;
917
918         if (listbase->last == link) listbase->last = link->prev;
919         if (listbase->first == link) listbase->first = link->next;
920 }
921
922 static void rem_memblock(MemHead *memh)
923 {
924         remlink(membase, &memh->next);
925         if (memh->prev) {
926                 if (memh->next)
927                         MEMNEXT(memh->prev)->nextname = MEMNEXT(memh->next)->name;
928                 else
929                         MEMNEXT(memh->prev)->nextname = NULL;
930         }
931
932         totblock--;
933         mem_in_use -= memh->len;
934
935 #ifdef DEBUG_MEMDUPLINAME
936         if (memh->need_free_name)
937                 free((char *) memh->name);
938 #endif
939
940         if (memh->mmap) {
941                 mmap_in_use -= memh->len;
942                 if (munmap(memh, memh->len + sizeof(MemHead) + sizeof(MemTail)))
943                         printf("Couldn't unmap memory %s\n", memh->name);
944         }
945         else {
946                 if (malloc_debug_memset && memh->len)
947                         memset(memh + 1, 255, memh->len);
948                 free(memh);
949         }
950 }
951
952 static void MemorY_ErroR(const char *block, const char *error)
953 {
954         print_error("Memoryblock %s: %s\n", block, error);
955
956 #ifdef WITH_ASSERT_ABORT
957         abort();
958 #endif
959 }
960
961 static const char *check_memlist(MemHead *memh)
962 {
963         MemHead *forw, *back, *forwok, *backok;
964         const char *name;
965
966         forw = membase->first;
967         if (forw) forw = MEMNEXT(forw);
968         forwok = NULL;
969         while (forw) {
970                 if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) break;
971                 forwok = forw;
972                 if (forw->next) forw = MEMNEXT(forw->next);
973                 else forw = NULL;
974         }
975
976         back = (MemHead *) membase->last;
977         if (back) back = MEMNEXT(back);
978         backok = NULL;
979         while (back) {
980                 if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) break;
981                 backok = back;
982                 if (back->prev) back = MEMNEXT(back->prev);
983                 else back = NULL;
984         }
985
986         if (forw != back) return ("MORE THAN 1 MEMORYBLOCK CORRUPT");
987
988         if (forw == NULL && back == NULL) {
989                 /* no wrong headers found then but in search of memblock */
990
991                 forw = membase->first;
992                 if (forw) forw = MEMNEXT(forw);
993                 forwok = NULL;
994                 while (forw) {
995                         if (forw == memh) break;
996                         if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) break;
997                         forwok = forw;
998                         if (forw->next) forw = MEMNEXT(forw->next);
999                         else forw = NULL;
1000                 }
1001                 if (forw == NULL) return NULL;
1002
1003                 back = (MemHead *) membase->last;
1004                 if (back) back = MEMNEXT(back);
1005                 backok = NULL;
1006                 while (back) {
1007                         if (back == memh) break;
1008                         if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) break;
1009                         backok = back;
1010                         if (back->prev) back = MEMNEXT(back->prev);
1011                         else back = NULL;
1012                 }
1013         }
1014
1015         if (forwok) name = forwok->nextname;
1016         else name = "No name found";
1017
1018         if (forw == memh) {
1019                 /* to be sure but this block is removed from the list */
1020                 if (forwok) {
1021                         if (backok) {
1022                                 forwok->next = (MemHead *)&backok->next;
1023                                 backok->prev = (MemHead *)&forwok->next;
1024                                 forwok->nextname = backok->name;
1025                         }
1026                         else {
1027                                 forwok->next = NULL;
1028                                 membase->last = (struct localLink *) &forwok->next;
1029                         }
1030                 }
1031                 else {
1032                         if (backok) {
1033                                 backok->prev = NULL;
1034                                 membase->first = &backok->next;
1035                         }
1036                         else {
1037                                 membase->first = membase->last = NULL;
1038                         }
1039                 }
1040         }
1041         else {
1042                 MemorY_ErroR(name, "Additional error in header");
1043                 return("Additional error in header");
1044         }
1045
1046         return(name);
1047 }
1048
1049 uintptr_t MEM_get_peak_memory(void)
1050 {
1051         uintptr_t _peak_mem;
1052
1053         mem_lock_thread();
1054         _peak_mem = peak_mem;
1055         mem_unlock_thread();
1056
1057         return _peak_mem;
1058 }
1059
1060 void MEM_reset_peak_memory(void)
1061 {
1062         mem_lock_thread();
1063         peak_mem = 0;
1064         mem_unlock_thread();
1065 }
1066
1067 uintptr_t MEM_get_memory_in_use(void)
1068 {
1069         uintptr_t _mem_in_use;
1070
1071         mem_lock_thread();
1072         _mem_in_use = mem_in_use;
1073         mem_unlock_thread();
1074
1075         return _mem_in_use;
1076 }
1077
1078 uintptr_t MEM_get_mapped_memory_in_use(void)
1079 {
1080         uintptr_t _mmap_in_use;
1081
1082         mem_lock_thread();
1083         _mmap_in_use = mmap_in_use;
1084         mem_unlock_thread();
1085
1086         return _mmap_in_use;
1087 }
1088
1089 int MEM_get_memory_blocks_in_use(void)
1090 {
1091         int _totblock;
1092
1093         mem_lock_thread();
1094         _totblock = totblock;
1095         mem_unlock_thread();
1096
1097         return _totblock;
1098 }
1099
1100 #ifndef NDEBUG
1101 const char *MEM_name_ptr(void *vmemh)
1102 {
1103         if (vmemh) {
1104                 MemHead *memh = vmemh;
1105                 memh--;
1106                 return memh->name;
1107         }
1108         else {
1109                 return "MEM_name_ptr(NULL)";
1110         }
1111 }
1112 #endif  /* NDEBUG */
1113
1114 #else  /* !WITH_GUARDEDALLOC */
1115
1116 #ifdef __GNUC__
1117 #  define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
1118 #else
1119 #  define UNUSED(x) UNUSED_ ## x
1120 #endif
1121
1122 #include <malloc.h>
1123
1124 size_t MEM_allocN_len(const void *vmemh)
1125 {
1126         return malloc_usable_size((void *)vmemh);
1127 }
1128
1129 void MEM_freeN(void *vmemh)
1130 {
1131         free(vmemh);
1132 }
1133
1134 void *MEM_dupallocN(const void *vmemh)
1135 {
1136         void *newp = NULL;
1137         if (vmemh) {
1138                 const size_t prev_size = MEM_allocN_len(vmemh);
1139                 newp = malloc(prev_size);
1140                 memcpy(newp, vmemh, prev_size);
1141         }
1142         return newp;
1143 }
1144
1145 void *MEM_reallocN_id(void *vmemh, size_t len, const char *UNUSED(str))
1146 {
1147         return realloc(vmemh, len);
1148 }
1149
1150 void *MEM_recallocN_id(void *vmemh, size_t len, const char *UNUSED(str))
1151 {
1152         void *newp = NULL;
1153
1154         if (vmemh) {
1155                 size_t vmemh_len = MEM_allocN_len(vmemh);
1156                 newp = malloc(len);
1157                 if (newp) {
1158                         if (len < vmemh_len) {
1159                                 /* shrink */
1160                                 memcpy(newp, vmemh, len);
1161                         }
1162                         else {
1163                                 memcpy(newp, vmemh, vmemh_len);
1164
1165                                 if (len > vmemh_len) {
1166                                         /* grow */
1167                                         /* zero new bytes */
1168                                         memset(((char *)newp) + vmemh_len, 0, len - vmemh_len);
1169                                 }
1170                         }
1171                 }
1172
1173                 free(vmemh);
1174         }
1175         else {
1176                 newp = calloc(1, len);
1177         }
1178
1179         return newp;
1180 }
1181
1182 void *MEM_callocN(size_t len, const char *UNUSED(str))
1183 {
1184         return calloc(1, len);
1185 }
1186
1187 void *MEM_mallocN(size_t len, const char *UNUSED(str))
1188 {
1189         return malloc(len);
1190 }
1191
1192 void *MEM_mapallocN(size_t len, const char *UNUSED(str))
1193 {
1194         /* could us mmap */
1195         return calloc(1, len);
1196 }
1197
1198 void MEM_printmemlist_pydict(void) {}
1199 void MEM_printmemlist(void) {}
1200
1201 /* unused */
1202 void MEM_callbackmemlist(void (*func)(void *))
1203 {
1204         (void)func;
1205 }
1206
1207 void MEM_printmemlist_stats(void) {}
1208
1209 void MEM_set_error_callback(void (*func)(const char *))
1210 {
1211         (void)func;
1212 }
1213
1214 int MEM_check_memory_integrity(void)
1215 {
1216         return 1;
1217 }
1218
1219 void MEM_set_lock_callback(void (*lock)(void), void (*unlock)(void))
1220 {
1221         (void)lock;
1222         (void)unlock;
1223 }
1224
1225 void MEM_set_memory_debug(void) {}
1226
1227 uintptr_t MEM_get_memory_in_use(void)
1228 {
1229         struct mallinfo mi;
1230         mi = mallinfo();
1231         return mi.uordblks;
1232 }
1233
1234 uintptr_t MEM_get_mapped_memory_in_use(void)
1235 {
1236         return MEM_get_memory_in_use();
1237 }
1238
1239 int MEM_get_memory_blocks_in_use(void)
1240 {
1241         struct mallinfo mi;
1242         mi = mallinfo();
1243         return mi.smblks + mi.hblks;
1244 }
1245
1246 /* dummy */
1247 void MEM_reset_peak_memory(void) {}
1248
1249 uintptr_t MEM_get_peak_memory(void)
1250 {
1251         return MEM_get_memory_in_use();
1252 }
1253
1254 #endif  /* WITH_GUARDEDALLOC */